Menu 菜单

在一个临时的面板上显示一组操作。

规则

  • 至少包含 2 个以上的菜单项。

  • 不应该被当做主要导航方式。

代码演示

菜单

  1. /* eslint no-nested-ternary:0 */
  2. import { Menu, ActivityIndicator, NavBar } from 'antd-mobile';
  3. const data = [
  4. {
  5. value: '1',
  6. label: 'Food',
  7. children: [
  8. {
  9. label: 'All Foods',
  10. value: '1',
  11. disabled: false,
  12. },
  13. {
  14. label: 'Chinese Food',
  15. value: '2',
  16. }, {
  17. label: 'Hot Pot',
  18. value: '3',
  19. }, {
  20. label: 'Buffet',
  21. value: '4',
  22. }, {
  23. label: 'Fast Food',
  24. value: '5',
  25. }, {
  26. label: 'Snack',
  27. value: '6',
  28. }, {
  29. label: 'Bread',
  30. value: '7',
  31. }, {
  32. label: 'Fruit',
  33. value: '8',
  34. }, {
  35. label: 'Noodle',
  36. value: '9',
  37. }, {
  38. label: 'Leisure Food',
  39. value: '10',
  40. }],
  41. }, {
  42. value: '2',
  43. label: 'Supermarket',
  44. children: [
  45. {
  46. label: 'All Supermarkets',
  47. value: '1',
  48. }, {
  49. label: 'Supermarket',
  50. value: '2',
  51. disabled: true,
  52. }, {
  53. label: 'C-Store',
  54. value: '3',
  55. }, {
  56. label: 'Personal Care',
  57. value: '4',
  58. }],
  59. },
  60. {
  61. value: '3',
  62. label: 'Extra',
  63. isLeaf: true,
  64. children: [
  65. {
  66. label: 'you can not see',
  67. value: '1',
  68. },
  69. ],
  70. },
  71. ];
  72. class MenuExample extends React.Component {
  73. constructor(...args) {
  74. super(...args);
  75. this.state = {
  76. initData: '',
  77. show: false,
  78. };
  79. }
  80. onChange = (value) => {
  81. let label = '';
  82. data.forEach((dataItem) => {
  83. if (dataItem.value === value[0]) {
  84. label = dataItem.label;
  85. if (dataItem.children && value[1]) {
  86. dataItem.children.forEach((cItem) => {
  87. if (cItem.value === value[1]) {
  88. label += ` ${cItem.label}`;
  89. }
  90. });
  91. }
  92. }
  93. });
  94. console.log(label);
  95. }
  96. handleClick = (e) => {
  97. e.preventDefault(); // Fix event propagation on Android
  98. this.setState({
  99. show: !this.state.show,
  100. });
  101. // mock for async data loading
  102. if (!this.state.initData) {
  103. setTimeout(() => {
  104. this.setState({
  105. initData: data,
  106. });
  107. }, 500);
  108. }
  109. }
  110. onMaskClick = () => {
  111. this.setState({
  112. show: false,
  113. });
  114. }
  115. render() {
  116. const { initData, show } = this.state;
  117. const menuEl = (
  118. <Menu
  119. className="foo-menu"
  120. data={initData}
  121. value={['1', '3']}
  122. onChange={this.onChange}
  123. height={document.documentElement.clientHeight * 0.6}
  124. />
  125. );
  126. const loadingEl = (
  127. <div style={{ width: '100%', height: document.documentElement.clientHeight * 0.6, display: 'flex', justifyContent: 'center' }}>
  128. <ActivityIndicator size="large" />
  129. </div>
  130. );
  131. return (
  132. <div className={show ? 'menu-active' : ''}>
  133. <div>
  134. <NavBar
  135. leftContent="Menu"
  136. mode="light"
  137. icon={<img src="https://gw.alipayobjects.com/zos/rmsportal/iXVHARNNlmdCGnwWxQPH.svg" className="am-icon am-icon-md" alt="" />}
  138. onLeftClick={this.handleClick}
  139. className="top-nav-bar"
  140. >
  141. Here is title
  142. </NavBar>
  143. </div>
  144. {show ? initData ? menuEl : loadingEl : null}
  145. {show ? <div className="menu-mask" onClick={this.onMaskClick} /> : null}
  146. </div>
  147. );
  148. }
  149. }
  150. ReactDOM.render(<MenuExample />, mountNode);
  1. .foo-menu {
  2. position: relative;
  3. z-index: 1000 !important;
  4. }
  5. .menu-active .top-nav-bar{
  6. z-index: 80;
  7. }
  8. .top-nav-bar {
  9. position: relative;
  10. background-color: #008AE6;
  11. color: #FFF;
  12. }
  13. .am-navbar-title {
  14. color: #FFF!important;
  15. }
  16. .menu-mask {
  17. content: ' ';
  18. position: absolute;
  19. top: 0;
  20. width: 100%;
  21. height: 100%;
  22. background-color: #000;
  23. opacity: 0.4;
  24. z-index: 79;
  25. }

单级菜单

  1. /* eslint global-require:0, no-nested-ternary:0 */
  2. import { Menu, ActivityIndicator, NavBar } from 'antd-mobile';
  3. const data = [
  4. {
  5. value: '1',
  6. label: 'Food',
  7. }, {
  8. value: '2',
  9. label: 'Supermarket',
  10. },
  11. {
  12. value: '3',
  13. label: 'Extra',
  14. isLeaf: true,
  15. },
  16. ];
  17. class MenuExample extends React.Component {
  18. constructor(...args) {
  19. super(...args);
  20. this.state = {
  21. initData: '',
  22. show: false,
  23. };
  24. }
  25. onChange = (value) => {
  26. let label = '';
  27. data.forEach((dataItem) => {
  28. if (dataItem.value === value[0]) {
  29. label = dataItem.label;
  30. if (dataItem.children && value[1]) {
  31. dataItem.children.forEach((cItem) => {
  32. if (cItem.value === value[1]) {
  33. label += ` ${cItem.label}`;
  34. }
  35. });
  36. }
  37. }
  38. });
  39. console.log(label);
  40. }
  41. handleClick = (e) => {
  42. e.preventDefault(); // Fix event propagation on Android
  43. this.setState({
  44. show: !this.state.show,
  45. });
  46. // mock for async data loading
  47. if (!this.state.initData) {
  48. setTimeout(() => {
  49. this.setState({
  50. initData: data,
  51. });
  52. }, 500);
  53. }
  54. }
  55. onMaskClick = () => {
  56. this.setState({
  57. show: false,
  58. });
  59. }
  60. render() {
  61. const { initData, show } = this.state;
  62. const menuEl = (
  63. <Menu
  64. className="single-foo-menu"
  65. data={initData}
  66. value={['1']}
  67. level={1}
  68. onChange={this.onChange}
  69. height={document.documentElement.clientHeight * 0.6}
  70. />
  71. );
  72. const loadingEl = (
  73. <div style={{ position: 'absolute', width: '100%', height: document.documentElement.clientHeight * 0.6, display: 'flex', justifyContent: 'center' }}>
  74. <ActivityIndicator size="large" />
  75. </div>
  76. );
  77. return (
  78. <div className={show ? 'single-menu-active' : ''}>
  79. <div>
  80. <NavBar
  81. leftContent="Menu"
  82. mode="light"
  83. onLeftClick={this.handleClick}
  84. className="single-top-nav-bar"
  85. >
  86. OneLevel menu
  87. </NavBar>
  88. </div>
  89. {show ? initData ? menuEl : loadingEl : null}
  90. {show ? <div className="menu-mask" onClick={this.onMaskClick} /> : null}
  91. </div>
  92. );
  93. }
  94. }
  95. ReactDOM.render(<MenuExample />, mountNode);
  1. .single-foo-menu {
  2. position: absolute;
  3. z-index: 90 !important;
  4. width: 100%;
  5. }
  6. .single-menu-active .single-top-nav-bar{
  7. z-index: 90;
  8. }
  9. .single-top-nav-bar {
  10. position: relative;
  11. background-color: #008AE6;
  12. color: #FFF;
  13. }
  14. .am-navbar-title {
  15. color: #FFF!important;
  16. }
  17. .menu-mask {
  18. position: absolute;
  19. top: 0;
  20. width: 100%;
  21. height: 100%;
  22. background-color: #000;
  23. opacity: 0.4;
  24. z-index: 89;
  25. }

菜单多选

  1. /* eslint global-require:0, no-nested-ternary:0 */
  2. import { Menu, ActivityIndicator, NavBar } from 'antd-mobile';
  3. const data = [
  4. {
  5. value: '1',
  6. label: 'Food',
  7. children: [
  8. {
  9. label: 'American Foods',
  10. value: '1',
  11. disabled: false,
  12. },
  13. {
  14. label: 'Chinese Food',
  15. value: '2',
  16. }, {
  17. label: 'Hot Pot',
  18. value: '3',
  19. }, {
  20. label: 'Buffet',
  21. value: '4',
  22. }, {
  23. label: 'Fast Food',
  24. value: '5',
  25. }, {
  26. label: 'Snack',
  27. value: '6',
  28. }, {
  29. label: 'Bread',
  30. value: '7',
  31. }, {
  32. label: 'Fruit',
  33. value: '8',
  34. }, {
  35. label: 'Noodle',
  36. value: '9',
  37. }, {
  38. label: 'Leisure Food',
  39. value: '10',
  40. }],
  41. }, {
  42. value: '2',
  43. label: 'Supermarket',
  44. children: [
  45. {
  46. label: 'All Supermarkets',
  47. value: '1',
  48. }, {
  49. label: 'Supermarket',
  50. value: '2',
  51. disabled: true,
  52. }, {
  53. label: 'C-Store',
  54. value: '3',
  55. }, {
  56. label: 'Personal Care',
  57. value: '4',
  58. }],
  59. },
  60. {
  61. value: '3',
  62. label: 'Extra',
  63. isLeaf: true,
  64. children: [
  65. {
  66. label: 'you can not see',
  67. value: '1',
  68. },
  69. ],
  70. },
  71. ];
  72. class MultiMenuExample extends React.Component {
  73. constructor(...args) {
  74. super(...args);
  75. this.state = {
  76. initData: '',
  77. show: false,
  78. };
  79. }
  80. onChange = (value) => {
  81. console.log(value);
  82. }
  83. onOk = (value) => {
  84. console.log(value);
  85. this.onCancel();
  86. }
  87. onCancel = () => {
  88. this.setState({ show: false });
  89. }
  90. handleClick = (e) => {
  91. e.preventDefault(); // Fix event propagation on Android
  92. this.setState({
  93. show: !this.state.show,
  94. });
  95. // mock for async data loading
  96. if (!this.state.initData) {
  97. setTimeout(() => {
  98. this.setState({
  99. initData: data,
  100. });
  101. }, 500);
  102. }
  103. }
  104. onMaskClick = () => {
  105. this.setState({
  106. show: false,
  107. });
  108. }
  109. render() {
  110. const { initData, show } = this.state;
  111. const menuEl = (
  112. <Menu
  113. className="multi-foo-menu"
  114. data={initData}
  115. value={['1', ['3', '4']]}
  116. onChange={this.onChange}
  117. onOk={this.onOk}
  118. onCancel={this.onCancel}
  119. height={document.documentElement.clientHeight * 0.6}
  120. multiSelect
  121. />
  122. );
  123. const loadingEl = (
  124. <div style={{ position: 'absolute', width: '100%', height: document.documentElement.clientHeight * 0.6, display: 'flex', justifyContent: 'center' }}>
  125. <ActivityIndicator size="large" />
  126. </div>
  127. );
  128. return (
  129. <div className={show ? 'multi-menu-active' : ''}>
  130. <div>
  131. <NavBar
  132. leftContent="Menu"
  133. mode="light"
  134. onLeftClick={this.handleClick}
  135. className="multi-top-nav-bar"
  136. >
  137. Multi select menu
  138. </NavBar>
  139. </div>
  140. {show ? initData ? menuEl : loadingEl : null}
  141. {show ? <div className="menu-mask" onClick={this.onMaskClick} /> : null}
  142. </div>
  143. );
  144. }
  145. }
  146. ReactDOM.render(<MultiMenuExample />, mountNode);
  1. .multi-foo-menu {
  2. position: absolute;
  3. z-index: 80 !important;
  4. width: 100%;
  5. }
  6. .multi-menu-active .multi-top-nav-bar{
  7. z-index: 80;
  8. }
  9. .multi-top-nav-bar {
  10. position: relative;
  11. background-color: #008AE6;
  12. color: #FFF;
  13. }
  14. .am-navbar-title {
  15. color: #FFF!important;
  16. }
  17. .menu-mask {
  18. content: ' ';
  19. position: absolute;
  20. top: 0;
  21. width: 100%;
  22. height: 100%;
  23. background-color: #000;
  24. opacity: 0.4;
  25. z-index: 79;
  26. }

单级菜单多选

  1. /* eslint global-require:0, no-nested-ternary:0 */
  2. import { Menu, ActivityIndicator, NavBar } from 'antd-mobile';
  3. const data = [
  4. {
  5. value: '1',
  6. label: 'Food',
  7. }, {
  8. value: '2',
  9. label: 'Supermarket',
  10. },
  11. {
  12. value: '3',
  13. label: 'Extra',
  14. isLeaf: true,
  15. },
  16. ];
  17. class MultiMenuExample extends React.Component {
  18. constructor(...args) {
  19. super(...args);
  20. this.state = {
  21. initData: '',
  22. show: false,
  23. };
  24. }
  25. onChange = (value) => {
  26. console.log(value);
  27. }
  28. onOk = (value) => {
  29. console.log(value);
  30. this.onCancel();
  31. }
  32. onCancel = () => {
  33. this.setState({ show: false });
  34. }
  35. handleClick = (e) => {
  36. e.preventDefault(); // Fix event propagation on Android
  37. this.setState({
  38. show: !this.state.show,
  39. });
  40. // mock for async data loading
  41. if (!this.state.initData) {
  42. setTimeout(() => {
  43. this.setState({
  44. initData: data,
  45. });
  46. }, 500);
  47. }
  48. }
  49. onMaskClick = () => {
  50. this.setState({
  51. show: false,
  52. });
  53. }
  54. render() {
  55. const { initData, show } = this.state;
  56. const menuEl = (
  57. <Menu
  58. className="single-multi-foo-menu"
  59. data={initData}
  60. value={['1']}
  61. level={1}
  62. onChange={this.onChange}
  63. onOk={this.onOk}
  64. onCancel={this.onCancel}
  65. height={document.documentElement.clientHeight * 0.6}
  66. multiSelect
  67. />
  68. );
  69. const loadingEl = (
  70. <div style={{ position: 'absolute', width: '100%', height: document.documentElement.clientHeight * 0.6, display: 'flex', justifyContent: 'center' }}>
  71. <ActivityIndicator size="large" />
  72. </div>
  73. );
  74. return (
  75. <div className={show ? 'single-multi-menu-active' : ''}>
  76. <div>
  77. <NavBar
  78. leftContent="Menu"
  79. mode="light"
  80. onLeftClick={this.handleClick}
  81. className="single-multi-top-nav-bar"
  82. >
  83. Single Multi menu
  84. </NavBar>
  85. </div>
  86. {show ? initData ? menuEl : loadingEl : null}
  87. {show ? <div className="menu-mask" onClick={this.onMaskClick} /> : null}
  88. </div>
  89. );
  90. }
  91. }
  92. ReactDOM.render(<MultiMenuExample />, mountNode);
  1. .single-multi-foo-menu {
  2. position: absolute;
  3. z-index: 70 !important;
  4. width: 100%;
  5. }
  6. .single-multi-menu-active .single-multi-top-nav-bar{
  7. z-index: 80;
  8. }
  9. .single-multi-top-nav-bar {
  10. position: relative;
  11. background-color: #008AE6;
  12. color: #FFF;
  13. }
  14. .am-navbar-title {
  15. color: #FFF!important;
  16. }
  17. .menu-mask {
  18. content: ' ';
  19. position: absolute;
  20. top: 0;
  21. width: 100%;
  22. height: 100%;
  23. background-color: #000;
  24. opacity: 0.4;
  25. z-index: 69;
  26. }

Menu菜单 - 图1

API

属性说明类型默认值
data数据(isLeaf 设置后 children 无效)Array<{label: ReactNode, value, disabled?, children<data>?, isLeaf?}>[]
level菜单级数,可选1/2number2
value初始值,一级和二级筛选数据的value组成的数组。在多选状态下,如果为二级菜单,则数组的第一个元素为一级菜单的选项,数组的第二个元素是一个数组,里面包含了二级菜单的多选项;如果为一级菜单,则数组所有元素都是多选项Array
onChange选择后的回调函数(item: Object): void
onOk多选状态下确认按钮回调(value: Object): void
onCancel多选状态下取消按钮回调(): void
height筛选组件的高度numberdocument.documentElement.clientHeight / 2
multiSelect是否支持菜单多选booleanfalse