2159 lines
		
	
	
		
			106 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
			
		
		
	
	
			2159 lines
		
	
	
		
			106 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
| // Tab系统管理 - 会员管理模块
 | ||
| 
 | ||
| // Tab管理器类
 | ||
| class MemberTabManager {
 | ||
|     constructor() {
 | ||
|         this.tabs = [];
 | ||
|         this.activeTabId = '';
 | ||
|         this.tabCounter = 0;
 | ||
|     }
 | ||
| 
 | ||
|     // 创建新tab
 | ||
|     createTab(id, title, content, canClose = true) {
 | ||
|         // 检查tab是否已存在
 | ||
|         const existingTab = this.tabs.find(tab => tab.id === id);
 | ||
|         if (existingTab) {
 | ||
|             this.showTab(id);
 | ||
|             return existingTab;
 | ||
|         }
 | ||
| 
 | ||
|         const tab = {
 | ||
|             id: id,
 | ||
|             title: title,
 | ||
|             content: content,
 | ||
|             canClose: canClose
 | ||
|         };
 | ||
|         
 | ||
|         this.tabs.push(tab);
 | ||
|         this.renderTabs();
 | ||
|         this.showTab(id);
 | ||
|         return tab;
 | ||
|     }
 | ||
| 
 | ||
|     // 关闭tab
 | ||
|     closeTab(tabId) {
 | ||
|         const tabIndex = this.tabs.findIndex(tab => tab.id === tabId);
 | ||
|         if (tabIndex === -1) return;
 | ||
| 
 | ||
|         const tab = this.tabs[tabIndex];
 | ||
|         if (!tab.canClose) return;
 | ||
| 
 | ||
|         this.tabs.splice(tabIndex, 1);
 | ||
| 
 | ||
|         // 如果关闭的是当前激活的tab,切换到其他tab
 | ||
|         if (this.activeTabId === tabId) {
 | ||
|             if (this.tabs.length > 0) {
 | ||
|                 this.showTab(this.tabs[this.tabs.length - 1].id);
 | ||
|             } else {
 | ||
|                 this.showDefaultContent();
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
|         this.renderTabs();
 | ||
|     }
 | ||
| 
 | ||
|     // 显示指定tab
 | ||
|     showTab(tabId) {
 | ||
|         const tab = this.tabs.find(t => t.id === tabId);
 | ||
|         if (!tab) return;
 | ||
| 
 | ||
|         this.activeTabId = tabId;
 | ||
|         
 | ||
|         // 更新tab激活状态
 | ||
|         document.querySelectorAll('.tab-item').forEach(tabEl => {
 | ||
|             if (tabEl.dataset.tabId === tabId) {
 | ||
|                 tabEl.classList.add('border-green-500', 'text-green-600');
 | ||
|                 tabEl.classList.remove('border-transparent', 'text-gray-500');
 | ||
|             } else {
 | ||
|                 tabEl.classList.remove('border-green-500', 'text-green-600');
 | ||
|                 tabEl.classList.add('border-transparent', 'text-gray-500');
 | ||
|             }
 | ||
|         });
 | ||
| 
 | ||
|         // 更新内容区域
 | ||
|         const contentArea = document.getElementById('tab-content-area');
 | ||
|         if (contentArea) {
 | ||
|             contentArea.innerHTML = tab.content;
 | ||
|         }
 | ||
| 
 | ||
|         // 显示tab容器
 | ||
|         this.showTabContainer();
 | ||
|     }
 | ||
| 
 | ||
|     // 渲染tab导航
 | ||
|     renderTabs() {
 | ||
|         const tabNav = document.getElementById('tab-nav');
 | ||
|         if (!tabNav) return;
 | ||
| 
 | ||
|         tabNav.innerHTML = '';
 | ||
| 
 | ||
|         this.tabs.forEach(tab => {
 | ||
|             const tabElement = document.createElement('div');
 | ||
|             tabElement.className = `tab-item flex items-center px-4 py-3 border-b-2 cursor-pointer transition-colors ${
 | ||
|                 this.activeTabId === tab.id 
 | ||
|                     ? 'border-green-500 text-green-600' 
 | ||
|                     : 'border-transparent text-gray-500 hover:text-gray-700'
 | ||
|             }`;
 | ||
|             tabElement.dataset.tabId = tab.id;
 | ||
|             
 | ||
|             tabElement.innerHTML = `
 | ||
|                 <span class="text-sm font-medium">${tab.title}</span>
 | ||
|                 ${tab.canClose ? `
 | ||
|                     <button class="ml-2 p-1 hover:bg-gray-100 rounded" onclick="event.stopPropagation(); memberTabManager.closeTab('${tab.id}')">
 | ||
|                         <svg class="h-3 w-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
 | ||
|                             <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
 | ||
|                         </svg>
 | ||
|                     </button>
 | ||
|                 ` : ''}
 | ||
|             `;
 | ||
| 
 | ||
|             tabElement.addEventListener('click', () => this.showTab(tab.id));
 | ||
|             tabNav.appendChild(tabElement);
 | ||
|         });
 | ||
|     }
 | ||
| 
 | ||
|     // 显示tab容器
 | ||
|     showTabContainer() {
 | ||
|         const tabContainer = document.getElementById('tab-container');
 | ||
|         if (tabContainer) {
 | ||
|             tabContainer.classList.remove('hidden');
 | ||
|             tabContainer.classList.add('fade-in');
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     // 显示默认内容
 | ||
|     showDefaultContent() {
 | ||
|         const tabContainer = document.getElementById('tab-container');
 | ||
|         if (tabContainer) {
 | ||
|             const contentArea = document.getElementById('tab-content-area');
 | ||
|             if (contentArea) {
 | ||
|                 contentArea.innerHTML = `
 | ||
|                     <div class="flex items-center justify-center h-96 text-gray-500">
 | ||
|                         <div class="text-center">
 | ||
|                             <svg class="h-16 w-16 mx-auto mb-4 text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
 | ||
|                                 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" />
 | ||
|                                 <circle cx="9" cy="7" r="4" />
 | ||
|                                 <path d="m22 21-3-3m0 0a8 8 0 1 1-11.31-11.31 8 8 0 0 1 11.31 11.31z" />
 | ||
|                             </svg>
 | ||
|                             <p class="text-lg">会员管理</p>
 | ||
|                             <p class="text-sm mt-2">请从左侧菜单选择功能</p>
 | ||
|                         </div>
 | ||
|                     </div>
 | ||
|                 `;
 | ||
|             }
 | ||
|         }
 | ||
|         
 | ||
|         this.activeTabId = '';
 | ||
|         this.tabs = [];
 | ||
|         this.renderTabs();
 | ||
|     }
 | ||
| 
 | ||
|     // 清空所有tabs
 | ||
|     clearAllTabs() {
 | ||
|         this.tabs = [];
 | ||
|         this.activeTabId = '';
 | ||
|         this.showDefaultContent();
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| // 创建全局tab管理器实例
 | ||
| const memberTabManager = new MemberTabManager();
 | ||
| 
 | ||
| // 打开等级设置Tab
 | ||
| function openLevelSettingsTab() {
 | ||
|     const tabId = 'level-settings';
 | ||
|     
 | ||
|     const content = `
 | ||
|         <div class="p-6">
 | ||
|             <div class="flex items-center justify-between mb-6">
 | ||
|                 <h2 class="text-lg font-medium text-gray-900">等级设置</h2>
 | ||
|             </div>
 | ||
|             
 | ||
|             <!-- 摊位名称筛选器 -->
 | ||
|             <div class="mb-6 flex items-center gap-4">
 | ||
|                 <div class="flex-1 max-w-md">
 | ||
|                     <label class="block text-sm font-medium text-gray-700 mb-2">摊位名称</label>
 | ||
|                     <div class="relative">
 | ||
|                         <select id="stall-filter" multiple class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-green-500 bg-white" size="1" onclick="toggleStallDropdown(event)">
 | ||
|                             <option value="">请选择摊位名称...</option>
 | ||
|                             <option value="时尚服装店">时尚服装店</option>
 | ||
|                             <option value="数码电子城">数码电子城</option>
 | ||
|                             <option value="美食餐厅">美食餐厅</option>
 | ||
|                             <option value="家居生活馆">家居生活馆</option>
 | ||
|                             <option value="运动健身店">运动健身店</option>
 | ||
|                         </select>
 | ||
|                         <div id="selected-stalls" class="mt-2 flex flex-wrap gap-2"></div>
 | ||
|                     </div>
 | ||
|                 </div>
 | ||
|                 <div class="mt-7 flex gap-3">
 | ||
|                     <button id="query-btn" class="px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 transition-colors" onclick="filterStallTable()">
 | ||
|                         查询
 | ||
|                     </button>
 | ||
|                     <button id="batch-edit-btn" class="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors" onclick="openBatchLevelEditTab()">
 | ||
|                         批量会员等级编辑
 | ||
|                     </button>
 | ||
|                 </div>
 | ||
|             </div>
 | ||
|             
 | ||
|             <div class="overflow-x-auto">
 | ||
|                 <table class="w-full">
 | ||
|                     <thead class="bg-gray-50">
 | ||
|                         <tr>
 | ||
|                             <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
 | ||
|                                 摊位名称
 | ||
|                             </th>
 | ||
|                             <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
 | ||
|                                 是否启用会员
 | ||
|                             </th>
 | ||
|                             <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
 | ||
|                                 会员等级
 | ||
|                             </th>
 | ||
|                             <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
 | ||
|                                 操作
 | ||
|                             </th>
 | ||
|                         </tr>
 | ||
|                     </thead>
 | ||
|                     <tbody class="bg-white divide-y divide-gray-200">
 | ||
|                         <tr>
 | ||
|                             <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">时尚服装店</td>
 | ||
|                             <td class="px-6 py-4 whitespace-nowrap">
 | ||
|                                 <label class="relative inline-flex items-center cursor-pointer">
 | ||
|                                     <input type="checkbox" class="sr-only peer" checked onchange="toggleSwitch(this)">
 | ||
|                                     <div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-green-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-green-600"></div>
 | ||
|                                 </label>
 | ||
|                             </td>
 | ||
|                             <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">lv1, lv2, lv3, lv4</td>
 | ||
|                             <td class="px-6 py-4 whitespace-nowrap">
 | ||
|                                 <button class="text-green-600 hover:text-green-900 text-sm font-medium" onclick="openLevelDetailTab('时尚服装店')">
 | ||
|                                     设置等级明细
 | ||
|                                 </button>
 | ||
|                             </td>
 | ||
|                         </tr>
 | ||
|                         <tr>
 | ||
|                             <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">数码电子城</td>
 | ||
|                             <td class="px-6 py-4 whitespace-nowrap">
 | ||
|                                 <label class="relative inline-flex items-center cursor-pointer">
 | ||
|                                     <input type="checkbox" class="sr-only peer" onchange="toggleSwitch(this)">
 | ||
|                                     <div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-green-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-green-600"></div>
 | ||
|                                 </label>
 | ||
|                             </td>
 | ||
|                             <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">lv1, lv2, lv3</td>
 | ||
|                             <td class="px-6 py-4 whitespace-nowrap">
 | ||
|                                 <button class="text-green-600 hover:text-green-900 text-sm font-medium" onclick="openLevelDetailTab('数码电子城')">
 | ||
|                                     设置等级明细
 | ||
|                                 </button>
 | ||
|                             </td>
 | ||
|                         </tr>
 | ||
|                         <tr>
 | ||
|                             <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">美食餐厅</td>
 | ||
|                             <td class="px-6 py-4 whitespace-nowrap">
 | ||
|                                 <label class="relative inline-flex items-center cursor-pointer">
 | ||
|                                     <input type="checkbox" class="sr-only peer" checked onchange="toggleSwitch(this)">
 | ||
|                                     <div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-green-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-green-600"></div>
 | ||
|                                 </label>
 | ||
|                             </td>
 | ||
|                             <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">lv1, lv2, lv3, lv4</td>
 | ||
|                             <td class="px-6 py-4 whitespace-nowrap">
 | ||
|                                 <button class="text-green-600 hover:text-green-900 text-sm font-medium" onclick="openLevelDetailTab('美食餐厅')">
 | ||
|                                     设置等级明细
 | ||
|                                 </button>
 | ||
|                             </td>
 | ||
|                         </tr>
 | ||
|                         <tr>
 | ||
|                             <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">家居生活馆</td>
 | ||
|                             <td class="px-6 py-4 whitespace-nowrap">
 | ||
|                                 <label class="relative inline-flex items-center cursor-pointer">
 | ||
|                                     <input type="checkbox" class="sr-only peer" onchange="toggleSwitch(this)">
 | ||
|                                     <div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-green-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-green-600"></div>
 | ||
|                                 </label>
 | ||
|                             </td>
 | ||
|                             <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">lv1, lv2</td>
 | ||
|                             <td class="px-6 py-4 whitespace-nowrap">
 | ||
|                                 <button class="text-green-600 hover:text-green-900 text-sm font-medium" onclick="openLevelDetailTab('家居生活馆')">
 | ||
|                                     设置等级明细
 | ||
|                                 </button>
 | ||
|                             </td>
 | ||
|                         </tr>
 | ||
|                         <tr>
 | ||
|                             <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">运动健身店</td>
 | ||
|                             <td class="px-6 py-4 whitespace-nowrap">
 | ||
|                                 <label class="relative inline-flex items-center cursor-pointer">
 | ||
|                                     <input type="checkbox" class="sr-only peer" checked onchange="toggleSwitch(this)">
 | ||
|                                     <div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-green-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-green-600"></div>
 | ||
|                                 </label>
 | ||
|                             </td>
 | ||
|                             <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">lv1, lv2, lv3, lv4</td>
 | ||
|                             <td class="px-6 py-4 whitespace-nowrap">
 | ||
|                                 <button class="text-green-600 hover:text-green-900 text-sm font-medium" onclick="openLevelDetailTab('运动健身店')">
 | ||
|                                     设置等级明细
 | ||
|                                 </button>
 | ||
|                             </td>
 | ||
|                         </tr>
 | ||
|                     </tbody>
 | ||
|                 </table>
 | ||
|             </div>
 | ||
|         </div>
 | ||
|     `;
 | ||
| 
 | ||
|     memberTabManager.createTab(tabId, '等级设置', content, false);
 | ||
| }
 | ||
| 
 | ||
| // 打开等级明细tab
 | ||
| function openLevelDetailTab(shopName) {
 | ||
|     const tabId = `level-detail-${shopName}`;
 | ||
|     
 | ||
|     // 生成模拟数据
 | ||
|     const mockLevels = [
 | ||
|         { name: 'LV1', growth: 100, members: 1250, benefits: '9.5折优惠' },
 | ||
|         { name: 'LV2', growth: 500, members: 850, benefits: '9折优惠, 积分双倍' },
 | ||
|         { name: 'LV3', growth: 1200, members: 450, benefits: '8.5折优惠, 积分双倍, 生日优惠券' },
 | ||
|         { name: 'LV4', growth: 2500, members: 180, benefits: '8折优惠, 积分三倍, 生日优惠券, 专属客服' }
 | ||
|     ];
 | ||
| 
 | ||
|     const content = `
 | ||
|         <div class="p-6">
 | ||
|             <div class="flex items-center justify-between mb-6">
 | ||
|                 <h2 class="text-lg font-medium text-gray-900">${shopName} - 等级设置明细</h2>
 | ||
|                 <div class="flex gap-3">
 | ||
|                     <button class="px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 transition-colors" onclick="openLevelEditTab('${shopName}')">
 | ||
|                         等级编辑
 | ||
|                     </button>
 | ||
|                     <button class="px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 transition-colors" onclick="submitLevelDetail('${shopName}')">
 | ||
|                         提交
 | ||
|                     </button>
 | ||
|                 </div>
 | ||
|             </div>
 | ||
|             
 | ||
|             <div class="overflow-x-auto">
 | ||
|                 <table class="w-full border border-gray-200 rounded-lg">
 | ||
|                     <thead class="bg-gray-50">
 | ||
|                         <tr>
 | ||
|                             <th class="px-4 py-3 text-left text-sm font-medium text-gray-900 border-b">等级名称</th>
 | ||
|                             <th class="px-4 py-3 text-left text-sm font-medium text-gray-900 border-b">所需成长值</th>
 | ||
|                             <th class="px-4 py-3 text-left text-sm font-medium text-gray-900 border-b">会员人数</th>
 | ||
|                             <th class="px-4 py-3 text-left text-sm font-medium text-gray-900 border-b">会员权益</th>
 | ||
|                         </tr>
 | ||
|                     </thead>
 | ||
|                     <tbody class="bg-white divide-y divide-gray-200">
 | ||
|                         ${mockLevels.map(level => `
 | ||
|                             <tr>
 | ||
|                                 <td class="px-4 py-3 text-sm text-gray-900">${level.name}</td>
 | ||
|                                 <td class="px-4 py-3 text-sm text-gray-900">${level.growth}</td>
 | ||
|                                 <td class="px-4 py-3 text-sm text-gray-900">${level.members}</td>
 | ||
|                                 <td class="px-4 py-3 text-sm text-gray-900">${level.benefits}</td>
 | ||
|                             </tr>
 | ||
|                         `).join('')}
 | ||
|                     </tbody>
 | ||
|                 </table>
 | ||
|             </div>
 | ||
|         </div>
 | ||
|     `;
 | ||
| 
 | ||
|     memberTabManager.createTab(tabId, `${shopName} - 等级明细`, content, true);
 | ||
| }
 | ||
| 
 | ||
| // 打开等级编辑Tab
 | ||
| function openLevelEditTab(shopName) {
 | ||
|     const tabId = `level-edit-${shopName}`;
 | ||
|     
 | ||
|     // 生成模拟等级编辑数据
 | ||
|     const mockEditLevels = [
 | ||
|         { 
 | ||
|             level: 'LV1', 
 | ||
|             name: '铜牌会员', 
 | ||
|             growthMin: 0, 
 | ||
|             growthMax: 100, 
 | ||
|             memberDiscount: true, 
 | ||
|             discountRate: 5, 
 | ||
|             pointExchange: false, 
 | ||
|             birthdayCoupon: false, 
 | ||
|             birthdayDoublePoints: false 
 | ||
|         },
 | ||
|         { 
 | ||
|             level: 'LV2', 
 | ||
|             name: '银牌会员', 
 | ||
|             growthMin: 101, 
 | ||
|             growthMax: 500, 
 | ||
|             memberDiscount: true, 
 | ||
|             discountRate: 10, 
 | ||
|             pointExchange: true, 
 | ||
|             birthdayCoupon: true, 
 | ||
|             birthdayDoublePoints: true 
 | ||
|         },
 | ||
|         { 
 | ||
|             level: 'LV3', 
 | ||
|             name: '金牌会员', 
 | ||
|             growthMin: 501, 
 | ||
|             growthMax: 1200, 
 | ||
|             memberDiscount: true, 
 | ||
|             discountRate: 15, 
 | ||
|             pointExchange: true, 
 | ||
|             birthdayCoupon: true, 
 | ||
|             birthdayDoublePoints: true 
 | ||
|         },
 | ||
|         { 
 | ||
|             level: 'LV4', 
 | ||
|             name: '钻石会员', 
 | ||
|             growthMin: 1201, 
 | ||
|             growthMax: 9999, 
 | ||
|             memberDiscount: true, 
 | ||
|             discountRate: 20, 
 | ||
|             pointExchange: true, 
 | ||
|             birthdayCoupon: true, 
 | ||
|             birthdayDoublePoints: true 
 | ||
|         }
 | ||
|     ];
 | ||
| 
 | ||
|     const content = `
 | ||
|         <div class="p-6">
 | ||
|             <div class="flex items-center justify-between mb-6">
 | ||
|                 <h2 class="text-lg font-medium text-gray-900">${shopName} - 等级编辑</h2>
 | ||
|                 <div class="flex gap-3">
 | ||
|                     <button class="px-4 py-2 bg-gray-500 text-white rounded-md hover:bg-gray-600 transition-colors" onclick="memberTabManager.closeTab('${tabId}')">
 | ||
|                         关闭
 | ||
|                     </button>
 | ||
|                     <button class="px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 transition-colors" onclick="submitLevelEdit('${shopName}')">
 | ||
|                         提交
 | ||
|                     </button>
 | ||
|                 </div>
 | ||
|             </div>
 | ||
|             
 | ||
|             <div class="overflow-x-auto">
 | ||
|                 <table class="w-full border border-gray-200 rounded-lg">
 | ||
|                     <thead class="bg-gray-50">
 | ||
|                         <tr>
 | ||
|                             <th class="px-3 py-3 text-left text-xs font-medium text-gray-900 border-b">等级</th>
 | ||
|                             <th class="px-3 py-3 text-left text-xs font-medium text-gray-900 border-b">等级名称</th>
 | ||
|                             <th class="px-3 py-3 text-left text-xs font-medium text-gray-900 border-b">成长值范围</th>
 | ||
|                             <th class="px-3 py-3 text-left text-xs font-medium text-gray-900 border-b">开启会员折扣</th>
 | ||
|                             <th class="px-3 py-3 text-left text-xs font-medium text-gray-900 border-b">折扣率(%)</th>
 | ||
|                             <th class="px-3 py-3 text-left text-xs font-medium text-gray-900 border-b">开启积分兑换</th>
 | ||
|                             <th class="px-3 py-3 text-left text-xs font-medium text-gray-900 border-b">开启生日优惠券</th>
 | ||
|                             <th class="px-3 py-3 text-left text-xs font-medium text-gray-900 border-b">生日优惠券操作</th>
 | ||
|                             <th class="px-3 py-3 text-left text-xs font-medium text-gray-900 border-b">开启生日双倍积分</th>
 | ||
|                         </tr>
 | ||
|                     </thead>
 | ||
|                     <tbody class="bg-white divide-y divide-gray-200">
 | ||
|                         ${mockEditLevels.map(level => `
 | ||
|                             <tr>
 | ||
|                                 <td class="px-3 py-3 text-sm text-gray-900">${level.level}</td>
 | ||
|                                 <td class="px-3 py-3">
 | ||
|                                     <input type="text" value="${level.name}" class="w-full px-2 py-1 border border-gray-300 rounded text-sm">
 | ||
|                                 </td>
 | ||
|                                 <td class="px-3 py-3 text-sm text-gray-900">
 | ||
|                                     <div class="flex items-center gap-1">
 | ||
|                                         <input type="number" value="${level.growthMin}" class="w-16 px-2 py-1 border border-gray-300 rounded text-sm">
 | ||
|                                         <span>-</span>
 | ||
|                                         <input type="number" value="${level.growthMax}" class="w-16 px-2 py-1 border border-gray-300 rounded text-sm">
 | ||
|                                     </div>
 | ||
|                                 </td>
 | ||
|                                 <td class="px-3 py-3">
 | ||
|                                     <label class="relative inline-flex items-center cursor-pointer">
 | ||
|                                         <input type="checkbox" class="sr-only peer" ${level.memberDiscount ? 'checked' : ''}>
 | ||
|                                         <div class="w-9 h-5 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-green-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-4 after:w-4 after:transition-all peer-checked:bg-green-600"></div>
 | ||
|                                     </label>
 | ||
|                                 </td>
 | ||
|                                 <td class="px-3 py-3">
 | ||
|                                     <input type="number" value="${level.discountRate}" class="w-16 px-2 py-1 border border-gray-300 rounded text-sm">
 | ||
|                                 </td>
 | ||
|                                 <td class="px-3 py-3">
 | ||
|                                     <label class="relative inline-flex items-center cursor-pointer">
 | ||
|                                         <input type="checkbox" class="sr-only peer" ${level.pointExchange ? 'checked' : ''}>
 | ||
|                                         <div class="w-9 h-5 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-green-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-4 after:w-4 after:transition-all peer-checked:bg-green-600"></div>
 | ||
|                                     </label>
 | ||
|                                 </td>
 | ||
|                                 <td class="px-3 py-3">
 | ||
|                                     <label class="relative inline-flex items-center cursor-pointer">
 | ||
|                                         <input type="checkbox" class="sr-only peer" ${level.birthdayCoupon ? 'checked' : ''}>
 | ||
|                                         <div class="w-9 h-5 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-green-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-4 after:w-4 after:transition-all peer-checked:bg-green-600"></div>
 | ||
|                                     </label>
 | ||
|                                 </td>
 | ||
|                                 <td class="px-3 py-3">
 | ||
|                                     <button class="px-3 py-1 bg-blue-600 text-white text-xs rounded hover:bg-blue-700 transition-colors" onclick="openCouponModal('${level.level}')">
 | ||
|                                         添加优惠券
 | ||
|                                     </button>
 | ||
|                                 </td>
 | ||
|                                 <td class="px-3 py-3">
 | ||
|                                     <label class="relative inline-flex items-center cursor-pointer">
 | ||
|                                         <input type="checkbox" class="sr-only peer" ${level.birthdayDoublePoints ? 'checked' : ''}>
 | ||
|                                         <div class="w-9 h-5 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-green-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-4 after:w-4 after:transition-all peer-checked:bg-green-600"></div>
 | ||
|                                     </label>
 | ||
|                                 </td>
 | ||
|                             </tr>
 | ||
|                         `).join('')}
 | ||
|                     </tbody>
 | ||
|                 </table>
 | ||
|             </div>
 | ||
|         </div>
 | ||
|     `;
 | ||
| 
 | ||
|     memberTabManager.createTab(tabId, `${shopName} - 等级编辑`, content, true);
 | ||
| }
 | ||
| 
 | ||
| // 打开优惠券弹窗
 | ||
| function openCouponModal(level) {
 | ||
|     const modal = document.getElementById('coupon-modal');
 | ||
|     if (modal) {
 | ||
|         modal.classList.remove('hidden');
 | ||
|         resetCouponForm();
 | ||
|         console.log(`为等级 ${level} 添加优惠券`);
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| // 测试优惠券弹窗功能
 | ||
| function testCouponModal() {
 | ||
|     openCouponModal('TEST');
 | ||
| }
 | ||
| 
 | ||
| // 优惠券相关函数
 | ||
| function closeCouponModal(event) {
 | ||
|     if (event && event.target !== event.currentTarget) {
 | ||
|         return;
 | ||
|     }
 | ||
|     
 | ||
|     const modal = document.getElementById('coupon-modal');
 | ||
|     if (modal) {
 | ||
|         modal.classList.add('hidden');
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| function resetCouponForm() {
 | ||
|     document.getElementById('coupon-name').value = '生日优惠券';
 | ||
|     document.getElementById('coupon-threshold').value = '10';
 | ||
|     document.getElementById('coupon-discount').value = '1';
 | ||
|     document.getElementById('coupon-validity').value = '1';
 | ||
| }
 | ||
| 
 | ||
| function adjustThreshold(delta) {
 | ||
|     const input = document.getElementById('coupon-threshold');
 | ||
|     if (input) {
 | ||
|         const currentValue = parseInt(input.value) || 0;
 | ||
|         const newValue = Math.max(0, currentValue + delta);
 | ||
|         input.value = newValue;
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| function adjustDiscount(delta) {
 | ||
|     const input = document.getElementById('coupon-discount');
 | ||
|     if (input) {
 | ||
|         const currentValue = parseInt(input.value) || 0;
 | ||
|         const newValue = Math.max(0, currentValue + delta);
 | ||
|         input.value = newValue;
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| function submitCoupon() {
 | ||
|     const couponData = {
 | ||
|         name: document.getElementById('coupon-name').value,
 | ||
|         threshold: document.getElementById('coupon-threshold').value,
 | ||
|         discount: document.getElementById('coupon-discount').value,
 | ||
|         validity: document.getElementById('coupon-validity').value
 | ||
|     };
 | ||
|     
 | ||
|     if (!couponData.name.trim()) {
 | ||
|         showNotification('请输入优惠券名称', 'error');
 | ||
|         return;
 | ||
|     }
 | ||
|     
 | ||
|     if (parseInt(couponData.discount) >= parseInt(couponData.threshold)) {
 | ||
|         showNotification('减免金额不能大于或等于门槛金额', 'error');
 | ||
|         return;
 | ||
|     }
 | ||
|     
 | ||
|     showNotification(`成功添加优惠券:${couponData.name}`, 'success');
 | ||
|     closeCouponModal();
 | ||
| }
 | ||
| 
 | ||
| function submitLevelDetail(shopName) {
 | ||
|     showNotification(`${shopName} 等级明细已提交`, 'success');
 | ||
| }
 | ||
| 
 | ||
| function submitLevelEdit(shopName) {
 | ||
|     showNotification(`${shopName} 等级编辑已提交`, 'success');
 | ||
| }
 | ||
| 
 | ||
| // 摊位筛选相关功能
 | ||
| let selectedStalls = [];
 | ||
| let stallDropdownOpen = false;
 | ||
| 
 | ||
| function toggleStallDropdown(event) {
 | ||
|     event.preventDefault();
 | ||
|     const select = document.getElementById('stall-filter');
 | ||
|     const selectedDiv = document.getElementById('selected-stalls');
 | ||
|     
 | ||
|     if (!stallDropdownOpen) {
 | ||
|         select.size = 6;
 | ||
|         stallDropdownOpen = true;
 | ||
|         
 | ||
|         // 添加点击事件监听器
 | ||
|         document.addEventListener('click', closeStallDropdown);
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| function closeStallDropdown(event) {
 | ||
|     const select = document.getElementById('stall-filter');
 | ||
|     const selectedDiv = document.getElementById('selected-stalls');
 | ||
|     
 | ||
|     if (!select || !select.contains(event.target)) {
 | ||
|         select.size = 1;
 | ||
|         stallDropdownOpen = false;
 | ||
|         document.removeEventListener('click', closeStallDropdown);
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| // 处理多选选项
 | ||
| document.addEventListener('change', function(event) {
 | ||
|     if (event.target.id === 'stall-filter') {
 | ||
|         const select = event.target;
 | ||
|         const selectedOptions = Array.from(select.selectedOptions);
 | ||
|         const selectedDiv = document.getElementById('selected-stalls');
 | ||
|         
 | ||
|         selectedStalls = selectedOptions
 | ||
|             .filter(option => option.value && option.value !== '')
 | ||
|             .map(option => option.value);
 | ||
|         
 | ||
|         updateSelectedStallsDisplay();
 | ||
|     }
 | ||
| });
 | ||
| 
 | ||
| function updateSelectedStallsDisplay() {
 | ||
|     const selectedDiv = document.getElementById('selected-stalls');
 | ||
|     if (!selectedDiv) return;
 | ||
|     
 | ||
|     selectedDiv.innerHTML = '';
 | ||
|     
 | ||
|     selectedStalls.forEach(stall => {
 | ||
|         const tag = document.createElement('span');
 | ||
|         tag.className = 'inline-flex items-center px-2 py-1 rounded-md text-xs font-medium bg-green-100 text-green-800';
 | ||
|         tag.innerHTML = `
 | ||
|             ${stall}
 | ||
|             <button type="button" class="ml-1 inline-flex items-center justify-center w-4 h-4 rounded-full hover:bg-green-200" onclick="removeSelectedStall('${stall}')">
 | ||
|                 <svg class="w-2 h-2" fill="currentColor" viewBox="0 0 20 20">
 | ||
|                     <path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path>
 | ||
|                 </svg>
 | ||
|             </button>
 | ||
|         `;
 | ||
|         selectedDiv.appendChild(tag);
 | ||
|     });
 | ||
| }
 | ||
| 
 | ||
| function removeSelectedStall(stallName) {
 | ||
|     selectedStalls = selectedStalls.filter(stall => stall !== stallName);
 | ||
|     
 | ||
|     // 更新select控件的选中状态
 | ||
|     const select = document.getElementById('stall-filter');
 | ||
|     if (select) {
 | ||
|         Array.from(select.options).forEach(option => {
 | ||
|             if (option.value === stallName) {
 | ||
|                 option.selected = false;
 | ||
|             }
 | ||
|         });
 | ||
|     }
 | ||
|     
 | ||
|     updateSelectedStallsDisplay();
 | ||
| }
 | ||
| 
 | ||
| function filterStallTable() {
 | ||
|     const tableRows = document.querySelectorAll('#tab-content-area tbody tr');
 | ||
|     
 | ||
|     if (selectedStalls.length === 0) {
 | ||
|         // 如果没有选择任何摊位,显示所有行
 | ||
|         tableRows.forEach(row => {
 | ||
|             row.style.display = '';
 | ||
|         });
 | ||
|         showNotification('已显示所有摊位', 'info');
 | ||
|     } else {
 | ||
|         // 根据选择的摊位筛选表格行
 | ||
|         let visibleCount = 0;
 | ||
|         tableRows.forEach(row => {
 | ||
|             const stallNameCell = row.querySelector('td:first-child');
 | ||
|             if (stallNameCell) {
 | ||
|                 const stallName = stallNameCell.textContent.trim();
 | ||
|                 if (selectedStalls.includes(stallName)) {
 | ||
|                     row.style.display = '';
 | ||
|                     visibleCount++;
 | ||
|                 } else {
 | ||
|                     row.style.display = 'none';
 | ||
|                 }
 | ||
|             }
 | ||
|         });
 | ||
|         showNotification(`已筛选显示 ${visibleCount} 个摊位`, 'success');
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| // 打开批量会员等级编辑Tab
 | ||
| function openBatchLevelEditTab() {
 | ||
|     const tabId = 'batch-level-edit';
 | ||
|     
 | ||
|     // 生成摊位数据
 | ||
|     const stallOptions = [
 | ||
|         '新世纪电子城',
 | ||
|         '金辉服装店',
 | ||
|         '华联超市',
 | ||
|         '美味餐厅',
 | ||
|         '家具城',
 | ||
|         '运动专营店',
 | ||
|         '书香文具店',
 | ||
|         '花园茶社'
 | ||
|     ];
 | ||
|     
 | ||
|     // 生成等级编辑数据(与等级编辑页面内容一致)
 | ||
|     const mockEditLevels = [
 | ||
|         { 
 | ||
|             level: 'LV1', 
 | ||
|             name: '铜牌会员', 
 | ||
|             growthMin: 0, 
 | ||
|             growthMax: 100, 
 | ||
|             memberDiscount: true, 
 | ||
|             discountRate: 5, 
 | ||
|             pointExchange: false, 
 | ||
|             birthdayCoupon: false, 
 | ||
|             birthdayDoublePoints: false 
 | ||
|         },
 | ||
|         { 
 | ||
|             level: 'LV2', 
 | ||
|             name: '银牌会员', 
 | ||
|             growthMin: 101, 
 | ||
|             growthMax: 500, 
 | ||
|             memberDiscount: true, 
 | ||
|             discountRate: 10, 
 | ||
|             pointExchange: true, 
 | ||
|             birthdayCoupon: true, 
 | ||
|             birthdayDoublePoints: true 
 | ||
|         },
 | ||
|         { 
 | ||
|             level: 'LV3', 
 | ||
|             name: '金牌会员', 
 | ||
|             growthMin: 501, 
 | ||
|             growthMax: 1200, 
 | ||
|             memberDiscount: true, 
 | ||
|             discountRate: 15, 
 | ||
|             pointExchange: true, 
 | ||
|             birthdayCoupon: true, 
 | ||
|             birthdayDoublePoints: true 
 | ||
|         },
 | ||
|         { 
 | ||
|             level: 'LV4', 
 | ||
|             name: '钻石会员', 
 | ||
|             growthMin: 1201, 
 | ||
|             growthMax: 9999, 
 | ||
|             memberDiscount: true, 
 | ||
|             discountRate: 20, 
 | ||
|             pointExchange: true, 
 | ||
|             birthdayCoupon: true, 
 | ||
|             birthdayDoublePoints: true 
 | ||
|         }
 | ||
|     ];
 | ||
| 
 | ||
|     const content = `
 | ||
|         <div class="p-6">
 | ||
|             <div class="flex items-center justify-between mb-6">
 | ||
|                 <h2 class="text-lg font-medium text-gray-900">批量会员等级编辑</h2>
 | ||
|                 <div class="flex gap-3">
 | ||
|                     <button class="px-4 py-2 bg-gray-500 text-white rounded-md hover:bg-gray-600 transition-colors" onclick="memberTabManager.closeTab('${tabId}')">
 | ||
|                         关闭
 | ||
|                     </button>
 | ||
|                     <button class="px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 transition-colors" onclick="submitBatchLevelEdit()">
 | ||
|                         提交
 | ||
|                     </button>
 | ||
|                 </div>
 | ||
|             </div>
 | ||
|             
 | ||
|             <!-- 生效摊位选择区域 -->
 | ||
|             <div class="mb-6 flex items-center gap-4">
 | ||
|                 <div class="flex-1 max-w-md">
 | ||
|                     <label class="block text-sm font-medium text-gray-700 mb-2">生效的摊位</label>
 | ||
|                     <div class="relative">
 | ||
|                         <button id="batch-stall-dropdown-btn" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-green-500 bg-white text-left flex items-center justify-between" onclick="toggleBatchStallDropdown()">
 | ||
|                             <span id="batch-dropdown-text">全部选择</span>
 | ||
|                             <svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
 | ||
|                                 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
 | ||
|                             </svg>
 | ||
|                         </button>
 | ||
|                         <div id="batch-stall-dropdown-list" class="absolute z-10 w-full mt-1 bg-white border border-gray-300 rounded-md shadow-lg hidden">
 | ||
|                             ${stallOptions.map(stall => `
 | ||
|                                 <label class="flex items-center px-3 py-2 hover:bg-gray-50 cursor-pointer">
 | ||
|                                     <input type="checkbox" value="${stall}" class="mr-2" checked onchange="updateBatchSelectedStalls()">
 | ||
|                                     ${stall}
 | ||
|                                 </label>
 | ||
|                             `).join('')}
 | ||
|                         </div>
 | ||
|                         <div id="batch-selected-stalls" class="mt-2 flex flex-wrap gap-2"></div>
 | ||
|                     </div>
 | ||
|                 </div>
 | ||
|                 <div class="mt-7">
 | ||
|                     <button class="px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 transition-colors" onclick="submitBatchLevelEdit()">
 | ||
|                         提交
 | ||
|                     </button>
 | ||
|                 </div>
 | ||
|             </div>
 | ||
|             
 | ||
|             <!-- 等级编辑表格(与等级编辑页面内容一致) -->
 | ||
|             <div class="overflow-x-auto">
 | ||
|                 <table class="w-full border border-gray-200 rounded-lg">
 | ||
|                     <thead class="bg-gray-50">
 | ||
|                         <tr>
 | ||
|                             <th class="px-3 py-3 text-left text-xs font-medium text-gray-900 border-b">等级</th>
 | ||
|                             <th class="px-3 py-3 text-left text-xs font-medium text-gray-900 border-b">等级名称</th>
 | ||
|                             <th class="px-3 py-3 text-left text-xs font-medium text-gray-900 border-b">成长值范围</th>
 | ||
|                             <th class="px-3 py-3 text-left text-xs font-medium text-gray-900 border-b">开启会员折扣</th>
 | ||
|                             <th class="px-3 py-3 text-left text-xs font-medium text-gray-900 border-b">折扣率(%)</th>
 | ||
|                             <th class="px-3 py-3 text-left text-xs font-medium text-gray-900 border-b">开启积分兑换</th>
 | ||
|                             <th class="px-3 py-3 text-left text-xs font-medium text-gray-900 border-b">开启生日优惠券</th>
 | ||
|                             <th class="px-3 py-3 text-left text-xs font-medium text-gray-900 border-b">生日优惠券操作</th>
 | ||
|                             <th class="px-3 py-3 text-left text-xs font-medium text-gray-900 border-b">开启生日双倍积分</th>
 | ||
|                         </tr>
 | ||
|                     </thead>
 | ||
|                     <tbody class="bg-white divide-y divide-gray-200">
 | ||
|                         ${mockEditLevels.map(level => `
 | ||
|                             <tr>
 | ||
|                                 <td class="px-3 py-3 text-sm text-gray-900">${level.level}</td>
 | ||
|                                 <td class="px-3 py-3">
 | ||
|                                     <input type="text" value="${level.name}" class="w-full px-2 py-1 border border-gray-300 rounded text-sm">
 | ||
|                                 </td>
 | ||
|                                 <td class="px-3 py-3 text-sm text-gray-900">
 | ||
|                                     <div class="flex items-center gap-1">
 | ||
|                                         <input type="number" value="${level.growthMin}" class="w-16 px-2 py-1 border border-gray-300 rounded text-sm">
 | ||
|                                         <span>-</span>
 | ||
|                                         <input type="number" value="${level.growthMax}" class="w-16 px-2 py-1 border border-gray-300 rounded text-sm">
 | ||
|                                     </div>
 | ||
|                                 </td>
 | ||
|                                 <td class="px-3 py-3">
 | ||
|                                     <label class="relative inline-flex items-center cursor-pointer">
 | ||
|                                         <input type="checkbox" class="sr-only peer" ${level.memberDiscount ? 'checked' : ''}>
 | ||
|                                         <div class="w-9 h-5 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-green-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-4 after:w-4 after:transition-all peer-checked:bg-green-600"></div>
 | ||
|                                     </label>
 | ||
|                                 </td>
 | ||
|                                 <td class="px-3 py-3">
 | ||
|                                     <input type="number" value="${level.discountRate}" class="w-16 px-2 py-1 border border-gray-300 rounded text-sm">
 | ||
|                                 </td>
 | ||
|                                 <td class="px-3 py-3">
 | ||
|                                     <label class="relative inline-flex items-center cursor-pointer">
 | ||
|                                         <input type="checkbox" class="sr-only peer" ${level.pointExchange ? 'checked' : ''}>
 | ||
|                                         <div class="w-9 h-5 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-green-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-4 after:w-4 after:transition-all peer-checked:bg-green-600"></div>
 | ||
|                                     </label>
 | ||
|                                 </td>
 | ||
|                                 <td class="px-3 py-3">
 | ||
|                                     <label class="relative inline-flex items-center cursor-pointer">
 | ||
|                                         <input type="checkbox" class="sr-only peer" ${level.birthdayCoupon ? 'checked' : ''}>
 | ||
|                                         <div class="w-9 h-5 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-green-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-4 after:w-4 after:transition-all peer-checked:bg-green-600"></div>
 | ||
|                                     </label>
 | ||
|                                 </td>
 | ||
|                                 <td class="px-3 py-3">
 | ||
|                                     <button class="px-3 py-1 bg-blue-600 text-white text-xs rounded hover:bg-blue-700 transition-colors" onclick="openCouponModal('${level.level}')">
 | ||
|                                         添加优惠券
 | ||
|                                     </button>
 | ||
|                                 </td>
 | ||
|                                 <td class="px-3 py-3">
 | ||
|                                     <label class="relative inline-flex items-center cursor-pointer">
 | ||
|                                         <input type="checkbox" class="sr-only peer" ${level.birthdayDoublePoints ? 'checked' : ''}>
 | ||
|                                         <div class="w-9 h-5 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-green-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-4 after:w-4 after:transition-all peer-checked:bg-green-600"></div>
 | ||
|                                     </label>
 | ||
|                                 </td>
 | ||
|                             </tr>
 | ||
|                         `).join('')}
 | ||
|                     </tbody>
 | ||
|                 </table>
 | ||
|             </div>
 | ||
|         </div>
 | ||
|     `;
 | ||
| 
 | ||
|     memberTabManager.createTab(tabId, '批量会员等级编辑', content, true);
 | ||
|     
 | ||
|     // 初始化批量选择的摊位显示(默认全选)
 | ||
|     setTimeout(() => {
 | ||
|         batchSelectedStalls = [...stallOptions];
 | ||
|         updateBatchSelectedStallsDisplay();
 | ||
|     }, 100);
 | ||
| }
 | ||
| 
 | ||
| // 批量编辑相关的全局变量
 | ||
| let batchSelectedStalls = [];
 | ||
| let batchDropdownOpen = false;
 | ||
| 
 | ||
| // 批量摊位选择相关功能
 | ||
| function toggleBatchStallDropdown() {
 | ||
|     const dropdown = document.getElementById('batch-stall-dropdown-list');
 | ||
|     const button = document.getElementById('batch-stall-dropdown-btn');
 | ||
|     
 | ||
|     if (dropdown && button) {
 | ||
|         batchDropdownOpen = !batchDropdownOpen;
 | ||
|         if (batchDropdownOpen) {
 | ||
|             dropdown.classList.remove('hidden');
 | ||
|             setTimeout(() => {
 | ||
|                 document.addEventListener('click', closeBatchDropdownOnClickOutside);
 | ||
|             }, 0);
 | ||
|         } else {
 | ||
|             dropdown.classList.add('hidden');
 | ||
|             document.removeEventListener('click', closeBatchDropdownOnClickOutside);
 | ||
|         }
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| function closeBatchDropdownOnClickOutside(event) {
 | ||
|     const dropdown = document.getElementById('batch-stall-dropdown-list');
 | ||
|     const button = document.getElementById('batch-stall-dropdown-btn');
 | ||
|     
 | ||
|     if (dropdown && button && !dropdown.contains(event.target) && !button.contains(event.target)) {
 | ||
|         dropdown.classList.add('hidden');
 | ||
|         batchDropdownOpen = false;
 | ||
|         document.removeEventListener('click', closeBatchDropdownOnClickOutside);
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| function updateBatchSelectedStalls() {
 | ||
|     const checkboxes = document.querySelectorAll('#batch-stall-dropdown-list input[type="checkbox"]');
 | ||
|     batchSelectedStalls = [];
 | ||
|     
 | ||
|     checkboxes.forEach(checkbox => {
 | ||
|         if (checkbox.checked) {
 | ||
|             batchSelectedStalls.push(checkbox.value);
 | ||
|         }
 | ||
|     });
 | ||
|     
 | ||
|     updateBatchDropdownText();
 | ||
|     updateBatchSelectedStallsDisplay();
 | ||
| }
 | ||
| 
 | ||
| function updateBatchDropdownText() {
 | ||
|     const dropdownText = document.getElementById('batch-dropdown-text');
 | ||
|     if (dropdownText) {
 | ||
|         if (batchSelectedStalls.length === 0) {
 | ||
|             dropdownText.textContent = '请选择摊位...';
 | ||
|         } else if (batchSelectedStalls.length === 8) {
 | ||
|             dropdownText.textContent = '全部选择';
 | ||
|         } else if (batchSelectedStalls.length === 1) {
 | ||
|             dropdownText.textContent = batchSelectedStalls[0];
 | ||
|         } else {
 | ||
|             dropdownText.textContent = `已选择 ${batchSelectedStalls.length} 个摊位`;
 | ||
|         }
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| function updateBatchSelectedStallsDisplay() {
 | ||
|     const selectedDiv = document.getElementById('batch-selected-stalls');
 | ||
|     if (!selectedDiv) return;
 | ||
|     
 | ||
|     selectedDiv.innerHTML = '';
 | ||
|     
 | ||
|     batchSelectedStalls.forEach(stall => {
 | ||
|         const tag = document.createElement('span');
 | ||
|         tag.className = 'inline-flex items-center px-2 py-1 rounded-md text-xs font-medium bg-blue-100 text-blue-800';
 | ||
|         tag.innerHTML = `
 | ||
|             ${stall}
 | ||
|             <button type="button" class="ml-1 inline-flex items-center justify-center w-4 h-4 rounded-full hover:bg-blue-200" onclick="removeBatchSelectedStall('${stall}')">
 | ||
|                 <svg class="w-2 h-2" fill="currentColor" viewBox="0 0 20 20">
 | ||
|                     <path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path>
 | ||
|                 </svg>
 | ||
|             </button>
 | ||
|         `;
 | ||
|         selectedDiv.appendChild(tag);
 | ||
|     });
 | ||
| }
 | ||
| 
 | ||
| function removeBatchSelectedStall(stallName) {
 | ||
|     batchSelectedStalls = batchSelectedStalls.filter(stall => stall !== stallName);
 | ||
|     
 | ||
|     // 更新checkbox状态
 | ||
|     const checkboxes = document.querySelectorAll('#batch-stall-dropdown-list input[type="checkbox"]');
 | ||
|     checkboxes.forEach(checkbox => {
 | ||
|         if (checkbox.value === stallName) {
 | ||
|             checkbox.checked = false;
 | ||
|         }
 | ||
|     });
 | ||
|     
 | ||
|     updateBatchDropdownText();
 | ||
|     updateBatchSelectedStallsDisplay();
 | ||
| }
 | ||
| 
 | ||
| function submitBatchLevelEdit() {
 | ||
|     if (batchSelectedStalls.length === 0) {
 | ||
|         showNotification('请至少选择一个摊位', 'error');
 | ||
|         return;
 | ||
|     }
 | ||
|     
 | ||
|     const selectedStallsText = batchSelectedStalls.length === 8 ? '全部摊位' : `${batchSelectedStalls.length}个摊位`;
 | ||
|     showNotification(`批量等级编辑已提交,将应用到${selectedStallsText}`, 'success');
 | ||
| }
 | ||
| 
 | ||
| // 打开会员列表Tab
 | ||
| function openMemberListTab() {
 | ||
|     const tabId = 'member-list';
 | ||
|     
 | ||
|     // 生成模拟会员数据
 | ||
|     const mockMembers = [
 | ||
|         {
 | ||
|             id: 'M001',
 | ||
|             avatar: 'https://ui-avatars.com/api/?name=张三&background=random',
 | ||
|             nickname: '张三',
 | ||
|             phone: '138****1234',
 | ||
|             stall: '时尚服装店',
 | ||
|             level: 'LV2',
 | ||
|             birthday: '1990-05-15',
 | ||
|             registerTime: '2024-01-15 14:30:25'
 | ||
|         },
 | ||
|         {
 | ||
|             id: 'M002',
 | ||
|             avatar: 'https://ui-avatars.com/api/?name=李四&background=random',
 | ||
|             nickname: '李四',
 | ||
|             phone: '139****5678',
 | ||
|             stall: '数码电子城',
 | ||
|             level: 'LV1',
 | ||
|             birthday: '1985-08-20',
 | ||
|             registerTime: '2024-02-20 09:15:30'
 | ||
|         },
 | ||
|         {
 | ||
|             id: 'M003',
 | ||
|             avatar: 'https://ui-avatars.com/api/?name=王五&background=random',
 | ||
|             nickname: '王五',
 | ||
|             phone: '136****9012',
 | ||
|             stall: '美食餐厅',
 | ||
|             level: 'LV3',
 | ||
|             birthday: '1992-12-08',
 | ||
|             registerTime: '2024-03-10 16:45:12'
 | ||
|         },
 | ||
|         {
 | ||
|             id: 'M004',
 | ||
|             avatar: 'https://ui-avatars.com/api/?name=赵六&background=random',
 | ||
|             nickname: '赵六',
 | ||
|             phone: '137****3456',
 | ||
|             stall: '家居生活馆',
 | ||
|             level: 'LV1',
 | ||
|             birthday: '1988-03-25',
 | ||
|             registerTime: '2024-04-05 11:20:45'
 | ||
|         },
 | ||
|         {
 | ||
|             id: 'M005',
 | ||
|             avatar: 'https://ui-avatars.com/api/?name=孙七&background=random',
 | ||
|             nickname: '孙七',
 | ||
|             phone: '135****7890',
 | ||
|             stall: '运动健身店',
 | ||
|             level: 'LV4',
 | ||
|             birthday: '1995-07-12',
 | ||
|             registerTime: '2024-05-22 13:55:18'
 | ||
|         },
 | ||
|         {
 | ||
|             id: 'M006',
 | ||
|             avatar: 'https://ui-avatars.com/api/?name=周八&background=random',
 | ||
|             nickname: '周八',
 | ||
|             phone: '134****2468',
 | ||
|             stall: '时尚服装店',
 | ||
|             level: 'LV2',
 | ||
|             birthday: '1991-11-30',
 | ||
|             registerTime: '2024-06-18 08:35:22'
 | ||
|         }
 | ||
|     ];
 | ||
| 
 | ||
|     const content = `
 | ||
|         <div class="p-6">
 | ||
|             <div class="flex items-center justify-between mb-6">
 | ||
|                 <h2 class="text-lg font-medium text-gray-900">会员列表</h2>
 | ||
|             </div>
 | ||
|             
 | ||
|             <!-- 筛选头部 -->
 | ||
|             <div class="mb-6 bg-gray-50 p-4 rounded-lg">
 | ||
|                 <div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-4">
 | ||
|                     <div>
 | ||
|                         <label class="block text-sm font-medium text-gray-700 mb-2">店铺</label>
 | ||
|                         <select id="member-stall-filter" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-green-500">
 | ||
|                             <option value="">全部店铺</option>
 | ||
|                             <option value="时尚服装店">时尚服装店</option>
 | ||
|                             <option value="数码电子城">数码电子城</option>
 | ||
|                             <option value="美食餐厅">美食餐厅</option>
 | ||
|                             <option value="家居生活馆">家居生活馆</option>
 | ||
|                             <option value="运动健身店">运动健身店</option>
 | ||
|                         </select>
 | ||
|                     </div>
 | ||
|                     <div>
 | ||
|                         <label class="block text-sm font-medium text-gray-700 mb-2">用户昵称</label>
 | ||
|                         <input type="text" id="member-nickname-filter" placeholder="请输入用户昵称" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-green-500">
 | ||
|                     </div>
 | ||
|                     <div>
 | ||
|                         <label class="block text-sm font-medium text-gray-700 mb-2">会员等级</label>
 | ||
|                         <select id="member-level-filter" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-green-500">
 | ||
|                             <option value="">全部等级</option>
 | ||
|                             <option value="LV1">LV1</option>
 | ||
|                             <option value="LV2">LV2</option>
 | ||
|                             <option value="LV3">LV3</option>
 | ||
|                             <option value="LV4">LV4</option>
 | ||
|                         </select>
 | ||
|                     </div>
 | ||
|                     <div>
 | ||
|                         <label class="block text-sm font-medium text-gray-700 mb-2">注册时间</label>
 | ||
|                         <input type="date" id="member-register-filter" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-green-500">
 | ||
|                     </div>
 | ||
|                 </div>
 | ||
|                 <div class="flex justify-end">
 | ||
|                     <button onclick="filterMemberList()" class="px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 transition-colors">
 | ||
|                         筛选查询
 | ||
|                     </button>
 | ||
|                     <button onclick="resetMemberFilters()" class="ml-2 px-4 py-2 bg-gray-500 text-white rounded-md hover:bg-gray-600 transition-colors">
 | ||
|                         重置
 | ||
|                     </button>
 | ||
|                 </div>
 | ||
|             </div>
 | ||
|             
 | ||
|             <!-- 信息统计区域 -->
 | ||
|             <div class="mb-6 grid grid-cols-1 md:grid-cols-2 gap-4">
 | ||
|                 <div class="bg-blue-50 p-4 rounded-lg border border-blue-200">
 | ||
|                     <div class="flex items-center">
 | ||
|                         <div class="flex-shrink-0">
 | ||
|                             <svg class="h-8 w-8 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
 | ||
|                                 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" />
 | ||
|                                 <circle cx="9" cy="7" r="4" />
 | ||
|                                 <path d="m22 21-3-3m0 0a8 8 0 1 1-11.31-11.31 8 8 0 0 1 11.31 11.31z" />
 | ||
|                             </svg>
 | ||
|                         </div>
 | ||
|                         <div class="ml-4">
 | ||
|                             <p class="text-sm font-medium text-blue-600">会员总数</p>
 | ||
|                             <p class="text-2xl font-bold text-blue-900" id="total-members">1,247</p>
 | ||
|                         </div>
 | ||
|                     </div>
 | ||
|                 </div>
 | ||
|                 <div class="bg-green-50 p-4 rounded-lg border border-green-200">
 | ||
|                     <div class="flex items-center">
 | ||
|                         <div class="flex-shrink-0">
 | ||
|                             <svg class="h-8 w-8 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
 | ||
|                                 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18 9v3m0 0v3m0-3h3m-3 0h-3m-2-5a4 4 0 11-8 0 4 4 0 018 0zM3 20a6 6 0 0112 0v1H3v-1z" />
 | ||
|                             </svg>
 | ||
|                         </div>
 | ||
|                         <div class="ml-4">
 | ||
|                             <p class="text-sm font-medium text-green-600">今日新增</p>
 | ||
|                             <p class="text-2xl font-bold text-green-900" id="today-new-members">23</p>
 | ||
|                         </div>
 | ||
|                     </div>
 | ||
|                 </div>
 | ||
|             </div>
 | ||
|             
 | ||
|             <!-- 会员数据表格 -->
 | ||
|             <div class="bg-white rounded-lg shadow-sm border border-gray-200 overflow-hidden">
 | ||
|                 <div class="overflow-x-auto">
 | ||
|                     <table class="w-full" id="member-table">
 | ||
|                         <thead class="bg-gray-50">
 | ||
|                             <tr>
 | ||
|                                 <th class="px-4 py-3 text-left">
 | ||
|                                     <input type="checkbox" id="select-all-members" onchange="toggleAllMembers(this)" class="rounded border-gray-300 text-green-600 shadow-sm focus:border-green-500 focus:ring-green-500">
 | ||
|                                 </th>
 | ||
|                                 <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer hover:bg-gray-100" onclick="sortMemberTable('stall')">
 | ||
|                                     所属摊位
 | ||
|                                     <svg class="inline-block ml-1 h-3 w-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
 | ||
|                                         <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 9l4-4 4 4m0 6l-4 4-4-4" />
 | ||
|                                     </svg>
 | ||
|                                 </th>
 | ||
|                                 <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer hover:bg-gray-100" onclick="sortMemberTable('id')">
 | ||
|                                     用户ID
 | ||
|                                     <svg class="inline-block ml-1 h-3 w-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
 | ||
|                                         <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 9l4-4 4 4m0 6l-4 4-4-4" />
 | ||
|                                     </svg>
 | ||
|                                 </th>
 | ||
|                                 <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
 | ||
|                                     用户头像
 | ||
|                                 </th>
 | ||
|                                 <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer hover:bg-gray-100" onclick="sortMemberTable('nickname')">
 | ||
|                                     用户昵称
 | ||
|                                     <svg class="inline-block ml-1 h-3 w-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
 | ||
|                                         <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 9l4-4 4 4m0 6l-4 4-4-4" />
 | ||
|                                     </svg>
 | ||
|                                 </th>
 | ||
|                                 <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
 | ||
|                                     手机号
 | ||
|                                 </th>
 | ||
|                                 <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer hover:bg-gray-100" onclick="sortMemberTable('level')">
 | ||
|                                     会员等级
 | ||
|                                     <svg class="inline-block ml-1 h-3 w-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
 | ||
|                                         <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 9l4-4 4 4m0 6l-4 4-4-4" />
 | ||
|                                     </svg>
 | ||
|                                 </th>
 | ||
|                                 <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
 | ||
|                                     生日
 | ||
|                                 </th>
 | ||
|                                 <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider cursor-pointer hover:bg-gray-100" onclick="sortMemberTable('registerTime')">
 | ||
|                                     注册时间
 | ||
|                                     <svg class="inline-block ml-1 h-3 w-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
 | ||
|                                         <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 9l4-4 4 4m0 6l-4 4-4-4" />
 | ||
|                                     </svg>
 | ||
|                                 </th>
 | ||
|                                 <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
 | ||
|                                     操作
 | ||
|                                 </th>
 | ||
|                             </tr>
 | ||
|                         </thead>
 | ||
|                         <tbody class="bg-white divide-y divide-gray-200" id="member-table-body">
 | ||
|                             ${mockMembers.map(member => `
 | ||
|                                 <tr class="hover:bg-gray-50 member-row" data-stall="${member.stall}" data-nickname="${member.nickname}" data-level="${member.level}" data-register-date="${member.registerTime.split(' ')[0]}">
 | ||
|                                     <td class="px-4 py-3">
 | ||
|                                         <input type="checkbox" value="${member.id}" class="member-checkbox rounded border-gray-300 text-green-600 shadow-sm focus:border-green-500 focus:ring-green-500">
 | ||
|                                     </td>
 | ||
|                                     <td class="px-4 py-3 text-sm text-gray-900">${member.stall}</td>
 | ||
|                                     <td class="px-4 py-3 text-sm font-mono text-gray-600">${member.id}</td>
 | ||
|                                     <td class="px-4 py-3">
 | ||
|                                         <img src="${member.avatar}" alt="${member.nickname}" class="h-8 w-8 rounded-full">
 | ||
|                                     </td>
 | ||
|                                     <td class="px-4 py-3 text-sm font-medium text-gray-900">${member.nickname}</td>
 | ||
|                                     <td class="px-4 py-3 text-sm text-gray-600">${member.phone}</td>
 | ||
|                                     <td class="px-4 py-3">
 | ||
|                                         <span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${getLevelBadgeColor(member.level)}">
 | ||
|                                             ${member.level}
 | ||
|                                         </span>
 | ||
|                                     </td>
 | ||
|                                     <td class="px-4 py-3 text-sm text-gray-600">${member.birthday}</td>
 | ||
|                                     <td class="px-4 py-3 text-sm text-gray-600">${member.registerTime}</td>
 | ||
|                                     <td class="px-4 py-3">
 | ||
|                                         <button onclick="viewMemberDetail('${member.id}')" class="text-green-600 hover:text-green-900 text-sm font-medium">
 | ||
|                                             详情
 | ||
|                                         </button>
 | ||
|                                     </td>
 | ||
|                                 </tr>
 | ||
|                             `).join('')}
 | ||
|                         </tbody>
 | ||
|                     </table>
 | ||
|                 </div>
 | ||
|                 
 | ||
|                 <!-- 分页区域 -->
 | ||
|                 <div class="bg-white px-4 py-3 border-t border-gray-200 sm:px-6">
 | ||
|                     <div class="flex items-center justify-between">
 | ||
|                         <div class="flex items-center text-sm text-gray-700">
 | ||
|                             <span>显示</span>
 | ||
|                             <select id="page-size" onchange="changePageSize(this.value)" class="mx-2 px-3 py-1 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500">
 | ||
|                                 <option value="10">10</option>
 | ||
|                                 <option value="20" selected>20</option>
 | ||
|                                 <option value="50">50</option>
 | ||
|                                 <option value="100">100</option>
 | ||
|                             </select>
 | ||
|                             <span>条记录,共 <span id="total-records">1,247</span> 条</span>
 | ||
|                         </div>
 | ||
|                         <div class="flex items-center space-x-2">
 | ||
|                             <button onclick="goToPage(1)" class="px-3 py-1 text-sm bg-gray-100 text-gray-600 rounded-md hover:bg-gray-200 transition-colors" id="first-page">
 | ||
|                                 首页
 | ||
|                             </button>
 | ||
|                             <button onclick="goToPage(currentPage - 1)" class="px-3 py-1 text-sm bg-gray-100 text-gray-600 rounded-md hover:bg-gray-200 transition-colors" id="prev-page">
 | ||
|                                 上一页
 | ||
|                             </button>
 | ||
|                             <span class="text-sm text-gray-700">
 | ||
|                                 第 <input type="number" id="current-page-input" value="1" min="1" max="63" class="w-12 px-2 py-1 text-center border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500" onchange="goToPage(this.value)"> 页,共 <span id="total-pages">63</span> 页
 | ||
|                             </span>
 | ||
|                             <button onclick="goToPage(currentPage + 1)" class="px-3 py-1 text-sm bg-gray-100 text-gray-600 rounded-md hover:bg-gray-200 transition-colors" id="next-page">
 | ||
|                                 下一页
 | ||
|                             </button>
 | ||
|                             <button onclick="goToPage(totalPages)" class="px-3 py-1 text-sm bg-gray-100 text-gray-600 rounded-md hover:bg-gray-200 transition-colors" id="last-page">
 | ||
|                                 尾页
 | ||
|                             </button>
 | ||
|                         </div>
 | ||
|                     </div>
 | ||
|                 </div>
 | ||
|             </div>
 | ||
|         </div>
 | ||
|     `;
 | ||
| 
 | ||
|     memberTabManager.createTab(tabId, '会员列表', content, false);
 | ||
|     
 | ||
|     // 初始化分页变量
 | ||
|     setTimeout(() => {
 | ||
|         initializeMemberListPagination();
 | ||
|     }, 100);
 | ||
| }
 | ||
| 
 | ||
| // 获取等级徽章颜色
 | ||
| function getLevelBadgeColor(level) {
 | ||
|     const colors = {
 | ||
|         'LV1': 'bg-gray-100 text-gray-800',
 | ||
|         'LV2': 'bg-blue-100 text-blue-800',
 | ||
|         'LV3': 'bg-green-100 text-green-800',
 | ||
|         'LV4': 'bg-purple-100 text-purple-800'
 | ||
|     };
 | ||
|     return colors[level] || 'bg-gray-100 text-gray-800';
 | ||
| }
 | ||
| 
 | ||
| // 分页相关变量
 | ||
| let currentPage = 1;
 | ||
| let pageSize = 20;
 | ||
| let totalPages = 63;
 | ||
| let totalRecords = 1247;
 | ||
| 
 | ||
| // 初始化分页
 | ||
| function initializeMemberListPagination() {
 | ||
|     updatePaginationDisplay();
 | ||
| }
 | ||
| 
 | ||
| // 更新分页显示
 | ||
| function updatePaginationDisplay() {
 | ||
|     document.getElementById('current-page-input').value = currentPage;
 | ||
|     document.getElementById('total-pages').textContent = totalPages;
 | ||
|     document.getElementById('total-records').textContent = totalRecords.toLocaleString();
 | ||
|     
 | ||
|     // 更新按钮状态
 | ||
|     const firstBtn = document.getElementById('first-page');
 | ||
|     const prevBtn = document.getElementById('prev-page');
 | ||
|     const nextBtn = document.getElementById('next-page');
 | ||
|     const lastBtn = document.getElementById('last-page');
 | ||
|     
 | ||
|     if (firstBtn) firstBtn.disabled = currentPage === 1;
 | ||
|     if (prevBtn) prevBtn.disabled = currentPage === 1;
 | ||
|     if (nextBtn) nextBtn.disabled = currentPage === totalPages;
 | ||
|     if (lastBtn) lastBtn.disabled = currentPage === totalPages;
 | ||
| }
 | ||
| 
 | ||
| // 跳转到指定页面
 | ||
| function goToPage(page) {
 | ||
|     const pageNum = parseInt(page);
 | ||
|     if (pageNum >= 1 && pageNum <= totalPages) {
 | ||
|         currentPage = pageNum;
 | ||
|         updatePaginationDisplay();
 | ||
|         showNotification(`已跳转到第 ${currentPage} 页`, 'info');
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| // 改变每页显示数量
 | ||
| function changePageSize(size) {
 | ||
|     pageSize = parseInt(size);
 | ||
|     totalPages = Math.ceil(totalRecords / pageSize);
 | ||
|     currentPage = 1;
 | ||
|     updatePaginationDisplay();
 | ||
|     showNotification(`每页显示 ${pageSize} 条记录`, 'info');
 | ||
| }
 | ||
| 
 | ||
| // 筛选会员列表
 | ||
| function filterMemberList() {
 | ||
|     const stallFilter = document.getElementById('member-stall-filter').value;
 | ||
|     const nicknameFilter = document.getElementById('member-nickname-filter').value.toLowerCase();
 | ||
|     const levelFilter = document.getElementById('member-level-filter').value;
 | ||
|     const registerFilter = document.getElementById('member-register-filter').value;
 | ||
|     
 | ||
|     const rows = document.querySelectorAll('.member-row');
 | ||
|     let visibleCount = 0;
 | ||
|     
 | ||
|     rows.forEach(row => {
 | ||
|         const stall = row.dataset.stall;
 | ||
|         const nickname = row.dataset.nickname.toLowerCase();
 | ||
|         const level = row.dataset.level;
 | ||
|         const registerDate = row.dataset.registerDate;
 | ||
|         
 | ||
|         let shouldShow = true;
 | ||
|         
 | ||
|         if (stallFilter && stall !== stallFilter) shouldShow = false;
 | ||
|         if (nicknameFilter && !nickname.includes(nicknameFilter)) shouldShow = false;
 | ||
|         if (levelFilter && level !== levelFilter) shouldShow = false;
 | ||
|         if (registerFilter && registerDate !== registerFilter) shouldShow = false;
 | ||
|         
 | ||
|         if (shouldShow) {
 | ||
|             row.style.display = '';
 | ||
|             visibleCount++;
 | ||
|         } else {
 | ||
|             row.style.display = 'none';
 | ||
|         }
 | ||
|     });
 | ||
|     
 | ||
|     showNotification(`筛选完成,显示 ${visibleCount} 条记录`, 'success');
 | ||
| }
 | ||
| 
 | ||
| // 重置筛选条件
 | ||
| function resetMemberFilters() {
 | ||
|     document.getElementById('member-stall-filter').value = '';
 | ||
|     document.getElementById('member-nickname-filter').value = '';
 | ||
|     document.getElementById('member-level-filter').value = '';
 | ||
|     document.getElementById('member-register-filter').value = '';
 | ||
|     
 | ||
|     // 显示所有行
 | ||
|     const rows = document.querySelectorAll('.member-row');
 | ||
|     rows.forEach(row => {
 | ||
|         row.style.display = '';
 | ||
|     });
 | ||
|     
 | ||
|     showNotification('筛选条件已重置', 'info');
 | ||
| }
 | ||
| 
 | ||
| // 全选/取消全选
 | ||
| function toggleAllMembers(checkbox) {
 | ||
|     const memberCheckboxes = document.querySelectorAll('.member-checkbox');
 | ||
|     memberCheckboxes.forEach(cb => {
 | ||
|         cb.checked = checkbox.checked;
 | ||
|     });
 | ||
|     
 | ||
|     const selectedCount = checkbox.checked ? memberCheckboxes.length : 0;
 | ||
|     showNotification(`已${checkbox.checked ? '选中' : '取消选中'} ${selectedCount} 个会员`, 'info');
 | ||
| }
 | ||
| 
 | ||
| // 表格排序
 | ||
| let sortDirection = {};
 | ||
| function sortMemberTable(column) {
 | ||
|     const direction = sortDirection[column] === 'asc' ? 'desc' : 'asc';
 | ||
|     sortDirection[column] = direction;
 | ||
|     
 | ||
|     showNotification(`按${column}${direction === 'asc' ? '升序' : '降序'}排序`, 'info');
 | ||
| }
 | ||
| 
 | ||
| // 查看会员详情
 | ||
| function viewMemberDetail(memberId) {
 | ||
|     // 获取会员模拟数据
 | ||
|     const memberData = getMemberDataById(memberId);
 | ||
|     if (!memberData) {
 | ||
|         showNotification('会员信息不存在', 'error');
 | ||
|         return;
 | ||
|     }
 | ||
|     
 | ||
|     openMemberDetailTab(memberData);
 | ||
| }
 | ||
| 
 | ||
| // 获取会员数据(模拟数据)
 | ||
| function getMemberDataById(memberId) {
 | ||
|     const mockMemberDetails = {
 | ||
|         'M001': {
 | ||
|             id: 'M001',
 | ||
|             name: '张三',
 | ||
|             phone: '138****1234',
 | ||
|             registerDate: '2024-01-15',
 | ||
|             birthday: '1990-05-15',
 | ||
|             growth: 1250,
 | ||
|             points: 890,
 | ||
|             coupons: 5
 | ||
|         },
 | ||
|         'M002': {
 | ||
|             id: 'M002',
 | ||
|             name: '李四',
 | ||
|             phone: '139****5678',
 | ||
|             registerDate: '2024-02-20',
 | ||
|             birthday: '1985-08-20',
 | ||
|             growth: 680,
 | ||
|             points: 450,
 | ||
|             coupons: 3
 | ||
|         },
 | ||
|         'M003': {
 | ||
|             id: 'M003',
 | ||
|             name: '王五',
 | ||
|             phone: '136****9012',
 | ||
|             registerDate: '2024-03-10',
 | ||
|             birthday: '1992-12-08',
 | ||
|             growth: 2100,
 | ||
|             points: 1200,
 | ||
|             coupons: 8
 | ||
|         },
 | ||
|         'M004': {
 | ||
|             id: 'M004',
 | ||
|             name: '赵六',
 | ||
|             phone: '137****3456',
 | ||
|             registerDate: '2024-04-05',
 | ||
|             birthday: '1988-03-25',
 | ||
|             growth: 350,
 | ||
|             points: 280,
 | ||
|             coupons: 2
 | ||
|         },
 | ||
|         'M005': {
 | ||
|             id: 'M005',
 | ||
|             name: '孙七',
 | ||
|             phone: '135****7890',
 | ||
|             registerDate: '2024-05-22',
 | ||
|             birthday: '1995-07-12',
 | ||
|             growth: 3200,
 | ||
|             points: 1650,
 | ||
|             coupons: 12
 | ||
|         },
 | ||
|         'M006': {
 | ||
|             id: 'M006',
 | ||
|             name: '周八',
 | ||
|             phone: '134****2468',
 | ||
|             registerDate: '2024-06-18',
 | ||
|             birthday: '1991-11-30',
 | ||
|             growth: 920,
 | ||
|             points: 560,
 | ||
|             coupons: 4
 | ||
|         }
 | ||
|     };
 | ||
|     
 | ||
|     return mockMemberDetails[memberId];
 | ||
| }
 | ||
| 
 | ||
| // 打开会员详情Tab
 | ||
| function openMemberDetailTab(memberData) {
 | ||
|     const tabId = `member-detail-${memberData.id}`;
 | ||
|     const tabTitle = `会员详情-${memberData.name}`;
 | ||
|     
 | ||
|     // 生成优惠券数据
 | ||
|     const couponData = generateMemberCoupons(memberData.coupons);
 | ||
|     
 | ||
|     // 生成成长值明细数据
 | ||
|     const growthDetailData = generateGrowthDetails(memberData.growth);
 | ||
|     
 | ||
|     // 生成积分明细数据
 | ||
|     const pointDetailData = generatePointDetails(memberData.points);
 | ||
|     
 | ||
|     const content = `
 | ||
|         <div class="p-6 max-w-7xl mx-auto">
 | ||
|             <div class="flex items-center justify-between mb-6">
 | ||
|                 <h2 class="text-xl font-semibold text-gray-900">${tabTitle}</h2>
 | ||
|                 <button onclick="memberTabManager.closeTab('${tabId}')" class="px-4 py-2 bg-gray-500 text-white rounded-md hover:bg-gray-600 transition-colors">
 | ||
|                     关闭
 | ||
|                 </button>
 | ||
|             </div>
 | ||
|             
 | ||
|             <!-- 会员基本信息 -->
 | ||
|             <div class="bg-white rounded-lg shadow-sm border border-gray-200 p-6 mb-6">
 | ||
|                 <h3 class="text-lg font-medium text-gray-900 mb-4">会员信息</h3>
 | ||
|                 <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
 | ||
|                     <div class="flex flex-col">
 | ||
|                         <span class="text-sm font-medium text-gray-500 mb-1">会员名称</span>
 | ||
|                         <span class="text-base text-gray-900">${memberData.name}</span>
 | ||
|                     </div>
 | ||
|                     <div class="flex flex-col">
 | ||
|                         <span class="text-sm font-medium text-gray-500 mb-1">会员手机号</span>
 | ||
|                         <span class="text-base text-gray-900">${memberData.phone}</span>
 | ||
|                     </div>
 | ||
|                     <div class="flex flex-col">
 | ||
|                         <span class="text-sm font-medium text-gray-500 mb-1">会员注册日期</span>
 | ||
|                         <span class="text-base text-gray-900">${memberData.registerDate}</span>
 | ||
|                     </div>
 | ||
|                     <div class="flex flex-col">
 | ||
|                         <span class="text-sm font-medium text-gray-500 mb-1">会员生日</span>
 | ||
|                         <span class="text-base text-gray-900">${memberData.birthday}</span>
 | ||
|                     </div>
 | ||
|                 </div>
 | ||
|             </div>
 | ||
|             
 | ||
|             <!-- 会员资产 -->
 | ||
|             <div class="bg-white rounded-lg shadow-sm border border-gray-200 p-6 mb-6">
 | ||
|                 <h3 class="text-lg font-medium text-gray-900 mb-4">会员资产</h3>
 | ||
|                 <div class="grid grid-cols-1 md:grid-cols-3 gap-6">
 | ||
|                     <div class="bg-blue-50 p-4 rounded-lg">
 | ||
|                         <div class="flex items-center">
 | ||
|                             <div class="flex-shrink-0">
 | ||
|                                 <svg class="h-8 w-8 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
 | ||
|                                     <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6" />
 | ||
|                                 </svg>
 | ||
|                             </div>
 | ||
|                             <div class="ml-4">
 | ||
|                                 <p class="text-sm font-medium text-blue-600">会员成长值</p>
 | ||
|                                 <p class="text-2xl font-bold text-blue-900">${memberData.growth}</p>
 | ||
|                             </div>
 | ||
|                         </div>
 | ||
|                     </div>
 | ||
|                     <div class="bg-green-50 p-4 rounded-lg">
 | ||
|                         <div class="flex items-center">
 | ||
|                             <div class="flex-shrink-0">
 | ||
|                                 <svg class="h-8 w-8 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
 | ||
|                                     <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1" />
 | ||
|                                 </svg>
 | ||
|                             </div>
 | ||
|                             <div class="ml-4">
 | ||
|                                 <p class="text-sm font-medium text-green-600">会员积分</p>
 | ||
|                                 <p class="text-2xl font-bold text-green-900">${memberData.points}</p>
 | ||
|                             </div>
 | ||
|                         </div>
 | ||
|                     </div>
 | ||
|                     <div class="bg-purple-50 p-4 rounded-lg">
 | ||
|                         <div class="flex items-center">
 | ||
|                             <div class="flex-shrink-0">
 | ||
|                                 <svg class="h-8 w-8 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
 | ||
|                                     <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z" />
 | ||
|                                 </svg>
 | ||
|                             </div>
 | ||
|                             <div class="ml-4">
 | ||
|                                 <p class="text-sm font-medium text-purple-600">会员优惠券</p>
 | ||
|                                 <p class="text-2xl font-bold text-purple-900 cursor-pointer hover:text-purple-700" onclick="showMemberCouponsModal('${memberData.id}')">${memberData.coupons}</p>
 | ||
|                             </div>
 | ||
|                         </div>
 | ||
|                     </div>
 | ||
|                 </div>
 | ||
|             </div>
 | ||
|             
 | ||
|             <!-- Tab导航 -->
 | ||
|             <div class="bg-white rounded-lg shadow-sm border border-gray-200">
 | ||
|                 <div class="border-b border-gray-200">
 | ||
|                     <nav class="-mb-px flex">
 | ||
|                         <button onclick="switchDetailTab('growth', '${memberData.id}')" id="growth-tab-${memberData.id}" class="detail-tab-btn active py-4 px-6 text-sm font-medium border-b-2 border-green-500 text-green-600">
 | ||
|                             会员成长值明细
 | ||
|                         </button>
 | ||
|                         <button onclick="switchDetailTab('points', '${memberData.id}')" id="points-tab-${memberData.id}" class="detail-tab-btn py-4 px-6 text-sm font-medium border-b-2 border-transparent text-gray-500 hover:text-gray-700">
 | ||
|                             会员积分明细
 | ||
|                         </button>
 | ||
|                     </nav>
 | ||
|                 </div>
 | ||
|                 
 | ||
|                 <!-- Tab内容区域 -->
 | ||
|                 <div class="p-6">
 | ||
|                     <!-- 成长值明细表格 -->
 | ||
|                     <div id="growth-content-${memberData.id}" class="detail-tab-content">
 | ||
|                         <div class="overflow-x-auto">
 | ||
|                             <table class="w-full">
 | ||
|                                 <thead class="bg-gray-50">
 | ||
|                                     <tr>
 | ||
|                                         <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">变化时间</th>
 | ||
|                                         <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">成长值增加</th>
 | ||
|                                         <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">关联订单</th>
 | ||
|                                         <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">详情</th>
 | ||
|                                     </tr>
 | ||
|                                 </thead>
 | ||
|                                 <tbody class="bg-white divide-y divide-gray-200" id="growth-table-body-${memberData.id}">
 | ||
|                                     ${growthDetailData.map(item => `
 | ||
|                                         <tr>
 | ||
|                                             <td class="px-4 py-3 text-sm text-gray-900">${item.time}</td>
 | ||
|                                             <td class="px-4 py-3 text-sm text-gray-900">+${item.growth}</td>
 | ||
|                                             <td class="px-4 py-3 text-sm text-gray-600">${item.orderId || '无'}</td>
 | ||
|                                             <td class="px-4 py-3">
 | ||
|                                                 <button onclick="showGrowthDetail('${item.id}', '${item.type}', ${JSON.stringify(item).replace(/"/g, '"')})" class="text-blue-600 hover:text-blue-900 text-sm font-medium">
 | ||
|                                                     详情
 | ||
|                                                 </button>
 | ||
|                                             </td>
 | ||
|                                         </tr>
 | ||
|                                     `).join('')}
 | ||
|                                 </tbody>
 | ||
|                             </table>
 | ||
|                         </div>
 | ||
|                         
 | ||
|                         <!-- 分页控件 -->
 | ||
|                         <div class="mt-4 flex items-center justify-between">
 | ||
|                             <div class="text-sm text-gray-700">
 | ||
|                                 共 <span class="font-medium">${growthDetailData.length}</span> 条记录
 | ||
|                             </div>
 | ||
|                             <div class="flex items-center space-x-2">
 | ||
|                                 <button onclick="growthPrevPage('${memberData.id}')" class="px-3 py-1 text-sm bg-gray-100 text-gray-600 rounded-md hover:bg-gray-200 transition-colors">
 | ||
|                                     上一页
 | ||
|                                 </button>
 | ||
|                                 <span class="text-sm text-gray-700">第 1 页,共 1 页</span>
 | ||
|                                 <button onclick="growthNextPage('${memberData.id}')" class="px-3 py-1 text-sm bg-gray-100 text-gray-600 rounded-md hover:bg-gray-200 transition-colors">
 | ||
|                                     下一页
 | ||
|                                 </button>
 | ||
|                             </div>
 | ||
|                         </div>
 | ||
|                     </div>
 | ||
|                     
 | ||
|                     <!-- 积分明细表格 -->
 | ||
|                     <div id="points-content-${memberData.id}" class="detail-tab-content hidden">
 | ||
|                         <div class="overflow-x-auto">
 | ||
|                             <table class="w-full">
 | ||
|                                 <thead class="bg-gray-50">
 | ||
|                                     <tr>
 | ||
|                                         <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">变化时间</th>
 | ||
|                                         <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">积分变化</th>
 | ||
|                                         <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">关联订单</th>
 | ||
|                                         <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">详情</th>
 | ||
|                                     </tr>
 | ||
|                                 </thead>
 | ||
|                                 <tbody class="bg-white divide-y divide-gray-200" id="points-table-body-${memberData.id}">
 | ||
|                                     ${pointDetailData.map(item => `
 | ||
|                                         <tr>
 | ||
|                                             <td class="px-4 py-3 text-sm text-gray-900">${item.time}</td>
 | ||
|                                             <td class="px-4 py-3 text-sm ${item.change > 0 ? 'text-green-600' : 'text-red-600'}">${item.change > 0 ? '+' : ''}${item.change}</td>
 | ||
|                                             <td class="px-4 py-3 text-sm text-gray-600">${item.orderId || '无'}</td>
 | ||
|                                             <td class="px-4 py-3">
 | ||
|                                                 <button onclick="showPointDetail('${item.id}', '${item.type}', ${JSON.stringify(item).replace(/"/g, '"')})" class="text-blue-600 hover:text-blue-900 text-sm font-medium">
 | ||
|                                                     详情
 | ||
|                                                 </button>
 | ||
|                                             </td>
 | ||
|                                         </tr>
 | ||
|                                     `).join('')}
 | ||
|                                 </tbody>
 | ||
|                             </table>
 | ||
|                         </div>
 | ||
|                         
 | ||
|                         <!-- 分页控件 -->
 | ||
|                         <div class="mt-4 flex items-center justify-between">
 | ||
|                             <div class="text-sm text-gray-700">
 | ||
|                                 共 <span class="font-medium">${pointDetailData.length}</span> 条记录
 | ||
|                             </div>
 | ||
|                             <div class="flex items-center space-x-2">
 | ||
|                                 <button onclick="pointsPrevPage('${memberData.id}')" class="px-3 py-1 text-sm bg-gray-100 text-gray-600 rounded-md hover:bg-gray-200 transition-colors">
 | ||
|                                     上一页
 | ||
|                                 </button>
 | ||
|                                 <span class="text-sm text-gray-700">第 1 页,共 1 页</span>
 | ||
|                                 <button onclick="pointsNextPage('${memberData.id}')" class="px-3 py-1 text-sm bg-gray-100 text-gray-600 rounded-md hover:bg-gray-200 transition-colors">
 | ||
|                                     下一页
 | ||
|                                 </button>
 | ||
|                             </div>
 | ||
|                         </div>
 | ||
|                     </div>
 | ||
|                 </div>
 | ||
|             </div>
 | ||
|         </div>
 | ||
|     `;
 | ||
|     
 | ||
|     memberTabManager.createTab(tabId, tabTitle, content, true);
 | ||
| }
 | ||
| 
 | ||
| // 生成会员优惠券数据
 | ||
| function generateMemberCoupons(count) {
 | ||
|     const couponTypes = ['满减券', '折扣券', '生日券', '新人券', '节日券'];
 | ||
|     const coupons = [];
 | ||
|     
 | ||
|     for (let i = 0; i < count; i++) {
 | ||
|         const type = couponTypes[Math.floor(Math.random() * couponTypes.length)];
 | ||
|         const issueDate = new Date(2024, Math.floor(Math.random() * 12), Math.floor(Math.random() * 28) + 1);
 | ||
|         const validityDays = Math.floor(Math.random() * 90) + 30;
 | ||
|         const expiryDate = new Date(issueDate.getTime() + validityDays * 24 * 60 * 60 * 1000);
 | ||
|         
 | ||
|         coupons.push({
 | ||
|             id: `C${String(i + 1).padStart(3, '0')}`,
 | ||
|             name: `${type}${i + 1}`,
 | ||
|             issueDate: issueDate.toISOString().split('T')[0],
 | ||
|             expiryDate: expiryDate.toISOString().split('T')[0],
 | ||
|             content: getRandomCouponContent(type)
 | ||
|         });
 | ||
|     }
 | ||
|     
 | ||
|     return coupons;
 | ||
| }
 | ||
| 
 | ||
| // 获取随机优惠券内容
 | ||
| function getRandomCouponContent(type) {
 | ||
|     const contents = {
 | ||
|         '满减券': ['满100减20', '满200减50', '满500减100'],
 | ||
|         '折扣券': ['9折优惠', '8.5折优惠', '8折优惠'],
 | ||
|         '生日券': ['生日专享8折', '生日满100减30'],
 | ||
|         '新人券': ['新人专享9折', '新人满50减10'],
 | ||
|         '节日券': ['节日满200减30', '节日8.8折优惠']
 | ||
|     };
 | ||
|     
 | ||
|     const typeContents = contents[type] || ['优惠券'];
 | ||
|     return typeContents[Math.floor(Math.random() * typeContents.length)];
 | ||
| }
 | ||
| 
 | ||
| // 生成成长值明细数据
 | ||
| function generateGrowthDetails(totalGrowth) {
 | ||
|     const details = [];
 | ||
|     const reasons = [
 | ||
|         { type: 'register', reason: '注册奖励', growth: 50, hasOrder: false },
 | ||
|         { type: 'birthday', reason: '生日奖励', growth: 100, hasOrder: false },
 | ||
|         { type: 'order', reason: '订单消费', growth: null, hasOrder: true },
 | ||
|         { type: 'activity', reason: '活动奖励', growth: 20, hasOrder: false }
 | ||
|     ];
 | ||
|     
 | ||
|     let currentGrowth = 0;
 | ||
|     let id = 1;
 | ||
|     
 | ||
|     while (currentGrowth < totalGrowth && details.length < 20) {
 | ||
|         const reasonData = reasons[Math.floor(Math.random() * reasons.length)];
 | ||
|         let growth = reasonData.growth;
 | ||
|         
 | ||
|         if (reasonData.type === 'order') {
 | ||
|             growth = Math.floor(Math.random() * 100) + 10;
 | ||
|         }
 | ||
|         
 | ||
|         if (currentGrowth + growth > totalGrowth) {
 | ||
|             growth = totalGrowth - currentGrowth;
 | ||
|         }
 | ||
|         
 | ||
|         const days = Math.floor(Math.random() * 180);
 | ||
|         const time = new Date(Date.now() - days * 24 * 60 * 60 * 1000);
 | ||
|         
 | ||
|         details.push({
 | ||
|             id: `G${String(id).padStart(3, '0')}`,
 | ||
|             time: time.toISOString().split('T')[0] + ' ' + time.toTimeString().split(' ')[0].substring(0, 5),
 | ||
|             growth: growth,
 | ||
|             orderId: reasonData.hasOrder ? `ORD${String(Math.floor(Math.random() * 9999) + 1).padStart(4, '0')}` : null,
 | ||
|             type: reasonData.type,
 | ||
|             reason: reasonData.reason
 | ||
|         });
 | ||
|         
 | ||
|         currentGrowth += growth;
 | ||
|         id++;
 | ||
|     }
 | ||
|     
 | ||
|     return details.sort((a, b) => new Date(b.time) - new Date(a.time));
 | ||
| }
 | ||
| 
 | ||
| // 生成积分明细数据
 | ||
| function generatePointDetails(totalPoints) {
 | ||
|     const details = [];
 | ||
|     const reasons = [
 | ||
|         { type: 'register', reason: '注册奖励', points: 100, hasOrder: false },
 | ||
|         { type: 'birthday', reason: '生日奖励', points: 200, hasOrder: false },
 | ||
|         { type: 'order_earn', reason: '订单获得积分', points: null, hasOrder: true },
 | ||
|         { type: 'order_use', reason: '积分抵扣', points: null, hasOrder: true, isNegative: true },
 | ||
|         { type: 'activity', reason: '活动奖励', points: 50, hasOrder: false }
 | ||
|     ];
 | ||
|     
 | ||
|     let currentPoints = 0;
 | ||
|     let id = 1;
 | ||
|     
 | ||
|     while (details.length < 25) {
 | ||
|         const reasonData = reasons[Math.floor(Math.random() * reasons.length)];
 | ||
|         let points = reasonData.points;
 | ||
|         
 | ||
|         if (reasonData.type === 'order_earn') {
 | ||
|             points = Math.floor(Math.random() * 200) + 20;
 | ||
|         } else if (reasonData.type === 'order_use') {
 | ||
|             points = -(Math.floor(Math.random() * 150) + 10);
 | ||
|         }
 | ||
|         
 | ||
|         const days = Math.floor(Math.random() * 180);
 | ||
|         const time = new Date(Date.now() - days * 24 * 60 * 60 * 1000);
 | ||
|         
 | ||
|         details.push({
 | ||
|             id: `P${String(id).padStart(3, '0')}`,
 | ||
|             time: time.toISOString().split('T')[0] + ' ' + time.toTimeString().split(' ')[0].substring(0, 5),
 | ||
|             change: points,
 | ||
|             orderId: reasonData.hasOrder ? `ORD${String(Math.floor(Math.random() * 9999) + 1).padStart(4, '0')}` : null,
 | ||
|             type: reasonData.type,
 | ||
|             reason: reasonData.reason
 | ||
|         });
 | ||
|         
 | ||
|         currentPoints += points;
 | ||
|         id++;
 | ||
|     }
 | ||
|     
 | ||
|     return details.sort((a, b) => new Date(b.time) - new Date(a.time));
 | ||
| }
 | ||
| 
 | ||
| // 切换详情Tab
 | ||
| function switchDetailTab(tabType, memberId) {
 | ||
|     // 更新按钮状态
 | ||
|     const allTabs = document.querySelectorAll(`#growth-tab-${memberId}, #points-tab-${memberId}`);
 | ||
|     allTabs.forEach(tab => {
 | ||
|         tab.classList.remove('active', 'border-green-500', 'text-green-600');
 | ||
|         tab.classList.add('border-transparent', 'text-gray-500');
 | ||
|     });
 | ||
|     
 | ||
|     const activeTab = document.getElementById(`${tabType}-tab-${memberId}`);
 | ||
|     if (activeTab) {
 | ||
|         activeTab.classList.add('active', 'border-green-500', 'text-green-600');
 | ||
|         activeTab.classList.remove('border-transparent', 'text-gray-500');
 | ||
|     }
 | ||
|     
 | ||
|     // 切换内容
 | ||
|     const allContents = document.querySelectorAll(`#growth-content-${memberId}, #points-content-${memberId}`);
 | ||
|     allContents.forEach(content => {
 | ||
|         content.classList.add('hidden');
 | ||
|     });
 | ||
|     
 | ||
|     const activeContent = document.getElementById(`${tabType}-content-${memberId}`);
 | ||
|     if (activeContent) {
 | ||
|         activeContent.classList.remove('hidden');
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| // 显示优惠券弹窗
 | ||
| function showMemberCouponsModal(memberId) {
 | ||
|     const memberData = getMemberDataById(memberId);
 | ||
|     if (!memberData) return;
 | ||
|     
 | ||
|     const coupons = generateMemberCoupons(memberData.coupons);
 | ||
|     
 | ||
|     const modalHtml = `
 | ||
|         <div id="member-coupons-modal" class="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50" onclick="closeMemberCouponsModal(event)">
 | ||
|             <div class="relative top-20 mx-auto p-5 border w-11/12 max-w-4xl shadow-lg rounded-md bg-white">
 | ||
|                 <div class="flex items-center justify-between mb-4">
 | ||
|                     <h3 class="text-lg font-medium text-gray-900">${memberData.name} - 优惠券列表</h3>
 | ||
|                     <button onclick="closeMemberCouponsModal()" class="text-gray-400 hover:text-gray-600">
 | ||
|                         <svg class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
 | ||
|                             <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
 | ||
|                         </svg>
 | ||
|                     </button>
 | ||
|                 </div>
 | ||
|                 
 | ||
|                 <div class="overflow-x-auto">
 | ||
|                     <table class="w-full">
 | ||
|                         <thead class="bg-gray-50">
 | ||
|                             <tr>
 | ||
|                                 <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">优惠券名称</th>
 | ||
|                                 <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">发放日期</th>
 | ||
|                                 <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">有效期</th>
 | ||
|                                 <th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">优惠券内容</th>
 | ||
|                             </tr>
 | ||
|                         </thead>
 | ||
|                         <tbody class="bg-white divide-y divide-gray-200">
 | ||
|                             ${coupons.map(coupon => `
 | ||
|                                 <tr>
 | ||
|                                     <td class="px-4 py-3 text-sm font-medium text-gray-900">${coupon.name}</td>
 | ||
|                                     <td class="px-4 py-3 text-sm text-gray-600">${coupon.issueDate}</td>
 | ||
|                                     <td class="px-4 py-3 text-sm text-gray-600">${coupon.expiryDate}</td>
 | ||
|                                     <td class="px-4 py-3 text-sm text-gray-900">${coupon.content}</td>
 | ||
|                                 </tr>
 | ||
|                             `).join('')}
 | ||
|                         </tbody>
 | ||
|                     </table>
 | ||
|                 </div>
 | ||
|                 
 | ||
|                 <div class="mt-4 flex justify-end">
 | ||
|                     <button onclick="closeMemberCouponsModal()" class="px-4 py-2 bg-gray-500 text-white rounded-md hover:bg-gray-600 transition-colors">
 | ||
|                         关闭
 | ||
|                     </button>
 | ||
|                 </div>
 | ||
|             </div>
 | ||
|         </div>
 | ||
|     `;
 | ||
|     
 | ||
|     // 添加到页面
 | ||
|     const existingModal = document.getElementById('member-coupons-modal');
 | ||
|     if (existingModal) {
 | ||
|         existingModal.remove();
 | ||
|     }
 | ||
|     
 | ||
|     document.body.insertAdjacentHTML('beforeend', modalHtml);
 | ||
| }
 | ||
| 
 | ||
| // 关闭优惠券弹窗
 | ||
| function closeMemberCouponsModal(event) {
 | ||
|     if (event && event.target !== event.currentTarget) {
 | ||
|         return;
 | ||
|     }
 | ||
|     
 | ||
|     const modal = document.getElementById('member-coupons-modal');
 | ||
|     if (modal) {
 | ||
|         modal.remove();
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| // 显示成长值详情弹窗
 | ||
| function showGrowthDetail(detailId, type, detailData) {
 | ||
|     const data = typeof detailData === 'string' ? JSON.parse(detailData) : detailData;
 | ||
|     
 | ||
|     let modalContent = '';
 | ||
|     
 | ||
|     if (data.orderId) {
 | ||
|         // 有关联订单的情况
 | ||
|         modalContent = `
 | ||
|             <h3 class="text-lg font-medium text-gray-900 mb-4">订单详情</h3>
 | ||
|             <div class="space-y-3">
 | ||
|                 <div class="flex justify-between">
 | ||
|                     <span class="text-sm font-medium text-gray-500">订单编号:</span>
 | ||
|                     <span class="text-sm text-gray-900">${data.orderId}</span>
 | ||
|                 </div>
 | ||
|                 <div class="flex justify-between">
 | ||
|                     <span class="text-sm font-medium text-gray-500">订单总金额:</span>
 | ||
|                     <span class="text-sm text-gray-900">¥${(Math.random() * 1000 + 100).toFixed(2)}</span>
 | ||
|                 </div>
 | ||
|                 <div class="flex justify-between">
 | ||
|                     <span class="text-sm font-medium text-gray-500">订单时间:</span>
 | ||
|                     <span class="text-sm text-gray-900">${data.time}</span>
 | ||
|                 </div>
 | ||
|                 <div class="flex justify-between">
 | ||
|                     <span class="text-sm font-medium text-gray-500">订单归属摊位:</span>
 | ||
|                     <span class="text-sm text-gray-900">${['时尚服装店', '数码电子城', '美食餐厅', '家居生活馆'][Math.floor(Math.random() * 4)]}</span>
 | ||
|                 </div>
 | ||
|                 <div class="flex justify-between">
 | ||
|                     <span class="text-sm font-medium text-gray-500">成长值变化:</span>
 | ||
|                     <span class="text-sm text-green-600">+${data.growth}</span>
 | ||
|                 </div>
 | ||
|             </div>
 | ||
|         `;
 | ||
|     } else {
 | ||
|         // 无关联订单的情况
 | ||
|         modalContent = `
 | ||
|             <h3 class="text-lg font-medium text-gray-900 mb-4">成长值变化详情</h3>
 | ||
|             <div class="space-y-3">
 | ||
|                 <div class="flex justify-between">
 | ||
|                     <span class="text-sm font-medium text-gray-500">变化原因:</span>
 | ||
|                     <span class="text-sm text-gray-900">${data.reason}</span>
 | ||
|                 </div>
 | ||
|                 <div class="flex justify-between">
 | ||
|                     <span class="text-sm font-medium text-gray-500">变化时间:</span>
 | ||
|                     <span class="text-sm text-gray-900">${data.time}</span>
 | ||
|                 </div>
 | ||
|                 <div class="flex justify-between">
 | ||
|                     <span class="text-sm font-medium text-gray-500">成长值增加:</span>
 | ||
|                     <span class="text-sm text-green-600">+${data.growth}</span>
 | ||
|                 </div>
 | ||
|                 <div class="pt-2 text-sm text-gray-600">
 | ||
|                     ${getGrowthReasonDescription(data.type)}
 | ||
|                 </div>
 | ||
|             </div>
 | ||
|         `;
 | ||
|     }
 | ||
|     
 | ||
|     showDetailModal('成长值详情', modalContent);
 | ||
| }
 | ||
| 
 | ||
| // 显示积分详情弹窗
 | ||
| function showPointDetail(detailId, type, detailData) {
 | ||
|     const data = typeof detailData === 'string' ? JSON.parse(detailData) : detailData;
 | ||
|     
 | ||
|     let modalContent = '';
 | ||
|     
 | ||
|     if (data.orderId) {
 | ||
|         // 有关联订单的情况
 | ||
|         modalContent = `
 | ||
|             <h3 class="text-lg font-medium text-gray-900 mb-4">订单详情</h3>
 | ||
|             <div class="space-y-3">
 | ||
|                 <div class="flex justify-between">
 | ||
|                     <span class="text-sm font-medium text-gray-500">订单编号:</span>
 | ||
|                     <span class="text-sm text-gray-900">${data.orderId}</span>
 | ||
|                 </div>
 | ||
|                 <div class="flex justify-between">
 | ||
|                     <span class="text-sm font-medium text-gray-500">订单总金额:</span>
 | ||
|                     <span class="text-sm text-gray-900">¥${(Math.random() * 1000 + 100).toFixed(2)}</span>
 | ||
|                 </div>
 | ||
|                 <div class="flex justify-between">
 | ||
|                     <span class="text-sm font-medium text-gray-500">订单时间:</span>
 | ||
|                     <span class="text-sm text-gray-900">${data.time}</span>
 | ||
|                 </div>
 | ||
|                 <div class="flex justify-between">
 | ||
|                     <span class="text-sm font-medium text-gray-500">订单归属摊位:</span>
 | ||
|                     <span class="text-sm text-gray-900">${['时尚服装店', '数码电子城', '美食餐厅', '家居生活馆'][Math.floor(Math.random() * 4)]}</span>
 | ||
|                 </div>
 | ||
|                 <div class="flex justify-between">
 | ||
|                     <span class="text-sm font-medium text-gray-500">积分变化:</span>
 | ||
|                     <span class="text-sm ${data.change > 0 ? 'text-green-600' : 'text-red-600'}">${data.change > 0 ? '+' : ''}${data.change}</span>
 | ||
|                 </div>
 | ||
|                 <div class="pt-2 text-sm text-gray-600">
 | ||
|                     ${data.change > 0 ? '订单消费获得积分奖励' : '使用积分抵扣订单金额'}
 | ||
|                 </div>
 | ||
|             </div>
 | ||
|         `;
 | ||
|     } else {
 | ||
|         // 无关联订单的情况
 | ||
|         modalContent = `
 | ||
|             <h3 class="text-lg font-medium text-gray-900 mb-4">积分变化详情</h3>
 | ||
|             <div class="space-y-3">
 | ||
|                 <div class="flex justify-between">
 | ||
|                     <span class="text-sm font-medium text-gray-500">变化原因:</span>
 | ||
|                     <span class="text-sm text-gray-900">${data.reason}</span>
 | ||
|                 </div>
 | ||
|                 <div class="flex justify-between">
 | ||
|                     <span class="text-sm font-medium text-gray-500">变化时间:</span>
 | ||
|                     <span class="text-sm text-gray-900">${data.time}</span>
 | ||
|                 </div>
 | ||
|                 <div class="flex justify-between">
 | ||
|                     <span class="text-sm font-medium text-gray-500">积分变化:</span>
 | ||
|                     <span class="text-sm ${data.change > 0 ? 'text-green-600' : 'text-red-600'}">${data.change > 0 ? '+' : ''}${data.change}</span>
 | ||
|                 </div>
 | ||
|                 <div class="pt-2 text-sm text-gray-600">
 | ||
|                     ${getPointReasonDescription(data.type)}
 | ||
|                 </div>
 | ||
|             </div>
 | ||
|         `;
 | ||
|     }
 | ||
|     
 | ||
|     showDetailModal('积分详情', modalContent);
 | ||
| }
 | ||
| 
 | ||
| // 显示通用详情弹窗
 | ||
| function showDetailModal(title, content) {
 | ||
|     const modalHtml = `
 | ||
|         <div id="detail-modal" class="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50" onclick="closeDetailModal(event)">
 | ||
|             <div class="relative top-20 mx-auto p-5 border w-11/12 max-w-md shadow-lg rounded-md bg-white">
 | ||
|                 <div class="flex items-center justify-between mb-4">
 | ||
|                     <h3 class="text-lg font-medium text-gray-900">${title}</h3>
 | ||
|                     <button onclick="closeDetailModal()" class="text-gray-400 hover:text-gray-600">
 | ||
|                         <svg class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
 | ||
|                             <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
 | ||
|                         </svg>
 | ||
|                     </button>
 | ||
|                 </div>
 | ||
|                 
 | ||
|                 <div class="mb-4">
 | ||
|                     ${content}
 | ||
|                 </div>
 | ||
|                 
 | ||
|                 <div class="flex justify-end">
 | ||
|                     <button onclick="closeDetailModal()" class="px-4 py-2 bg-gray-500 text-white rounded-md hover:bg-gray-600 transition-colors">
 | ||
|                         关闭
 | ||
|                     </button>
 | ||
|                 </div>
 | ||
|             </div>
 | ||
|         </div>
 | ||
|     `;
 | ||
|     
 | ||
|     // 移除现有弹窗
 | ||
|     const existingModal = document.getElementById('detail-modal');
 | ||
|     if (existingModal) {
 | ||
|         existingModal.remove();
 | ||
|     }
 | ||
|     
 | ||
|     document.body.insertAdjacentHTML('beforeend', modalHtml);
 | ||
| }
 | ||
| 
 | ||
| // 关闭详情弹窗
 | ||
| function closeDetailModal(event) {
 | ||
|     if (event && event.target !== event.currentTarget) {
 | ||
|         return;
 | ||
|     }
 | ||
|     
 | ||
|     const modal = document.getElementById('detail-modal');
 | ||
|     if (modal) {
 | ||
|         modal.remove();
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| // 获取成长值原因描述
 | ||
| function getGrowthReasonDescription(type) {
 | ||
|     const descriptions = {
 | ||
|         'register': '新用户注册时系统自动发放的奖励成长值',
 | ||
|         'birthday': '会员生日当天系统自动发放的生日礼品成长值',
 | ||
|         'activity': '参与平台活动获得的奖励成长值',
 | ||
|         'order': '订单消费根据消费金额按比例获得的成长值'
 | ||
|     };
 | ||
|     
 | ||
|     return descriptions[type] || '其他原因获得的成长值';
 | ||
| }
 | ||
| 
 | ||
| // 获取积分原因描述
 | ||
| function getPointReasonDescription(type) {
 | ||
|     const descriptions = {
 | ||
|         'register': '新用户注册时系统自动发放的奖励积分',
 | ||
|         'birthday': '会员生日当天系统自动发放的生日礼品积分',
 | ||
|         'activity': '参与平台活动获得的奖励积分',
 | ||
|         'order_earn': '订单消费根据消费金额按比例获得的积分奖励',
 | ||
|         'order_use': '在订单中使用积分抵扣部分金额'
 | ||
|     };
 | ||
|     
 | ||
|     return descriptions[type] || '其他原因的积分变化';
 | ||
| }
 | ||
| 
 | ||
| // 分页功能(成长值)
 | ||
| function growthPrevPage(memberId) {
 | ||
|     showNotification('已到第一页', 'info');
 | ||
| }
 | ||
| 
 | ||
| function growthNextPage(memberId) {
 | ||
|     showNotification('已到最后一页', 'info');
 | ||
| }
 | ||
| 
 | ||
| // 分页功能(积分)
 | ||
| function pointsPrevPage(memberId) {
 | ||
|     showNotification('已到第一页', 'info');
 | ||
| }
 | ||
| 
 | ||
| function pointsNextPage(memberId) {
 | ||
|     showNotification('已到最后一页', 'info');
 | ||
| }
 | ||
| 
 | ||
| // 导出函数
 | ||
| window.memberTabManager = memberTabManager;
 | ||
| window.openLevelSettingsTab = openLevelSettingsTab;
 | ||
| window.openLevelDetailTab = openLevelDetailTab;
 | ||
| window.openLevelEditTab = openLevelEditTab;
 | ||
| window.openBatchLevelEditTab = openBatchLevelEditTab;
 | ||
| window.openCouponModal = openCouponModal;
 | ||
| window.testCouponModal = testCouponModal;
 | ||
| window.closeCouponModal = closeCouponModal;
 | ||
| window.resetCouponForm = resetCouponForm;
 | ||
| window.adjustThreshold = adjustThreshold;
 | ||
| window.adjustDiscount = adjustDiscount;
 | ||
| window.addCouponThreshold = addCouponThreshold;
 | ||
| window.submitCoupon = submitCoupon;
 | ||
| window.submitLevelDetail = submitLevelDetail;
 | ||
| window.submitLevelEdit = submitLevelEdit;
 | ||
| window.submitBatchLevelEdit = submitBatchLevelEdit;
 | ||
| window.toggleStallDropdown = toggleStallDropdown;
 | ||
| window.toggleBatchStallDropdown = toggleBatchStallDropdown;
 | ||
| window.updateBatchSelectedStalls = updateBatchSelectedStalls;
 | ||
| window.removeBatchSelectedStall = removeBatchSelectedStall;
 | ||
| window.removeSelectedStall = removeSelectedStall;
 | ||
| window.filterStallTable = filterStallTable;
 | ||
| window.openMemberListTab = openMemberListTab;
 | ||
| window.getLevelBadgeColor = getLevelBadgeColor;
 | ||
| window.initializeMemberListPagination = initializeMemberListPagination;
 | ||
| window.updatePaginationDisplay = updatePaginationDisplay;
 | ||
| window.goToPage = goToPage;
 | ||
| window.changePageSize = changePageSize;
 | ||
| window.filterMemberList = filterMemberList;
 | ||
| window.resetMemberFilters = resetMemberFilters;
 | ||
| window.toggleAllMembers = toggleAllMembers;
 | ||
| window.sortMemberTable = sortMemberTable;
 | ||
| window.viewMemberDetail = viewMemberDetail;
 | ||
| window.getMemberDataById = getMemberDataById;
 | ||
| window.openMemberDetailTab = openMemberDetailTab;
 | ||
| window.generateMemberCoupons = generateMemberCoupons;
 | ||
| window.generateGrowthDetails = generateGrowthDetails;
 | ||
| window.generatePointDetails = generatePointDetails;
 | ||
| window.switchDetailTab = switchDetailTab;
 | ||
| window.showMemberCouponsModal = showMemberCouponsModal;
 | ||
| window.closeMemberCouponsModal = closeMemberCouponsModal;
 | ||
| window.showGrowthDetail = showGrowthDetail;
 | ||
| window.showPointDetail = showPointDetail;
 | ||
| window.showDetailModal = showDetailModal;
 | ||
| window.closeDetailModal = closeDetailModal;
 | ||
| window.getGrowthReasonDescription = getGrowthReasonDescription;
 | ||
| window.getPointReasonDescription = getPointReasonDescription;
 | ||
| window.growthPrevPage = growthPrevPage;
 | ||
| window.growthNextPage = growthNextPage;
 | ||
| window.pointsPrevPage = pointsPrevPage;
 | ||
| window.pointsNextPage = pointsNextPage; |