如何简化Wix Mega Menu代码,点击主菜单直接加载过滤后的分类数据
Wix Velo 超级菜单实现问题及解决方案
问题背景
我正在开发一个Wix网站,目前卡在超级菜单的逻辑调整上。现有4个内容管理器数据库存储菜单相关数据:
- 第一个数据库为mainMenu,用于填充站点顶部的主菜单按钮,点击按钮可展开对应分类的子菜单
- 第二个数据库名为subMenu,根据点击的主菜单按钮过滤数据,展示子分类名称的独立菜单,当前逻辑下点击子菜单按钮才会打开超级菜单
- 第三个数据库为productsMenu,用于填充超级菜单展示的所有产品分类,根据点击的子菜单按钮过滤数据
- 第四个数据库为newMenu,仅用于填充超级菜单右侧的图片和文本
当前逻辑
点击主菜单按钮 → 展开过滤后的子菜单 → 点击子菜单按钮 → 展开过滤后的超级菜单
期望效果
点击主菜单项 → 直接展开对应该主分类的过滤后超级菜单,子菜单标题作为不同列的表头
现有效果截图:
期望效果截图:
原有代码
前端页面代码
import { getMenus } from 'backend/data.jsw'; // ID: 5119D613F3A02481B7F825C8A883DE93 let menusData, repeaters; let selectionState = { currMain: null, currSub: null, }; const colors = { mainUnselected: 'white', subUnselected: 'black', selected: 'red' } $w.onReady(async function () { await iniPage(); }); async function iniPage() { try { const menus = await getMenus(); repeaters = menus.menusNames.map(id => ({ menuId: id, repeaterSelector: $w('#' + id + 'Repeater') })); menusData = menus.menusData; $w('#mainMenuRepeater').data = menusData.mainMenu; connectDataToRepeatersItems(); selectMenu('mainMenu'); } catch (err) { console.error('error ', err); } } function connectDataToRepeatersItems() { repeaters.forEach(({ menuId, repeaterSelector }) => { repeaterSelector.onItemReady(($item, itemData) => { const $itemBtn = $item('#' + menuId + 'Button'); if (itemData.name) $itemBtn.label = itemData.name; if (itemData.image) $item('#' + menuId + 'Image').src = itemData.image; if (itemData.link) $itemBtn.link = itemData.link; else $itemBtn.onClick(() => { resetColorForPrevSelection(menuId); if (menuId === 'mainMenu') { selectionState.currMain = $itemBtn; selectionState.currSub = null; } else { selectionState.currSub = $itemBtn; } $itemBtn.style.color = colors.selected; selectMenu(menuId, itemData._id) }); }); }); } function resetColorForPrevSelection(menuId) { if (menuId === 'mainMenu' && selectionState.currMain) { selectionState.currMain.style.color = colors.mainUnselected; } else if (selectionState.currSub) { selectionState.currSub.style.color = colors.subUnselected; } } function selectMenu(menu, clickedElementId) { let nestedMenus; if (menu === 'mainMenu') { nestedMenus = ['subMenu']; $w('#megaMenuStrip').collapse(); } else if (menu === 'subMenu') { nestedMenus = ['productMenu', 'rangeMenu', 'newMenu']; $w('#megaMenuStrip').expand(); } nestedMenus.forEach(nestedMenu => { $w('#' + nestedMenu + 'Repeater').data = filterRepeaterData(menu, nestedMenu, clickedElementId); }); } function filterRepeaterData(clickedMenu, nestedMenu, elementId) { return menusData[nestedMenu].filter(item => item[clickedMenu] === elementId); }
后端data.jsw代码
import wixData from 'wix-data'; const menusNames = ['mainMenu', 'subMenu', 'productMenu', 'newMenu']; export async function getMenus() { const collectionsNames = menusNames; const promises = []; collectionsNames.forEach(collection => { promises.push(wixData.query(collection).find()); }); const resolvedData = await Promise.all(promises); const menusData = { 'mainMenu': resolvedData[0].items, 'subMenu': resolvedData[1].items, 'productMenu': resolvedData[2].items, 'newMenu': resolvedData[3].items }; return { menusNames, menusData }; }
解决方案
后端代码无需修改,仅调整前端页面的selectMenu和filterRepeaterData两个函数即可实现需求。
修改后完整前端代码
import { getMenus } from 'backend/data.jsw'; // ID: 5119D613F3A02481B7F825C8A883DE93 let menusData, repeaters; let selectionState = { currMain: null, currSub: null, }; const colors = { mainUnselected: 'white', subUnselected: 'black', selected: 'red' } $w.onReady(async function () { await iniPage(); }); async function iniPage() { try { const menus = await getMenus(); repeaters = menus.menusNames.map(id => ({ menuId: id, repeaterSelector: $w('#' + id + 'Repeater') })); menusData = menus.menusData; $w('#mainMenuRepeater').data = menusData.mainMenu; connectDataToRepeatersItems(); selectMenu('mainMenu'); } catch (err) { console.error('error ', err); } } function connectDataToRepeatersItems() { repeaters.forEach(({ menuId, repeaterSelector }) => { repeaterSelector.onItemReady(($item, itemData) => { const $itemBtn = $item('#' + menuId + 'Button'); if (itemData.name) $itemBtn.label = itemData.name; if (itemData.image) $item('#' + menuId + 'Image').src = itemData.image; if (itemData.link) $itemBtn.link = itemData.link; else $itemBtn.onClick(() => { resetColorForPrevSelection(menuId); if (menuId === 'mainMenu') { selectionState.currMain = $itemBtn; selectionState.currSub = null; } else { selectionState.currSub = $itemBtn; } $itemBtn.style.color = colors.selected; selectMenu(menuId, itemData._id) }); }); }); } function resetColorForPrevSelection(menuId) { if (menuId === 'mainMenu' && selectionState.currMain) { selectionState.currMain.style.color = colors.mainUnselected; } else if (selectionState.currSub) { selectionState.currSub.style.color = colors.subUnselected; } } // 调整主菜单点击逻辑,直接加载全部菜单数据并展开超级菜单 function selectMenu(menu, clickedElementId) { let nestedMenus; if (menu === 'mainMenu') { nestedMenus = ['subMenu', 'productMenu', 'newMenu']; $w('#megaMenuStrip').expand(); } else if (menu === 'subMenu') { nestedMenus = ['productMenu', 'newMenu']; } nestedMenus.forEach(nestedMenu => { $w('#' + nestedMenu + 'Repeater').data = filterRepeaterData(menu, nestedMenu, clickedElementId); }); } // 新增主菜单维度的产品过滤逻辑 function filterRepeaterData(clickedMenu, nestedMenu, elementId) { if (clickedMenu === 'mainMenu' && nestedMenu === 'productMenu') { // 先筛选当前主菜单对应的所有子菜单ID const relatedSubIds = menusData.subMenu .filter(sub => sub.mainMenu === elementId) .map(sub => sub._id); // 过滤出所有关联子菜单对应的产品 return menusData[nestedMenu].filter(item => relatedSubIds.includes(item.subMenu)); } return menusData[nestedMenu].filter(item => item[clickedMenu] === elementId); }
布局调整说明
需要将原来独立的subMenu Repeater移入超级菜单容器#megaMenuStrip中,设置为横向列布局,每个子菜单项作为列头,下方绑定对应筛选后的productMenu Repeater即可实现期望的多列展示效果。
内容的提问来源于stack exchange,提问作者niclozano
相关产品推荐
相关产品推荐

