# MMenu 菜单

- 来源：[E10 UI 组件库](https://www.e-cology.com.cn/ui/mobile/menu/freepass)
- 平台：移动端
- 维护者：陈佳敏
- 设计稿：http://pd.e-cology.com.cn/pd/E10/interface/e10xiaoguotu/H5/E10zjys/#s21
- Issue：http://gitlab.ecology10.cn/FRONTEND/weapp-ui/-/issues?state=all&search=[MMenu]

## 代码演示

### Tab效果-基础用法

data中content为文字内容,带icon图标需配置icon属性,disabled配置禁用效果

```jsx
import React from 'react';
import { MMenu, MButton } from '@weapp/ui';

const data = [
  { id: 'tab1', content: '常规标签' },
  { id: 'tab2', content: '禁用标签', disabled: true },
  { id: 'tab3', content: '带图标标签', icon: 'Icon-folder' },
]

export default class Demo extends React.PureComponent<{}> {

  onChange = (value: string) => {
    console.log('value', value);
  }

  render() {
    return(
      <MMenu
        defaultValue={'tab1'}
        data={data}
        extraContent={<MButton >Extra Content</MButton>} // 右侧附加项
        onChange={this.onChange}
      />
    );
  }
}
```

### Tab效果-滚动+3级数据

data中数据项过多时,超长自动滚动。多级菜单,通过配置菜单的children属性

```jsx
import React from "react";
import { MMenu } from "@weapp/ui";

const data = [
  { id: "tab1", content: "选项1" },
  { id: "tab2", content: "选项2" },
  { id: "tab3", content: "选项3" },
  {
    id: "tab4",
    content: "选项4(含下级不可选)",
    children: [
      {
        id: "tab4_1",
        content: "选项4.1(含下级不可选)",
        parentId: "tab4",
        children: [
          { id: "tab4_1_1", content: "选项4.1.1", parentId: "tab4" },
          { id: "tab4_1_2", content: "选项4.1.2", parentId: "tab2" },
          { id: "tab4_1_3", content: "选项4.1.3", parentId: "tab4" },
          { id: "tab4_1_4", content: "选项4.1.4", parentId: "tab4" },
          {
            id: "tab4_1_5",
            content: "选项4.1.5字段很长很长很长很长很长很长的时候",
            parentId: "tab4",
          },
        ],
      },
      { id: "tab4_2", content: "选项4.2", parentId: "tab2" },
      { id: "tab4_3", content: "选项4.3", parentId: "tab4" },
      { id: "tab4_4", content: "选项4.4", parentId: "tab4" },
      {
        id: "tab4_5",
        content: "选项4.5字段很长很长很长很长很长很长的时候",
        parentId: "tab4",
      },
    ],
  },
  {
    id: "tab5",
    content: "选项5(可选)",
    canBeSelected: true, // 设置当前父级节点是否可选
    children: [
      { id: "tab5_1", content: "选项5.1", parentId: "tab5" },
      {
        id: "tab5_2",
        content: "选项5.2(可选)",
        canBeSelected: true, // 设置当前父级节点是否可选
        parentId: "tab2",
        children: [
          { id: "tab5_2_1", content: "选项5.2.1", parentId: "tab5_2" },
          { id: "tab5_2_2", content: "选项5.2.2", parentId: "tab5_2" },
          { id: "tab5_2_3", content: "选项5.2.3", parentId: "tab5_2" },
        ],
      },
      { id: "tab5_3", content: "选项5.3", parentId: "tab5" },
      { id: "tab5_4", content: "选项5.4", parentId: "tab5" },
      { id: "tab5_5", content: "选项5.5", parentId: "tab5" },
      { id: "tab5_6", content: "选项5.6", parentId: "tab5" },
      { id: "tab5_7", content: "选项5.7", parentId: "tab5" },
      { id: "tab5_8", content: "选项5.8", parentId: "tab5" },
      { id: "tab5_9", content: "选项5.9", parentId: "tab5" },
      { id: "tab5_10", content: "选项5.10", parentId: "tab5" },
      { id: "tab5_11", content: "选项5.11", parentId: "tab5" },
      { id: "tab5_12", content: "选项5.12", parentId: "tab5" },
    ],
  },
  { id: "tab6", content: "选项6字段很长很长很长很长很长很长的时候" },
  { id: "tab7", content: "选项7" },
  { id: "tab8", content: "选项8" },
  { id: "tab9", content: "选项9" },
  { id: "tab10", content: "选项10" },
  { id: "tab11", content: "选项11" },
  { id: "tab12", content: "选项12" },
  { id: "tab13", content: "选项13" },
];

export default class Demo extends React.PureComponent<{}> {
  onChange = (value: string) => {
    console.log("value", value);
  };

  render() {
    return (
      <>
        <MMenu
         
          defaultValue={"tab5_2"}
          data={data}
          onChange={this.onChange}
        />
        <div>
          <p>测试验证下方有内容场景: 展开菜单，内容位置不变1</p>
          <p>测试验证下方有内容场景: 展开菜单，内容位置不变2</p>
          <p>测试验证下方有内容场景: 展开菜单，内容位置不变3</p>
          <p>测试验证下方有内容场景: 展开菜单，内容位置不变4</p>
          <p>测试验证下方有内容场景: 展开菜单，内容位置不变5</p>
          <p>测试验证下方有内容场景: 展开菜单，内容位置不变6</p>
          <p>测试验证下方有内容场景: 展开菜单，内容位置不变7</p>
          <p>测试验证下方有内容场景: 展开菜单，内容位置不变8</p>
          <p>测试验证下方有内容场景: 展开菜单，内容位置不变9</p>
          <p>测试验证下方有内容场景: 展开菜单，内容位置不变10</p>
        </div>
      </>
    );
  }
}
```

### Tab效果-滚动+3级数据+显示全部

showAll属性配置展示全部菜单按钮,展开下级面板的时候，第一级菜单也会同样显示在下级面板中

```jsx
import React from "react";
import { MMenu, Button } from "@weapp/ui";

const initData = [
  { id: "tab1", content: "选项1" },
  {
    id: "tab2",
    content: "选项2",
    children: [{ id: "tab2_1", content: "选项2.1", parentId: "tab2" }],
  },
  { id: "tab3", content: "选项3" },
  {
    id: "tab4",
    content: "选项4(含下级不可选)",
    children: [
      {
        id: "tab4_1",
        content: "选项4.1(含下级不可选)",
        parentId: "tab4",
      },
      { id: "tab4_2", content: "选项4.2", parentId: "tab2" },
      { id: "tab4_3", content: "选项4.3", parentId: "tab4" },
      { id: "tab4_4", content: "选项4.4", parentId: "tab4" },
      {
        id: "tab4_5",
        content: "选项4.5字段很长很长很长很长很长很长的时候",
        parentId: "tab4",
      },
    ],
  },
  {
    id: "tab5",
    content: "选项5(可选)",
    canBeSelected: true,
    children: [
      { id: "tab5_1", content: "选项5.1", parentId: "tab5" },
      {
        id: "tab5_2",
        content: "选项5.2(可选)",
        canBeSelected: true,
        parentId: "tab2",
      },
      { id: "tab5_3", content: "选项5.3", parentId: "tab5" },
      { id: "tab5_4", content: "选项5.4", parentId: "tab5" },
      { id: "tab5_5", content: "选项5.5", parentId: "tab5" },
      { id: "tab5_6", content: "选项5.6", parentId: "tab5" },
      { id: "tab5_7", content: "选项5.7", parentId: "tab5" },
      { id: "tab5_8", content: "选项5.8", parentId: "tab5" },
      { id: "tab5_9", content: "选项5.9", parentId: "tab5" },
      { id: "tab5_10", content: "选项5.10", parentId: "tab5" },
      { id: "tab5_11", content: "选项5.11", parentId: "tab5" },
      { id: "tab5_12", content: "选项5.12", parentId: "tab5" },
    ],
  },
  { id: "tab6", content: "选项6字段很长很长很长很长很长很长的时候" },
  { id: "tab7", content: "选项7" },
  { id: "tab8", content: "选项8" },
  { id: "tab9", content: "选项9" },
  { id: "tab10", content: "选项10" },
  {
    id: "tab11",
    content: "选项11",
    children: [
      { id: "tab11_1", content: "选项11.1", parentId: "tab11" },
      { id: "tab11_2", content: "选项11.2", parentId: "tab11" },
      { id: "tab11_3", content: "选项11.3", parentId: "tab11" },
      { id: "tab11_4", content: "选项11.4", parentId: "tab11" },
    ],
  },
  { id: "tab12", content: "选项12" },
  { id: "tab13", content: "选项13" },
];

export default class Demo extends React.PureComponent<any, any> {
  constructor(props: any) {
    super(props);
    this.state = {
      data: initData,
    };
  }

  onChange = (value: string) => {
    console.log("value", value);
  };

  handleAdd = () => {
    const { data } = this.state;
    const target = [...data, {
      id: Math.random(),
      content: `新增项-${data.length}`
    }]
    this.setState({
      data: target
    })
  }

  render() {
    const { data } = this.state;
    return (
      <div style={{ margin: '0 10px'}}>
        <p>占位</p>
        <p>占位</p>
        <p>占位</p>
        <MMenu
         
          defaultValue={"tab1"}
          data={data}
          showAll={true} // 展开全部会显示包括一级菜单所有的菜单
          onChange={this.onChange}
          floating
          mode="vertical"
        />
        <p>占位</p>
        <p>占位</p>
        <p>占位</p>
        <p>占位</p>
        <MMenu
         
          defaultValue={"tab1"}
          data={data}
          showAll={true} // 展开全部会显示包括一级菜单所有的菜单
          onChange={this.onChange}
          type="overflow"
        />

      </div>
    );
  }
}
```

### Tab效果-滚动+无限级数据+显示全部

data中数据项过多时,超长自动滚动。多级菜单,通过配置菜单的children属性。无限级通过infinite属性设置

```jsx
import React from "react";
import { MMenu } from "@weapp/ui";

const data = [
  { id: "tab1", content: "选项1" },
  { id: "tab2", content: "选项2" },
  { id: "tab3", content: "选项3" },
  {
    id: "tab4",
    content: "选项4(含下级不可选)",
    children: [
      {
        id: "tab4_1",
        content: "选项4.1(含下级不可选)",
        parentId: "tab4",
        children: [
          {
            id: "tab4_1_1",
            content: "选项4.1.1(含下级不可选)",
            parentId: "tab4_1",
            children: [
              {
                id: "tab4_1_1_1",
                content: "选项4.1.1-1",
                parentId: "tab4_1_1",
              },
              {
                id: "tab4_1_1_2",
                content: "选项4.1.2-2",
                parentId: "tab4_1_1",
              },
              {
                id: "tab4_1_1_3",
                content: "选项4.1.3-3",
                parentId: "tab4_1_1",
              },
            ],
          },
          { id: "tab4_1_2", content: "选项4.1.2", parentId: "tab4_1" },
          { id: "tab4_1_3", content: "选项4.1.3", parentId: "tab4_1" },
        ],
      },
      {
        id: "tab4_2",
        content: "选项4.2",
        parentId: "tab2",
      },
      { id: "tab4_3", content: "选项4.3", parentId: "tab4" },
      { id: "tab4_4", content: "选项4.4", parentId: "tab4" },
      {
        id: "tab4_5",
        content: "选项4.5字段很长很长很长很长很长很长的时候",
        parentId: "tab4",
      },
    ],
  },
  {
    id: "tab5",
    content: "选项5(含下级可选)",
    canBeSelected: true, // 设置当前父级节点是否可选
    children: [
      { id: "tab5_1", content: "选项5.1", parentId: "tab5" },
      {
        id: "tab5_2",
        content: "选项5.2(含下级可选)",
        canBeSelected: true, // 设置当前父级节点是否可选
        parentId: "tab2",
        children: [
          { id: "tab5_2_1", content: "选项5.2.1", parentId: "tab5_2" },
          { id: "tab5_2_2", content: "选项5.2.2", parentId: "tab5_2" },
          { id: "tab5_2_3", content: "选项5.2.3", parentId: "tab5_2" },
        ],
      },
      { id: "tab5_3", content: "选项5.3", parentId: "tab5" },
      { id: "tab5_4", content: "选项5.4", parentId: "tab5" },
      { id: "tab5_5", content: "选项5.5", parentId: "tab5" },
      { id: "tab5_6", content: "选项5.6", parentId: "tab5" },
      { id: "tab5_7", content: "选项5.7", parentId: "tab5" },
      { id: "tab5_8", content: "选项5.8", parentId: "tab5" },
      { id: "tab5_9", content: "选项5.9", parentId: "tab5" },
      { id: "tab5_10", content: "选项5.10", parentId: "tab5" },
      { id: "tab5_11", content: "选项5.11", parentId: "tab5" },
      { id: "tab5_12", content: "选项5.12", parentId: "tab5" },
    ],
  },
  {
    id: "tab6",
    content: "选项6超级超级超级超级超级超级超级超级超级超级长的选项",
  },
  { id: "tab7", content: "选项7" },
  { id: "tab8", content: "选项8" },
  { id: "tab9", content: "选项9" },
  { id: "tab10", content: "选项10" },
  { id: "tab11", content: "选项11" },
  { id: "tab12", content: "选项12" },
  { id: "tab13", content: "选项13" },
];

export default class Demo extends React.PureComponent<{}> {
  onChange = (value: string) => {
    console.log("value", value);
  };

  render() {
    return (
      <MMenu
       
        defaultValue={"tab4_1_1_1"}
        data={data}
        showAll
        infinite
        onChange={this.onChange}
      />
    );
  }
}
```

### Tab效果-横向拖拽、增删改

横向滚动tab,带拖拽、新增、编辑、删除效果

```jsx
import React from "react";
import {
  MMenu,
  Icon,
  MenuItemData,
  Input,
  MDialog,
  MActionSheet,
  MValueType,
} from "@weapp/ui";

import "./index.less";

const defaultData = [
  {
    id: "tab1",
    content: "标签1",
    editable: true,
    editIcon: "Icon-set-up-o",
  },
  {
    id: "tab2",
    content: "标签2",
    editable: true,
    editIcon: "Icon-set-up-o",
  },
];

interface DemoState {
  value: string;
  data: MenuItemData[];
  visible: boolean;
  inputValue: string;
  editId: string;
}

export default class Demo extends React.PureComponent<{}, DemoState> {
  state = {
    value: "tab1",
    data: defaultData,
    visible: false,
    inputValue: "",
    editId: "",
  };

  getHash = () => Math.random().toString(36).substr(2, 6);

  onChange = (value: string) => {
    this.setState({ value });
  }

  onAdd = () => {
    this.setState({ visible: true, editId: "", inputValue: '' });
  };

  onSetting = (editItem: MenuItemData) => {
    this.setState({ editId: editItem.id, inputValue: editItem.content || '' });
    this.showActionSheet();
  }

  onEdit = () => {
    this.setState({ visible: true });
  }

  onDelete = () => {
    const { editId, data, value } = this.state;
    MDialog.prompt({
      title: "确认删除吗?",
      mask: true,
      maskClosable: true,
      prompt: false,
      onOk: () => {
        const newData = data.filter(i => i.id !== editId);
        if (value === editId) { // 模拟删除为当前选中菜单，自动更新到第一个菜单
          const valueIndex = data.findIndex(i => i.id === value);
          this.setState({ data: newData, value: (valueIndex === 0 ? data[1]?.id : data[0]?.id) || '' })
        } else {
          this.setState({ data: newData })
        }
      },
    })
  }

  onOK = () => {
    const { editId, data, inputValue } = this.state;
    if (editId) {
      // 编辑tab
      const newData = data.map((item) => {
        if (item.id === editId) {
          return { ...item, content: inputValue };
        }
        return item;
      });
      this.setState({ data: newData, visible: false, inputValue: "" });
    } else {
      // 新增tab: 更新Menu的data
      const newKey = `newTab_${this.getHash()}`;
      const newItem = {
        id: newKey,
        content: inputValue,
        editable: true,
        editIcon: "Icon-set-up-o",
      };
      const newData = data.concat(newItem);
      this.setState({ data: newData, visible: false, inputValue: "" });
    }
  }

  onClose = () => {
    this.setState({ visible: false });
  }

  onInputChange = (value: MValueType) => {
    this.setState({ inputValue: value as string });
  }

  onDrag = (newData: MenuItemData[], evt: any) => {
    console.log('onDrag11', newData)
    this.setState({ data: newData });
  }

  showActionSheet = () => {
    const btns = [
      {
        content: '编辑菜单',
        onClick: this.onEdit,
      },
      {
        content: '删除',
        onClick: this.onDelete,
      },
      {
        content: '取消',
      },
    ];
    MActionSheet.showActionSheetWithOptions({
      options: btns,
      cancelButtonIndex: btns.length - 1,
      cancelButtonText: '取消',
      maskClosable: true,
    })
  }

  render() {
    const { value, data, visible, inputValue, editId } = this.state;
    return (
      <>
        <MMenu
         
          value={value}
          data={data}
          mode="horizontal"
          floating={true} // 是否浮动布局
          sortable={true} // 是否可拖拽
          showSortableIcon={true} // 是否显示拖拽按钮
          onChange={this.onChange}
          onEdit={this.onSetting}
          onDrag={this.onDrag}
          extraContent={ // 新增
            <span className="demo-mtab-add" onClick={this.onAdd}>
              <Icon
               
                name="Icon-add-to01"
                size="sm"
              />
            </span>
          }
        />
        <MDialog
         
          className={`demo-mtab-dialog`}
          visible={visible}
          onClose={this.onClose}
          onOk={this.onOK}
          title={editId ? "编辑菜单" : "添加菜单"}
          destroyOnClose
          mask
          maskClosable
          okText="确定"
          cancelText="取消"
        >
          <Input
           
            className={`demo-mtab-dialog-input`}
            value={inputValue}
            onChange={this.onInputChange}
          />
        </MDialog>
      </>
    );
  }
}
```

### Tab效果-纵向拖拽、增删改

纵向滚动tab,带拖拽、新增、编辑、删除效果

```jsx
import React from "react";
import {
  MMenu,
  Icon,
  MenuItemData,
  Input,
  MDialog,
  MActionSheet,
  MValueType,
} from "@weapp/ui";

import "./index.less";

const defaultData = [
  {
    id: "tab1",
    content: "标签1",
    editable: true,
    editIcon: "Icon-set-up-o",
  },
  {
    id: "tab2",
    content: "标签2",
    editable: true,
    editIcon: "Icon-set-up-o",
  },
  {
    id: "tab3",
    content: "标签3超长标签超长标签超长标签超长标签超长标签超长标签",
    editable: true,
    editIcon: "Icon-set-up-o",
  },
  {
    id: "tab4",
    content: "标签4",
    editable: true,
    editIcon: "Icon-set-up-o",
  },
];

interface DemoState {
  value: string;
  data: MenuItemData[];
  visible: boolean;
  inputValue: string;
  editId: string;
}

export default class Demo extends React.PureComponent<{}, DemoState> {
  state = {
    value: "tab1",
    data: defaultData,
    visible: false,
    inputValue: "",
    editId: "",
  };

  getHash = () => Math.random().toString(36).substr(2, 6);

  onChange = (value: string) => {
    this.setState({ value });
  }

  onAdd = () => {
    this.setState({ visible: true, editId: "", inputValue: '' });
  };

  onSetting = (editItem: MenuItemData) => {
    this.setState({ editId: editItem.id, inputValue: editItem.content || '' });
    this.showActionSheet();
  }

  onEdit = () => {
    this.setState({ visible: true });
  }

  onDelete = () => {
    const { editId, data, value } = this.state;
    MDialog.prompt({
      title: "确认删除吗?",
      mask: true,
      maskClosable: true,
      prompt: false,
      onOk: () => {
        const newData = data.filter(i => i.id !== editId);
        if (value === editId) { // 模拟删除为当前选中菜单，自动更新到第一个菜单
          const valueIndex = data.findIndex(i => i.id === value);
          this.setState({ data: newData, value: (valueIndex === 0 ? data[1]?.id : data[0]?.id) || '' })
        } else {
          this.setState({ data: newData })
        }
      },
    })
  }

  onOK = () => {
    const { editId, data, inputValue } = this.state;
    if (editId) {
      // 编辑tab
      const newData = data.map((item) => {
        if (item.id === editId) {
          return { ...item, content: inputValue };
        }
        return item;
      });
      this.setState({ data: newData, visible: false, inputValue: "" });
    } else {
      // 新增tab: 更新Menu的data
      const newKey = `newTab_${this.getHash()}`;
      const newItem = {
        id: newKey,
        content: inputValue,
        editable: true,
        editIcon: "Icon-set-up-o",
      };
      const newData = data.concat(newItem);
      this.setState({ data: newData, visible: false, inputValue: "" });
    }
  };

  onClose = () => {
    this.setState({ visible: false });
  }

  onInputChange = (value: MValueType) => {
    this.setState({ inputValue: value as string });
  }

  onDrag = (newData: MenuItemData[], evt: any) => {
    this.setState({ data: newData });
  }

  showActionSheet = () => {
    const btns = [
      {
        content: '编辑菜单',
        onClick: this.onEdit,
      },
      {
        content: '删除',
        onClick: this.onDelete,
      },
      {
        content: '取消',
      },
    ];
    MActionSheet.showActionSheetWithOptions({
      options: btns,
      cancelButtonIndex: btns.length - 1,
      cancelButtonText: '取消',
      maskClosable: true,
    })
  }

  render() {
    const { value, data, visible, inputValue, editId } = this.state;
    return (
      <>
        <MMenu
         
          value={value}
          data={data}
          mode="vertical"
          floating={true} // 是否浮动布局
          sortable={true} // 是否可拖拽
          showSortableIcon={true} // 是否显示拖拽按钮
          onChange={this.onChange}
          onEdit={this.onSetting}
          onDrag={this.onDrag}
          // sortableOptions={{
          //   onEnd: (evt: any) => {
          //     console.log('sortableOptions_onEnd', evt, this.state, this.state.data);
          //   },
          // }}
          extraContent={ // 新增
            <span className="demo-mtab-vertical-add" onClick={this.onAdd}>
              <Icon
               
                name="Icon-add-to01"
                size="sm"
              />
            </span>
          }
        />
        <MDialog
         
          className={`demo-mtab-dialog`}
          visible={visible}
          onClose={this.onClose}
          onOk={this.onOK}
          title={editId ? "编辑菜单" : "添加菜单"}
          destroyOnClose
          mask
          maskClosable
          okText="确定"
          cancelText="取消"
        >
          <Input
           
            className={`demo-mtab-dialog-input`}
            value={inputValue}
            onChange={this.onInputChange}
          />
        </MDialog>
      </>
    );
  }
}
```

### Tab菜单-通过data中的hide隐藏页签

```jsx
import React from 'react';
import { MMenu, MSelect, SelectValueType } from '@weapp/ui';

const data = [
  { id: 'tab1', content: '可选标签1' },
  { id: 'tab2', content: '可选标签2' },
  { id: 'tab3', content: '可选标签3' },
  { id: 'tab4', content: '可选标签4' },
  { id: 'tab5', content: '可选标签5' },
  { id: 'tab6', content: '可选标签6' },
  { id: 'tab7', content: '可选标签7' },
  { id: 'tab8', content: '可选标签8' },
  { id: 'tab9', content: '可选标签9' },
  { id: 'tab10', content: '可选标签10' },
  { id: 'tab11', content: '可选标签11' },
  { id: 'tab12', content: '可选标签12' },
  { id: 'tab13', content: '可选标签13' },
  { id: 'tab14', content: '可选标签14' },
  { id: 'tab15', content: '可选标签15' },
  { id: 'tab16', content: '可选标签16' },
  { id: 'tab17', content: '可选标签17' },
  { id: 'tab18', content: '可选标签18' },
  { id: 'tab19', content: '可选标签19' },
  { id: 'tab20', content: '可选标签20' },
];
const dataMore = [
  { id: "tab1", content: "选项1" },
  {
    id: "tab2",
    content: "选项2",
    children: [{ id: "tab2_1", content: "选项2.1", parentId: "tab2" }],
  },
  { id: "tab3", content: "选项3" },
  {
    id: "tab4",
    content: "选项4(含下级不可选)",
    children: [
      {
        id: "tab4_1",
        content: "选项4.1(含下级不可选)",
        parentId: "tab4",
      },
      { id: "tab4_2", content: "选项4.2", parentId: "tab2" },
      { id: "tab4_3", content: "选项4.3", parentId: "tab4" },
      { id: "tab4_4", content: "选项4.4", parentId: "tab4" },
      {
        id: "tab4_5",
        content: "选项4.5字段很长很长很长很长很长很长的时候",
        parentId: "tab4",
      },
    ],
  },
  {
    id: "tab5",
    content: "选项5(可选)",
    canBeSelected: true,
    children: [
      { id: "tab5_1", content: "选项5.1", parentId: "tab5" },
      {
        id: "tab5_2",
        content: "选项5.2(可选)",
        canBeSelected: true, // 设置当前父级节点是否可选
        parentId: "tab2",
        children: [
          { id: "tab5_2_1", content: "选项5.2.1", parentId: "tab5_2" },
          { id: "tab5_2_2", content: "选项5.2.2", parentId: "tab5_2" },
          { id: "tab5_2_3", content: "选项5.2.3", parentId: "tab5_2" },
        ],
      },
      { id: "tab5_3", content: "选项5.3", parentId: "tab5" },
      { id: "tab5_4", content: "选项5.4", parentId: "tab5" },
      { id: "tab5_5", content: "选项5.5", parentId: "tab5" },
      { id: "tab5_6", content: "选项5.6", parentId: "tab5" },
      { id: "tab5_7", content: "选项5.7", parentId: "tab5" },
      { id: "tab5_8", content: "选项5.8", parentId: "tab5" },
      { id: "tab5_9", content: "选项5.9", parentId: "tab5" },
      { id: "tab5_10", content: "选项5.10", parentId: "tab5" },
      { id: "tab5_11", content: "选项5.11", parentId: "tab5" },
      { id: "tab5_12", content: "选项5.12", parentId: "tab5" },
    ],
  },
  { id: "tab6", content: "选项6字段很长很长很长很长很长很长的时候" },
  { id: "tab7", content: "选项7" },
  { id: "tab8", content: "选项8" },
  { id: "tab9", content: "选项9" },
  { id: "tab10", content: "选项10" },
  {
    id: "tab11",
    content: "选项11",
    children: [
      { id: "tab11_1", content: "选项11.1", parentId: "tab11" },
      { id: "tab11_2", content: "选项11.2", parentId: "tab11" },
      { id: "tab11_3", content: "选项11.3", parentId: "tab11" },
      { id: "tab11_4", content: "选项11.4", parentId: "tab11" },
    ],
  },
  { id: "tab12", content: "选项12" },
  { id: "tab13", content: "选项13" },
];

export default class Demo extends React.PureComponent<{}, any> {
  constructor(props: any) {
    super(props);
    const dataSelectMore: any = [];
    dataMore.forEach((d: any) => {
      dataSelectMore.push(d);
      d?.children?.forEach((a: any) => {
        dataSelectMore.push(a);
      })
    })
    this.state = {
      data,
      value: 'tab1',
      valueMore: 'tab1',
      dataMore,
      dataSelectMore,
    }
  }
  onSelectChange = (value: SelectValueType) => {
    const result = [...this.state.data]?.map((da: { id: any; }) => {
      return {
        ...da,
        hide: value?.indexOf(da?.id) >= 0
      }
    })

    // 隐藏显示状态调整后value更新
    let newValue = this.state.value;
    const curHide = !result.some(i => i.id === value && i.hide);
    if (curHide) {
      newValue = result.filter(v => !v.hide)[0].id;
    }

    this.setState({ data: result, value: newValue });
  }
  onSelectMoreChange = (value: SelectValueType) => {
    const result = [...this.state.dataMore]?.map((da: any) => {
      return {
        ...da,
        hide: value?.indexOf(da?.id) >= 0,
        children: da?.children?.map((a: { id: any; }) => ({
          ...a,
          hide: value?.indexOf(a?.id) >= 0,
        }))
      }
    })

    // 隐藏显示状态调整后value更新
    let newValue = this.state.value;
    const curHide = !result.some(i => i.id === value && i.hide);
    if (curHide) {
      newValue = result.filter(v => !v.hide)[0].id;
    }

    this.setState({ dataMore: result, valueMore: newValue });
  }
  onChange = (value: string) => {
    this.setState({ value })
  }
  onChangeMore = (valueMore: string) => {
    this.setState({ valueMore })
  }

  render() {
    const { value, data, dataMore, valueMore, dataSelectMore } = this.state;
    return (
      <>
        <MSelect data={data} onChange={this.onSelectChange} multiple
          placeholder='请选择需要隐藏的页签'
        />
        <MMenu
          value={value}
          data={data}
          onChange={this.onChange}
        />
        <MMenu 
          value={value}
          data={data}
          onChange={this.onChange}
          showAll
          type="overflow"
        />
        <MMenu
          value={value}
          data={data}
          onChange={this.onChange}
          showAll
          infinite
        />
        <br/>
        <MSelect
          data={dataSelectMore} onChange={this.onSelectMoreChange} multiple
          placeholder='请选择需要隐藏的页签'
        />
        <MMenu 
          value={valueMore}
          data={dataMore}
          onChange={this.onChangeMore}
          showParentCanBeSelectedIcon
        />
      </>
    );
  }
}
```

### Menu菜单-基础用法

设置type='menu'

```jsx
import React from 'react';
import { MMenu, MButton } from '@weapp/ui';

const data = [
  { 
    id: 'tab1',
    content: '可选标签',
    icon: 'Icon-time02',
    children: [
      { id: 'tab1_1', content: '子菜单', icon: 'Icon-time02', parentId: 'tab2' },
      { id: 'tab1_2', content: '子菜单', icon: 'Icon-time02', parentId: 'tab2', children: [
        { id: 'tab1_2_1', content: '二级子菜单', parentId: 'tab1_2' },
        { id: 'tab1_2_2', content: '二级子菜单', parentId: 'tab1_2' },
        { id: 'tab1_2_3', content: '二级子菜单', parentId: 'tab1_2' },
      ]},
      { id: 'tab1_3', content: '子菜单', icon: 'Icon-time02', parentId: 'tab2' }
    ]
  },
  {
    id: 'tab2',
    content: '可选标签文本超长折行效果演示',
    icon: 'Icon-time02',
    children: [
      { id: 'tab2_1', content: '子菜单文本超长折行效果演示', icon: 'Icon-time02', parentId: 'tab2' },
      { id: 'tab2_2', content: '子菜单文本超长折行效果演示', icon: 'Icon-time02', parentId: 'tab2', children: [
        { id: 'tab2_2_1', content: '二级子菜单文本超长折行效果演示', parentId: 'tab2_2' },
        { id: 'tab2_2_2', content: '二级子菜单文本超长折行效果演示', parentId: 'tab2_2' },
        { id: 'tab2_2_3', content: '二级子菜单文本超长折行效果演示', parentId: 'tab2_2' },
      ]},
      { id: 'tab2_3', content: '子菜单文本超长折行效果演示', icon: 'Icon-time02', parentId: 'tab2' }
    ]
  },
  {
    id: 'tab3',
    icon: 'Icon-time02',
    content: '可选标签文本超长折行效果演示' 
  },
];

export default class Demo extends React.PureComponent<{}> {

  onChange = (value: string, item: object, e: React.MouseEvent) => {
    console.log('onChange', value, item, e);
  }

  onItemClick = (value: string, item: object, e: React.MouseEvent) => {
    console.log('onItemClick', value, item, e);
  }

  onExpandChange = (expandKeys: string[], id?: string) => {
    console.log('onExpandChange', expandKeys, 'id', id);
  }

  render() {
    return(
      <MMenu
        defaultValue={'tab2_2_2'}
        data={data}
        type="menu"
        // panelOverflowType='ellipsis' // 默认为break
        extraContent={<MButton>Extra Content</MButton>}
        onChange={this.onChange}
        onItemClick={this.onItemClick}
        onExpandChange={this.onExpandChange}
      />
    );
  }
}
```

### Menu菜单-自定义渲染

设置customRender属性,可自定义选中的text

```jsx
import React from 'react'
import { MMenu, MButton } from '@weapp/ui'

const data = [
  { id: 'tab1', content: '可选标签', icon: 'Icon-folder' },
  {
    id: 'tab2',
    content: '可选标签',
    icon: 'Icon-folder',
    children: [
      { id: 'childTab1', content: '子菜单1', parentId: 'tab2' },
      {
        id: 'childTab2',
        content: '子菜单2',
        parentId: 'tab2',
        children: [
          {
            id: 'secondchildTab1',
            content: '二级子菜单1',
            parentId: 'childTab2',
          },
          {
            id: 'secondchildTab2',
            content: '二级子菜单2',
            parentId: 'childTab2',
          },
          {
            id: 'secondchildTab3',
            content: '二级子菜单3',
            parentId: 'childTab2',
          },
        ],
      },
      { id: 'childTab3', content: '子菜单2', parentId: 'tab2' },
    ],
  },
  { id: 'tab3', content: '可选标签3很长很长很长很长很长', icon: 'Icon-folder' },
]

export default class Demo extends React.PureComponent<{}> {
  onChange = (value: string, item: object, e: React.MouseEvent) => {
    console.log('onChange', value, item, e)
  }

  onItemClick = (value: string, item: object, e: React.MouseEvent) => {
    console.log('onItemClick', value, item, e)
  }

  onExpandChange = (expandKeys: string[], id?: string) => {
    console.log('onExpandChange', expandKeys, 'id', id)
  }

  render() {
    return (
      <MMenu
       
        defaultValue={'tab1'}
        data={data}
        type='menu'
        onChange={this.onChange}
        onItemClick={this.onItemClick}
        onExpandChange={this.onExpandChange}
        customMenuText={<>自定义选中内容</>}
      />
    )
  }
}
```

### Navigation导航菜单-基础用法

```jsx
import React from 'react';
import { MMenu } from '@weapp/ui';

const data = [
  { id: 'tab1', content: '导航1', icon: 'Icon-application' },
  { id: 'tab2', content: '导航2', icon: 'Icon-application' },
  { id: 'tab3', content: '导航3', icon: 'Icon-application' },
];

export default class Demo extends React.PureComponent<{}> {

  onChange = (value: string) => {
    console.log('value', value);
  }

  render() {
    return (
      <MMenu
        defaultValue={'tab1'}
        data={data}
        type="navigation"
        onChange={this.onChange}
      />
    );
  }
}
```

### Navigation导航菜单-横向排列

type='navigation',设置mode="horizontal"可改变底部导航icon文字排列方向

```jsx
import React from 'react';
import { MMenu, MButton } from '@weapp/ui';

const data = [
    { id: 'tab1', content: '导航1', icon: 'Icon-application' },
    { id: 'tab2', content: '导航2', icon: 'Icon-application' },
    { id: 'tab3', content: '导航3', icon: 'Icon-application' },
  ];

export default class Demo extends React.PureComponent<{}> {

  onChange = (value: string) => {
    console.log('value', value);
  }

  render() {
    return(
      <MMenu
        defaultValue={'tab1'}
        data={data}
        type="navigation"
        mode="horizontal"
        extraContent={<MButton type="primary">提交</MButton>}
        onChange={this.onChange}
      />
    );
  }
}
```

### Navigation导航菜单-自定义渲染

设置customRenderItem属性,自定义渲染底部导航效果

```jsx
import React from 'react';
import { MMenu, MPopover, MMenuItemData } from '@weapp/ui';

const data = [
  { id: 'tab1', content: '导航1', icon: 'Icon-application' },
  { id: 'tab2', content: '导航2', icon: 'Icon-application' },
  { id: 'tab3', content: '导航3', icon: 'Icon-application' },
  { id: 'tab4', content: '更多', icon: 'Icon-application' },
];

const menuStyle: React.CSSProperties = {
  position: 'absolute',
  bottom: 0,
  width: '100%'
}

export default class Demo extends React.PureComponent<{}> {

  state = {
    value:"tab1"
  }

  onChange = (value: string) => {
    console.log('value', value);
    this.setState({value});
  }
  
  onVisibleChange=(visible:boolean|undefined)=>{
    console.log(visible);
    if(visible===false){
      this.setState({value:""})
    }
  }

  customRenderItem = (_this: any, ele: React.ReactNode, itemData: MMenuItemData) => {
    const id = itemData.id;
    if (id === 'tab4') {
      const popup = (
        <div>test</div>
      );
      return (
        <MPopover
          placement="top"
          popup={popup}
          action="click"
          mask
          maskClosable
          // triggerProps = {{ forceRender: true }} // 注意: popup内容为异步加载组件的情况使用,防止trigger的初次位置不正确
          onVisibleChange={this.onVisibleChange}
        >
          {ele}
        </MPopover>
      )
    }
    return ele;
  }

  render() {
    const {value} = this.state;
    return (
      <MMenu
        value={value}
        data={data}
        type="navigation"
        onChange={this.onChange}
        customRenderItem={this.customRenderItem}
        style={menuStyle}
      />
    );
  }
}
```

## API

### 参数说明如下：

| 参数 | 说明 | 类型 | 是否必填 | 默认值 |  |
| --- | --- | --- | --- | --- | --- |
| className | class 名 | string | 否 | - |  |
| preficCls | 默认组件 class 名 | string | 否 | - |  |
| style | 样式 | CSSProperties | 否 | - |  |
| customRender | 自定义组件渲染 | (props: any, ele: ReactElement) => ReactElement | 否 | - |  |
| type | 显示类型 | 'tab' \| 'menu' \| 'navigation' | 否 | 'tab' |  |
| value | 当前选中值 | string | 否 | '' |  |
| defaultValue | 默认选中值 | string | 否 | '' |  |
| expandKeys | 展开子菜单的 id 组成的数组 | string[] | 否 | '' |  |
| defaultExpandKeys | 默认展开子菜单的 id 组成的数组 | string[] | 否 | - |  |
| data | 数据 | MMenuItemData[] | 是 | [] |  |
| mode | 内部 item 的布局（垂直 \| 水平), type 为 navigation 或者 floating为true的tab 使用 | 'vertical' \| 'horizontal' | 否 | 'vertical' |  |
| extraContent | 自定义额外内容 | React.ReactElement | 否 | '' |  |
| hideActiveLine | 是否隐藏选中效果底部蓝色边线 | boolean | 否 | false |  |
| maskVisible | 蒙层显示隐藏受控 | boolean | 否 | - |  |
| panelOverflowType | 面板内子菜单超长显示效果（换行 \| ...) | `break` \| `ellipsis` | 否 | `break` |  |
| sortable | 是否可排序(tab效果使用) | boolean | 否 | false |  |
| showSortableIcon | 是否显示拖拽图标(tab效果使用) | boolean | 否 | false |  |
| sortableIcon | 自定义拖拽图标 (注意: 如果使用自定义图标, 需要同步配置sortableOptions中的handle属性, tab效果使用) | React.ReactNode | 否 | - |  |
| sortableOptions | 排序参数(tab效果使用) | object | 否 | {} |  |
| floating | 是否为浮动布局(tab效果使用,默认会根据菜单自动均分, 仅浮动布局支持编辑和删除功能) | boolean | 否 | false |  |
| infinite | 是否为无限极(tab效果使用,无限极面板展开的菜单始终为当前选中最近的两级) | boolean | 否 | false |  |
| forceChange | 连续点击同一个菜单，是否触发onChange | boolean | 否 | true |  |
| onChange | 选中值改变的回调 | (value: string, item: MMenuItemData, e: React.MouseEvent) => void | 否 | - |  |
| onBeforeChange | 选中值改变前的回调 | (value: string, item: MMenuItemData, e: React.MouseEvent) => Promise<boolean \| void> | 否 | - |  |
| onItemClick | 子菜单的点击回调 | (id: string, item: MMenuItemData, e: React.MouseEvent) => viod | 否 | - |  |
| onExpandChange | 多级菜单展开收起等的回调 | (newExpandKeys: string[], key?: string) => void | 否 | - |  |
| onBeforeExpandChange | 多级菜单展开收起前的回调 | (newExpandKeys: string[], key?: string) => Promise<boolean \| void> | 否 | - |  |
| onMaskVisibleChange | 蒙层显示隐藏的回调 | (visible: boolean) => void | 否 | - |  |
| onDrag | 拖拽更改菜单顺序后的回调(tab效果使用) | (newData: MenuItemData[], evt: any) => void | sortable为true时需要 |  |  |
| onEdit | 点击编辑按钮的回调 | (itemData: MenuItemData, event: React.MouseEvent) => void | 否 | - |  |
| customMenuText | type=menu 时 自定义渲染选中的 text | React.ReactNode \| (data: MenuItemData[], value: string) => React.ReactNode | 否 | - |  |
| customRenderItemContent | 自定义渲染每一项 item 的文本内容,第二个content为组件内部转换后的content(兼容tab带下级选中更换为下级菜单内容的场景) | (itemData: MenuItemData,  _content?: string | React.ReactNode) => React.ReactNode | 否 | - |
| customRenderItem | 自定义每一项的内容 | (this: any, itemNode: React.ReactNode, itemData: MenuItemData) => ReactElement | 否 | - |  |
| maskStyle | 自定义遮罩层样式(type=menu的时候生效) | React.CSSProperties | 否 | - |  |
| contentStyle | 自定义ui-m-menu-content的样式(type=menu的时候生效) | React.CSSProperties | 否 | - |  |
| showParentCanBeSelectedIcon | 父级可选时如果父级有icon时展示icon信息 | boolean | 否 | - |  |
#### MMenuItemData 参数说明如下：

| 参数 | 说明 | 类型 | 是否必填 | 默认值 |
| --- | --- | --- | --- | --- |
| id | 唯一 | string | 是 | - |
| title | 标题 | string \| React.ReactNode | 是 | - |
| parentId | 对应父级的 id | string | 如果是子菜单则需要,父级菜单不用 | - |
| content | 显示内容 | string \| React.ReactNode | 否 | - |
| icon | 左侧图标 | string \| React.ReactNode | 否 | - |
| closeIcon | 自定义删除图标 | string \| React.ReactNode | 否 | - |
| closeTitle | 删除按钮鼠标浮动的的title | string | 否 | '删除' |
| closeable | 是否可删除 | boolean | 否 | false |
| editable | 是否可编辑 | boolean | 否 | false |
| editTitle | 编辑按钮鼠标浮动的的title | string | 否 | '编辑' |
| editIcon | 自定义编辑图标 | string \| React.ReactNode | 否 | - |
| disabled | 是否是禁用项 | boolean | 否 | false |
| children | 子数据 | MMenuItemData[] | 否 | - |
| canBeSelected | 用于控制父级菜单是否可选, 默认为 false | boolean | 否 | - |
| hide | 能否展示, 仅在type 为'tab'类型生效 (hide=true, 表示存在数据的情况,页签仍需隐藏) | boolean | 否 | - |
