Browse Source

feat: add menu (#731)

feat: add menu (#731)
yangjinjun3 4 years ago
parent
commit
57834e66de

+ 4 - 3
src/config.json

@@ -579,9 +579,10 @@
           "sort": 2,
           "cName": "菜单组件",
           "type": "component",
-          "show": false,
+          "taro":true,
+          "show": true,
           "desc": "下拉菜单组件",
-          "author": "vickyYE"
+          "author": "yangjinjun3"
         },
         {
           "version": "3.0.0",
@@ -593,7 +594,7 @@
           "exportEmpty": true,
           "show": false,
           "desc": "下拉菜单组件",
-          "author": "vickyYE"
+          "author": "yangjinjun3"
         },
         {
           "version": "3.0.0",

+ 54 - 122
src/packages/__VUE/menu/demo.vue

@@ -1,84 +1,46 @@
 <template>
-  <div class="demo full menu-demo">
+  <div class="demo">
     <h2>基础用法</h2>
     <nut-menu>
-      <nut-menu-item :menu-list="menuList" title="最新商品"></nut-menu-item>
-      <nut-menu-item :menu-list="menuList" :title="title"></nut-menu-item>
+      <nut-menu-item title="全部商品" :options="options1"></nut-menu-item>
     </nut-menu>
-
-    <h2>多列展示</h2>
+    <h2>两列标题</h2>
     <nut-menu>
-      <nut-menu-item
-        class="base-style"
-        :menu-list="menuList2"
-        title="单列展示"
-        multi-style="1"
-        max-height="200"
-      ></nut-menu-item>
-      <nut-menu-item
-        :menu-list="menuList2"
-        title="双列展示"
-        multi-style="2"
-      ></nut-menu-item>
-      <nut-menu-item
-        :menu-list="menuList2"
-        title="三列展示"
-        multi-style="3"
-      ></nut-menu-item>
+      <nut-menu-item title="全部商品" :options="options1"></nut-menu-item>
+      <nut-menu-item title="默认排序" :options="options2"></nut-menu-item>
     </nut-menu>
-
-    <h2>禁用操作</h2>
-    <p class="tips"
-      >`disabled` 属性可对菜单列表进行禁用操作。`autoClose`
-      属性控制下拉菜单列表是否选择后自动收起,默认自动收起。</p
-    >
-    <nut-menu>
-      <nut-menu-item :menu-list="menuList" title="最新商品"></nut-menu-item>
-      <nut-menu-item
-        :menu-list="menuList"
-        title="禁止自动收起"
-        :auto-close="false"
-      ></nut-menu-item>
-      <nut-menu-item
-        :menu-list="menuList2"
-        title="筛选"
-        disabled
-      ></nut-menu-item>
+    <h2>获取选择的列表对象</h2>
+    <nut-menu @choose="handleChoose">
+      <nut-menu-item title="全部商品" :options="options1"></nut-menu-item>
     </nut-menu>
-
-    <h2>禁止蒙层展示</h2>
-    <p class="tips">属性`hasMask`控制是否有蒙层,默认为 `true`展示蒙层 </p>
-    <nut-menu :has-mask="false">
-      <nut-menu-item :menu-list="menuList" title="最新商品"></nut-menu-item>
-      <nut-menu-item :menu-list="menuList" :title="title"></nut-menu-item>
+    <h2>一行两列列表对象</h2>
+    <nut-menu col="2">
+      <nut-menu-item title="全部商品" :options="options1"></nut-menu-item>
     </nut-menu>
-
-    <h2>点击事件</h2>
-    <p class="tips">标题点击事件`menu-click`,菜单列表选择点击事件`change`</p>
+    <h2>禁用菜单</h2>
     <nut-menu>
       <nut-menu-item
-        :menu-list="menuList"
-        title="选择菜单列表项"
-        multi-style="2"
-        @menu-click="alertText($event, 'title')"
-        @change="getChecked"
-      ></nut-menu-item>
-      <nut-menu-item
-        :menu-list="menuList2"
-        title="选中标题触发"
-        disabled
-        @menu-click="alertText"
+        title="全部商品"
+        disabled="true"
+        :options="options1"
       ></nut-menu-item>
     </nut-menu>
-
-    <h2>自定义内容</h2>
+    <h2>自定义选项的选中态图标颜色</h2>
+    <nut-menu active-color="#0f0">
+      <nut-menu-item title="全部商品" :options="options1"></nut-menu-item>
+    </nut-menu>
+    <h2>自定义菜单内容</h2>
     <nut-menu>
-      <nut-menu-item title="自定义选项">
-        <div class="user-style">
-          <nut-cell title="我是标题" desc="描述文字"> </nut-cell>
-          <nut-cell>
-            <nut-button size="large" type="primary">确认提交</nut-button>
-          </nut-cell>
+      <nut-menu-item title="筛选" ref="item">
+        <div
+          :style="{
+            display: 'flex',
+            'justify-content': 'space-between',
+            'align-items': 'center'
+          }"
+        >
+          <div :style="{ 'font-size': '12px' }">我是自定义内容</div>
+          <nut-button @click="handleClick">关闭</nut-button>
         </div>
       </nut-menu-item>
     </nut-menu>
@@ -92,67 +54,37 @@ const { createDemo } = createComponent('menu');
 export default createDemo({
   props: {},
   setup() {
-    // const title1 = ref('热门推荐');
-    const resData = reactive({
-      title: '热门推荐',
-      menuList: [
-        { value: '手机' },
-        { value: '电脑' },
-        { value: '家用电器' },
-        { value: '日用百货' }
-      ],
-      menuList2: [
-        { value: '热门推荐', id: 111 },
-        { value: '手机数码', id: 112 },
-        { value: '电脑办公', id: 113 },
-        { value: '美妆护肤', id: 114 },
-        { value: '个护清洁', id: 115 },
-        { value: '汽车生活', id: 116 },
-        { value: '京东超市', id: 117 },
-        { value: '母婴童装', id: 118 }
-      ]
-    });
+    const options1 = [
+      { text: '全部商品', value: 0 },
+      { text: '新款商品', value: 1 },
+      { text: '活动商品', value: 2 }
+    ];
+
+    const options2 = [
+      { text: '默认排序', value: 'a' },
+      { text: '好评排序', value: 'b' },
+      { text: '销量排序', value: 'c' }
+    ];
+
+    const item = ref<HTMLElement>();
 
-    const getChecked = (info: any, name: string) => {
-      alert('选择菜单选项:' + name);
-      console.log(11, info, name);
+    const handleChoose = (val, index) => {
+      console.log(val, index);
     };
-    const alertText = (info, type) => {
-      console.log(info, type);
-      if (type == 'title') {
-        alert('菜单标题点击:' + info);
-      } else {
-        alert('禁用操作');
-      }
+
+    const handleClick = () => {
+      item.value.toggle();
     };
 
     return {
-      ...toRefs(resData),
-      getChecked,
-      alertText
+      options1,
+      options2,
+      item,
+      handleChoose,
+      handleClick
     };
   }
 });
 </script>
 
-<style lang="scss" scoped>
-.tips {
-  font-size: 12px;
-  color: #909ca4;
-  margin-top: -10px;
-  margin-bottom: 10px;
-  padding: 0 22px;
-}
-.base-style.nut-menu-item {
-  .nut-menu-panel {
-    max-height: 300px !important;
-    overflow: auto;
-  }
-}
-.user-style {
-  padding: 20px;
-}
-#app .demo.menu-demo {
-  padding-bottom: 200px;
-}
-</style>
+<style lang="scss" scoped></style>

+ 69 - 89
src/packages/__VUE/menu/doc.md

@@ -2,7 +2,7 @@
 
 ### 介绍
 
-下拉选择菜单组件
+向下弹出的菜单列表
 
 ### 安装
 
@@ -20,140 +20,120 @@ app.use(MenuItem);
 
 ## 代码演示
 
-### 基础用法1
-
-`Menu`  属性支持传入列表数据menuList和title名称设置。
+### 基础用法
 
 ```html
 <nut-menu>
-  <nut-menu-item :menu-list="menuList" title="最新商品" ></nut-menu-item>
-  <nut-menu-item :menu-list="menuList" :title="title" ></nut-menu-item>
+  <nut-menu-item title="全部商品" :options="options1"></nut-menu-item>
 </nut-menu>
 ```
 ```js
  setup() {
-    const resData = reactive({
-      title: '热门推荐',
-      menuList: [
-        {value: '手机'},
-        {value: '电脑'},
-        {value: '家用电器'},
-        {value: '日用百货'}
-      ]
-    });
+  const options1 = [
+      { text: '全部商品', value: 0 },
+      { text: '新款商品', value: 1 },
+      { text: '活动商品', value: 2 },
+  ]
+
+  const options2 = [
+    { text: '默认排序', value: 'a' },
+    { text: '好评排序', value: 'b' },
+    { text: '销量排序', value: 'c' },
+  ]
  }
-
 ```
 
-### 菜单多列展示
-
-`Menu` 的 ` multiStyle` 属性配置1列、2列、3列展示菜单列表,默认单列展示。
-`maxHeight` 属性可控制菜单列表的最大高度。
+### 两列标题
 
 ```html
 <nut-menu>
-    <nut-menu-item :menu-list="menuList2" title="单列展示" multi-style="1" maxHeight="200"></nut-menu-item>
-    <nut-menu-item :menu-list="menuList2" title="双列展示"  multi-style="2"></nut-menu-item>
-    <nut-menu-item :menu-list="menuList2" title="三列展示"  multi-style="3"></nut-menu-item>
+  <nut-menu-item title="全部商品" :options="options1"></nut-menu-item>
+  <nut-menu-item title="默认排序" :options="options2"></nut-menu-item>
 </nut-menu>
 ```
 
-### 禁用操作
-
-`Menu` 的 `disabled` 属性可对菜单列表进行禁用操作。
-`autoClose` 属性控制下拉菜单列表是否选择后自动收起,默认自动收起。
+### 获取选择的列表对象
 
 ```html
-<nut-menu>
-    <nut-menu-item :menu-list="menuList" title="最新商品"></nut-menu-item>
-    <nut-menu-item :menu-list="menuList" title="title" :auto-close="false"></nut-menu-item>
-    <nut-menu-item :menu-list="menuList2" title="筛选" disabled ></nut-menu-item>
+<nut-menu @choose="handleChoose">
+  <nut-menu-item title="全部商品" :options="options1"></nut-menu-item>
 </nut-menu>
 ```
+```js
+ setup() {
+  const handleChoose = (val, index) => {
+    console.log(val, index)
+  }
+ }
+```
 
-### 禁止蒙层展示
-属性`hasMask`控制是否有蒙层,默认为 `true`展示蒙层 
+### 一行两列列表对象
 
 ```html
-<nut-menu :hasMask="false">
-    <nut-menu-item :menu-list="menuList" title="最新商品">
-    </nut-menu-item>
-    <nut-menu-item :menu-list="menuList" :title="title">
-    </nut-menu-item>
+<nut-menu col="2">
+  <nut-menu-item title="全部商品" :options="options1"></nut-menu-item>
 </nut-menu>
 ```
 
-### 点击事件
-
-`Menu` 的 `@menu-click` 事件返回点击的菜单标题,`@change`事件返回菜单列表选中的数据。
+### 禁用菜单
 
 ```html
 <nut-menu>
-    <nut-menu-item
-        :menu-list="menuList2"
-        title="选择菜单列表项"
-        multi-style="2"
-        @menu-click="alertText($event, 'title')"
-        @change="getChecked"
-    ></nut-menu-item>
-    <nut-menu-item
-        :menu-list="menuList2"
-        title="选中标题触发"
-        disabled
-        @menu-click="alertText"
-    ></nut-menu-item>
- </nut-menu>
-```
-```js
-const getChecked = (info: any, name: string) => {
-    alert('选择菜单选项:' + name);
-    console.log(11, info, name);
-};
-const alertText = (info, type) => {
-    console.log(info, type);
-    if (type == 'title') {
-        alert('菜单标题点击:' + info);
-    } else {
-        alert('禁用操作');
-    }
-};
+  <nut-menu-item title="全部商品" disabled="true" :options="options1"></nut-menu-item>
+</nut-menu>
 ```
 
-### 自定义内容
+### 自定义选项的选中态图标颜色
 
+```html
+<nut-menu active-color="green">
+  <nut-menu-item title="全部商品" :options="options1"></nut-menu-item>
+</nut-menu>
+```
+
+### 自定义菜单内容
 
 ```html
 <nut-menu>
-    <nut-menu-item title="自定义选项">
-        <div class="user-style">
-          <nut-cell>
-            设置为默认 <nut-switch></nut-switch>
-          </nut-cell>
-          <nut-cell>
-            <nut-button size="large" type="primary">确认提交</nut-button>
-          </nut-cell>
-        </div>
-    </nut-menu-item>
+  <nut-menu-item title="筛选" ref="item">
+    <div class="customer-item-demo">
+      <div :style="{'display': 'flex','justify-content': 'space-between', 'align-items': 'center'}">
+      <nut-button @click="handleClick">关闭</nut-button>
+    </div>
+  </nut-menu-item>
 </nut-menu>
 ```
+```js
+ setup() {
+  const item = ref<HTMLElement>()
+
+  const handleClick = () => {
+    item.value.toggle()
+  }
+ }
+```
 
 ## API
 
 ### Props
 
+### nut-menu
+
+| 参数         | 说明                             | 类型   | 默认值           |
+|--------------|----------------------------------|--------|------------------|
+| col         | 显示的列数     | String/Number | 1                |
+| active-color         | 选项的选中态图标颜色     | String | #f00               |
+
+### nut-menu-item
+
 | 参数         | 说明                             | 类型   | 默认值           |
 |--------------|----------------------------------|--------|------------------|
-| title         | 菜单标题名称或可为菜单列表第一项,必填     | String | -                |
-| menu-list        | 菜单列表数据,必填                     | Array | -                |
-| multi-style        | 列表列数设置,默认1列,可选值 `1` `2` `3` | String, Number | 1                |
-| disabled | 是否开启禁用设置,默认不开启    | Boolean | false |
-| max-height | 菜单列表最大高度,单位px    | String, Number | - |
-| auto-close | 选择后下拉菜单列表是否自动收起,默认自动收起   | Boolean | true |
-|has-mask| 是否有蒙层 | Boolean | true|
+| title         | 标题     | String | -                |
+| options         | 列表对象     | Array | -                |
+| disabled         | 是否禁用菜单     | Boolean | false                |
 
 ### Events
 
 | 事件名 | 说明           | 回调参数     |
 |--------|----------------|--------------|
-| menu-click  | 点击菜单标题触发,返回菜单标题名称 | event: Event |
-| change  | 点击菜单列表选项触发,返回选中菜单项数据、名称 | event: Event |
+| choose  | 单选下,选择之后触发 | 1.选择的列表对象。2.列表索引 |

+ 66 - 4
src/packages/__VUE/menu/index.scss

@@ -1,7 +1,69 @@
 .nut-menu {
-  background: $white;
-  display: flex;
-  align-items: center;
   position: relative;
-  height: 46px;
+  font-size: 14px;
+  color: #1a1a1a;
+
+  .title-list {
+    display: flex;
+    line-height: 46px;
+    align-items: center;
+    background-color: #fff;
+    border-bottom: 1px solid #eaf0fb;
+
+    .title {
+      flex: 1;
+      text-align: center;
+
+      &.is-active {
+        font-weight: 600;
+      }
+
+      &.disabled {
+        color: #999;
+      }
+
+      [class*='nut-icon-arrow'] {
+        vertical-align: middle;
+        margin-right: 2px;
+      }
+    }
+  }
+
+  %itemCommon {
+    position: absolute;
+    left: 0;
+    width: 100%;
+    padding-top: 12px;
+    padding-bottom: 12px;
+    border-radius: 0 0 12px 12px;
+    background-color: #fff;
+  }
+
+  .option-list {
+    @extend %itemCommon;
+    max-height: 214px;
+    overflow-y: auto;
+
+    ul {
+      display: flex;
+      flex-wrap: wrap;
+    }
+
+    li {
+      line-height: 38px;
+      padding-left: 24px;
+
+      .nut-icon-Check {
+        vertical-align: middle;
+        margin-right: 6px;
+      }
+    }
+  }
+
+  .customer-item {
+    @extend %itemCommon;
+    padding-left: 24px;
+    padding-right: 24px;
+    box-sizing: border-box;
+  }
 }

+ 187 - 0
src/packages/__VUE/menu/index.taro.vue

@@ -0,0 +1,187 @@
+<template>
+  <view>
+    <nut-popup
+      v-bind="$attrs"
+      v-model:visible="showMask"
+      @close="handleClose"
+    ></nut-popup>
+    <div :class="classes" :style="{ 'z-index': nutMenuIndex }">
+      <div class="title-list">
+        <div
+          v-for="(menu, index) in menuList"
+          :key="index"
+          class="title"
+          :class="{
+            'is-active': activeTitle === menu.title,
+            disabled: menu.disabled
+          }"
+          @click="!menu.disabled && handleClickTitle(menu.title, index)"
+        >
+          {{ menu.title }}
+          <nut-icon
+            size="10"
+            color="#333"
+            name="arrow-up"
+            v-if="activeTitle === menu.title"
+          ></nut-icon>
+          <nut-icon size="10" color="#999" name="arrow-down" v-else></nut-icon>
+        </div>
+      </div>
+      <div
+        :key="index"
+        v-for="(menu, index) in menuList"
+        v-show="hasOptions && activeTitle === menu.title"
+        class="option-list"
+      >
+        <ul>
+          <li
+            v-for="(option, index2) in menu.options"
+            :key="index2"
+            @click="handleClickOption(option.text, index, option.value)"
+            :style="styleObj"
+          >
+            <nut-icon
+              size="20"
+              :color="activeColor"
+              name="Check"
+              v-if="menu.title === option.text"
+            ></nut-icon
+            >{{ option.text }}
+          </li>
+        </ul>
+      </div>
+      <view v-show="!hasOptions && isShowCustomer" class="customer-item">
+        <slot></slot>
+      </view>
+    </div>
+  </view>
+</template>
+<script lang="ts">
+import { toRefs, ref, computed, onMounted, reactive, toRaw } from 'vue';
+import { createComponent } from '../../utils/create';
+import Icon from '../icon/index.vue';
+const { componentName, create } = createComponent('menu');
+export default create({
+  components: {
+    [Icon.name]: Icon
+  },
+  props: {
+    col: {
+      type: [String, Number],
+      default: 1
+    },
+    activeColor: {
+      type: String,
+      default: '#f00'
+    }
+  },
+  emits: ['choose'],
+  setup(props, { slots, emit }) {
+    const menuList = reactive([]);
+    let activeTitle = ref('');
+    let showMask = ref(false);
+    let styleObj = reactive({
+      flexBasis: 100 / Number(props.col) + '%'
+    });
+    let nutMenuIndex = ref<String | Number>('auto');
+    let hasOptions = ref(true);
+    let isShowCustomer = ref(false);
+
+    for (let i = 0; i < slots.default().length; i++) {
+      if (slots.default()[i].type['name'] === 'nut-menu-item') {
+        let item = {
+          title: slots.default()[i].props['title'],
+          disabled: !!slots.default()[i].props['disabled']
+        };
+        if (slots.default()[i].props['options']) {
+          item['options'] = slots.default()[i].props['options'];
+        } else {
+          hasOptions.value = false;
+        }
+        menuList.push(item);
+      }
+    }
+
+    const handleClickTitle = (title, index) => {
+      if (!hasOptions.value) {
+        if (activeTitle.value) {
+          activeTitle.value = '';
+          isShowCustomer.value = false;
+          showMask.value = false;
+          nutMenuIndex.value = 'auto';
+        } else {
+          activeTitle.value = title;
+          isShowCustomer.value = true;
+          showMask.value = true;
+          nutMenuIndex.value = 2001;
+        }
+        return;
+      }
+
+      if (menuList.length > 1) {
+        if (activeTitle.value === title) {
+          activeTitle.value = '';
+          nutMenuIndex.value = 'auto';
+          showMask.value = false;
+        } else {
+          activeTitle.value = title;
+          nutMenuIndex.value = 2001;
+          showMask.value = true;
+        }
+      } else {
+        if (activeTitle.value) {
+          activeTitle.value = '';
+          nutMenuIndex.value = 'auto';
+          showMask.value = false;
+        } else {
+          activeTitle.value = title;
+          nutMenuIndex.value = 2001;
+          showMask.value = true;
+        }
+      }
+    };
+
+    const handleClickOption = (text, index, value) => {
+      menuList[index].title = text;
+      activeTitle.value = '';
+      showMask.value = false;
+      nutMenuIndex.value = 'auto';
+      emit('choose', text, value);
+    };
+
+    const classes = computed(() => {
+      const prefixCls = componentName;
+      return {
+        [prefixCls]: true
+      };
+    });
+
+    const handleClose = () => {
+      activeTitle.value = '';
+      nutMenuIndex.value = 'auto';
+
+      if (isShowCustomer.value) {
+        isShowCustomer.value = false;
+      }
+    };
+
+    return {
+      menuList,
+      activeTitle,
+      classes,
+      showMask,
+      styleObj,
+      nutMenuIndex,
+      hasOptions,
+      isShowCustomer,
+      handleClickTitle,
+      handleClickOption,
+      handleClose
+    };
+  }
+});
+</script>
+
+<style lang="scss">
+@import 'index.scss';
+</style>

+ 165 - 20
src/packages/__VUE/menu/index.vue

@@ -1,42 +1,187 @@
 <template>
-  <view :class="classes" :style="showMask && `z-index:9999`">
-    <slot></slot>
+  <view>
+    <nut-popup
+      v-bind="$attrs"
+      v-model:visible="showMask"
+      @close="handleClose"
+    ></nut-popup>
+    <div :class="classes" :style="{ 'z-index': nutMenuIndex }">
+      <div class="title-list">
+        <div
+          v-for="(menu, index) in menuList"
+          :key="index"
+          class="title"
+          :class="{
+            'is-active': activeTitle === menu.title,
+            disabled: menu.disabled
+          }"
+          @click="!menu.disabled && handleClickTitle(menu.title, index)"
+        >
+          {{ menu.title }}
+          <nut-icon
+            size="10"
+            color="#333"
+            name="arrow-up"
+            v-if="activeTitle === menu.title"
+          ></nut-icon>
+          <nut-icon size="10" color="#999" name="arrow-down" v-else></nut-icon>
+        </div>
+      </div>
+      <div
+        :key="index"
+        v-for="(menu, index) in menuList"
+        v-show="hasOptions && activeTitle === menu.title"
+        class="option-list"
+      >
+        <ul>
+          <li
+            v-for="(option, index2) in menu.options"
+            :key="index2"
+            @click="handleClickOption(option.text, index, option.value)"
+            :style="styleObj"
+          >
+            <nut-icon
+              size="20"
+              :color="activeColor"
+              name="Check"
+              v-if="menu.title === option.text"
+            ></nut-icon
+            >{{ option.text }}
+          </li>
+        </ul>
+      </div>
+      <view v-show="!hasOptions && isShowCustomer" class="customer-item">
+        <slot></slot>
+      </view>
+    </div>
   </view>
 </template>
 <script lang="ts">
-import { toRefs, reactive, provide, computed } from 'vue';
+import { toRefs, ref, computed, onMounted, reactive, toRaw } from 'vue';
 import { createComponent } from '../../utils/create';
+import Icon from '../icon/index.vue';
 const { componentName, create } = createComponent('menu');
 export default create({
+  components: {
+    [Icon.name]: Icon
+  },
   props: {
-    type: {
-      //单选 simple  多选  multiple,暂留
-      type: String,
-      default: 'simple'
+    col: {
+      type: [String, Number],
+      default: 1
     },
-    hasMask: {
-      type: Boolean,
-      default: true
+    activeColor: {
+      type: String,
+      default: '#f00'
     }
   },
-  setup(props, { emit }) {
-    const state = reactive({
-      showMask: false
+  emits: ['choose'],
+  setup(props, { slots, emit }) {
+    const menuList = reactive([]);
+    let activeTitle = ref('');
+    let showMask = ref(false);
+    let styleObj = reactive({
+      flexBasis: 100 / Number(props.col) + '%'
     });
+    let nutMenuIndex = ref<String | Number>('auto');
+    let hasOptions = ref(true);
+    let isShowCustomer = ref(false);
+
+    for (let i = 0; i < slots.default().length; i++) {
+      if (slots.default()[i].type['name'] === 'nut-menu-item') {
+        let item = {
+          title: slots.default()[i].props['title'],
+          disabled: !!slots.default()[i].props['disabled']
+        };
+        if (slots.default()[i].props['options']) {
+          item['options'] = slots.default()[i].props['options'];
+        } else {
+          hasOptions.value = false;
+        }
+        menuList.push(item);
+      }
+    }
+
+    const handleClickTitle = (title, index) => {
+      if (!hasOptions.value) {
+        if (activeTitle.value) {
+          activeTitle.value = '';
+          isShowCustomer.value = false;
+          showMask.value = false;
+          nutMenuIndex.value = 'auto';
+        } else {
+          activeTitle.value = title;
+          isShowCustomer.value = true;
+          showMask.value = true;
+          nutMenuIndex.value = 2001;
+        }
+        return;
+      }
+
+      if (menuList.length > 1) {
+        if (activeTitle.value === title) {
+          activeTitle.value = '';
+          nutMenuIndex.value = 'auto';
+          showMask.value = false;
+        } else {
+          activeTitle.value = title;
+          nutMenuIndex.value = 2001;
+          showMask.value = true;
+        }
+      } else {
+        if (activeTitle.value) {
+          activeTitle.value = '';
+          nutMenuIndex.value = 'auto';
+          showMask.value = false;
+        } else {
+          activeTitle.value = title;
+          nutMenuIndex.value = 2001;
+          showMask.value = true;
+        }
+      }
+    };
+
+    const handleClickOption = (text, index, value) => {
+      menuList[index].title = text;
+      activeTitle.value = '';
+      showMask.value = false;
+      nutMenuIndex.value = 'auto';
+      emit('choose', text, value);
+    };
+
     const classes = computed(() => {
       const prefixCls = componentName;
       return {
         [prefixCls]: true
       };
     });
-    const handleMaskShow = (status: boolean) => {
-      state.showMask = status;
+
+    const handleClose = () => {
+      activeTitle.value = '';
+      nutMenuIndex.value = 'auto';
+
+      if (isShowCustomer.value) {
+        isShowCustomer.value = false;
+      }
+    };
+
+    return {
+      menuList,
+      activeTitle,
+      classes,
+      showMask,
+      styleObj,
+      nutMenuIndex,
+      hasOptions,
+      isShowCustomer,
+      handleClickTitle,
+      handleClickOption,
+      handleClose
     };
-    provide('menuRelation', {
-      handleMaskShow,
-      hasMask: props.hasMask
-    });
-    return { ...toRefs(state), classes };
   }
 });
 </script>
+
+<style lang="scss">
+@import 'index.scss';
+</style>

+ 25 - 0
src/packages/__VUE/menuitem/index.taro.vue

@@ -0,0 +1,25 @@
+<template>
+  <div><slot></slot></div>
+</template>
+<script lang="ts">
+import { getCurrentInstance } from 'vue';
+import { createComponent } from '../../utils/create';
+const { create, componentName } = createComponent('menu-item');
+export default create({
+  setup() {
+    let pro = (getCurrentInstance() as any).proxy;
+    pro.toggle = () => {
+      pro.$parent.activeTitle = '';
+      pro.$parent.isShowCustomer = false;
+      pro.$parent.showMask = false;
+      pro.$parent.nutMenuIndex = 'auto';
+    };
+
+    return {};
+  }
+});
+</script>
+
+<style lang="scss" scoped>
+@import './index.scss';
+</style>

+ 14 - 148
src/packages/__VUE/menuitem/index.vue

@@ -1,159 +1,25 @@
 <template>
-  <view :class="classes">
-    <nut-popup v-model:visible="showMask"></nut-popup>
-    <view class="nut-menu-title" @click="handleMenuPanel">
-      <view class="title-name" v-html="menuTitle"></view>
-      <nut-icon class-prefix="icon"></nut-icon>
-    </view>
-
-    <view class="nut-menu-panel" :style="`max-height:${maxHeight}px`">
-      <view
-        v-if="menuList && menuList.length"
-        class="menu-list"
-        :class="[
-          { 'bubble-line': multiStyle == 2 },
-          { 'three-line': multiStyle == 3 }
-        ]"
-      >
-        <view
-          class="menu-option"
-          :class="{ checked: currMenu === index }"
-          v-for="(item, index) in menuList"
-          :key="index"
-          @click="checkMenus(item, index)"
-        >
-          <nut-icon
-            class="check-icon"
-            v-if="currMenu === index"
-            name="Check"
-            size="14px"
-          ></nut-icon
-          >{{ item.value }}
-        </view>
-      </view>
-      <slot></slot>
-    </view>
-  </view>
+  <div><slot></slot></div>
 </template>
 <script lang="ts">
-import {
-  reactive,
-  toRefs,
-  onMounted,
-  ref,
-  computed,
-  watch,
-  onUnmounted,
-  inject
-} from 'vue';
+import { getCurrentInstance } from 'vue';
 import { createComponent } from '../../utils/create';
 const { create, componentName } = createComponent('menu-item');
 export default create({
-  props: {
-    title: {
-      type: String,
-      default: ''
-    },
-    disabled: {
-      type: Boolean,
-      default: false
-    },
-    menuList: {
-      type: Array,
-      default: () => {
-        return [];
-      }
-    },
-    autoClose: {
-      type: Boolean,
-      default: true
-    },
-    multiStyle: {
-      type: [String, Number],
-      default: 1
-    },
-    maxHeight: {
-      type: [String, Number],
-      default: ''
-    }
-  },
-  emits: ['change', 'menu-click'],
-  setup(props, { emit }) {
-    const menuTitle = ref(props.title);
-    const menu = inject('menuRelation');
-    const parent: any = reactive(menu as any);
-    const state = reactive({
-      showPanel: false,
-      currMenu: 0,
-      showMask: false
-    });
-
-    const classes = computed(() => {
-      const prefixCls = componentName;
-      return {
-        [prefixCls]: true,
-        disabled: props.disabled,
-        [`${prefixCls}-active`]: state.showPanel
-      };
-    });
-
-    const handleMenuPanel = () => {
-      emit('menu-click', menuTitle.value);
-      //禁用
-      if (props.disabled) {
-        return;
-      }
-
-      state.showPanel = !state.showPanel;
-      if (parent.hasMask) {
-        state.showMask = !state.showMask;
-        parent.handleMaskShow(state.showPanel);
-      }
+  setup() {
+    let pro = (getCurrentInstance() as any).proxy;
+    pro.toggle = () => {
+      pro.$parent.activeTitle = '';
+      pro.$parent.isShowCustomer = false;
+      pro.$parent.showMask = false;
+      pro.$parent.nutMenuIndex = 'auto';
     };
 
-    const handleShowAndHide = (event: any) => {
-      const menuBox = document.querySelectorAll('.nut-menu-active')[0];
-      if (menuBox && state.showPanel) {
-        if (!menuBox.contains(event.target)) {
-          state.showPanel = false;
-          state.showMask = false;
-          parent.handleMaskShow(false);
-        }
-      }
-    };
-    const checkMenus = (item: any, index: number) => {
-      menuTitle.value = item.value;
-      state.currMenu = index;
-      if (props.autoClose) {
-        state.showPanel = false;
-        state.showMask = false;
-        parent.handleMaskShow(false);
-      }
-      emit('change', item, menuTitle.value);
-    };
-    onMounted(() => {
-      document.addEventListener(
-        'mouseup',
-        (event: any) => {
-          handleShowAndHide(event);
-        },
-        false
-      );
-    });
-
-    onUnmounted(() => {
-      document.removeEventListener('mouseup', (event: any) => {
-        handleShowAndHide(event);
-      }),
-        false;
-    });
-    return {
-      classes,
-      ...toRefs(state),
-      handleMenuPanel,
-      checkMenus,
-      menuTitle
-    };
+    return {};
   }
 });
 </script>
+
+<style lang="scss" scoped>
+@import './index.scss';
+</style>

+ 1 - 0
src/sites/mobile-taro/vue/src/app.config.ts

@@ -48,6 +48,7 @@ export default {
         'pages/tabs/index',
         'pages/fixednav/index',
         'pages/elevator/index',
+        'pages/menu/index',
         'pages/pagination/index'
       ]
     },

+ 3 - 0
src/sites/mobile-taro/vue/src/nav/pages/menu/index.config.ts

@@ -0,0 +1,3 @@
+export default {
+  navigationBarTitleText: 'Menu'
+};

+ 88 - 0
src/sites/mobile-taro/vue/src/nav/pages/menu/index.vue

@@ -0,0 +1,88 @@
+<template>
+  <div class="demo">
+    <h2>基础用法</h2>
+    <nut-menu>
+      <nut-menu-item title="全部商品" :options="options1"></nut-menu-item>
+    </nut-menu>
+    <h2>两列标题</h2>
+    <nut-menu>
+      <nut-menu-item title="全部商品" :options="options1"></nut-menu-item>
+      <nut-menu-item title="默认排序" :options="options2"></nut-menu-item>
+    </nut-menu>
+    <h2>获取选择的列表对象</h2>
+    <nut-menu @choose="handleChoose">
+      <nut-menu-item title="全部商品" :options="options1"></nut-menu-item>
+    </nut-menu>
+    <h2>一行两列列表对象</h2>
+    <nut-menu col="2">
+      <nut-menu-item title="全部商品" :options="options1"></nut-menu-item>
+    </nut-menu>
+    <h2>禁用菜单</h2>
+    <nut-menu>
+      <nut-menu-item
+        title="全部商品"
+        disabled="true"
+        :options="options1"
+      ></nut-menu-item>
+    </nut-menu>
+    <h2>自定义选项的选中态图标颜色</h2>
+    <nut-menu active-color="#0f0">
+      <nut-menu-item title="全部商品" :options="options1"></nut-menu-item>
+    </nut-menu>
+    <h2>自定义菜单内容</h2>
+    <nut-menu>
+      <nut-menu-item title="筛选" ref="item">
+        <div
+          :style="{
+            display: 'flex',
+            'justify-content': 'space-between',
+            'align-items': 'center'
+          }"
+        >
+          <div :style="{ 'font-size': '12px' }">我是自定义内容</div>
+          <nut-button @click="handleClick">关闭</nut-button>
+        </div>
+      </nut-menu-item>
+    </nut-menu>
+  </div>
+</template>
+
+<script lang="ts">
+import { ref, reactive, toRefs } from 'vue';
+export default {
+  props: {},
+  setup() {
+    const options1 = [
+      { text: '全部商品', value: 0 },
+      { text: '新款商品', value: 1 },
+      { text: '活动商品', value: 2 }
+    ];
+
+    const options2 = [
+      { text: '默认排序', value: 'a' },
+      { text: '好评排序', value: 'b' },
+      { text: '销量排序', value: 'c' }
+    ];
+
+    const item = ref<HTMLElement>();
+
+    const handleChoose = (val, index) => {
+      console.log(val, index);
+    };
+
+    const handleClick = () => {
+      item.value.toggle();
+    };
+
+    return {
+      options1,
+      options2,
+      item,
+      handleChoose,
+      handleClick
+    };
+  }
+};
+</script>
+
+<style lang="scss" scoped></style>