使用new_web中的内容开始重写会员部分原型
This commit is contained in:
parent
75f3418a7e
commit
e6065a5f8f
|
|
@ -0,0 +1,203 @@
|
||||||
|
# 信息管理系统原型项目
|
||||||
|
|
||||||
|
## 项目概述
|
||||||
|
这是一个传统的中文信息管理系统原型,采用纯HTML/CSS/JavaScript技术栈,实现左侧菜单栏 + 中央tab展示区域的经典布局。项目专注于演示系统架构和交互流程,不追求视觉美观。
|
||||||
|
|
||||||
|
## 核心架构
|
||||||
|
|
||||||
|
### 技术栈
|
||||||
|
- **前端**: 纯HTML5 + CSS3 + 原生JavaScript ES6+
|
||||||
|
- **架构模式**: 单页应用(SPA) + 动态内容加载
|
||||||
|
- **样式系统**: 自定义CSS,无外部框架依赖
|
||||||
|
- **模块化**: 页面内容与逻辑完全分离
|
||||||
|
|
||||||
|
### 文件结构
|
||||||
|
```
|
||||||
|
merchant/
|
||||||
|
├── index.html # 主框架页面
|
||||||
|
├── styles.css # 全局样式文件
|
||||||
|
├── script.js # 核心JavaScript逻辑
|
||||||
|
├── pages/ # 页面内容目录
|
||||||
|
│ ├── level-settings.html # 等级设置页面
|
||||||
|
│ └── member-view.html # 会员查看页面
|
||||||
|
└── CLAUDE.md # 项目文档
|
||||||
|
```
|
||||||
|
|
||||||
|
### 架构特点
|
||||||
|
1. **内容与逻辑分离**: 所有页面内容存储为独立HTML文件
|
||||||
|
2. **动态加载**: 使用fetch API异步加载页面内容
|
||||||
|
3. **Tab管理**: 支持多页面同时打开,可独立关闭
|
||||||
|
4. **菜单系统**: 二级菜单展开/收起,防重复打开
|
||||||
|
|
||||||
|
## 核心功能
|
||||||
|
|
||||||
|
### 左侧菜单系统
|
||||||
|
- **一级菜单**: 主功能模块入口
|
||||||
|
- **二级菜单**: 具体功能页面,点击展开/收起
|
||||||
|
- **当前实现**: 会员管理 > 等级设置、会员查看
|
||||||
|
|
||||||
|
### 中央Tab系统
|
||||||
|
- **多页面管理**: 支持同时打开多个页面tab
|
||||||
|
- **防重复**: 相同页面不会重复打开,直接激活已存在tab
|
||||||
|
- **可关闭**: 每个tab都有独立关闭按钮(首页除外)
|
||||||
|
- **智能切换**: 关闭当前tab时自动激活相邻tab
|
||||||
|
|
||||||
|
### 页面加载机制
|
||||||
|
- **异步加载**: 使用`fetch`API动态加载HTML内容
|
||||||
|
- **错误处理**: 包含完整的加载失败降级方案
|
||||||
|
- **缓存管理**: 每次点击都重新加载,保证内容最新
|
||||||
|
|
||||||
|
## 开发约束和规范
|
||||||
|
|
||||||
|
### 重要约束 ⚠️
|
||||||
|
1. **禁止硬编码HTML**: 所有页面内容必须存储在独立的HTML文件中
|
||||||
|
2. **优先编辑现有文件**: 禁止随意创建新文件,优先修改现有文件
|
||||||
|
3. **保持架构一致性**: 新页面必须遵循现有的加载和展示模式
|
||||||
|
4. **中文本地化**: 所有界面文本必须使用中文
|
||||||
|
|
||||||
|
### 开发规范
|
||||||
|
```javascript
|
||||||
|
// 添加新页面的步骤:
|
||||||
|
// 1. 在pages/目录创建对应的HTML文件
|
||||||
|
// 2. 在HTML中添加新的菜单项,指向该页面
|
||||||
|
// 3. 确保contentType参数与文件名匹配
|
||||||
|
|
||||||
|
// 正确的调用方式:
|
||||||
|
onclick="openTab('新页面', 'new-page')"
|
||||||
|
// 对应文件:pages/new-page.html
|
||||||
|
```
|
||||||
|
|
||||||
|
### 文件命名规范
|
||||||
|
- 页面文件:`pages/功能名称.html` (使用kebab-case)
|
||||||
|
- contentType参数:与文件名保持一致
|
||||||
|
- Tab标题:使用中文友好名称
|
||||||
|
|
||||||
|
## 核心函数说明
|
||||||
|
|
||||||
|
### `openTab(title, contentType)`
|
||||||
|
- **参数**: title(中文标题), contentType(文件名前缀)
|
||||||
|
- **功能**: 创建新tab并异步加载页面内容
|
||||||
|
- **特性**: 防重复打开、错误处理、自动激活
|
||||||
|
|
||||||
|
### `loadPageContent(contentType)`
|
||||||
|
- **参数**: contentType(对应pages/目录下的文件名)
|
||||||
|
- **返回**: Promise<string> HTML内容
|
||||||
|
- **错误处理**: 加载失败时返回默认提示内容
|
||||||
|
|
||||||
|
### `toggleSubmenu(submenuId)`
|
||||||
|
- **参数**: submenuId(子菜单DOM元素ID)
|
||||||
|
- **功能**: 切换子菜单展开/收起状态
|
||||||
|
- **特性**: 互斥展开(同时只能展开一个子菜单)
|
||||||
|
|
||||||
|
## 扩展指南
|
||||||
|
|
||||||
|
### 添加新的功能模块
|
||||||
|
1. **创建一级菜单**:
|
||||||
|
```html
|
||||||
|
<div class="menu-item">
|
||||||
|
<div class="menu-title" onclick="toggleSubmenu('new-submenu')">
|
||||||
|
<span>新功能模块</span>
|
||||||
|
<span class="arrow">▼</span>
|
||||||
|
</div>
|
||||||
|
<div class="submenu" id="new-submenu">
|
||||||
|
<!-- 二级菜单项 -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **添加二级菜单项**:
|
||||||
|
```html
|
||||||
|
<div class="submenu-item" onclick="openTab('功能名称', 'function-name')">
|
||||||
|
功能名称
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **创建页面内容**:
|
||||||
|
```bash
|
||||||
|
# 在pages/目录创建对应HTML文件
|
||||||
|
pages/function-name.html
|
||||||
|
```
|
||||||
|
|
||||||
|
### 页面内容模板
|
||||||
|
```html
|
||||||
|
<div class="page-content">
|
||||||
|
<h2>页面标题</h2>
|
||||||
|
|
||||||
|
<!-- 表单区域 -->
|
||||||
|
<div class="form-group">
|
||||||
|
<label>字段名称:</label>
|
||||||
|
<input type="text" placeholder="请输入...">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 操作按钮 -->
|
||||||
|
<button class="btn btn-primary">主要操作</button>
|
||||||
|
<button class="btn">次要操作</button>
|
||||||
|
|
||||||
|
<!-- 数据表格 -->
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>列标题</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>数据内容</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 样式系统
|
||||||
|
|
||||||
|
### CSS类命名规范
|
||||||
|
- `.page-content`: 页面内容容器
|
||||||
|
- `.form-group`: 表单字段组
|
||||||
|
- `.btn`, `.btn-primary`: 按钮样式
|
||||||
|
- `.table`: 数据表格
|
||||||
|
- `.menu-item`, `.submenu-item`: 菜单项
|
||||||
|
|
||||||
|
### 主题色彩
|
||||||
|
- 主色调:深蓝色 `#2c3e50`
|
||||||
|
- 高亮色:蓝色 `#3498db`
|
||||||
|
- 背景色:浅灰 `#f5f5f5`
|
||||||
|
- 边框色:灰色 `#bdc3c7`
|
||||||
|
|
||||||
|
## 测试和部署
|
||||||
|
|
||||||
|
### 本地运行
|
||||||
|
```bash
|
||||||
|
# 推荐使用本地HTTP服务器(必需,因为使用了fetch API)
|
||||||
|
python -m http.server 8000
|
||||||
|
# 或
|
||||||
|
npx serve .
|
||||||
|
# 然后访问 http://localhost:8000
|
||||||
|
```
|
||||||
|
|
||||||
|
### 浏览器兼容性
|
||||||
|
- Chrome 60+ (支持fetch API和ES6 async/await)
|
||||||
|
- Firefox 55+
|
||||||
|
- Safari 12+
|
||||||
|
- Edge 79+
|
||||||
|
|
||||||
|
### 快捷键
|
||||||
|
- `ESC`: 关闭当前激活的tab(首页除外)
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
### 开发限制
|
||||||
|
- ❌ 不要在JavaScript中硬编码HTML内容
|
||||||
|
- ❌ 不要随意创建新文件,优先编辑现有文件
|
||||||
|
- ❌ 不要破坏现有的架构模式
|
||||||
|
- ✅ 所有页面内容必须存储在pages/目录
|
||||||
|
- ✅ 使用中文界面文本
|
||||||
|
- ✅ 遵循现有的命名和结构约定
|
||||||
|
|
||||||
|
### 技术限制
|
||||||
|
- 必须运行在HTTP服务器环境(不支持file://协议)
|
||||||
|
- 依赖现代浏览器的fetch API和ES6特性
|
||||||
|
- 所有数据都是前端模拟,无后端API集成
|
||||||
|
|
||||||
|
## 项目目标
|
||||||
|
此项目是一个快速原型系统,用于演示传统管理系统的布局意图和交互流程。专注于功能架构而非视觉设计,为后续开发提供清晰的技术基础和扩展方向。
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
<!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="styles.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<!-- 左侧菜单栏 -->
|
||||||
|
<div class="sidebar">
|
||||||
|
<div class="logo">
|
||||||
|
<h2>管理系统</h2>
|
||||||
|
</div>
|
||||||
|
<nav class="menu">
|
||||||
|
<div class="menu-item">
|
||||||
|
<div class="menu-title" onclick="toggleSubmenu('member-submenu')">
|
||||||
|
<span>会员管理</span>
|
||||||
|
<span class="arrow">▼</span>
|
||||||
|
</div>
|
||||||
|
<div class="submenu" id="member-submenu">
|
||||||
|
<div class="submenu-item" onclick="openTab('等级设置', 'level-settings')">等级设置</div>
|
||||||
|
<div class="submenu-item" onclick="openTab('会员查看', 'member-view')">会员查看</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 中央内容区域 -->
|
||||||
|
<div class="main-content">
|
||||||
|
<!-- Tab栏 -->
|
||||||
|
<div class="tab-bar">
|
||||||
|
<div class="tab active" id="home-tab">
|
||||||
|
<span>首页</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 内容展示区域 -->
|
||||||
|
<div class="content-area">
|
||||||
|
<div class="tab-content active" id="home-content">
|
||||||
|
<h1>欢迎使用信息管理系统</h1>
|
||||||
|
<p>请从左侧菜单选择功能模块</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="script.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
<div class="page-content">
|
||||||
|
<h2>等级设置</h2>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>等级名称:</label>
|
||||||
|
<input type="text" placeholder="请输入等级名称">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>等级要求:</label>
|
||||||
|
<input type="number" placeholder="积分要求">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>等级描述:</label>
|
||||||
|
<textarea placeholder="请输入等级描述" rows="3"></textarea>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-primary">保存设置</button>
|
||||||
|
<button class="btn">取消</button>
|
||||||
|
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>等级名称</th>
|
||||||
|
<th>积分要求</th>
|
||||||
|
<th>会员数量</th>
|
||||||
|
<th>操作</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>普通会员</td>
|
||||||
|
<td>0</td>
|
||||||
|
<td>1,234</td>
|
||||||
|
<td>
|
||||||
|
<button class="btn">编辑</button>
|
||||||
|
<button class="btn">删除</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>银牌会员</td>
|
||||||
|
<td>1,000</td>
|
||||||
|
<td>567</td>
|
||||||
|
<td>
|
||||||
|
<button class="btn">编辑</button>
|
||||||
|
<button class="btn">删除</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>金牌会员</td>
|
||||||
|
<td>5,000</td>
|
||||||
|
<td>123</td>
|
||||||
|
<td>
|
||||||
|
<button class="btn">编辑</button>
|
||||||
|
<button class="btn">删除</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
<div class="page-content">
|
||||||
|
<h2>会员查看</h2>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>搜索会员:</label>
|
||||||
|
<input type="text" placeholder="请输入会员姓名或手机号">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>会员等级:</label>
|
||||||
|
<select>
|
||||||
|
<option value="">全部等级</option>
|
||||||
|
<option value="normal">普通会员</option>
|
||||||
|
<option value="silver">银牌会员</option>
|
||||||
|
<option value="gold">金牌会员</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-primary">搜索</button>
|
||||||
|
<button class="btn">重置</button>
|
||||||
|
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>会员ID</th>
|
||||||
|
<th>姓名</th>
|
||||||
|
<th>手机号</th>
|
||||||
|
<th>等级</th>
|
||||||
|
<th>积分</th>
|
||||||
|
<th>注册时间</th>
|
||||||
|
<th>操作</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>M001</td>
|
||||||
|
<td>张小明</td>
|
||||||
|
<td>138****1234</td>
|
||||||
|
<td>金牌会员</td>
|
||||||
|
<td>8,500</td>
|
||||||
|
<td>2024-01-15</td>
|
||||||
|
<td>
|
||||||
|
<button class="btn">查看详情</button>
|
||||||
|
<button class="btn">编辑</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>M002</td>
|
||||||
|
<td>李小红</td>
|
||||||
|
<td>139****5678</td>
|
||||||
|
<td>银牌会员</td>
|
||||||
|
<td>2,300</td>
|
||||||
|
<td>2024-02-20</td>
|
||||||
|
<td>
|
||||||
|
<button class="btn">查看详情</button>
|
||||||
|
<button class="btn">编辑</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>M003</td>
|
||||||
|
<td>王小刚</td>
|
||||||
|
<td>137****9012</td>
|
||||||
|
<td>普通会员</td>
|
||||||
|
<td>450</td>
|
||||||
|
<td>2024-03-10</td>
|
||||||
|
<td>
|
||||||
|
<button class="btn">查看详情</button>
|
||||||
|
<button class="btn">编辑</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,190 @@
|
||||||
|
// 全局变量
|
||||||
|
let tabCounter = 0;
|
||||||
|
let activeTabId = 'home-tab';
|
||||||
|
|
||||||
|
// 页面加载完成后初始化
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
initializeSystem();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 初始化系统
|
||||||
|
function initializeSystem() {
|
||||||
|
console.log('信息管理系统已加载');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换子菜单显示/隐藏
|
||||||
|
function toggleSubmenu(submenuId) {
|
||||||
|
const submenu = document.getElementById(submenuId);
|
||||||
|
const arrow = submenu.parentElement.querySelector('.arrow');
|
||||||
|
|
||||||
|
if (submenu.classList.contains('expanded')) {
|
||||||
|
submenu.classList.remove('expanded');
|
||||||
|
arrow.classList.remove('rotated');
|
||||||
|
} else {
|
||||||
|
// 先关闭其他所有子菜单
|
||||||
|
const allSubmenus = document.querySelectorAll('.submenu');
|
||||||
|
const allArrows = document.querySelectorAll('.arrow');
|
||||||
|
|
||||||
|
allSubmenus.forEach(menu => menu.classList.remove('expanded'));
|
||||||
|
allArrows.forEach(arrow => arrow.classList.remove('rotated'));
|
||||||
|
|
||||||
|
// 展开当前子菜单
|
||||||
|
submenu.classList.add('expanded');
|
||||||
|
arrow.classList.add('rotated');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开新的tab页面
|
||||||
|
async function openTab(title, contentType) {
|
||||||
|
tabCounter++;
|
||||||
|
const tabId = `tab-${tabCounter}`;
|
||||||
|
const contentId = `content-${tabCounter}`;
|
||||||
|
|
||||||
|
// 检查是否已经存在相同标题的tab
|
||||||
|
const existingTab = Array.from(document.querySelectorAll('.tab')).find(tab =>
|
||||||
|
tab.querySelector('span').textContent === title
|
||||||
|
);
|
||||||
|
|
||||||
|
if (existingTab) {
|
||||||
|
// 如果已存在,直接激活该tab
|
||||||
|
switchTab(existingTab.id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建新的tab
|
||||||
|
const tabBar = document.querySelector('.tab-bar');
|
||||||
|
const newTab = document.createElement('div');
|
||||||
|
newTab.className = 'tab';
|
||||||
|
newTab.id = tabId;
|
||||||
|
newTab.innerHTML = `
|
||||||
|
<span>${title}</span>
|
||||||
|
<button class="close-btn" onclick="closeTab('${tabId}')" aria-label="关闭">×</button>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// 添加点击事件
|
||||||
|
newTab.addEventListener('click', function(e) {
|
||||||
|
if (!e.target.classList.contains('close-btn')) {
|
||||||
|
switchTab(tabId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
tabBar.appendChild(newTab);
|
||||||
|
|
||||||
|
// 创建对应的内容区域
|
||||||
|
const contentArea = document.querySelector('.content-area');
|
||||||
|
const newContent = document.createElement('div');
|
||||||
|
newContent.className = 'tab-content';
|
||||||
|
newContent.id = contentId;
|
||||||
|
|
||||||
|
// 动态加载页面内容
|
||||||
|
try {
|
||||||
|
const pageContent = await loadPageContent(contentType);
|
||||||
|
newContent.innerHTML = pageContent;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载页面内容失败:', error);
|
||||||
|
newContent.innerHTML = `
|
||||||
|
<div class="page-content">
|
||||||
|
<h2>${title}</h2>
|
||||||
|
<p>页面加载失败,请稍后重试。</p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
contentArea.appendChild(newContent);
|
||||||
|
|
||||||
|
// 激活新创建的tab
|
||||||
|
switchTab(tabId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换到指定的tab
|
||||||
|
function switchTab(tabId) {
|
||||||
|
// 移除所有tab的active状态
|
||||||
|
document.querySelectorAll('.tab').forEach(tab => {
|
||||||
|
tab.classList.remove('active');
|
||||||
|
});
|
||||||
|
|
||||||
|
// 隐藏所有内容
|
||||||
|
document.querySelectorAll('.tab-content').forEach(content => {
|
||||||
|
content.classList.remove('active');
|
||||||
|
});
|
||||||
|
|
||||||
|
// 激活指定的tab
|
||||||
|
const targetTab = document.getElementById(tabId);
|
||||||
|
if (targetTab) {
|
||||||
|
targetTab.classList.add('active');
|
||||||
|
activeTabId = tabId;
|
||||||
|
|
||||||
|
// 显示对应的内容
|
||||||
|
const contentId = tabId.replace('tab', 'content');
|
||||||
|
const targetContent = document.getElementById(contentId);
|
||||||
|
if (targetContent) {
|
||||||
|
targetContent.classList.add('active');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭tab
|
||||||
|
function closeTab(tabId) {
|
||||||
|
const tab = document.getElementById(tabId);
|
||||||
|
const contentId = tabId.replace('tab', 'content');
|
||||||
|
const content = document.getElementById(contentId);
|
||||||
|
|
||||||
|
if (!tab) return;
|
||||||
|
|
||||||
|
// 如果关闭的是当前激活的tab
|
||||||
|
if (tabId === activeTabId) {
|
||||||
|
// 找到前一个tab来激活
|
||||||
|
const allTabs = document.querySelectorAll('.tab');
|
||||||
|
let targetTab = null;
|
||||||
|
|
||||||
|
for (let i = 0; i < allTabs.length; i++) {
|
||||||
|
if (allTabs[i].id === tabId) {
|
||||||
|
// 优先选择前一个tab,如果没有就选择后一个
|
||||||
|
targetTab = allTabs[i - 1] || allTabs[i + 1];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetTab) {
|
||||||
|
switchTab(targetTab.id);
|
||||||
|
} else {
|
||||||
|
// 如果没有其他tab,激活首页
|
||||||
|
switchTab('home-tab');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移除tab和对应的内容
|
||||||
|
tab.remove();
|
||||||
|
if (content) {
|
||||||
|
content.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 动态加载页面内容
|
||||||
|
async function loadPageContent(contentType) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`pages/${contentType}.html`);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
const content = await response.text();
|
||||||
|
return content;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载页面失败:', error);
|
||||||
|
// 返回默认内容
|
||||||
|
return `
|
||||||
|
<div class="page-content">
|
||||||
|
<h2>页面加载中...</h2>
|
||||||
|
<p>页面正在开发中,敬请期待...</p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 键盘快捷键支持
|
||||||
|
document.addEventListener('keydown', function(e) {
|
||||||
|
// ESC键关闭当前tab(除了首页)
|
||||||
|
if (e.key === 'Escape' && activeTabId !== 'home-tab') {
|
||||||
|
closeTab(activeTabId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,243 @@
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 左侧菜单栏样式 */
|
||||||
|
.sidebar {
|
||||||
|
width: 250px;
|
||||||
|
background-color: #2c3e50;
|
||||||
|
color: white;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
padding: 20px;
|
||||||
|
border-bottom: 1px solid #34495e;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo h2 {
|
||||||
|
color: white;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu {
|
||||||
|
padding: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-item {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-title {
|
||||||
|
padding: 12px 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-title:hover {
|
||||||
|
background-color: #34495e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrow {
|
||||||
|
transition: transform 0.3s;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrow.rotated {
|
||||||
|
transform: rotate(-90deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.submenu {
|
||||||
|
background-color: #34495e;
|
||||||
|
max-height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: max-height 0.3s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submenu.expanded {
|
||||||
|
max-height: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submenu-item {
|
||||||
|
padding: 10px 40px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
border-left: 3px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submenu-item:hover {
|
||||||
|
background-color: #3d566e;
|
||||||
|
border-left-color: #3498db;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 中央内容区域样式 */
|
||||||
|
.main-content {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-bar {
|
||||||
|
background-color: #ecf0f1;
|
||||||
|
border-bottom: 1px solid #bdc3c7;
|
||||||
|
display: flex;
|
||||||
|
padding: 0 10px;
|
||||||
|
min-height: 40px;
|
||||||
|
align-items: end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab {
|
||||||
|
padding: 8px 16px;
|
||||||
|
background-color: #bdc3c7;
|
||||||
|
border: 1px solid #95a5a6;
|
||||||
|
border-bottom: none;
|
||||||
|
margin-right: 2px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
min-width: 100px;
|
||||||
|
border-radius: 4px 4px 0 0;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab.active {
|
||||||
|
background-color: white;
|
||||||
|
border-color: #bdc3c7;
|
||||||
|
border-bottom: 1px solid white;
|
||||||
|
margin-bottom: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab span {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab .close-btn {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: #7f8c8d;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 16px;
|
||||||
|
padding: 0;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab .close-btn:hover {
|
||||||
|
background-color: #e74c3c;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-area {
|
||||||
|
flex: 1;
|
||||||
|
padding: 20px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-content {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-content.active {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-content h1 {
|
||||||
|
color: #2c3e50;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-content p {
|
||||||
|
color: #7f8c8d;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 页面内容样式 */
|
||||||
|
.page-content {
|
||||||
|
max-width: 800px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-content h2 {
|
||||||
|
color: #2c3e50;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
border-bottom: 2px solid #3498db;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #2c3e50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group input,
|
||||||
|
.form-group select,
|
||||||
|
.form-group textarea {
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border: 1px solid #bdc3c7;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
padding: 8px 16px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
background-color: #3498db;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary:hover {
|
||||||
|
background-color: #2980b9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table th,
|
||||||
|
.table td {
|
||||||
|
padding: 10px;
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 1px solid #ecf0f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table th {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #2c3e50;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue