Bläddra i källkod

添加新页面

GTong 9 månader sedan
förälder
incheckning
57e8720ecc

+ 623 - 0
static/license_info/license_info.css

@@ -0,0 +1,623 @@
+/* 全局页面样式 */
+body {
+    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
+    margin: 0;
+    padding: 0;
+    background-color: #ffffff; /* iOS风格的浅灰背景 */
+}
+
+/* 头部样式 */
+header {
+    background-color: #ffffff;
+    color: #333;
+  
+    height: 50px; /* 明确指定较小的高度 */
+    text-align: center;
+    align-items: center; /* 垂直居中内容 */
+    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); /* 轻微阴影 */
+}
+
+/* 容器布局:包含侧边栏和主内容 */
+.container {
+    display: flex;
+    /*height: calc(100vh - 70px);  视口高度减去头部高度 */
+    height: 100vh; 
+}
+
+/* 侧边栏样式 */
+/* 固定侧边栏 */
+aside {
+
+    top: 0;
+    left: 0;
+    width: 200px; /* 侧边栏的宽度 */
+    height: 100vh; /* 高度占满视口 */
+    background-color: #2c3e50; /* 背景颜色 */
+    color: #ffffff;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    padding-top: 20px;
+    box-shadow: 2px 0 5px rgba(0, 0, 0, 0.1); /* 添加阴影效果 */
+ 
+}
+
+/* 侧边栏内的导航菜单 */
+nav ul {
+    list-style: none; /* 去除默认的列表样式 */
+    padding: 0;
+    width: 100%; /* 使菜单项占满宽度 */
+}
+
+nav ul li {
+    width: 100%; /* 每个菜单项占满整个宽度 */
+    margin-bottom: 10px; /* 菜单项之间的间距 */
+}
+
+nav ul li a {
+    display: flex; /* 使图标和文字并排显示 */
+    align-items: center; /* 垂直居中 */
+    padding: 15px 20px; /* 内边距 */
+    color: #ffffff; /* 文字颜色 */
+    text-decoration: none; /* 去掉下划线 */
+    font-size: 16px; /* 字体大小 */
+    font-weight: 500; /* 字体粗细 */
+    transition: background 0.3s; /* 背景色的过渡效果 */
+}
+
+nav ul li a.active,
+nav ul li a:hover {
+    background-color: #1abc9c; /* 激活和悬停状态的背景颜色 */
+    color: #ffffff; /* 保持文字为白色 */
+    border-radius: 5px; /* 添加圆角 */
+}
+
+/* 图标样式(假设使用 FontAwesome 或其他图标库) */
+nav ul li a i {
+    font-size: 20px; /* 图标大小 */
+    margin-right: 10px; /* 图标和文字的间距 */
+}
+
+
+/* 主内容区域 */
+main {
+    flex: 1; /* 占据剩余宽度 */
+    padding: 20px;
+    overflow-y: auto; /* 启用垂直滚动 */
+    background-color: #ecf0f1; /* 浅灰色背景 */
+}
+
+
+
+/* 瀑布流容器样式,每行最多显示4个卡片 */
+.license-info-container {
+    display: flex;
+    flex-wrap: wrap;
+    gap: 20px; /* 卡片之间的间距 */
+}
+
+/* 卡片样式 */
+.license-info-card {
+    position: relative;
+    width: calc(25% - 20px); /* 每行4个卡片,减去间距 */
+    background-color: #ffffff; /* 白色背景 */
+    border-radius: 15px; /* iOS风格的圆角 */
+    box-shadow: 0 10px 20px rgba(0, 0, 0, 0.05); /* 轻微柔和的阴影 */
+    overflow: hidden;
+    transition: transform 0.3s ease-in-out, box-shadow 0.3s ease-in-out; /* 过渡效果 */
+    margin-bottom: 20px;
+    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; /* iOS 系统字体 */
+    padding-bottom: 20px; /* 添加底部填充,以防止内容溢出 */
+}
+
+/* 悬停效果 */
+.license-info-card:hover {
+    transform: translateY(-3px); /* 悬停时轻微向上浮动 */
+    box-shadow: 0 15px 25px rgba(0, 0, 0, 0.1); /* 悬停时增加阴影效果 */
+}
+
+/* 卡片顶部蓝色区域 */
+.license-info-card-header {
+    background-color: #007aff; /* iOS风格的蓝色 */
+    height: 50px; /* 顶部蓝色部分的高度 */
+    border-top-left-radius: 15px;
+    border-top-right-radius: 15px;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    padding: 10px;
+    text-align: center;
+    color: white; /* 项目名称字体颜色为白色 */
+}
+
+/* 标题样式:现在位于蓝色区域 */
+.card-title {
+    font-size: 20px; /* 标题字体加大 */
+    font-weight: 600; /* 标题字体加粗 */
+    color: white; /* 项目名称字体颜色为白色,和背景颜色形成对比 */
+    margin: 0;
+    line-height: 1.2; /* 行间距 */
+}
+
+/* 副标题和其他文本样式 */
+.license-info-card-content {
+    padding: 20px;
+    color: #333;
+}
+
+.card-text {
+    margin: 10px 0; /* 增加字段之间的间距 */
+    font-size: 18px; /* 提高字体大小 */
+    color: #3a3a3c; /* 中灰色,符合iOS风格 */
+    line-height: 1.6; /* 行间距调整,确保更好的可读性 */
+}
+
+/* 许可证状态的颜色 */
+.license-status-green {
+    color: #34c759; /* iOS的绿色 */
+}
+
+.license-status-yellow {
+    color: #ffcc00; /* iOS的黄色 */
+}
+
+.license-status-red {
+    color: #ff3b30; /* iOS的红色 */
+}
+
+
+
+/*这部分代码仅在设备屏幕宽度小于或等于 768 像素时生效
+*/
+@media (max-width: 768px) {
+  /* 小屏幕下卡片调整为每行显示2个 */
+    .license-info-card {
+        width: calc(50% - 20px);
+        height: auto; /* 调整小屏幕下的高度 */
+    }
+}
+
+
+/*-----------------*/
+
+/* 响应式设计,适配小屏幕 */
+@media (max-width: 768px) {
+    .license-info-modal-content.show {
+        width: 90vw; /* 在小屏设备中宽度为90% */
+        height: 90vh; /* 高度为90% */
+    }
+}
+
+/* 模态框样式,初始隐藏 */
+.license-info-modal {
+    display: none; /* 初始隐藏模态框 */
+    position: fixed; /* 固定位置,模态框将一直可见,随页面滚动保持位置 */
+    top: 0;
+    left: 0;
+    width: 100%; /* 宽度占据整个屏幕 */
+    height: 100%; /* 高度占据整个屏幕 */
+    background-color: rgba(0, 0, 0, 0.2); /* 半透明背景,模态框弹出时背景变暗 */
+    justify-content: center; /* 居中模态框水平 */
+    align-items: center; /* 居中模态框垂直 */
+    /* z-index: 1000; 保证模态框在所有元素之上显示 */
+    overflow: hidden; /* 禁止模态框背景滚动 */
+}
+
+
+/* 模态框内容样式,带有圆角和阴影 */
+.license-info-modal-content {
+    background-color: white; /* 模态框内部背景色为白色 */
+    padding: 20px; /* 内部填充 */
+    border-radius: 20px; /* 设置较大的圆角,符合iOS风格 */
+    position: absolute; /* 使用绝对定位 */
+    transform-origin: center; /* 动画从中心展开 */
+    opacity: 0; /* 初始透明度为0,不可见 */
+    transition: opacity 0.3s ease, transform 0.3s ease; /* 使用渐变效果使模态框逐渐显示 */
+    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1); /* 模态框阴影,增加浮动感 */
+    max-height: 80vh; /* 最大高度不超过视口的80% */
+    overflow-y: auto; /* 如果内容超出高度则滚动 */
+}
+/* 模态框的初始缩放效果,配合过渡动画 */
+.license-info-modal-content {
+    transform: scale(0.95); /* 初始有轻微缩放效果 */
+}
+/* 动画结束后的模态框样式,设置为80%大小且居中 */
+.license-info-modal-content.show {
+    opacity: 1; /* 动画显示模态框 */
+    width: 80vw; /* 宽度占据视口80% */
+    height: 70vh; /* 高度占据视口70% */
+    top: 50%; /* 垂直居中 */
+    left: 50%; /* 水平居中 */
+    transform: translate(-50%, -50%) scale(1); /* 完全居中,且带有缩放效果 */
+}
+
+
+
+
+
+/* 关闭按钮样式 */
+.license-info-close {
+    position: absolute; /* 绝对定位,确保关闭按钮在模态框的右上角 */
+    top: 10px;
+    right: 20px;
+    font-size: 28px; /* 关闭按钮的字体大小 */
+    color: #007aff; /* iOS风格的蓝色 */
+    cursor: pointer; /* 鼠标悬停时显示手指图标 */
+    font-weight: 600; /* 设置按钮的字体粗细 */
+}
+
+/* 模态框上半部分的样式 */
+.license-info-modal-header {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    margin-bottom: 20px;
+    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
+}
+
+/* 标题的 iOS 风格 */
+.license-info-modal-header h2 {
+    font-size: 24px;
+    font-weight: 600;
+    color: #333;
+    margin-bottom: 10px;
+}
+
+/* 按钮组的样式 */
+.license-info-modal-button-group {
+    display: flex;
+    justify-content: space-around;
+    width: 100%;
+    margin-top: 10px;
+}
+
+/* 组样式:每组数据方框 */
+.license-info-group-box {
+    border: 1px solid #ccc; /* 灰色边框 */
+    border-radius: 8px; /* 设置圆角 */
+    padding: 15px; /* 内部填充 */
+    margin-bottom: 15px; /* 组之间的间隔 */
+    background-color: #f9f9f9; /* 设置浅灰色背景 */
+    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); /* 设置轻微的阴影 */
+}
+
+.license-info-group-box p {
+    margin: 5px 0;
+    font-size: 16px;
+    color: #333;
+    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; /* 符合 iOS 风格的字体 */
+}
+
+.license-info-group-title {
+    font-weight: 600;
+    margin-bottom: 10px;
+    color: #007aff; /* iOS 风格的蓝色标题 */
+}
+
+
+.license-info-modal-button {
+    padding: 12px 20px;
+    background-color: #007aff; /* iOS风格的蓝色按钮 */
+    color: white; /* 按钮字体颜色为白色 */
+    border: none;
+    border-radius: 12px; /* 设置圆角按钮 */
+    font-size: 16px;
+    font-weight: 500;
+    cursor: pointer; /* 鼠标悬停时显示手指图标 */
+    transition: background-color 0.3s ease; /* 背景颜色过渡效果 */
+}
+
+.license-info-modal-button:hover {
+    background-color: #005bb5; /* 按钮的悬停效果 */
+}
+
+.license-info-modal-button:disabled {
+    background-color: #ccc;
+    cursor: not-allowed;
+}
+
+/* 模态框下半部分的样式 */
+.license-info-modal-body {
+    overflow-y: auto; /* 当内容超出容器时,启用垂直滚动条 */
+    flex-grow: 1; /* 占据模态框的剩余空间 */
+    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
+    color: #3a3a3c; /* 字体颜色为深灰色 */
+    font-size: 18px;
+    line-height: 1.6; /* 行高 */
+}
+
+/* 添加滚动条样式,符合iOS的滑动效果 */
+.license-info-modal-body::-webkit-scrollbar {
+    width: 6px;
+}
+
+.license-info-modal-body::-webkit-scrollbar-thumb {
+    background-color: rgba(0, 0, 0, 0.2);
+    border-radius: 3px;
+}
+
+
+/* 分页按钮组样式 */
+.license-info-modal-pagination {
+    display: flex;
+    justify-content: center;
+    margin-top: 20px;
+}
+
+.license-info-modal-pagination button {
+    padding: 10px 15px;
+    margin: 0 5px;
+    background-color: #007aff;
+    color: white;
+    border: none;
+    border-radius: 5px;
+    cursor: pointer;
+    font-size: 16px;
+    transition: background-color 0.3s ease;
+}
+
+.license-info-modal-pagination button:hover {
+    background-color: #005bb5; /* 鼠标悬停时按钮背景颜色变深 */
+}
+
+.license-info-modal-pagination button:disabled {
+    background-color: #ccc; /* 禁用时按钮为灰色 */
+    cursor: not-allowed;
+}
+
+/* 分页下拉框样式 */
+.license-info-modal-pagination select {
+    padding: 10px 15px;
+    margin: 0 5px;
+    background-color: #007aff;
+    color: white;
+    border: none;
+    border-radius: 5px;
+    cursor: pointer;
+    font-size: 16px;
+    transition: background-color 0.3s ease;
+}
+
+/* 下拉框中的选项样式 */
+.license-info-modal-pagination select option {
+    background-color: white; /* 默认背景颜色 */
+    color: black; /* 默认字体颜色 */
+}
+
+/* 鼠标悬停选项时 */
+.license-info-modal-pagination select option:hover {
+    background-color: #007aff; /* 鼠标悬停时选项变为蓝色 */
+    color: white; /* 悬停时字体颜色变为白色 */
+}
+
+/* 选中的选项的样式 */
+.license-info-modal-pagination select option:checked {
+    background-color: #ccc; /* 当前选中的选项背景为蓝色 */
+    color: white; /* 当前选中的选项字体颜色为白色 */
+}
+
+/* 侧边栏的菜单项与搜索框保持间距 */
+nav ul {
+    padding-top: 20px;
+}
+
+/* 活跃菜单项样式 */
+nav ul li a.active {
+    background-color: #007aff;
+    color: white;
+}
+
+
+/*进度条-----------------------------------------------------*/
+/* 模态框的基础样式 */
+.loading-modal {
+    position: fixed;
+    z-index: 9999;
+    left: 0;
+    top: 0;
+    width: 100%;
+    height: 100%;
+    background-color: rgba(0, 0, 0, 0.5);
+    display: flex;
+    justify-content: center;
+    align-items: center;
+}
+
+.loading-modal-content {
+    background-color: white;
+    padding: 20px;
+    border-radius: 8px;
+    text-align: center;
+    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
+}
+
+.spinner {
+    border: 4px solid rgba(0, 0, 0, 0.1);
+    border-left-color: #09f;
+    border-radius: 50%;
+    width: 40px;
+    height: 40px;
+    animation: spin 1s linear infinite;
+    margin-bottom: 10px;
+}
+
+@keyframes spin {
+    from { transform: rotate(0deg); }
+    to { transform: rotate(360deg); }
+}
+
+/* Loading message text style */
+.loading-modal p {
+    margin: 0;
+    font-size: 16px;
+    color: #333;
+}
+
+
+/* 用户管理模态框------------------------------------------------------ */
+/* 用户管理模态框的背景层 */
+/* 响应式设计,适配小屏幕 */
+/* 用户管理模态框的背景层 */
+
+
+
+
+/* 搜索栏样式*/
+/* 搜索框和下拉框容器样式 */
+.search-container {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    gap: 10px;
+    padding: 10px 0;
+    background-color: #ffffff;
+ 
+}
+
+
+/* License 状态下拉框、日期输入框和搜索框的样式 */
+#license-status-filter, #start-date, #end-date, #search-bar {
+    width: 18%;
+    padding: 12px;
+    border-radius: 15px;
+    border: none;
+    background-color: #ffffff;
+    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+    font-size: 16px;
+    color: #333;
+    transition: all 0.3s ease;
+}
+
+/* 日期框在获得焦点时的效果 */
+#start-date:focus, #end-date:focus {
+    outline: none;
+    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
+}
+
+/* 确定按钮样式 */
+#submit-button {
+    padding: 12px 20px;
+    background-color: #007aff;
+    color: white;
+    border: none;
+    border-radius: 15px;
+    font-size: 16px;
+    cursor: pointer;
+    transition: background-color 0.3s ease;
+}
+
+/* 鼠标悬停时按钮效果 */
+#submit-button:hover {
+    background-color: #005bb5;
+}
+
+
+
+/*-----------------分发*/
+
+/* 模态框整体样式 */
+#distribute-modal {
+    display: none; /* 默认隐藏 */
+    position: fixed;
+    z-index: 1000;
+    left: 0;
+    top: 0;
+    width: 100%;
+    height: 100%;
+    background-color: rgba(0, 0, 0, 0.5); /* 半透明背景 */
+    justify-content: center;
+    align-items: center;
+    overflow: hidden;
+}
+
+/* 模态框内容样式 */
+#distribute-modal .distribute-modal-content {
+    background-color: #fff;
+    width: 400px; /* 模态框的宽度 */
+    max-width: 90%; /* 最大宽度为视口的90% */
+    padding: 20px;
+    border-radius: 10px;
+    box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3); /* 轻微阴影 */
+    position: relative;
+    text-align: center;
+}
+
+/* 关闭按钮样式 */
+#distribute-modal .distribute-close {
+    position: absolute;
+    top: 10px;
+    right: 15px;
+    font-size: 24px;
+    color: #333;
+    cursor: pointer;
+}
+
+/* 模态框标题样式 */
+#distribute-modal .distribute-header h2,
+#distribute-modal .distribute-body h2 {
+    font-size: 20px;
+    font-weight: bold;
+    margin-bottom: 15px;
+    color: #333;
+}
+
+/* 分发邮箱部分样式 */
+#distribute-modal #distribute-email-section {
+    margin-bottom: 20px;
+}
+
+#distribute-modal #distribute-email-section h2 {
+    font-size: 18px;
+    margin-bottom: 10px;
+    color: #007bff;
+}
+
+/* 分发用户部分样式 */
+#distribute-modal #distribute-user-section {
+    margin-top: 20px;
+}
+
+#distribute-modal #distribute-user-section h2 {
+    font-size: 18px;
+    margin-bottom: 10px;
+    color: #28a745;
+}
+
+/* 输入框区域样式 */
+#distribute-modal #emailInputs,
+#distribute-modal #userInputs {
+    display: flex;
+    flex-direction: column;
+    align-items: flex-start;
+}
+
+#distribute-modal #emailInputs div,
+#distribute-modal #userInputs div {
+    margin-bottom: 10px;
+}
+
+#distribute-modal input[type="checkbox"] {
+    margin-right: 10px;
+}
+
+#distribute-modal label {
+    font-size: 16px;
+    color: #555;
+    cursor: pointer;
+}
+
+/* 确认按钮样式 */
+#distribute-modal .distribute-confirm-button {
+    background-color: #007bff;
+    color: white;
+    padding: 10px 20px;
+    border: none;
+    border-radius: 5px;
+    cursor: pointer;
+    margin-top: 20px;
+    font-size: 16px;
+}
+
+#distribute-modal .distribute-confirm-button:hover {
+    background-color: #0056b3;
+}

+ 84 - 13
static/license_info/license_info.html

@@ -3,45 +3,116 @@
 <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <title>License 信息加载</title>
-    <link rel="stylesheet" href="styles.css">
+    <title>XUGULicense</title>
+    <link rel="stylesheet" href="license_info.css">
 </head>
 <body>
+
+
     <header>
-        <h1>License 管理系统</h1>
+        <h1>XUGU License </h1>
+
     </header>
 
     <div class="container">
         <aside>
             <nav>
                 <ul>
-                    <li>菜单项1</li>
-                    <li>菜单项2</li>
-                    <li>菜单项3</li>
+                    <li><a href="#" class="active" id="license-info-link"><i class="fas fa-home"></i> License 信息</a></li>
+                    <li><a href="#" id="user-management-link"><i class="fas fa-users"></i> 用户管理</a></li>
+                    <li><a href="#" id="role-management-link"><i class="fas fa-users"></i> 角色管理</a></li>
                 </ul>
             </nav>
         </aside>
 
-        <main>
-            <div class="license-info-container" id="restaurant-list">
+<main>
  
-            </div>
+         <!-- 包裹搜索框、下拉框、时间选择框和确定按钮的 div -->
+         <div class="search-container">
+            <!-- License 状态下拉框 -->
+            <select id="license-status-filter" aria-label="选择 License 状态">
+                <option value="">License 状态</option>
+                <option value="已生成">已生成</option>
+                <option value="未生成">未生成</option>
+                <option value="已失效">已失效</option>
+            </select>
+        
+            <!-- 开始时间选择框,类型改为 date -->
+            <input type="date" id="start-date" placeholder="开始时间" />
+        
+            <!-- 结束时间选择框,类型改为 date -->
+            <input type="date" id="end-date" placeholder="结束时间" />
+        
+            <!-- 搜索框 -->
+            <input type="text" id="search-bar" placeholder="搜索..." />
+        
+            <!-- 确定按钮 -->
+            <button id="submit-button">确定</button>
+        </div>
+            <div class="license-info-container" id="license-info-restaurant-list">   </div>
+              <!-- 这是显示内容的区域 -->
+        </main> 
+    </div>
 
-    
+<!-- 模态框结构 -->
+<div id="license-info-modal" class="license-info-modal">
+    <div class="license-info-modal-content">
+        <!-- 关闭模态框的按钮 -->
+        <span class="license-info-close">&times;</span>
 
+        <!-- 上半部分: 按钮和项目信息 -->
+        <div id="license-info-modal-header" class="license-info-modal-header">
+          
+        </div>
+        
 
-        </main>
+        <!-- 下半部分: 动态显示字段内容 -->
+        <div id="license-info-modal-body" class="license-info-modal-body"></div>
 
+        <!-- 分页按钮 -->
+        <div class="license-info-modal-pagination">
+            <button id="prev-page">上一页</button>
+            <button id="next-page">下一页</button>
+        </div>
+    
+    </div>
+</div>
 
+<!-- 新的分发模态框 -->
+<div id="distribute-modal" class="distribute-modal">
+    <div class="distribute-modal-content">
+        <span class="distribute-close">&times;</span>
 
+        <!-- 上半部分: 分发邮箱 -->
+        <div id="distribute-email-section" class="distribute-header">
+            <h2>分发邮箱</h2>
+            <div id="emailInputs">
+                <!-- 动态生成邮箱输入区域 -->
+            </div>
+        </div>
 
-        
+        <!-- 下半部分: 分发用户 -->
+        <div id="distribute-user-section" class="distribute-body">
+            <h2>分发用户</h2>
+            <div id="userInputs">
+                <!-- 动态生成用户选择输入区域 -->
+            </div>
+        </div>
+
+        <button class="distribute-confirm-button">确认分发</button>
     </div>
+</div>
+
+
+   
+
 
 
+    <script src="license_info.js"></script>
+    <script
+    src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.7.1/jszip.min.js"></script>
 
 
-    <script src="script.js"></script>
 </body>
 
 

+ 977 - 0
static/license_info/license_info.js

@@ -0,0 +1,977 @@
+// 全局变量定义
+let page = 11; // 初始页码为1,代表从第1条数据开始获取
+let pageSize = 20; // 初始每次固定获取10条数据
+let total = 0; // 数据总量(从接口获取)
+let loadedItems = 0; // 已加载的数据条目数量
+let isLoading = false; // 防止多次加载
+const timeoutDuration = 10000; // 超时时间10秒
+const preLoadDistance = 300; // 距离底部300px时提前加载
+
+// 假设 Authorization 值存储在 localStorage 中,key 为 "authToken"
+const authToken = localStorage.getItem("Authorization");
+const currentUserInfo  = fetchUsername(); // 获取当前用户登录信息
+let currentUserPermissions; // 用于存储用户权限信息
+
+//获取  主内容区域
+const license_info_mainElement = document.querySelector('main'); // 主内容区域
+
+//模态框
+const license_info_modal = document.getElementById('license-info-modal'); // 模态框容器
+const license_info_modalContent = document.querySelector('.license-info-modal-content'); // 模态框内容区域
+const license_info_modalDescription = document.getElementById('license-info-modal-description'); // 模态框描述
+const license_info_modalPrice = document.getElementById('license-info-modal-price'); // 模态框产品信息
+const license_info_modalRating = document.getElementById('license-info-modal-rating'); // 模态框MAC地址
+const license_info_closeModal = document.querySelector('.license-info-close'); // 模态框关闭按钮
+const license_info_loadingIndicator = document.getElementById('loading-indicator'); // 加载提示元素
+
+//存储
+let LicApplicationData = []; // 用于存储从接口获取的数据
+
+
+//-----------侧边栏----------------------------
+
+// 获取所有菜单项
+const menuItems = document.querySelectorAll('nav ul li a');
+
+// 为每个菜单项添加点击事件监听器
+menuItems.forEach(item => {
+    item.addEventListener('click', function() {
+        // 移除其他项的 active 类
+        menuItems.forEach(i => i.classList.remove('active'));
+        
+        // 为当前点击的项添加 active 类
+        this.classList.add('active');
+    });
+});
+
+//用户管理-
+//获取用户管理和 License 信息按钮
+const userManagementLink = document.getElementById('user-management-link');
+const licenseInfoLink = document.getElementById('license-info-link');
+const roleManagementLink = document.getElementById('role-management-link');
+
+// 监听用户管理按钮的点击事件
+userManagementLink.addEventListener('click', function(event) {
+    event.preventDefault(); // 阻止默认的跳转行为
+    removeScrollListeners(); // 移除滚动监听器
+    // 使用 fetch 来加载 user_management.html 的内容
+    fetch('../user/user_management.html')
+        .then(response => response.text())
+        .then(data => {
+            // 将 user_management.html 的内容插入到主内容区域
+            license_info_mainElement.innerHTML = data;
+         // 动态引入 user.js 文件
+         const script = document.createElement('script');
+         script.src = 'user.js';
+         document.body.appendChild(script);
+            
+        })
+        .catch(error => console.error('加载用户管理页面失败:', error));
+});
+
+// 监听 License 信息按钮的点击事件
+licenseInfoLink.addEventListener('click', function(event) {
+    event.preventDefault(); // 阻止默认的跳转行为
+
+    // 将瀑布流的 License 信息内容恢复到主内容区域
+    const licenseInfoHtml = `
+            <div class="license-info-container" id="license-info-restaurant-list">   </div>
+    `; // 这是原来的 License 信息区域 HTML
+
+    //mainContainer.innerHTML = licenseInfoHtml;
+    license_info_mainElement.innerHTML  = licenseInfoHtml;
+          //清楚lic信息组的数据
+          LicApplicationData = [];
+          initializeScrollListeners(); // 重新初始化滚动监听器
+    // 再次加载 License 信息数据并渲染卡片
+    (async function() {
+        const data = await fetchLicenseData(1, 10);
+        if (data.length > 0) {
+            console.log('加载的数据:', data); // 检查是否成功获取数据
+            renderLicenseCards(data, true); // 渲染数据到页面并清空之前的内容
+      
+        } else {
+            console.error('未加载到数据');
+        }
+    })();
+});
+
+roleManagementLink.addEventListener('click', function(event) {
+    event.preventDefault(); // 阻止默认的跳转行为
+    removeScrollListeners(); // 移除滚动监听器
+    // 使用 fetch 来加载 user_management.html 的内容
+    fetch('../role/role.html')
+        .then(response => response.text())
+        .then(data => {
+            // 将 user_management.html 的内容插入到主内容区域
+            license_info_mainElement.innerHTML = data;
+         // 动态引入 user.js 文件
+         const script = document.createElement('script');
+         script.src = 'user.js';
+         document.body.appendChild(script);
+            
+        })
+        .catch(error => console.error('加载用户管理页面失败:', error));
+});
+
+//-------license数据显示------------------------------------------------------
+
+// 获取数据函数
+async function fetchLicenseData(page, pageSize) {
+    try {
+        const response = await fetch(`http://127.0.0.1:8080/api/admin/GetAllLicenseInfo?page=${page}&pageSize=${pageSize}`, {
+            method: 'GET',
+            headers: {
+                'Authorization': `Bearer ${authToken}`,
+                'Content-Type': 'application/json'
+            }
+        });
+        
+        const result = await response.json();
+        
+        // 设置总量,如果第一次加载,获取total字段
+        if (total === 0 && result.total) {
+            total = result.total;
+        }
+   
+           // 使用 concat 方法将新数据与之前的数据进行累加
+           LicApplicationData = LicApplicationData.concat(result.data || []);
+        console.log("LicApplicationData: ",LicApplicationData);
+        return result.data || [];
+
+    } catch (error) {
+        console.error("加载数据失败", error);
+        return []; // 返回空数组,防止后续操作出错
+    }
+}
+
+
+// 渲染 license_info 卡片数据函数
+function renderLicenseCards(data, clearContainer = false) {
+    console.log("-----------渲染次数");
+    // 获取与 license_info 相关的 HTML 元素
+    const license_info_container = document.getElementById('license-info-restaurant-list'); // 卡片列表容器
+    
+
+    if (clearContainer) {
+        license_info_container.innerHTML = ''; // 清空容器内容
+        isLoading = false; // 重置加载状态
+        loadedItems = 0; // 重置已加载项数
+        page = 11; // 每次请求后,page 增加10,表示从下一组数据开始
+        pageSize = 20; // pageSize 每次递增10
+        console.log("-----------渲染清除");
+    }
+    console.log("-----------data:",data);
+       // 获取子行的数量
+   const childRowCount = data.length;
+
+    data.forEach(group => {
+        const firstItem = group[0]; // 获取该组的第一个数据项
+
+        let statusClass = '';
+        if (firstItem.LicenseFlage === '已生成') {
+            statusClass = 'license-status-green';
+        } else if (firstItem.LicenseFlage === '未生成') {
+            statusClass = 'license-status-yellow';
+        } else if (firstItem.LicenseFlage === '已失效') {
+            statusClass = 'license-status-red';
+        }
+
+        const card = document.createElement('div');
+        card.className = 'license-info-card';
+           // 给卡片添加一个唯一标识符的 data 属性
+        card.setAttribute('data-oa-request-id', firstItem.oa_request_id);
+
+        // 在卡片的第一行显示申请时间
+        card.innerHTML = `
+            <div class="license-info-card-header">
+                <h3 class="card-title">${firstItem.GlxmName}</h3>
+            </div>
+            <div class="license-info-card-content">
+                <p class="card-text">${firstItem.ApplicationDate} ${firstItem.ApplicationTime}</p> <!-- 显示日期和时间 -->
+                <p class="card-text">创建者:${firstItem.Creator}</p>
+                <p class="card-text">公司:${firstItem.Company}</p>
+                <p class="card-text">集群:${childRowCount} 套  共计:${firstItem.TotalNodes} 节点</p>
+                <p class="card-text license-status ${statusClass}">许可证状态:${firstItem.LicenseFlage}</p>
+                <p class="card-text">oa_request_id:${firstItem.oa_request_id}</p>
+            </div>
+        `;
+
+
+     // 给卡片添加点击事件,点击后显示模态框
+     card.addEventListener('click', () => {
+          // 传递当前卡片的详细数据到模态框
+          const oaRequestId = card.getAttribute('data-oa-request-id');
+          showModalForCard(group, oaRequestId); // 传递 oa_request_id
+        //showModalForCard(group); // 传递当前卡片的详细数据到模态框
+    });
+  
+
+    // 将卡片添加到容器中
+    license_info_container.appendChild(card); 
+
+
+
+    });
+
+    
+}
+
+
+
+
+// 检查是否滚动到底部并触发加载
+async function checkAndLoadMore(scrollHeight, scrollTop, clientHeight) {
+    if (isLoading || loadedItems >= total) return; // 如果正在加载或已加载完所有数据则退出
+
+  //  console.log(`Scroll Info - scrollHeight: ${scrollHeight}, scrollTop: ${scrollTop}, clientHeight: ${clientHeight}`);
+
+    if (scrollTop + clientHeight >= scrollHeight - preLoadDistance) {
+        console.log(`触发加载更多数据:page=${page}, pageSize=${pageSize}`); // 每次触发时打印输出
+        await loadMoreData();
+    }
+}
+
+// 加载更多数据函数
+async function loadMoreData() {
+    if (isLoading) return; // 防止重复加载
+
+    isLoading = true;
+    console.log('开始加载更多数据');
+
+    // 显示加载提示
+    // license_info_loadingIndicator.style.display = 'block'; // 显示提示
+    // license_info_loadingIndicator.innerText = '正在加载...'; // 重置加载提示
+    
+    // 设置超时处理
+    const timeout = setTimeout(() => {
+        license_info_loadingIndicator.innerText = '加载超时,请重试'; // 修改提示语为超时提示
+        isLoading = false;
+        license_info_loadingIndicator.style.display = 'none'; // 超时后隐藏加载提示
+    }, timeoutDuration);
+
+    // 获取数据
+    const data = await fetchLicenseData(page, pageSize);
+    console.log(`加载的新数据 data`,data); // 每次触发时打印输出
+    // 清除超时定时器
+    clearTimeout(timeout);
+
+    if (data.length > 0) {
+      
+
+        // 更新 page 和 pageSize,下一次请求从新的位置开始
+        page += 10; // 每次请求后,page 增加10,表示从下一组数据开始
+        pageSize += 10; // pageSize 每次递增10
+
+        // 更新已加载的条目数
+        loadedItems += data.length;
+
+          // 渲染数据到页面
+          renderLicenseCards(data);
+
+        console.log('数据加载完成,更新页面');
+    }
+
+    // 隐藏加载提示
+    //license_info_loadingIndicator.style.display = 'none'; // 加载完成后隐藏提示
+    isLoading = false; // 请求完成,允许下次请求
+
+    // 检查内容高度,必要时继续加载
+    checkContentHeight();
+}
+
+
+//--------------------------监听 window 滚动---监听 main 容器的滚动-----------------------------------------------
+
+
+// // 监听 window 滚动
+// window.addEventListener('scroll', () => {
+//     checkAndLoadMore(document.body.scrollHeight, window.scrollY, window.innerHeight);
+// });
+
+// // 监听 main 容器的滚动
+// license_info_mainElement.addEventListener('scroll', () => {
+//     checkAndLoadMore(license_info_mainElement.scrollHeight, license_info_mainElement.scrollTop, license_info_mainElement.clientHeight);
+// });
+function initializeScrollListeners() {
+    // 只监听 main 容器的滚动
+    license_info_mainElement.addEventListener('scroll', handleMainScroll);
+    // console.log('滚动监听已初始化');
+}
+
+function removeScrollListeners() {
+    // 移除 main 容器的滚动监听
+    license_info_mainElement.removeEventListener('scroll', handleMainScroll);
+}
+
+function handleMainScroll() {
+    // console.log('handleMainScroll', license_info_mainElement.scrollHeight, license_info_mainElement.scrollTop, license_info_mainElement.clientHeight);
+    checkAndLoadMore(license_info_mainElement.scrollHeight, license_info_mainElement.scrollTop, license_info_mainElement.clientHeight);
+}
+
+async function checkAndLoadMore(scrollHeight, scrollTop, clientHeight) {
+    if (isLoading || loadedItems >= total) return; // 如果正在加载或已加载完所有数据则退出
+
+    // console.log(`Scroll Info - scrollHeight: ${scrollHeight}, scrollTop: ${scrollTop}, clientHeight: ${clientHeight}`);
+
+    if (scrollTop + clientHeight >= scrollHeight - preLoadDistance) {
+        console.log(`触发加载更多数据:page=${page}, pageSize=${pageSize}`); // 每次触发时打印输出
+        await loadMoreData();
+    }
+}
+
+//-----------------------------------------------------------------------------------------
+
+
+
+// 初始化加载第一页
+(async function() {
+    const data = await fetchLicenseData(1, 10);
+    if (data.length > 0) {
+        renderLicenseCards(data); // 渲染数据到页面
+        loadedItems += data.length; // 更新已加载的条目数
+    }
+    //license_info_loadingIndicator.style.display = 'none'; // 初始化后隐藏加载提示
+
+    // 检查内容高度
+    checkContentHeight();
+})();
+//初始化监听滚动条
+initializeScrollListeners()
+
+//-----------点击卡片弹出模态框------------------------------------------------------
+
+// 模态框显示函数
+// 模态框显示函数
+function showModalForCard(item,oaRequestId) {
+    const modal = document.getElementById('license-info-modal');
+    const modalContent = document.querySelector('.license-info-modal-content');
+    const modalBody = document.getElementById('license-info-modal-body'); // 获取下半部分容器
+    console.log(`当前点击的卡片 ID: ${oaRequestId}`);
+    
+    // 设置分页相关的变量
+    let currentPage = 1;
+    const itemsPerPage = 2; // 每页显示两组
+
+    // 对 item 数组按 oa_id 进行升序排序
+    const sortedItem = item.sort((a, b) => a.oa_id - b.oa_id);
+
+    const totalPages = Math.ceil(sortedItem.length / itemsPerPage); // 计算总页数
+
+    // 获取分页容器
+    const paginationContainer = document.querySelector('.license-info-modal-pagination');
+
+    // 清空分页容器,避免重复创建元素
+    paginationContainer.innerHTML = '';
+
+    // 创建"上一页"按钮
+    const prevButton = document.createElement('button');
+    prevButton.classList.add('prev-page');
+    prevButton.innerText = '上一页';
+    paginationContainer.appendChild(prevButton);
+
+    // 创建下拉框
+    const selectPageDropdown = document.createElement('select');
+    paginationContainer.appendChild(selectPageDropdown);
+
+    // 创建"下一页"按钮
+    const nextButton = document.createElement('button');
+    nextButton.classList.add('next-page');
+    nextButton.innerText = '下一页';
+    paginationContainer.appendChild(nextButton);
+
+ 
+   // 初始化上半部分内容(Company, Creator, ApplicationDate, ApplicationTime 和两个按钮)
+function initializeHeaderContent(firstItem,sortedItem) {
+    console.log("initializeHeaderContent");  // 检查是否找到按钮
+    // 清空上半部分内容
+    const modalHeader = document.querySelector('.license-info-modal-header');
+    modalHeader.innerHTML = ''; // 确保不会重复创建
+
+    let statusClass = '';
+    if (firstItem.LicenseFlage === '已生成') {
+        statusClass = 'license-status-green';
+    } else if (firstItem.LicenseFlage === '未生成') {
+        statusClass = 'license-status-yellow';
+    } else if (firstItem.LicenseFlage === '已失效') {
+        statusClass = 'license-status-red';
+    }
+
+   // 检查当前用户是否有权限生成或分发
+   const hasGeneratePermission = currentUserPermissions.includes('generate_license');
+   const hasDispatchPermission = currentUserPermissions.includes('dispat_license');
+
+console.log(`当前用户是否有生成权限: ${hasGeneratePermission}, ${hasDispatchPermission}`);
+    // 设置卡片内容
+    modalHeader.innerHTML = `
+    <div class="license-info-card-header">
+        <h3 class="card-title">${firstItem.GlxmName}</h3>
+    </div>
+    <div class="license-info-card-content">
+        <p class="card-text">${firstItem.ApplicationDate} ${firstItem.ApplicationTime}</p> <!-- 显示日期和时间 -->
+        <p class="card-text">公司:${firstItem.Company}</p>
+        <p class="card-text">${firstItem.Project}</p>
+        <p class="card-text">创建者:${firstItem.Creator}</p>
+        <p class="card-text license-status ${statusClass}">许可证状态:${firstItem.LicenseFlage}</p>
+        <div class="license-info-card-buttons">
+            ${
+            firstItem.LicenseFlage === '已生成' && hasDispatchPermission
+                ? `<button class="license-info-modal-button" id="generateOrDistribute">分发</button>`
+                : firstItem.LicenseFlage !== '已生成' && hasGeneratePermission
+                ? `<button class="license-info-modal-button" id="generateOrDistribute">生成</button>`
+                : ''
+            }
+            <button class="license-info-modal-button" id="downloadAllLicenses-button">打包下载所有license.dat</button>
+        </div>
+    </div>
+    `;
+        // 绑定 button1 的点击事件(如果按钮存在)
+        const generateOrDistribute = modalHeader.querySelector('#generateOrDistribute');
+        console.log("generateOrDistribute",generateOrDistribute);  // 检查是否找到按钮
+        if (generateOrDistribute) {
+            generateOrDistribute.addEventListener('click', () => {
+                if (firstItem.LicenseFlage === '已生成') {
+                    // 执行分发逻辑
+                    showDistributeModal(firstItem.oa_request_id, firstItem.SupportEmail, firstItem.SalesEmail, '', firstItem.oa_request_id);
+                
+                } else {
+                    // 执行生成逻辑
+                 generateLicense(firstItem.oa_request_id, true);
+                }
+                console.log('Button 1 clicked');
+            });
+        }
+;
+            
+          
+
+        // 绑定 "打包下载所有license.dat" 按钮的点击事件
+        const downloadButton = modalHeader.querySelector('#downloadAllLicenses-button');
+
+        // 如果 LicenseFlage 是 "未生成" 或 "已失效",按钮变灰且不可点击
+        if (firstItem.LicenseFlage === '未生成' || firstItem.LicenseFlage === '已失效') {
+            downloadButton.disabled = true;
+            downloadButton.style.backgroundColor = '#ccc'; // 设置为灰色
+            downloadButton.style.cursor = 'not-allowed'; // 修改鼠标样式
+        } else {
+            // 只有当 LicenseFlage 是 "已生成" 时,才能点击下载按钮
+            downloadButton.addEventListener('click', () => {
+                downloadAllLicenses(sortedItem);
+            });
+        }
+
+    }
+
+
+    // 初始化下拉框的页码选项
+    function initializeDropdown() {
+            selectPageDropdown.innerHTML = ''; // 清空下拉框中的选项
+            for (let page = 1; page <= totalPages; page++) {
+                const option = document.createElement('option');
+                option.value = page;
+                option.innerText = `第 ${page} 页`;
+                selectPageDropdown.appendChild(option);
+            }
+            selectPageDropdown.value = currentPage; // 设置默认选项为当前页
+        }
+
+        // 渲染当前页内容
+    function ModalForCardRenderPage(page) {
+            modalBody.innerHTML = ''; // 清空之前的内容
+
+            // 计算当前页的起始和结束索引
+            const startIndex = (page - 1) * itemsPerPage;
+            const endIndex = Math.min(startIndex + itemsPerPage, sortedItem.length);
+
+            // 更新上半部分的字段内容 (以第一个元素为例)
+            const firstItem = sortedItem[0];
+            initializeHeaderContent(firstItem,sortedItem); // 动态生成上半部分内容
+
+            // 遍历当前页的数据
+            for (let i = startIndex; i < endIndex; i++) {
+                const group = sortedItem[i];
+                const groupBox = document.createElement('div');
+                groupBox.classList.add('license-info-group-box');
+
+                // 动态生成组内容,显示编号为 i+1(表示组1, 组2...)
+                groupBox.innerHTML = `
+                    <div class="license-info-group-title">组 ${i + 1}</div>
+                    <p><strong>UniqueID:</strong> ${group.UniqueID}</p>
+                    <p><strong>oa_id:</strong> ${group.oa_id}</p>
+                    <p><strong>oa_request_id:</strong> ${group.oa_request_id}</p>
+                    <p><strong>Creator:</strong> ${group.Creator}</p>
+                    <p><strong>oa_request_name_new:</strong> ${group.oa_request_name_new}</p>
+                `;
+
+                // 将生成的组内容加入到 modalBody
+                modalBody.appendChild(groupBox);
+            }
+
+            // 更新下拉框的值
+            selectPageDropdown.value = page;
+
+            // 更新按钮状态
+            prevButton.disabled = (page === 1);
+            nextButton.disabled = (page === totalPages);
+    }
+
+    // 下拉框页码选择事件
+    selectPageDropdown.addEventListener('change', function() {
+            currentPage = parseInt(this.value);
+            ModalForCardRenderPage(currentPage); // 根据选择的页码渲染对应页面
+    });
+
+    // 上一页按钮点击事件
+    prevButton.addEventListener('click', function() {
+            if (currentPage > 1) {
+                currentPage--;
+                ModalForCardRenderPage(currentPage);
+            }
+    });
+
+        // 下一页按钮点击事件
+    nextButton.addEventListener('click', function() {
+            if (currentPage < totalPages) {
+                currentPage++;
+                ModalForCardRenderPage(currentPage);
+            }
+    });
+
+    // 显示或隐藏分页相关控件
+    function togglePaginationVisibility() {
+            if (totalPages <= 1) {
+                // 隐藏下拉框和分页容器
+                paginationContainer.style.display = 'none';
+            } else {
+                // 显示下拉框和分页容器
+                paginationContainer.style.display = 'flex';
+            }
+    }
+
+    // 初始化下拉框并渲染第一页
+    initializeDropdown();
+    // 渲染模态框内容
+    ModalForCardRenderPage(currentPage);
+    // 根据页数决定是否显示分页控件
+    togglePaginationVisibility();
+
+        // 显示模态框
+    modal.style.display = 'flex'; // 显示模态框背景
+        setTimeout(() => {
+            modalContent.classList.add('show'); // 添加动画效果
+        }, 10); // 延时确保动画生效
+
+    // 关闭模态框逻辑
+    const closeModal = document.querySelector('.license-info-close');
+        closeModal.addEventListener('click', () => {
+            modalContent.classList.remove('show'); // 移除动画类
+            setTimeout(() => {
+                modal.style.display = 'none'; // 完全隐藏模态框
+            }, 500); // 等待动画结束后再隐藏
+        });
+
+    // 点击模态框外部关闭模态框
+    window.addEventListener('click', (event) => {
+            if (event.target === modal) {
+                modalContent.classList.remove('show');
+                setTimeout(() => {
+                    modal.style.display = 'none'; // 完全隐藏模态框
+                }, 500); // 等待动画结束后再隐藏
+            }
+        });
+}
+
+//-------下载全部licstr按钮
+// 下载许可证功能
+function downloadAllLicenses(sortedApplicationArray) {
+    const zip = new JSZip();
+    console.log("传进来的 sortedApplicationArray:", sortedApplicationArray);
+
+    // 初始化计数器,从1开始
+    let idCounter = 1;
+    let Project = sortedApplicationArray[0].Project;
+    console.log("Project", Project);
+
+    // 遍历 sortedApplicationArray,下载 lic1 和 lic2 数据
+    sortedApplicationArray.forEach(row => {
+        if (row.LicenseFlage === "已生成") {
+            // 替换 oa_main_mac 和 oa_second_mac 中的冒号为点
+            const mainMac = row.oa_main_mac.replace(/:/g, '.');
+            const secondMac = row.oa_second_mac.replace(/:/g, '.');
+
+            // 使用递增的 idCounter 替换 row.oa_id
+            if (row.lic1) {
+                const filename1 = `${idCounter}_license.dat_1_${mainMac}`;
+                zip.file(filename1, row.lic1);
+            }
+            if (row.lic2) {
+                const filename2 = `${idCounter}_license.dat_2_${secondMac}`;
+                zip.file(filename2, row.lic2);
+            }
+
+            // 每次循环后递增计数器
+            idCounter++;
+        }
+    });
+
+    // 生成 ZIP 文件并触发下载
+    zip.generateAsync({ type: "blob" }).then(content => {
+        const link = document.createElement('a');
+        link.href = URL.createObjectURL(content);
+        link.download = `${Project}_license.zip`;
+        link.click();
+    });
+}
+
+//分发
+// 打开分发模态框
+function showDistributeModal(supportEmail, salesEmail, userOptions) {
+    console.log("showDistributeModal", supportEmail);
+    const modal = document.getElementById('distribute-modal');
+    const emailInputs = document.getElementById('emailInputs');
+    const userInputs = document.getElementById('userInputs');
+
+    // 清空内容
+    emailInputs.innerHTML = '';
+    userInputs.innerHTML = '';
+
+    // 动态生成分发邮箱部分
+    if (supportEmail) {
+        emailInputs.innerHTML += `<div><input type="checkbox" id="supportEmail" value="${supportEmail}"> 运维邮箱: ${supportEmail}</div>`;
+    }
+    if (salesEmail) {
+        emailInputs.innerHTML += `<div><input type="checkbox" id="salesEmail" value="${salesEmail}"> 销售邮箱: ${salesEmail}</div>`;
+    }
+
+    // 动态生成分发用户部分
+    // 如果有用户选项,可以解开以下注释并生成用户选项
+    // userOptions.forEach(user => {
+    //     const userOption = document.createElement('div');
+    //     userOption.innerHTML = `<input type="checkbox" value="${user}"> 用户: ${user}`;
+    //     userInputs.appendChild(userOption);
+    // });
+
+    // 显示模态框
+    modal.style.display = 'flex';
+
+    // 关闭按钮事件
+    const closeButton = document.querySelector('#distribute-modal .distribute-close');
+    closeButton.addEventListener('click', () => {
+        modal.style.display = 'none';
+    });
+
+    // 点击模态框外部区域时关闭模态框
+    window.addEventListener('click', function(event) {
+        if (event.target === modal) {
+            modal.style.display = 'none';
+        }
+    });
+}
+
+
+
+
+// 刷新数据并滚动到目标卡片的函数
+// 刷新数据并滚动到目标卡片,同时重新打开目标卡片的模态框
+async function refreshLicenseDataAndScrollAndOpenModal(selfPage,selfPageSize, targetCardId) {
+    const data = await fetchLicenseData(selfPage, selfPageSize);
+    if (data.length > 0) {
+        isLoading = true;
+        console.log('加载的数据:', data); // 检查是否成功获取数据
+        renderLicenseCards(data, true); // 渲染数据到页面并清空之前的内容
+        page = selfPageSize+1;
+        pageSize= selfPageSize +10;
+
+        // 滚动到目标卡片
+        if (targetCardId) {
+            const targetCard = document.querySelector(`[data-oa-request-id="${targetCardId}"]`);
+            if (targetCard) {
+                targetCard.scrollIntoView({ behavior: 'smooth', block: 'center' });
+                
+                // 延迟打开模态框,确保页面滚动完成
+                setTimeout(() => {
+                    targetCard.click(); // 模拟点击卡片,触发模态框
+                }, 500); // 延时500ms确保滚动完成
+            }
+        }
+
+        
+         setTimeout(() => {
+            isLoading = false;
+        }, 2000); 
+     
+    } else {
+        console.error('未加载到数据');
+    }
+}
+
+
+
+
+
+function generateLicense(id, isParentRow) {
+        // 显示加载进度条并设置动态提示信息
+        showLoadingModal('正在生成 License...');
+    const payload = isParentRow ? { oa_request_id: JSON.stringify(id) } : { uniqueID:JSON.stringify(id) };
+    
+    console.log("generateLicense",payload ,id, isParentRow)
+    fetch('http://127.0.0.1:8080/api/admin/GenerateLicense', {
+        method: 'POST',
+        headers: {
+            'Authorization': `Bearer ${authToken}`,
+            'Content-Type': 'application/json'
+        },
+        body: JSON.stringify(payload)
+    })
+    .then(response => response.json())
+    .then(data => {
+        if (data.success) {
+          // 隐藏加载进度条
+                hideLoadingModal();
+                alert('License 生成成功!');
+            //刷新页面
+                 // 调用封装的更新方法
+            //updateCardAndModalStatus(id, isParentRow);
+            // 调用刷新函数,传入当前的 page 和 pageSize
+            refreshLicenseDataAndScrollAndOpenModal(1,pageSize,id);
+            } else {
+                    // 请求失败时隐藏加载进度条
+                hideLoadingModal();
+                alert('License 生成失败:' + data.error);
+            }
+            
+    })
+    .catch(error => {
+        console.error('生成过程中出现错误Error:', error);
+     // 请求失败时隐藏加载进度条
+     hideLoadingModal();
+        alert('生成过程中出现错误,请稍后再试。',error);
+    });
+}
+
+
+//更新卡片样式
+function updateCardAndModalStatus(id, isParentRow) {
+    // 获取对应的卡片元素,通过 oa_request_id 或 uniqueID 定位
+    const cardSelector = isParentRow ? `[data-oa-request-id="${id}"]` : `[data-unique-id="${id}"]`;
+    const card = document.querySelector(cardSelector);
+    console.log("generateLicense card", cardSelector, card);
+    
+    if (card) {
+        // 1. 更新卡片内许可证状态
+        const statusElement = card.querySelector('.license-status');
+        console.log("statusElement:", statusElement); // 检查状态元素是否存在
+        
+        if (statusElement) {
+            // 只更新许可证状态部分的文本,而不是整个 p 标签
+            statusElement.innerHTML = '许可证状态:已生成';
+            
+            // 更新状态的 CSS 类
+            statusElement.classList.remove('license-status-yellow', 'license-status-red');
+            statusElement.classList.add('license-status-green');
+        } else {
+            console.error('找不到 .license-status 元素');
+        }
+
+        // 2. 更新模态框中的状态(如果模态框已经打开)
+        const modalStatusElement = document.querySelector('.license-info-modal-header .license-status');
+        console.log("modalStatusElement:", modalStatusElement); // 检查状态元素是否存在
+        
+        if (modalStatusElement) {
+            modalStatusElement.textContent = '许可证状态:已生成';
+            modalStatusElement.classList.remove('license-status-yellow', 'license-status-red');
+            modalStatusElement.classList.add('license-status-green');
+        } else {
+            console.error('找不到 #license-info-modal-body .license-status 元素');
+        }
+
+        // 3. 更新模态框中的按钮为“分发”
+        const generateButton = document.getElementById('button1'); // 获取生成按钮
+        if (generateButton) {
+            generateButton.textContent = '分发'; // 修改按钮文本为“分发”
+            generateButton.removeEventListener('click', generateLicense); // 移除生成逻辑
+            generateButton.addEventListener('click', () => {
+                distributeLicense(id); // 添加分发逻辑
+            });
+        }
+
+        // 4. 启用 #downloadAllLicenses-button
+        const downloadButton = document.getElementById('downloadAllLicenses-button');
+        if (downloadButton) {
+            downloadButton.disabled = false; // 启用按钮
+            downloadButton.style.backgroundColor = '#007aff'; // 恢复正常的背景颜色
+            downloadButton.style.cursor = 'pointer'; // 修改鼠标样式为可点击
+        }
+
+    } else {
+        console.error(`找不到与 ID ${id} 对应的卡片`);
+    }
+}
+
+
+///-----------获取登录用户信息----------------------------------------
+async function fetchUsername() {
+    try {
+        const response = await fetch(`http://127.0.0.1:8080/api/admin/userInfo`, {
+            method: 'GET',
+            headers: {
+                'Authorization': `Bearer ${authToken}`,
+                'Content-Type': 'application/json'
+            }
+        });
+        
+        const data = await response.json();
+
+        currentUserRole = data.data.Role; // 存储当前用户的角色
+        currentUserName = data.data.Username;
+        
+        // 使用获取到的角色,调用获取权限的接口
+        await fetchPermissionsByRole(currentUserRole);
+
+        return data.data; // 返回获取到的用户信息数据
+    
+    } catch (error) {
+        console.error('Error fetching user info or permissions:', error);
+    }
+}
+
+// 将 fetchPermissionsByRole 转换为异步函数
+async function fetchPermissionsByRole(role) {
+    try {
+        const response = await fetch(`http://127.0.0.1:8080/api/admin/GetSelfRoles`, {
+            method: 'POST',
+            headers: {
+                'Authorization': `Bearer ${authToken}`,
+                'Content-Type': 'application/json'
+            },
+            body: JSON.stringify({ name: role })
+        });
+
+        const data = await response.json();
+        currentUserPermissions = data.data.Permissions; // 获取用户的权限数组
+        console.log('currentUserPermissions:', currentUserPermissions);
+        
+        // 定义权限类别
+        // const licensePermissions = ['upload_license', 'read_license'];
+        // const userPermissionsCheck = ['create_user', 'read_user', 'update_user', 'delete_user'];
+        // const rolePermissions = ['create_role', 'delete_role', 'update_role', 'get_role'];
+
+        // const hasLicenseAccess = licensePermissions.some(permission => userPermissions.includes(permission));
+        // const hasUserManagementAccess = userPermissionsCheck.some(permission => userPermissions.includes(permission));
+        // const hasRoleManagementAccess = rolePermissions.some(permission => userPermissions.includes(permission));
+
+        // 根据权限渲染菜单并显示初始页面
+        //renderMenuAndInitialPage(hasLicenseAccess, hasUserManagementAccess, hasRoleManagementAccess);
+    } catch (error) {
+        console.error('Error fetching permissions:', error);
+    }
+}
+
+
+//--------------进度条-------------------------------------------------
+// 创建模态框的 DOM 元素并插入到 body 中
+function createLoadingModal() {
+    const modalHTML = `
+        <div id="loadingModal" class="loading-modal" style="display: none;">
+            <div class="loading-modal-content">
+                <div class="spinner"></div>
+                <p id="loadingMessage">加载中...</p>
+            </div>
+        </div>
+    `;
+    document.body.insertAdjacentHTML('beforeend', modalHTML);
+}
+
+// 显示加载模态框
+function showLoadingModal(message = "加载中...") {
+    const loadingModal = document.getElementById('loadingModal');
+    const loadingMessage = document.getElementById('loadingMessage');
+    
+    if (loadingModal && loadingMessage) {
+        loadingMessage.textContent = message; // 设置显示的消息
+        loadingModal.style.display = 'flex'; // 显示模态框
+    }
+}
+
+// 隐藏加载模态框
+function hideLoadingModal() {
+    const loadingModal = document.getElementById('loadingModal');
+    
+    if (loadingModal) {
+        loadingModal.style.display = 'none'; // 隐藏模态框
+    }
+}
+
+// 页面加载时创建模态框
+document.addEventListener('DOMContentLoaded', createLoadingModal);
+
+
+
+
+
+
+//----------------------------------------
+
+
+
+
+//-----------搜索栏----------------------------
+
+// 获取搜索框元素
+// 获取搜索框、状态下拉框、时间选择框和按钮元素
+const searchBar = document.getElementById('search-bar');
+const statusFilter = document.getElementById('license-status-filter');
+const startDate = document.getElementById('start-date');
+const endDate = document.getElementById('end-date');
+const submitButton = document.getElementById('submit-button');
+const licenseInfoContainer = document.getElementById('license-info-restaurant-list');
+
+// 监听确定按钮的点击事件,点击后触发过滤功能
+submitButton.addEventListener('click', filterContent);
+
+// 过滤功能实现
+function filterContent() {
+    const searchTerm = searchBar.value.toLowerCase();
+    const selectedStatus = statusFilter.value;
+    const selectedStartDate = startDate.value;  // 日期格式为 YYYY-MM-DD
+    const selectedEndDate = endDate.value;      // 日期格式为 YYYY-MM-DD
+
+    // 获取所有 License 卡片
+    const licenseCards = document.querySelectorAll('.license-info-card');
+
+    licenseCards.forEach(function(card) {
+        const cardText = card.textContent.toLowerCase();  // 获取卡片文本
+
+        // 获取许可证状态,确保许可证状态元素存在
+        const statusElement = card.querySelector('.license-status');
+        const cardStatus = statusElement ? statusElement.textContent.trim() : '';
+
+        // 获取日期,确保日期元素存在
+        const dateElement = card.querySelector('.card-text.date');
+        const cardDate = dateElement ? dateElement.textContent.trim() : '';
+
+        // 检查是否符合搜索条件
+        const matchesSearch = cardText.includes(searchTerm);
+        
+        // 检查许可证状态是否符合过滤条件
+        const matchesStatus = !selectedStatus || cardStatus === selectedStatus;
+
+        // 检查日期范围是否符合过滤条件
+        let matchesDate = true;
+        if (selectedStartDate) {
+            matchesDate = cardDate >= selectedStartDate;
+        }
+        if (selectedEndDate) {
+            matchesDate = matchesDate && cardDate <= selectedEndDate;
+        }
+
+        // 显示符合所有过滤条件的卡片,隐藏不符合条件的卡片
+        if (matchesSearch && matchesStatus && matchesDate) {
+            card.style.display = '';  // 显示卡片
+        } else {
+            card.style.display = 'none';  // 隐藏卡片
+        }
+    });
+}
+
+

+ 0 - 0
static/license_info/old/license_info.css → static/license_info/old/old_license_info.css


+ 0 - 0
static/license_info/old/license_info.html → static/license_info/old/old_license_info.html


+ 0 - 199
static/license_info/script.js

@@ -1,199 +0,0 @@
-// 全局变量定义
-let page = 1; // 初始页码为1,代表从第1条数据开始获取
-let pageSize = 10; // 初始每次固定获取10条数据
-let total = 0; // 数据总量(从接口获取)
-let loadedItems = 0; // 已加载的数据条目数量
-let isLoading = false; // 防止多次加载
-const timeoutDuration = 10000; // 超时时间10秒
-const preLoadDistance = 300; // 距离底部300px时提前加载
-
-// 假设 Authorization 值存储在 localStorage 中,key 为 "authToken"
-const authToken = localStorage.getItem("Authorization");
-
-// 获取与 license_info 相关的 HTML 元素
-const license_info_container = document.getElementById('restaurant-list'); // 卡片列表容器
-const license_info_modal = document.getElementById('license-info-modal'); // 模态框容器
-const license_info_modalContent = document.querySelector('.license-info-modal-content'); // 模态框内容区域
-const license_info_modalTitle = document.getElementById('license-info-modal-title'); // 模态框标题
-const license_info_modalDescription = document.getElementById('license-info-modal-description'); // 模态框描述
-const license_info_modalPrice = document.getElementById('license-info-modal-price'); // 模态框产品信息
-const license_info_modalRating = document.getElementById('license-info-modal-rating'); // 模态框MAC地址
-const license_info_closeModal = document.querySelector('.license-info-close'); // 模态框关闭按钮
-const license_info_loadingIndicator = document.getElementById('loading-indicator'); // 加载提示元素
-const license_info_mainElement = document.querySelector('main'); // 主内容区域
-
-
-// 获取数据函数
-async function fetchLicenseData(page, pageSize) {
-    try {
-        const response = await fetch(`http://127.0.0.1:8080/api/admin/GetAllLicenseInfo?page=${page}&pageSize=${pageSize}`, {
-            method: 'GET',
-            headers: {
-                'Authorization': `Bearer ${authToken}`,
-                'Content-Type': 'application/json'
-            }
-        });
-        
-        const result = await response.json();
-        
-        // 设置总量,如果第一次加载,获取total字段
-        if (total === 0 && result.total) {
-            total = result.total;
-        }
-
-        return result.data || []; // 返回获取到的数据,或空数组
-
-    } catch (error) {
-        console.error("加载数据失败", error);
-        return []; // 返回空数组,防止后续操作出错
-    }
-}
-
-
-// 渲染 license_info 卡片数据函数
-function renderLicenseCards(data, clearContainer = false) {
-    if (clearContainer) {
-        license_info_container.innerHTML = ''; // 清空容器内容
-    }
-
-    data.forEach(group => {
-        const firstItem = group[0]; // 获取该组的第一个数据项
-
-        let statusClass = '';
-        if (firstItem.LicenseFlage === '已生成') {
-            statusClass = 'license-status-green';
-        } else if (firstItem.LicenseFlage === '未生成') {
-            statusClass = 'license-status-yellow';
-        } else if (firstItem.LicenseFlage === '已失效') {
-            statusClass = 'license-status-red';
-        }
-
-        const card = document.createElement('div');
-        card.className = 'license-info-card';
-        
-
-        
-        // 在卡片的第一行显示申请时间
-        card.innerHTML = `
-            <div class="license-info-card-header">
-                <h3 class="card-title">${firstItem.GlxmName}</h3>
-            </div>
-            <div class="license-info-card-content">
-                <p class="card-text">${firstItem.ApplicationDate} ${firstItem.ApplicationTime}</p> <!-- 显示日期和时间 -->
-                <p class="card-text">创建者:${firstItem.Creator}</p>
-                <p class="card-text">公司:${firstItem.Company}</p>
-                <p class="card-text ${statusClass}">许可证状态:${firstItem.LicenseFlage}</p>
-            </div>
-        `;
-    // 将卡片添加到容器中
-    license_info_container.appendChild(card); 
-
-
-
-    });
-    console.log("卡片渲染完成,准备添加监听器"); // 确保卡片已经渲染
-
-    // 确保在渲染完成后调用监听器绑定
-    addCardClickListeners();
-}
-
-
-
-
-// 检查页面是否填满,如果没有则继续加载更多数据
-function checkContentHeight() {
-    const documentHeight = document.documentElement.scrollHeight;
-    const windowHeight = window.innerHeight;
-
-    if (documentHeight <= windowHeight) {
-        console.log("页面内容不足,继续加载更多数据");
-        loadMoreData(); // 当内容高度不足时,继续加载更多数据
-    }
-}
-
-// 检查是否滚动到底部并触发加载
-async function checkAndLoadMore(scrollHeight, scrollTop, clientHeight) {
-    if (isLoading || loadedItems >= total) return; // 如果正在加载或已加载完所有数据则退出
-
-    console.log(`Scroll Info - scrollHeight: ${scrollHeight}, scrollTop: ${scrollTop}, clientHeight: ${clientHeight}`);
-
-    if (scrollTop + clientHeight >= scrollHeight - preLoadDistance) {
-        console.log(`触发加载更多数据:page=${page}, pageSize=${pageSize}`); // 每次触发时打印输出
-        await loadMoreData();
-    }
-}
-
-// 加载更多数据函数
-async function loadMoreData() {
-    if (isLoading) return; // 防止重复加载
-
-    isLoading = true;
-    console.log('开始加载更多数据');
-
-    // 显示加载提示
-    // license_info_loadingIndicator.style.display = 'block'; // 显示提示
-    // license_info_loadingIndicator.innerText = '正在加载...'; // 重置加载提示
-    
-    // 设置超时处理
-    const timeout = setTimeout(() => {
-        license_info_loadingIndicator.innerText = '加载超时,请重试'; // 修改提示语为超时提示
-        isLoading = false;
-        license_info_loadingIndicator.style.display = 'none'; // 超时后隐藏加载提示
-    }, timeoutDuration);
-
-    // 获取数据
-    const data = await fetchLicenseData(page, pageSize);
-
-    // 清除超时定时器
-    clearTimeout(timeout);
-
-    if (data.length > 0) {
-        // 渲染数据到页面
-        renderLicenseCards(data);
-
-        // 更新 page 和 pageSize,下一次请求从新的位置开始
-        page += 10; // 每次请求后,page 增加10,表示从下一组数据开始
-        pageSize += 10; // pageSize 每次递增10
-
-        // 更新已加载的条目数
-        loadedItems += data.length;
-
-        console.log('数据加载完成,更新页面');
-    }
-
-    // 隐藏加载提示
-    //license_info_loadingIndicator.style.display = 'none'; // 加载完成后隐藏提示
-    isLoading = false; // 请求完成,允许下次请求
-
-    // 检查内容高度,必要时继续加载
-    checkContentHeight();
-}
-
-
-// 监听 window 滚动
-window.addEventListener('scroll', () => {
-    checkAndLoadMore(document.body.scrollHeight, window.scrollY, window.innerHeight);
-});
-
-// 监听 main 容器的滚动
-license_info_mainElement.addEventListener('scroll', () => {
-    checkAndLoadMore(license_info_mainElement.scrollHeight, license_info_mainElement.scrollTop, license_info_mainElement.clientHeight);
-});
-
-
-
-// 初始化加载第一页
-(async function() {
-    const data = await fetchLicenseData(page, pageSize);
-    if (data.length > 0) {
-        renderLicenseCards(data); // 渲染数据到页面
-        loadedItems += data.length; // 更新已加载的条目数
-    }
-    //license_info_loadingIndicator.style.display = 'none'; // 初始化后隐藏加载提示
-
-    // 检查内容高度
-    checkContentHeight();
-})();
-
-//-----------点击卡片弹出模态框------------------------------------------------------
-

+ 0 - 144
static/license_info/styles.css

@@ -1,144 +0,0 @@
-/* 全局页面样式 */
-body {
-    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
-    margin: 0;
-    padding: 0;
-    background-color: #f2f2f7; /* iOS风格的浅灰背景 */
-}
-
-/* 头部样式 */
-header {
-    background-color: #ffffff;
-    color: #333;
-    padding: 20px;
-    text-align: center;
-    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); /* 轻微阴影 */
-}
-
-/* 容器布局:包含侧边栏和主内容 */
-.container {
-    display: flex;
-    height: calc(100vh - 70px); /* 视口高度减去头部高度 */
-}
-
-/* 侧边栏样式 */
-aside {
-    width: 200px;
-    background-color: #f2f2f7;
-    color: #000;
-    padding: 20px;
-    height: 100%;
-}
-
-aside ul {
-    list-style: none;
-    padding: 0;
-}
-
-aside ul li {
-    margin: 20px 0;
-    cursor: pointer;
-}
-
-/* 主内容区域 */
-main {
-    flex: 1; /* 占据剩余宽度 */
-    padding: 20px;
-    overflow-y: auto; /* 启用垂直滚动 */
-}
-
-/* 瀑布流容器样式,每行最多显示4个卡片 */
-.license-info-container {
-    display: flex;
-    flex-wrap: wrap;
-    gap: 20px; /* 卡片之间的间距 */
-}
-
-/* 卡片样式 */
-.license-info-card {
-    position: relative;
-    width: calc(25% - 20px); /* 每行4个卡片,减去间距 */
-    background-color: #ffffff; /* 白色背景 */
-    border-radius: 15px; /* iOS风格的圆角 */
-    box-shadow: 0 10px 20px rgba(0, 0, 0, 0.05); /* 轻微柔和的阴影 */
-    overflow: hidden;
-    transition: transform 0.3s ease-in-out, box-shadow 0.3s ease-in-out; /* 过渡效果 */
-    margin-bottom: 20px;
-    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; /* iOS 系统字体 */
-    padding-bottom: 20px; /* 添加底部填充,以防止内容溢出 */
-}
-
-/* 悬停效果 */
-.license-info-card:hover {
-    transform: translateY(-3px); /* 悬停时轻微向上浮动 */
-    box-shadow: 0 15px 25px rgba(0, 0, 0, 0.1); /* 悬停时增加阴影效果 */
-}
-
-/* 卡片顶部蓝色区域 */
-.license-info-card-header {
-    background-color: #007aff; /* iOS风格的蓝色 */
-    height: 50px; /* 顶部蓝色部分的高度 */
-    border-top-left-radius: 15px;
-    border-top-right-radius: 15px;
-    display: flex;
-    justify-content: center;
-    align-items: center;
-    padding: 10px;
-    text-align: center;
-    color: white; /* 项目名称字体颜色为白色 */
-}
-
-/* 标题样式:现在位于蓝色区域 */
-.card-title {
-    font-size: 20px; /* 标题字体加大 */
-    font-weight: 600; /* 标题字体加粗 */
-    color: white; /* 项目名称字体颜色为白色,和背景颜色形成对比 */
-    margin: 0;
-    line-height: 1.2; /* 行间距 */
-}
-
-/* 副标题和其他文本样式 */
-.license-info-card-content {
-    padding: 20px;
-    color: #333;
-}
-
-.card-text {
-    margin: 10px 0; /* 增加字段之间的间距 */
-    font-size: 18px; /* 提高字体大小 */
-    color: #3a3a3c; /* 中灰色,符合iOS风格 */
-    line-height: 1.6; /* 行间距调整,确保更好的可读性 */
-}
-
-/* 许可证状态的颜色 */
-.license-status-green {
-    color: #34c759; /* iOS的绿色 */
-}
-
-.license-status-yellow {
-    color: #ffcc00; /* iOS的黄色 */
-}
-
-.license-status-red {
-    color: #ff3b30; /* iOS的红色 */
-}
-
-
-
-/*这部分代码仅在设备屏幕宽度小于或等于 768 像素时生效
-*/
-@media (max-width: 768px) {
-    .license-info-modal-content.show {
-        width: 90vw;
-        height: 90vh;
-    }
-
-    /* 小屏幕下卡片调整为每行显示2个 */
-    .license-info-card {
-        width: calc(50% - 20px);
-        height: auto; /* 调整小屏幕下的高度 */
-    }
-}
-
-
-/*-----------------*/

+ 0 - 0
static/role/role.css


+ 38 - 0
static/role/role.html

@@ -0,0 +1,38 @@
+
+
+
+<!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="../role/role.css">
+</head>
+<body>
+ 
+    <h2>用角色页面</h2>
+    <p>这是角色管理相关的内容。</p>
+   
+
+
+    <!-- 新增的角色管理容器 -->
+    <div id="role-management" style="display: none;">
+        <div
+            style="display: flex; align-items: center; justify-content: space-between;">
+            <h2>角色管理</h2>
+            <button id="createRoleButton"
+                class="create-role-button">创建角色</button>
+        </div>
+        <div id="roles-container"></div>
+    </div>
+    
+    
+    
+<script src="../role/role.js"></script>
+
+</body>
+</html>
+
+

+ 0 - 0
static/role/role.js


+ 197 - 0
static/user/user.css

@@ -0,0 +1,197 @@
+
+    .user-info-cards-container {
+        display: flex;
+        flex-wrap: wrap;
+        gap: 16px;
+        justify-content: flex-start;
+    }
+    
+    .user-info-card {
+        background-color: #f9f9f9;
+        border-radius: 8px;
+        padding: 16px;
+        box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+        width: 300px;
+        transition: transform 0.2s ease-in-out;
+    }
+    
+    .user-info-card:hover {
+        transform: scale(1.05);
+    }
+    
+    .user-info-card-content p {
+        margin: 8px 0;
+        font-size: 14px;
+    }
+    
+    .user-info-create-role-button {
+        background-color: #007aff;
+        color: white;
+        border: none;
+        padding: 10px 20px;
+        border-radius: 10px;
+        cursor: pointer;
+        font-size: 16px;
+        font-weight: 500;
+        box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+        transition: background-color 0.3s;
+        margin-left: 20px; /* 与标题之间的间距 */
+    }
+    
+    .user-info-create-role-button:hover {
+        background-color: #005bb5;
+    }
+    
+    
+    /*点击用户信息卡片弹出模态框*/
+    /* 模态框的背景(遮罩层) */
+.user-info-modal {
+        display: none; /* 默认隐藏 */
+        position: fixed; /* 固定位置 */
+        z-index: 1; /* 位于页面的最上层 */
+        left: 0;
+        top: 0;
+        width: 100%;
+        height: 100%;
+        overflow: auto; /* 允许滚动 */
+        background-color: rgba(0, 0, 0, 0.5); /* 半透明黑色背景 */
+    }
+
+/* 模态框内容 */
+    .user-info-modal-content {
+        background-color: #fefefe;
+        margin: 15% auto; /* 模态框居中 */
+        padding: 20px;
+        border: 1px solid #888;
+        width: 400px; /* 模态框宽度 */
+        border-radius: 8px;
+    }
+
+/* 关闭按钮 */
+.user-info-close {
+    color: #aaa;
+    float: right;
+    font-size: 28px;
+    font-weight: bold;
+}
+
+.user-info-close:hover {
+    color: #ff3b30; /* iOS 关闭按钮的红色 */
+}
+.user-info-close:focus {
+    color: black;
+    text-decoration: none;
+    cursor: pointer;
+}
+
+/* 模态框内的按钮 */
+.user-info-modal-buttons {
+    margin-top: 20px;
+    display: flex;
+    justify-content: space-between;
+}
+
+.user-info-button {
+    padding: 10px 20px;
+    background-color: #4CAF50;
+    color: white;
+    border: none;
+    border-radius: 4px;
+    cursor: pointer;
+    transition: background-color 0.3s;
+}
+
+.user-info-button:hover {
+    background-color: #45a049;
+}
+
+
+/*添加用户模态框样式-----------------------------------------------*/
+/* 表单组样式 */
+/* 表单组样式 */
+/* 添加用户模态框的背景(遮罩层) */
+.add-user-modal {
+    display: none; /* 默认隐藏模态框 */
+    position: fixed;
+    left: 0;
+    top: 0;
+    width: 100%;
+    height: 100%;
+    background-color: rgba(0, 0, 0, 0.5); /* 背景半透明 */
+    display: flex; /* 使用 Flexbox 布局来居中内容 */
+    align-items: center; /* 垂直居中 */
+    justify-content: center; /* 水平居中 */
+}
+
+/* 添加用户模态框内容 */
+.add-user-modal-content {
+    background-color: #fefefe;
+    margin: 1% auto; /* 模态框居中 */
+    padding: 20px;
+    border: 1px solid #888;
+    max-width: 1000px;
+    width: 550px; /* 模态框宽度 */
+    border-radius: 8px;
+}
+
+/* 关闭按钮 */
+.add-user-close {
+    position: absolute;
+    right: 20px;
+    top: 20px;
+    color: #888;
+    font-size: 24px;
+    font-weight: bold;
+    cursor: pointer;
+    transition: color 0.2s ease;
+}
+
+.add-user-close:hover {
+    color: #ff3b30; /* iOS 关闭按钮的红色 */
+}
+
+/* 表单组样式 */
+.add-user-form-group {
+    margin-bottom: 20px;
+}
+
+/* iOS 风格输入框 */
+.add-user-flat-rounded-input {
+    width: 100%;
+    padding: 12px;
+    font-size: 16px;
+    border: none;
+    border-radius: 12px;
+    background-color: #f7f7f7; /* iOS风格浅灰色背景 */
+    box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1); /* iOS风格内阴影 */
+    margin: 10px 0;
+    transition: background-color 0.2s ease;
+    appearance: none; /* 移除浏览器默认样式 */
+}
+
+.add-user-flat-rounded-input:focus {
+    outline: none;
+    background-color: #e8e8e8; /* 聚焦时的浅色背景 */
+}
+
+/* iOS 风格按钮 */
+.add-user-flat-rounded-button {
+    width: 100%;
+    background-color: #007aff; /* iOS经典蓝色 */
+    color: white;
+    padding: 14px 0;
+    font-size: 16px;
+    border: none;
+    border-radius: 12px;
+    cursor: pointer;
+    transition: background-color 0.2s ease;
+    font-weight: 600;
+}
+
+.add-user-flat-rounded-button:hover {
+    background-color: #005ecb; /* 按钮 hover 时更深的蓝色 */
+}
+
+.add-user-flat-rounded-button:active {
+    background-color: #004a99; /* 按下时的颜色变化 */
+}

+ 231 - 0
static/user/user.js

@@ -0,0 +1,231 @@
+
+document.addEventListener('DOMContentLoaded', function() {
+    // 确保在页面内容完全加载后执行
+    console.log('user.js loaded');
+    // 其他 JavaScript 代码
+});
+
+
+/*-------------------------------------------------- */
+
+// 统一的打开模态框函数
+function openModal(modalId) {
+    const modal = document.getElementById(modalId);
+    if (modal) {
+        modal.style.display = "block"; // 显示模态框
+    } else {
+        console.error('模态框不存在,ID:', modalId);
+    }
+}
+
+// 统一的关闭模态框函数
+function closeModal(modalId) {
+    const modal = document.getElementById(modalId);
+    if (modal) {
+        modal.style.display = "none"; // 隐藏模态框
+    } else {
+        console.error('模态框不存在,ID:', modalId);
+    }
+}
+
+// 点击"新增用户"按钮时,打开添加用户的模态框
+document.getElementById("addUserButton").addEventListener('click', function() {
+    saveNewUserActionModal(); // 打开的是添加用户的模态框
+});
+/*-------------------------------------------------- */
+
+fetchUsers();
+function fetchUsers() {
+    console.log('Fetching users...');
+    fetch('http://127.0.0.1:8080/api/admin/userInfoAll', {
+        method: 'GET',
+        headers: {
+            'Authorization': `Bearer ${authToken}`,
+            'Content-Type': 'application/json'
+        }
+    })
+    .then(response => response.json())
+    .then(data => {
+        const cardsContainer = document.querySelector('#users-cards');
+        cardsContainer.innerHTML = ''; // 清空旧的卡片内容
+        data.data.forEach(user => {
+            if (user.Account === 'admin') {
+                return;
+            }
+
+            const card = document.createElement('div');
+            card.classList.add('user-info-card');
+            card.innerHTML = `
+                <div class="user-info-card-content">
+                    <p><strong>用户名:</strong> ${user.Username}</p>
+                    <p><strong>电话:</strong> ${user.Telephone}</p>
+                    <p><strong>邮箱:</strong> ${user.Email}</p>
+                    <p><strong>权限:</strong> ${user.Role}</p>
+                    <p><strong>账号:</strong> ${user.Account}</p>
+                </div>
+            `;
+
+            card.addEventListener('click', (event) => {
+                openUserActionModal(user);
+            });
+
+            cardsContainer.appendChild(card);
+        });
+    });
+}
+
+//打开用户信息模态框
+function openUserActionModal(user) {
+    // 填充用户信息到模态框
+    document.getElementById("modal-username").innerText = `用户名: ${user.Username}`;
+    document.getElementById("modal-telephone").innerText = `电话: ${user.Telephone}`;
+    document.getElementById("modal-email").innerText = `邮箱: ${user.Email}`;
+    document.getElementById("modal-role").innerText = `权限: ${user.Role}`;
+    document.getElementById("modal-account").innerText = `账号: ${user.Account}`;
+
+    // 调用统一的 openModal 函数来显示模态框
+    openModal('userActionModal');
+
+    // 处理修改用户按钮点击
+    document.getElementById("editUserButton").onclick = () => {
+        editUser(user);
+    };
+
+    // 处理删除用户按钮点击
+    document.getElementById("deleteUserButton").onclick = () => {
+        deleteUser(user);
+    };
+
+    // 处理修改密码按钮点击
+    document.getElementById("resetPasswordButton").onclick = () => {
+        resetUserPassword(user);
+    };
+
+    // 关闭模态框的点击事件(右上角关闭按钮)
+    document.querySelector(".user-info-close").onclick = function() {
+        closeModal("userActionModal"); // 使用封装好的 closeModal 函数
+    };
+
+    // 点击模态框外部时关闭模态框
+    window.onclick = function(event) {
+        const modal = document.getElementById("userActionModal");
+        if (event.target === modal) {
+            closeModal("userActionModal"); // 使用封装好的 closeModal 函数
+        }
+    };
+}
+
+
+
+
+function editUser(user) {
+    console.log("Edit user", user);
+    // 在这里实现修改用户逻辑
+}
+
+function deleteUser(user) {
+    console.log("Delete user", user);
+    // 在这里实现删除用户逻辑
+}
+
+function resetUserPassword(user) {
+    console.log("Reset password for user", user);
+    // 在这里实现修改密码逻辑
+}
+
+
+
+
+
+
+// 保存新用户
+// 保存新用户的函数,并在适当的时候打开和关闭模态框
+function saveNewUserActionModal(user) {
+
+
+    openModal('user-info-addUserModal'); // 打开的是添加用户的模态框
+
+    // 点击模态框外部时关闭模态框
+    window.onclick = function(event) {
+        const modal = document.getElementById("user-info-addUserModal");
+        if (event.target === modal) {
+            closeModal("user-info-addUserModal"); // 使用封装好的 closeModal 函数
+        }
+    };
+}
+
+
+
+
+function saveNewUser() {
+ 
+    const username = document.getElementById('user-info-addUsername').value.trim();
+    const password = document.getElementById('user-info-addPassword').value.trim();
+    const account = document.getElementById('user-info-addAccount').value.trim();
+    const telephone = document.getElementById('user-info-addTelephone').value.trim();
+    const email = document.getElementById('user-info-addEmail').value.trim();
+
+    // 验证每个字段都不能为空
+    if (!username || !password || !account || !telephone || !email) {
+        alert('有空选项未填写');
+        return;
+    }
+
+    // 验证电话是否为11位数字
+    const phonePattern = /^\d{11}$/;
+    if (!phonePattern.test(telephone)) {
+        alert('电话必须是11位数字');
+        return;
+    }
+
+    // 验证密码长度是否至少为6位
+    if (password.length < 6) {
+        alert('密码长度必须至少为6位');
+        return;
+    }
+
+    // 验证邮箱是否包含@符号
+    if (!email.includes('@')) {
+        alert('邮箱必须包含@符号');
+        return;
+    }
+
+    // 验证账号长度是否至少为3位
+    if (account.length < 3) {
+        alert('账号长度必须至少为3位');
+        return;
+    }
+
+    // 构造新用户对象
+    const newUser = {
+        Username: username,
+        Password: password,
+        Account: account,
+        Telephone: telephone,
+        Email: email
+    };
+
+    // 发起请求保存新用户
+    fetch('http://127.0.0.1:8080/api/register', {  // 修改为实际的API路径
+        method: 'POST',
+        headers: {
+            'Authorization': `Bearer ${authToken}`,  // 确保 authToken 是有效的
+            'Content-Type': 'application/json'
+        },
+        body: JSON.stringify(newUser)
+    })
+    .then(response => response.json())
+    .then(data => {
+        if (data.success) {
+            alert('用户创建成功!');
+            fetchUsers(); // 重新加载用户列表
+            closeModal('user-info-addUserModal'); // 关闭模态框
+        } else {
+            alert('用户创建失败:' + data.error);
+        }
+    })
+    .catch(error => {
+        alert('请求失败:' + error);
+        closeModal('user-info-addUserModal'); // 即使请求失败也可以选择关闭模态框
+    });
+}

+ 89 - 0
static/user/user_management.html

@@ -0,0 +1,89 @@
+<!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="../user/user.css">
+</head>
+<body>
+ 
+    <h2>用户管理页面</h2>
+    <p>这是用户管理相关的内容。</p>
+   
+
+
+    <div id="user-management">
+        <div style="display: flex; align-items: center; justify-content: space-between;">
+            <h2>用户管理</h2> 
+            <button id="addUserButton" class="user-info-create-role-button" >添加用户</button>           <!-- style="display: none;" -->
+        </div>
+        <div id="users-cards" class="user-info-cards-container">
+ 
+        </div>
+    </div>
+    
+
+
+<!-- 模态框结构 -->
+<div id="userActionModal" class="user-info-modal" >
+    <div class="user-info-modal-content">
+        <span class="user-info-close">&times;</span>
+        <h2>用户信息</h2>
+        <p id="modal-username"><strong>用户名:</strong> </p>
+        <p id="modal-telephone"><strong>电话:</strong> </p>
+        <p id="modal-email"><strong>邮箱:</strong> </p>
+        <p id="modal-role"><strong>权限:</strong> </p>
+        <p id="modal-account"><strong>账号:</strong> </p>
+        <div class="user-info-modal-buttons">
+            <button id="editUserButton" class="user-info-button">修改用户</button>
+            <button id="deleteUserButton" class="user-info-button">删除用户</button>
+            <button id="resetPasswordButton" class="user-info-button">修改密码</button>
+        </div>
+    </div>
+</div>
+
+
+<!-- 用户管理的添加用户模态框 -->
+<!-- 添加用户模态框 -->
+<div id="user-info-addUserModal" class="add-user-modal" style="display: none;">
+    <div class="add-user-modal-content">
+        <span class="add-user-close">&times;</span>
+        <h3>添加用户</h3>
+        <form id="addUserForm">
+            <div class="add-user-form-group">
+                <label for="user-info-addUsername">用户名:</label>
+                <input type="text" id="user-info-addUsername" name="username" class="add-user-flat-rounded-input" required>
+            </div>
+
+            <div class="add-user-form-group">
+                <label for="user-info-addPassword">密码:</label>
+                <input type="password" id="user-info-addPassword" name="password" class="add-user-flat-rounded-input" required>
+            </div>
+
+            <div class="add-user-form-group">
+                <label for="user-info-addAccount">账号:</label>
+                <input type="text" id="user-info-addAccount" name="account" class="add-user-flat-rounded-input" required>
+            </div>
+
+            <div class="add-user-form-group">
+                <label for="user-info-addTelephone">电话:</label>
+                <input type="text" id="user-info-addTelephone" name="telephone" class="add-user-flat-rounded-input" required>
+            </div>
+
+            <div class="add-user-form-group">
+                <label for="user-info-addEmail">邮箱:</label>
+                <input type="email" id="user-info-addEmail" name="email" class="add-user-flat-rounded-input" required>
+            </div>
+
+            <button type="button" onclick="saveNewUser()" class="add-user-flat-rounded-button">保存</button>
+        </form>
+    </div>
+</div>
+<script src="../user/user.js"></script>
+
+</body>
+</html>
+
+

+ 30 - 29
模拟oa库环境.sql

@@ -67,6 +67,35 @@ CREATE TABLE "XUGU"."PRJ_PROJECTINFO" (
 );
 
 
+        -- 插入到 WORKFLOW_SELECTITEM 表
+        INSERT INTO XUGU.WORKFLOW_SELECTITEM (
+            "SELECTVALUE", 
+            "SELECTNAME", 
+            "FIELDID"
+        ) VALUES (
+            1,  -- SELECTVALUE
+            '虚谷v12 ' ,  -- SELECTNAME
+             14627  -- FIELDID
+        );
+        INSERT INTO XUGU.WORKFLOW_SELECTITEM (
+            "SELECTVALUE", 
+            "SELECTNAME", 
+            "FIELDID"
+        ) VALUES (
+            1,  -- SELECTVALUE
+            '分布式 ' ,  -- SELECTNAME
+             14628  -- FIELDID
+        );
+        -- 插入到 HRMRESOURCE 表
+        INSERT INTO XUGU.HRMRESOURCE (
+    "ID", 
+    "LASTNAME"
+) VALUES 
+    (1, '勾童'),
+    (2, '何放'),
+    (3, '魏粤川'),
+    (4, '王安迪'),
+    (5, '范文涛');
 
 
 
@@ -176,38 +205,10 @@ BEGIN
     COMMIT;
 END;
 /
+InsertDataLoop();
 
 
 
-        -- 插入到 WORKFLOW_SELECTITEM 表
-        INSERT INTO XUGU.WORKFLOW_SELECTITEM (
-            "SELECTVALUE", 
-            "SELECTNAME", 
-            "FIELDID"
-        ) VALUES (
-            1,  -- SELECTVALUE
-            '虚谷v12 ' ,  -- SELECTNAME
-             14627  -- FIELDID
-        );
-        INSERT INTO XUGU.WORKFLOW_SELECTITEM (
-            "SELECTVALUE", 
-            "SELECTNAME", 
-            "FIELDID"
-        ) VALUES (
-            1,  -- SELECTVALUE
-            '分布式 ' ,  -- SELECTNAME
-             14628  -- FIELDID
-        );
-        -- 插入到 HRMRESOURCE 表
-        INSERT INTO XUGU.HRMRESOURCE (
-            "ID", 
-            "LASTNAME"
-        ) VALUES (
-           1,  -- ID
-            '勾童测试'  -- LASTNAME
-        );
-
-InsertDataLoop();
 
 
 --------