使用new_web来逐步构建

This commit is contained in:
linbin 2025-08-01 04:05:15 +08:00
parent e6065a5f8f
commit b373ae6752
9 changed files with 1331 additions and 48 deletions

View File

@ -17,6 +17,9 @@ merchant/
├── index.html # 主框架页面
├── styles.css # 全局样式文件
├── script.js # 核心JavaScript逻辑
├── js/ # 页面功能模块目录
│ ├── level-settings.js # 等级设置页面功能
│ └── member-view.js # 会员查看页面功能(待创建)
├── pages/ # 页面内容目录
│ ├── level-settings.html # 等级设置页面
│ └── member-view.html # 会员查看页面
@ -25,9 +28,11 @@ merchant/
### 架构特点
1. **内容与逻辑分离**: 所有页面内容存储为独立HTML文件
2. **动态加载**: 使用fetch API异步加载页面内容
3. **Tab管理**: 支持多页面同时打开,可独立关闭
4. **菜单系统**: 二级菜单展开/收起,防重复打开
2. **模块化JavaScript**: 每个页面的功能逻辑独立存储在对应的JS文件中
3. **动态加载**: 使用fetch API异步加载页面内容和对应的JavaScript模块
4. **智能初始化**: 自动检测并加载页面对应的JavaScript文件调用初始化函数
5. **Tab管理**: 支持多页面同时打开,可独立关闭
6. **菜单系统**: 二级菜单展开/收起,防重复打开
## 核心功能
@ -43,32 +48,71 @@ merchant/
- **智能切换**: 关闭当前tab时自动激活相邻tab
### 页面加载机制
- **异步加载**: 使用`fetch`API动态加载HTML内容
- **异步加载**: 使用`fetch`API动态加载HTML内容和JavaScript模块
- **自动检测**: 根据页面名称自动查找对应的JavaScript文件
- **智能缓存**: JavaScript模块避免重复加载提高性能
- **初始化调用**: 自动调用页面对应的初始化函数
- **错误处理**: 包含完整的加载失败降级方案
- **缓存管理**: 每次点击都重新加载,保证内容最新
## 开发约束和规范
### 重要约束 ⚠️
1. **禁止硬编码HTML**: 所有页面内容必须存储在独立的HTML文件中
2. **优先编辑现有文件**: 禁止随意创建新文件,优先修改现有文件
3. **保持架构一致性**: 新页面必须遵循现有的加载和展示模式
4. **中文本地化**: 所有界面文本必须使用中文
2. **模块化JavaScript**: 每个页面的JavaScript功能必须独立存储在对应的JS文件中
3. **优先编辑现有文件**: 禁止随意创建新文件,优先修改现有文件
4. **保持架构一致性**: 新页面必须遵循现有的加载和展示模式
5. **中文本地化**: 所有界面文本必须使用中文
6. **避免内联脚本**: 页面HTML中不应包含大段JavaScript代码
### JavaScript模块化规范
```javascript
// 页面JavaScript文件结构示例js/page-name.js
/**
* 页面功能模块
* 包含该页面的所有交互逻辑
*/
// 页面初始化函数(必需)
function initPageName() {
console.log('页面已初始化');
// 页面特定的初始化逻辑
setupEventListeners();
}
// 页面功能函数
function someFunction() {
// 功能实现
}
// 将需要在HTML中调用的函数暴露到全局作用域
window.someFunction = someFunction;
// 页面清理函数(可选)
function cleanupPageName() {
// 清理事件监听器等
}
```
### 开发规范
```javascript
// 添加新页面的步骤:
// 1. 在pages/目录创建对应的HTML文件
// 2. 在HTML中添加新的菜单项指向该页面
// 3. 确保contentType参数与文件名匹配
// 2. 在js/目录创建对应的JavaScript文件
// 3. 在HTML中添加新的菜单项指向该页面
// 4. 确保contentType参数与文件名匹配
// 正确的调用方式:
onclick="openTab('新页面', 'new-page')"
// 对应文件pages/new-page.html
// 对应文件:
// - pages/new-page.html (页面内容)
// - js/new-page.js (页面功能)
```
### 文件命名规范
- 页面文件:`pages/功能名称.html` (使用kebab-case)
- JavaScript文件`js/功能名称.js` (使用kebab-case与页面文件名保持一致)
- 初始化函数:`init[功能名称CamelCase]()` (自动调用)
- contentType参数与文件名保持一致
- Tab标题使用中文友好名称
@ -82,8 +126,15 @@ onclick="openTab('新页面', 'new-page')"
### `loadPageContent(contentType)`
- **参数**: contentType(对应pages/目录下的文件名)
- **返回**: Promise<string> HTML内容
- **功能**: 异步加载页面HTML内容并自动加载对应的JavaScript模块
- **错误处理**: 加载失败时返回默认提示内容
### `loadPageScript(contentType)`
- **参数**: contentType(对应js/目录下的文件名)
- **功能**: 动态加载页面对应的JavaScript文件
- **特性**: 避免重复加载,自动调用初始化函数
- **命名转换**: 自动将kebab-case转换为CamelCase调用初始化函数
### `toggleSubmenu(submenuId)`
- **参数**: submenuId(子菜单DOM元素ID)
- **功能**: 切换子菜单展开/收起状态
@ -112,10 +163,29 @@ onclick="openTab('新页面', 'new-page')"
</div>
```
3. **创建页面内容**:
3. **创建页面内容和功能**:
```bash
# 在pages/目录创建对应HTML文件
pages/function-name.html
# 在js/目录创建对应JavaScript文件
js/function-name.js
```
4. **JavaScript模块结构**:
```javascript
// js/function-name.js
function initFunctionName() {
console.log('功能模块已初始化');
// 初始化逻辑
}
function someFeature() {
// 功能实现
}
// 暴露到全局作用域供HTML调用
window.someFeature = someFeature;
```
### 页面内容模板
@ -188,9 +258,13 @@ npx serve .
### 开发限制
- ❌ 不要在JavaScript中硬编码HTML内容
- ❌ 不要在页面HTML中编写大段JavaScript代码
- ❌ 不要随意创建新文件,优先编辑现有文件
- ❌ 不要破坏现有的架构模式
- ❌ 不要跳过JavaScript模块的初始化函数
- ✅ 所有页面内容必须存储在pages/目录
- ✅ 所有页面功能必须存储在js/目录对应文件中
- ✅ 每个JavaScript模块必须包含初始化函数
- ✅ 使用中文界面文本
- ✅ 遵循现有的命名和结构约定
@ -201,3 +275,20 @@ npx serve .
## 项目目标
此项目是一个快速原型系统,用于演示传统管理系统的布局意图和交互流程。专注于功能架构而非视觉设计,为后续开发提供清晰的技术基础和扩展方向。
## JavaScript模块化架构优势
### 维护性
- **代码分离**: 每个页面的功能逻辑独立,便于维护和调试
- **模块清晰**: 避免全局script.js文件过于庞大
- **团队协作**: 不同开发者可以独立开发不同页面的功能
### 性能优化
- **按需加载**: 只有打开页面时才加载对应的JavaScript文件
- **避免重复**: 已加载的模块不会重复加载
- **内存管理**: 可以在页面关闭时进行资源清理
### 可扩展性
- **标准化流程**: 新增页面功能有明确的开发流程
- **自动化集成**: 新JavaScript模块会被系统自动检测和加载
- **灵活扩展**: 可以轻松为现有页面添加新功能

View File

@ -0,0 +1,128 @@
/**
* 等级明细页面功能模块
* 包含该页面的所有交互逻辑
*/
// 模拟不同商户的等级数据
const merchantLevelData = {
'牛牛蔬菜店': [
{ level: 'LV1', growth: 100, members: 1250, benefits: '9.5折优惠' },
{ level: 'LV2', growth: 500, members: 850, benefits: '9折优惠, 积分双倍' },
{ level: 'LV3', growth: 1200, members: 450, benefits: '8.5折优惠, 积分双倍, 生日优惠券' },
{ level: 'LV4', growth: 2500, members: 180, benefits: '8折优惠, 积分三倍, 生日优惠券, 专属客服' }
],
'羊羊水果店': [
{ level: 'LV1', growth: 80, members: 980, benefits: '9.8折优惠' },
{ level: 'LV2', growth: 400, members: 650, benefits: '9.2折优惠, 积分双倍' },
{ level: 'LV3', growth: 1000, members: 320, benefits: '8.8折优惠, 积分双倍, 生日优惠券' }
],
'小狗羊肉': [
{ level: 'LV1', growth: 120, members: 800, benefits: '9.3折优惠' },
{ level: 'LV2', growth: 600, members: 520, benefits: '8.8折优惠, 积分双倍' },
{ level: 'LV3', growth: 1500, members: 280, benefits: '8.3折优惠, 积分双倍, 生日优惠券' },
{ level: 'LV4', growth: 3000, members: 120, benefits: '7.8折优惠, 积分三倍, 生日优惠券, 专属客服' }
],
'小马猪蹄': [
{ level: 'LV1', growth: 150, members: 600, benefits: '9.5折优惠' },
{ level: 'LV2', growth: 800, members: 350, benefits: '9折优惠, 积分双倍' }
],
'小鱼生鲜': [
{ level: 'LV1', growth: 90, members: 1100, benefits: '9.6折优惠' },
{ level: 'LV2', growth: 450, members: 750, benefits: '9.1折优惠, 积分双倍' },
{ level: 'LV3', growth: 1100, members: 400, benefits: '8.6折优惠, 积分双倍, 生日优惠券' },
{ level: 'LV4', growth: 2200, members: 200, benefits: '8.1折优惠, 积分三倍, 生日优惠券, 专属客服' }
]
};
// 页面初始化函数(必需)
function initLevelDetails() {
console.log('等级明细页面已初始化');
// 从URL参数或全局变量获取商户名称
const merchantName = getCurrentMerchantName();
if (merchantName) {
updatePageContent(merchantName);
}
}
// 获取当前商户名称
function getCurrentMerchantName() {
// 可以从tab标题中提取商户名称
const activeTab = document.querySelector('.tab.active');
if (activeTab) {
const tabTitle = activeTab.textContent.trim();
// 提取商户名称(去掉" - 等级设置明细"后缀)
const merchantName = tabTitle.replace(' - 等级设置明细', '');
// 如果标题中包含了商户名称,则返回商户名称
if (merchantName !== tabTitle) {
return merchantName;
}
// 否则尝试从关闭按钮的 × 前面获取标题文本
const tabTextContent = activeTab.querySelector('.tab-text');
if (tabTextContent) {
return tabTextContent.textContent.replace(' - 等级设置明细', '');
}
}
return '时尚服装店'; // 默认值
}
// 更新页面内容
function updatePageContent(merchantName) {
// 更新页面标题
const merchantNameElement = document.getElementById('merchantName');
if (merchantNameElement) {
merchantNameElement.textContent = merchantName;
}
// 更新表格数据
const levelData = merchantLevelData[merchantName];
if (levelData) {
updateLevelTable(levelData);
}
}
// 更新等级表格数据
function updateLevelTable(levelData) {
const tbody = document.getElementById('levelDetailsBody');
if (!tbody) return;
tbody.innerHTML = '';
levelData.forEach(level => {
const row = document.createElement('tr');
row.innerHTML = `
<td>${level.level}</td>
<td>${level.growth}</td>
<td>${level.members}</td>
<td>${level.benefits}</td>
`;
tbody.appendChild(row);
});
}
// 等级编辑功能
function addLevel() {
// 获取当前商户名称
const merchantName = getCurrentMerchantName();
// 打开新的tab页面显示该商户的等级编辑
const tabTitle = `${merchantName} - 等级编辑`;
if (typeof window.openTab === 'function') {
window.openTab(tabTitle, 'level-edit');
} else {
console.error('openTab函数未找到');
}
}
// 保存等级设置
function saveLevelSettings() {
alert('等级设置已保存!');
}
// 将需要在HTML中调用的函数暴露到全局作用域
window.addLevel = addLevel;
window.saveLevelSettings = saveLevelSettings;
// 页面清理函数(可选)
function cleanupLevelDetails() {
// 清理事件监听器等
}

View File

@ -0,0 +1,239 @@
/**
* 等级编辑页面功能模块
* 包含该页面的所有交互逻辑
*/
// 模拟不同商户的等级编辑数据
const merchantEditData = {
'牛牛蔬菜店': [
{
level: 'LV1',
name: '铜牌会员',
growthStart: 0,
growthEnd: 100,
discountEnabled: true,
discountRate: 5,
pointsEnabled: false,
birthdayCouponEnabled: false,
birthdayPointsEnabled: false
},
{
level: 'LV2',
name: '银牌会员',
growthStart: 101,
growthEnd: 500,
discountEnabled: true,
discountRate: 10,
pointsEnabled: true,
birthdayCouponEnabled: true,
birthdayPointsEnabled: true
},
{
level: 'LV3',
name: '金牌会员',
growthStart: 501,
growthEnd: 1200,
discountEnabled: true,
discountRate: 15,
pointsEnabled: true,
birthdayCouponEnabled: true,
birthdayPointsEnabled: true
},
{
level: 'LV4',
name: '钻石会员',
growthStart: 1201,
growthEnd: 9999,
discountEnabled: true,
discountRate: 20,
pointsEnabled: true,
birthdayCouponEnabled: true,
birthdayPointsEnabled: true
}
],
'羊羊水果店': [
{
level: 'LV1',
name: '普通会员',
growthStart: 0,
growthEnd: 80,
discountEnabled: true,
discountRate: 2,
pointsEnabled: false,
birthdayCouponEnabled: false,
birthdayPointsEnabled: false
},
{
level: 'LV2',
name: '优质会员',
growthStart: 81,
growthEnd: 400,
discountEnabled: true,
discountRate: 8,
pointsEnabled: true,
birthdayCouponEnabled: false,
birthdayPointsEnabled: false
},
{
level: 'LV3',
name: '贵宾会员',
growthStart: 401,
growthEnd: 1000,
discountEnabled: true,
discountRate: 12,
pointsEnabled: true,
birthdayCouponEnabled: true,
birthdayPointsEnabled: true
}
]
};
// 页面初始化函数(必需)
function initLevelEdit() {
console.log('等级编辑页面已初始化');
// 从URL参数或全局变量获取商户名称
const merchantName = getCurrentMerchantName();
if (merchantName) {
updateEditPageContent(merchantName);
}
}
// 获取当前商户名称
function getCurrentMerchantName() {
// 可以从tab标题中提取商户名称
const activeTab = document.querySelector('.tab.active');
if (activeTab) {
const tabTitle = activeTab.textContent.trim();
// 提取商户名称(去掉" - 等级编辑"后缀)
const merchantName = tabTitle.replace(' - 等级编辑', '');
// 如果标题中包含了商户名称,则返回商户名称
if (merchantName !== tabTitle) {
return merchantName;
}
// 否则尝试从关闭按钮的 × 前面获取标题文本
const tabTextContent = activeTab.querySelector('.tab-text');
if (tabTextContent) {
return tabTextContent.textContent.replace(' - 等级编辑', '');
}
}
return '时尚服装店'; // 默认值
}
// 更新页面内容
function updateEditPageContent(merchantName) {
// 更新页面标题
const merchantNameElement = document.getElementById('merchantName');
if (merchantNameElement) {
merchantNameElement.textContent = merchantName;
}
// 更新表格数据
const editData = merchantEditData[merchantName];
if (editData) {
updateEditTable(editData);
}
}
// 更新编辑表格数据
function updateEditTable(editData) {
const tbody = document.getElementById('levelEditBody');
if (!tbody) return;
tbody.innerHTML = '';
editData.forEach(level => {
const row = document.createElement('tr');
row.innerHTML = `
<td>${level.level}</td>
<td>
<input type="text" value="${level.name}" class="form-input">
</td>
<td>
<div class="range-input">
<input type="number" value="${level.growthStart}" class="form-input range-start"> -
<input type="number" value="${level.growthEnd}" class="form-input range-end">
</div>
</td>
<td>
<label class="switch">
<input type="checkbox" ${level.discountEnabled ? 'checked' : ''}>
<span class="slider"></span>
</label>
</td>
<td>
<input type="number" value="${level.discountRate}" class="form-input discount-input">
</td>
<td>
<label class="switch">
<input type="checkbox" ${level.pointsEnabled ? 'checked' : ''}>
<span class="slider"></span>
</label>
</td>
<td>
<label class="switch">
<input type="checkbox" ${level.birthdayCouponEnabled ? 'checked' : ''}>
<span class="slider"></span>
</label>
</td>
<td>
<button class="btn btn-action" onclick="addCoupon('${level.level}')">添加优惠券</button>
</td>
<td>
<label class="switch">
<input type="checkbox" ${level.birthdayPointsEnabled ? 'checked' : ''}>
<span class="slider"></span>
</label>
</td>
`;
tbody.appendChild(row);
});
}
// 添加优惠券功能
function addCoupon(level) {
alert(`${level}添加生日优惠券功能开发中...`);
}
// 保存等级编辑
function saveLevelEdit() {
// 收集表单数据
const formData = collectFormData();
console.log('保存的等级编辑数据:', formData);
alert('等级编辑设置已保存!');
}
// 收集表单数据
function collectFormData() {
const tbody = document.getElementById('levelEditBody');
if (!tbody) return [];
const rows = tbody.querySelectorAll('tr');
const data = [];
rows.forEach((row, index) => {
const levelData = {
level: row.cells[0].textContent,
name: row.querySelector('input[type="text"]').value,
growthStart: parseInt(row.querySelector('.range-start').value),
growthEnd: parseInt(row.querySelector('.range-end').value),
discountEnabled: row.querySelectorAll('input[type="checkbox"]')[0].checked,
discountRate: parseInt(row.querySelector('.discount-input').value),
pointsEnabled: row.querySelectorAll('input[type="checkbox"]')[1].checked,
birthdayCouponEnabled: row.querySelectorAll('input[type="checkbox"]')[2].checked,
birthdayPointsEnabled: row.querySelectorAll('input[type="checkbox"]')[3].checked
};
data.push(levelData);
});
return data;
}
// 将需要在HTML中调用的函数暴露到全局作用域
window.addCoupon = addCoupon;
window.saveLevelEdit = saveLevelEdit;
// 页面清理函数(可选)
function cleanupLevelEdit() {
// 清理事件监听器等
}

View File

@ -0,0 +1,104 @@
/**
* 等级设置页面功能模块
* 包含多选下拉框功能
*/
// 页面初始化函数
function initLevelSettings() {
console.log('等级设置页面已初始化');
// 设置外部点击监听器
setupOutsideClickListener();
}
// 切换下拉框显示/隐藏
function toggleDropdown() {
const options = document.getElementById('stallOptions');
const arrow = document.querySelector('.multiselect-arrow');
if (options && arrow) {
if (options.classList.contains('show')) {
options.classList.remove('show');
arrow.classList.remove('rotated');
} else {
options.classList.add('show');
arrow.classList.add('rotated');
}
}
}
// 全选/取消全选功能
function selectAll(checkbox) {
const allCheckboxes = document.querySelectorAll('#stallOptions input[type="checkbox"]:not([value="all"])');
if (checkbox.checked) {
allCheckboxes.forEach(cb => cb.checked = true);
} else {
allCheckboxes.forEach(cb => cb.checked = false);
}
updateSelection();
}
// 更新选择状态和显示文本
function updateSelection() {
const allCheckbox = document.querySelector('input[value="all"]');
const otherCheckboxes = document.querySelectorAll('#stallOptions input[type="checkbox"]:not([value="all"])');
const selectedText = document.querySelector('.multiselect-selected');
if (!allCheckbox || !selectedText) return;
const checkedBoxes = Array.from(otherCheckboxes).filter(cb => cb.checked);
const allChecked = checkedBoxes.length === otherCheckboxes.length;
// 更新"全部"复选框状态
allCheckbox.checked = allChecked;
// 更新显示文本
if (checkedBoxes.length === 0) {
selectedText.textContent = '请选择摊位名称...';
} else if (allChecked) {
selectedText.textContent = '全部';
} else if (checkedBoxes.length === 1) {
selectedText.textContent = checkedBoxes[0].value;
} else {
selectedText.textContent = `已选择${checkedBoxes.length}个摊位`;
}
}
// 点击外部关闭下拉框的事件监听器
function setupOutsideClickListener() {
document.addEventListener('click', function(event) {
const container = document.querySelector('.multiselect-container');
if (container && !container.contains(event.target)) {
const options = document.getElementById('stallOptions');
const arrow = document.querySelector('.multiselect-arrow');
if (options && arrow) {
options.classList.remove('show');
arrow.classList.remove('rotated');
}
}
});
}
// 页面清理函数(当页面被关闭时调用)
function cleanupLevelSettings() {
// 移除事件监听器等清理工作
console.log('等级设置页面已清理');
}
// 打开等级明细页面
function openLevelDetails(merchantName) {
// 打开新的tab页面显示该商户的等级明细
const tabTitle = `${merchantName} - 等级设置明细`;
if (typeof window.openTab === 'function') {
window.openTab(tabTitle, 'level-details');
} else {
console.error('openTab函数未找到');
}
}
// 将函数暴露到全局作用域以便HTML中的onclick可以调用
window.toggleDropdown = toggleDropdown;
window.selectAll = selectAll;
window.updateSelection = updateSelection;
window.openLevelDetails = openLevelDetails;

View File

@ -0,0 +1,101 @@
<div class="page-content">
<div class="page-header">
<h2 id="merchantName">时尚服装店</h2>
<span class="page-subtitle">- 等级设置明细</span>
</div>
<!-- 操作按钮区域 -->
<div class="action-section">
<button class="btn btn-primary" onclick="addLevel()">等级编辑</button>
<button class="btn" onclick="saveLevelSettings()">提交</button>
</div>
<!-- 等级明细表格 -->
<table class="table level-details-table">
<thead>
<tr>
<th>等级名称</th>
<th>所需成长值</th>
<th>会员人数</th>
<th>会员权益</th>
</tr>
</thead>
<tbody id="levelDetailsBody">
<tr>
<td>LV1</td>
<td>100</td>
<td>1250</td>
<td>9.5折优惠</td>
</tr>
<tr>
<td>LV2</td>
<td>500</td>
<td>850</td>
<td>9折优惠, 积分双倍</td>
</tr>
<tr>
<td>LV3</td>
<td>1200</td>
<td>450</td>
<td>8.5折优惠, 积分双倍, 生日优惠券</td>
</tr>
<tr>
<td>LV4</td>
<td>2500</td>
<td>180</td>
<td>8折优惠, 积分三倍, 生日优惠券, 专属客服</td>
</tr>
</tbody>
</table>
</div>
<style>
.page-header {
display: flex;
align-items: baseline;
margin-bottom: 20px;
}
.page-header h2 {
margin: 0;
margin-right: 10px;
}
.page-subtitle {
color: #666;
font-size: 16px;
}
.action-section {
margin-bottom: 20px;
text-align: right;
}
.action-section .btn {
margin-left: 10px;
}
.level-details-table {
width: 100%;
margin-top: 20px;
}
.level-details-table th,
.level-details-table td {
text-align: center;
padding: 12px;
}
.level-details-table th {
background-color: #f8f9fa;
font-weight: bold;
}
.level-details-table tr:nth-child(even) {
background-color: #f8f9fa;
}
.level-details-table tr:hover {
background-color: #e8f4f8;
}
</style>

View File

@ -0,0 +1,328 @@
<div class="page-content">
<div class="page-header">
<h2 id="merchantName">时尚服装店</h2>
<span class="page-subtitle">- 等级编辑</span>
</div>
<!-- 操作按钮区域 -->
<div class="action-section">
<button class="btn btn-primary" onclick="saveLevelEdit()">提交</button>
</div>
<!-- 等级编辑表格 -->
<table class="table level-edit-table">
<thead>
<tr>
<th>等级</th>
<th>等级名称</th>
<th>成长值范围</th>
<th>开启会员折扣</th>
<th>折扣率(%)</th>
<th>开启积分兑换</th>
<th>开启生日优惠券</th>
<th>生日优惠券操作</th>
<th>开启生日双倍积分</th>
</tr>
</thead>
<tbody id="levelEditBody">
<tr>
<td>LV1</td>
<td>
<input type="text" value="铜牌会员" class="form-input">
</td>
<td>
<div class="range-input">
<input type="number" value="0" class="form-input range-start"> -
<input type="number" value="100" class="form-input range-end">
</div>
</td>
<td>
<label class="switch">
<input type="checkbox" checked>
<span class="slider"></span>
</label>
</td>
<td>
<input type="number" value="5" class="form-input discount-input">
</td>
<td>
<label class="switch">
<input type="checkbox">
<span class="slider"></span>
</label>
</td>
<td>
<label class="switch">
<input type="checkbox">
<span class="slider"></span>
</label>
</td>
<td>
<button class="btn btn-action" onclick="addCoupon('LV1')">添加优惠券</button>
</td>
<td>
<label class="switch">
<input type="checkbox">
<span class="slider"></span>
</label>
</td>
</tr>
<tr>
<td>LV2</td>
<td>
<input type="text" value="银牌会员" class="form-input">
</td>
<td>
<div class="range-input">
<input type="number" value="101" class="form-input range-start"> -
<input type="number" value="500" class="form-input range-end">
</div>
</td>
<td>
<label class="switch">
<input type="checkbox" checked>
<span class="slider"></span>
</label>
</td>
<td>
<input type="number" value="10" class="form-input discount-input">
</td>
<td>
<label class="switch">
<input type="checkbox" checked>
<span class="slider"></span>
</label>
</td>
<td>
<label class="switch">
<input type="checkbox" checked>
<span class="slider"></span>
</label>
</td>
<td>
<button class="btn btn-action" onclick="addCoupon('LV2')">添加优惠券</button>
</td>
<td>
<label class="switch">
<input type="checkbox" checked>
<span class="slider"></span>
</label>
</td>
</tr>
<tr>
<td>LV3</td>
<td>
<input type="text" value="金牌会员" class="form-input">
</td>
<td>
<div class="range-input">
<input type="number" value="501" class="form-input range-start"> -
<input type="number" value="1200" class="form-input range-end">
</div>
</td>
<td>
<label class="switch">
<input type="checkbox" checked>
<span class="slider"></span>
</label>
</td>
<td>
<input type="number" value="15" class="form-input discount-input">
</td>
<td>
<label class="switch">
<input type="checkbox" checked>
<span class="slider"></span>
</label>
</td>
<td>
<label class="switch">
<input type="checkbox" checked>
<span class="slider"></span>
</label>
</td>
<td>
<button class="btn btn-action" onclick="addCoupon('LV3')">添加优惠券</button>
</td>
<td>
<label class="switch">
<input type="checkbox" checked>
<span class="slider"></span>
</label>
</td>
</tr>
<tr>
<td>LV4</td>
<td>
<input type="text" value="钻石会员" class="form-input">
</td>
<td>
<div class="range-input">
<input type="number" value="1201" class="form-input range-start"> -
<input type="number" value="9999" class="form-input range-end">
</div>
</td>
<td>
<label class="switch">
<input type="checkbox" checked>
<span class="slider"></span>
</label>
</td>
<td>
<input type="number" value="20" class="form-input discount-input">
</td>
<td>
<label class="switch">
<input type="checkbox" checked>
<span class="slider"></span>
</label>
</td>
<td>
<label class="switch">
<input type="checkbox" checked>
<span class="slider"></span>
</label>
</td>
<td>
<button class="btn btn-action" onclick="addCoupon('LV4')">添加优惠券</button>
</td>
<td>
<label class="switch">
<input type="checkbox" checked>
<span class="slider"></span>
</label>
</td>
</tr>
</tbody>
</table>
</div>
<style>
.page-header {
display: flex;
align-items: baseline;
margin-bottom: 20px;
}
.page-header h2 {
margin: 0;
margin-right: 10px;
}
.page-subtitle {
color: #666;
font-size: 16px;
}
.action-section {
margin-bottom: 20px;
text-align: right;
}
.action-section .btn {
margin-left: 10px;
}
.level-edit-table {
width: 100%;
margin-top: 20px;
table-layout: fixed;
}
.level-edit-table th,
.level-edit-table td {
text-align: center;
padding: 12px 8px;
vertical-align: middle;
}
.level-edit-table th {
background-color: #f8f9fa;
font-weight: bold;
font-size: 13px;
}
.level-edit-table tr:nth-child(even) {
background-color: #f8f9fa;
}
.level-edit-table tr:hover {
background-color: #e8f4f8;
}
/* 表单输入框样式 */
.form-input {
width: 100%;
padding: 6px 8px;
border: 1px solid #bdc3c7;
border-radius: 4px;
font-size: 12px;
text-align: center;
}
/* 范围输入框样式 */
.range-input {
display: flex;
align-items: center;
gap: 5px;
justify-content: center;
}
.range-start, .range-end {
width: 60px;
flex-shrink: 0;
}
.discount-input {
width: 50px;
}
/* 开关按钮调整 */
.level-edit-table .switch {
width: 40px;
height: 20px;
margin: 0 auto;
}
.level-edit-table .slider {
border-radius: 20px;
}
.level-edit-table .slider:before {
height: 14px;
width: 14px;
left: 3px;
bottom: 3px;
border-radius: 50%;
}
.level-edit-table input:checked + .slider:before {
transform: translateX(20px);
}
/* 操作按钮样式 */
.btn-action {
background-color: #3498db;
color: white;
font-size: 12px;
padding: 4px 8px;
border: none;
border-radius: 4px;
cursor: pointer;
}
.btn-action:hover {
background-color: #2980b9;
}
/* 表格列宽调整 */
.level-edit-table th:nth-child(1) { width: 60px; } /* 等级 */
.level-edit-table th:nth-child(2) { width: 100px; } /* 等级名称 */
.level-edit-table th:nth-child(3) { width: 120px; } /* 成长值范围 */
.level-edit-table th:nth-child(4) { width: 80px; } /* 开启会员折扣 */
.level-edit-table th:nth-child(5) { width: 80px; } /* 折扣率 */
.level-edit-table th:nth-child(6) { width: 80px; } /* 开启积分兑换 */
.level-edit-table th:nth-child(7) { width: 80px; } /* 开启生日优惠券 */
.level-edit-table th:nth-child(8) { width: 100px; } /* 生日优惠券操作 */
.level-edit-table th:nth-child(9) { width: 80px; } /* 开启生日双倍积分 */
</style>

View File

@ -1,57 +1,189 @@
<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">
<!-- 搜索区域 -->
<div class="search-section">
<div class="form-group">
<label>摊位名称</label>
<div class="multiselect-container">
<div class="multiselect-dropdown" onclick="toggleDropdown()">
<span class="multiselect-selected">请选择摊位名称...</span>
<span class="multiselect-arrow"></span>
</div>
<div class="multiselect-options" id="stallOptions">
<label class="multiselect-option">
<input type="checkbox" value="all" onchange="selectAll(this)">
<span>全部</span>
</label>
<label class="multiselect-option">
<input type="checkbox" value="牛牛蔬菜店" onchange="updateSelection()">
<span>牛牛蔬菜店</span>
</label>
<label class="multiselect-option">
<input type="checkbox" value="羊羊水果店" onchange="updateSelection()">
<span>羊羊水果店</span>
</label>
<label class="multiselect-option">
<input type="checkbox" value="小狗羊肉" onchange="updateSelection()">
<span>小狗羊肉</span>
</label>
<label class="multiselect-option">
<input type="checkbox" value="小马猪蹄" onchange="updateSelection()">
<span>小马猪蹄</span>
</label>
<label class="multiselect-option">
<input type="checkbox" value="小鱼生鲜" onchange="updateSelection()">
<span>小鱼生鲜</span>
</label>
</div>
</div>
<button class="btn btn-search">查询</button>
<button class="btn btn-primary">批量会员等级编辑</button>
</div>
</div>
<!-- 商户列表表格 -->
<table class="table merchant-table">
<thead>
<tr>
<th>等级名称</th>
<th>积分要求</th>
<th>会员数量</th>
<th>摊位名称</th>
<th>是否启用会员</th>
<th>会员等级</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr>
<td>普通会员</td>
<td>0</td>
<td>1,234</td>
<td>牛牛蔬菜店</td>
<td>
<button class="btn">编辑</button>
<button class="btn">删除</button>
<label class="switch">
<input type="checkbox" checked>
<span class="slider"></span>
</label>
</td>
<td>lv1, lv2, lv3, lv4</td>
<td>
<button class="btn-link" onclick="openLevelDetails('牛牛蔬菜店')">设置等级明细</button>
</td>
</tr>
<tr>
<td>银牌会员</td>
<td>1,000</td>
<td>567</td>
<td>羊羊水果店</td>
<td>
<button class="btn">编辑</button>
<button class="btn">删除</button>
<label class="switch">
<input type="checkbox">
<span class="slider"></span>
</label>
</td>
<td>lv1, lv2, lv3</td>
<td>
<button class="btn-link" onclick="openLevelDetails('羊羊水果店')">设置等级明细</button>
</td>
</tr>
<tr>
<td>金牌会员</td>
<td>5,000</td>
<td>123</td>
<td>小狗羊肉</td>
<td>
<button class="btn">编辑</button>
<button class="btn">删除</button>
<label class="switch">
<input type="checkbox" checked>
<span class="slider"></span>
</label>
</td>
<td>lv1, lv2, lv3, lv4</td>
<td>
<button class="btn-link" onclick="openLevelDetails('小狗羊肉')">设置等级明细</button>
</td>
</tr>
<tr>
<td>小马猪蹄</td>
<td>
<label class="switch">
<input type="checkbox">
<span class="slider"></span>
</label>
</td>
<td>lv1, lv2</td>
<td>
<button class="btn-link" onclick="openLevelDetails('小马猪蹄')">设置等级明细</button>
</td>
</tr>
<tr>
<td>小鱼生鲜</td>
<td>
<label class="switch">
<input type="checkbox" checked>
<span class="slider"></span>
</label>
</td>
<td>lv1, lv2, lv3, lv4</td>
<td>
<button class="btn-link" onclick="openLevelDetails('小鱼生鲜')">设置等级明细</button>
</td>
</tr>
</tbody>
</table>
</div>
<style>
.multiselect-container {
position: relative;
display: inline-block;
min-width: 200px;
}
.multiselect-dropdown {
border: 1px solid #bdc3c7;
padding: 8px 12px;
background: white;
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center;
border-radius: 4px;
}
.multiselect-dropdown:hover {
border-color: #3498db;
}
.multiselect-arrow {
transition: transform 0.2s;
}
.multiselect-arrow.rotated {
transform: rotate(180deg);
}
.multiselect-options {
position: absolute;
top: 100%;
left: 0;
right: 0;
background: white;
border: 1px solid #bdc3c7;
border-top: none;
max-height: 200px;
overflow-y: auto;
z-index: 1000;
display: none;
border-radius: 0 0 4px 4px;
}
.multiselect-options.show {
display: block;
}
.multiselect-option {
display: flex;
align-items: center;
padding: 8px 12px;
cursor: pointer;
margin: 0;
}
.multiselect-option:hover {
background-color: #f8f9fa;
}
.multiselect-option input[type="checkbox"] {
margin-right: 8px;
}
</style>

View File

@ -160,6 +160,9 @@ function closeTab(tabId) {
}
}
// 存储已加载的页面脚本,避免重复加载
const loadedPageScripts = new Set();
// 动态加载页面内容
async function loadPageContent(contentType) {
try {
@ -168,6 +171,10 @@ async function loadPageContent(contentType) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const content = await response.text();
// 页面内容加载完成后尝试加载对应的JavaScript文件
await loadPageScript(contentType);
return content;
} catch (error) {
console.error('加载页面失败:', error);
@ -181,6 +188,58 @@ async function loadPageContent(contentType) {
}
}
// 动态加载页面对应的JavaScript文件
async function loadPageScript(contentType) {
const scriptPath = `js/${contentType}.js`;
// 如果已经加载过该脚本,直接返回
if (loadedPageScripts.has(scriptPath)) {
// 调用页面初始化函数(如果存在)
const initFunctionName = `init${toCamelCase(contentType)}`;
if (typeof window[initFunctionName] === 'function') {
window[initFunctionName]();
}
return;
}
try {
// 检查文件是否存在
const checkResponse = await fetch(scriptPath, { method: 'HEAD' });
if (!checkResponse.ok) {
console.log(`页面脚本文件不存在: ${scriptPath}`);
return;
}
// 动态创建script标签加载JavaScript文件
const script = document.createElement('script');
script.src = scriptPath;
script.onload = () => {
console.log(`页面脚本已加载: ${scriptPath}`);
loadedPageScripts.add(scriptPath);
// 调用页面初始化函数(如果存在)
const initFunctionName = `init${toCamelCase(contentType)}`;
if (typeof window[initFunctionName] === 'function') {
window[initFunctionName]();
}
};
script.onerror = () => {
console.error(`页面脚本加载失败: ${scriptPath}`);
};
document.head.appendChild(script);
} catch (error) {
console.error('检查页面脚本文件时出错:', error);
}
}
// 将kebab-case转换为CamelCase
function toCamelCase(str) {
return str.replace(/-([a-z])/g, (match, letter) => letter.toUpperCase())
.replace(/^[a-z]/, match => match.toUpperCase());
}
// 键盘快捷键支持
document.addEventListener('keydown', function(e) {
// ESC键关闭当前tab除了首页

View File

@ -174,7 +174,7 @@ body {
/* 页面内容样式 */
.page-content {
max-width: 800px;
width: 100%;
}
.page-content h2 {
@ -241,3 +241,104 @@ body {
font-weight: bold;
color: #2c3e50;
}
/* 搜索区域样式 */
.search-section {
margin-bottom: 20px;
padding: 15px;
background-color: #f8f9fa;
border-radius: 4px;
}
.search-section .form-group {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 0;
}
.search-section label {
min-width: 80px;
margin-bottom: 0;
}
.search-input {
flex: 1;
max-width: 300px;
}
.btn-search {
background-color: #27ae60;
color: white;
}
.btn-search:hover {
background-color: #219a52;
}
/* 开关按钮样式 */
.switch {
position: relative;
display: inline-block;
width: 50px;
height: 24px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
border-radius: 24px;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 18px;
width: 18px;
left: 3px;
bottom: 3px;
background-color: white;
border-radius: 50%;
transition: .4s;
}
input:checked + .slider {
background-color: #27ae60;
}
input:checked + .slider:before {
transform: translateX(26px);
}
/* 链接按钮样式 */
.btn-link {
background: none;
border: none;
color: #27ae60;
cursor: pointer;
text-decoration: none;
font-size: 14px;
padding: 0;
}
.btn-link:hover {
color: #219a52;
text-decoration: underline;
}
/* 商户表格特殊样式 */
.merchant-table td {
vertical-align: middle;
}