Browse Source

feat: menu

yewenwen 5 years ago
parent
commit
e091902c28

+ 9 - 0
src/config.ts

@@ -180,6 +180,15 @@ export const nav = [
         show: true,
         show: true,
         desc: '头像',
         desc: '头像',
         author: 'ailululu'
         author: 'ailululu'
+      },
+      {
+        name: 'Menu',
+        sort: 8,
+        cName: '菜单',
+        type: 'component',
+        show: true,
+        desc: '菜单',
+        author: 'vickyYE'
       }
       }
     ]
     ]
   },
   },

+ 81 - 0
src/packages/menu/demo.vue

@@ -0,0 +1,81 @@
+<template>
+  <div class="demo">
+    <h2>基础用法</h2>
+    <nut-menu>
+      <nut-menu-item
+        :menuList="menuList"
+        :title="title1"
+        @on-checked="getChecked"
+      ></nut-menu-item>
+      <nut-menu-item
+        :menuList="menuList2"
+        title="最新商品"
+        multiLine="2"
+      ></nut-menu-item>
+      <nut-menu-item
+        :menuList="menuList2"
+        title="筛选"
+        disabled
+      ></nut-menu-item>
+    </nut-menu>
+  </div>
+</template>
+
+<script lang="ts">
+import { ref, reactive, toRefs } from 'vue';
+import { createComponent } from '@/utils/create';
+const { createDemo } = createComponent('menu');
+export default createDemo({
+  props: {},
+  setup() {
+    const title1 = ref('标题1');
+    const resData = reactive({
+      menuList: [
+        {
+          value: '标签一'
+        },
+        {
+          value: '标签二标签二标签二标签二'
+        },
+        {
+          value: '标签三'
+        }
+      ],
+      menuList2: [
+        {
+          value: '标签1'
+        },
+        {
+          value: '标签2'
+        },
+        {
+          value: '标签3'
+        }
+      ]
+    });
+
+    const getChecked = (info: any) => {
+      console.log(11, info.value);
+    };
+    // function changeList() {
+    //   resData.editList.push({
+    //     title: '标签' + resData.editList.length
+    //   });
+    // }
+    // function switchTab(activeInddex: number, event: MouseEvent) {
+    //   console.log(activeInddex, event);
+    // }
+    return {
+      ...toRefs(resData),
+      title1
+      // changeList,
+      // switchTab
+    };
+  }
+});
+</script>
+
+<style lang="scss" scoped>
+.nut-menu {
+}
+</style>

+ 66 - 0
src/packages/menu/doc.md

@@ -0,0 +1,66 @@
+# Menu 菜单组件
+
+### 介绍
+
+基于 xxxxxxx
+
+### 安装
+
+``` javascript
+import { createApp } from 'vue';
+import { Menu } from '@nutui/nutui';
+
+const app = createApp();
+app.use(Menu);
+
+```
+
+## 代码演示
+
+### 基础用法1
+
+`Menu`  属性支持传入menu列表数据和title名称。
+
+```html
+<nut-menu>
+  <nut-menu-item :menuList="menuList" :title="title1"></nut-menu-item>
+  <nut-menu-item :menuList="menuList2" :title="title1"></nut-menu-item>
+</nut-menu>
+```
+
+### 基础用法2
+
+`Icon` 的 `name` 属性支持传入图标名称或图片链接。
+
+```html
+<nut-temp name="wifi"></nut-temp>
+<nut-temp name="mail"></nut-temp>
+```
+
+### 基础用法3
+
+`Icon` 的 `name` 属性支持传入图标名称或图片链接。
+
+```html
+<nut-temp name="wifi"></nut-temp>
+<nut-temp name="mail"></nut-temp>
+```
+
+
+## API
+
+### Props
+
+| 参数         | 说明                             | 类型   | 默认值           |
+|--------------|----------------------------------|--------|------------------|
+| name         | 图标名称或图片链接               | String | -                |
+| color        | 图标颜色                         | String | -                |
+| size         | 图标大小,如 `20px` `2em` `2rem` | String | -                |
+| class-prefix | 类名前缀,用于使用自定义图标     | String | `nutui-iconfont` |
+| tag          | HTML 标签                        | String | `i`              |
+
+### Events
+
+| 事件名 | 说明           | 回调参数     |
+|--------|----------------|--------------|
+| click  | 点击图标时触发 | event: Event |

+ 7 - 0
src/packages/menu/index.scss

@@ -0,0 +1,7 @@
+.nut-menu {
+  background: #fff;
+  display: flex;
+  align-items: center;
+  position: relative;
+  height: 46px;
+}

+ 45 - 0
src/packages/menu/index.vue

@@ -0,0 +1,45 @@
+<template>
+  <view class="nut-menu">
+    <slot></slot>
+  </view>
+</template>
+<script lang="ts">
+import { toRefs } from 'vue';
+import { createComponent } from '@/utils/create';
+import { useChildren } from '@/utils/useRelation/useChildren';
+export const MENU_KEY = 'nutMenu';
+const { componentName, create } = createComponent('menu');
+
+export default create({
+  props: {
+    disabled: {
+      //是否显示
+      type: Boolean,
+      default: false
+    },
+    type: {
+      //单选 simple  多选  multiple
+      type: String,
+      default: 'simple'
+    }
+  },
+  components: {},
+  emits: ['click'],
+
+  setup(props, { emit }) {
+    console.log('componentName', componentName);
+
+    // const { name, txt } = toRefs(props);
+
+    const handleClick = (event: Event) => {
+      emit('click', event);
+    };
+
+    // return { name, txt, handleClick };
+  }
+});
+</script>
+
+<style lang="scss">
+@import 'index.scss';
+</style>

+ 102 - 0
src/packages/menuitem/index.scss

@@ -0,0 +1,102 @@
+.nut-menu-item {
+  flex: 1;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  cursor: pointer;
+  padding: 0 5px;
+  &.disabled {
+    color: #999;
+  }
+  &.nut-menu-active {
+    .nut-menu-title {
+      font-weight: bold;
+      .icon {
+        background: url('https://img10.360buyimg.com/imagetools/jfs/t1/156044/7/582/372/5fd990b5Ea62c2694/551e7f58d421a9ae.png')
+          no-repeat;
+        transition: all ease 0.3s;
+        transform: rotate(-360deg);
+        background-size: contain;
+      }
+    }
+    .nut-menu-panel {
+      display: block;
+    }
+  }
+}
+.nut-menu-title {
+  font-size: 14px;
+  display: flex;
+  align-items: center;
+  .title-name {
+    white-space: nowrap;
+    max-width: 100px;
+    overflow: hidden;
+    text-overflow: ellipsis;
+  }
+  .icon {
+    display: inline-block;
+    width: 6px;
+    height: 6px;
+    background: url('https://img13.360buyimg.com/imagetools/jfs/t1/152898/12/10149/452/5fd990b5Ec7c12d70/3bf06076b758bed1.png')
+      no-repeat;
+    background-size: contain;
+    transform: rotate(0deg);
+    transition: all ease 0.3s;
+    margin: 0 2px;
+  }
+}
+.nut-menu-panel {
+  display: none;
+  width: 100%;
+  position: absolute;
+  left: 0;
+  top: 46px;
+  color: #2d2d2d;
+  overflow: hidden;
+  background-color: #fff;
+  -webkit-box-sizing: border-box;
+  box-sizing: border-box;
+  -webkit-overflow-scrolling: touch;
+  z-index: 9998;
+  border-top: 1px solid #f7f8fa;
+  border-radius: 0 0 15px 15px;
+  box-shadow: 0 4px 5px 0px rgba(236, 236, 236, 0.25);
+  // &.active{
+  //   display: block;
+  // }
+}
+
+.menu-list {
+  display: flex;
+  padding: 10px 24px;
+  flex-direction: column;
+  &.bubble-line {
+    flex-flow: wrap;
+    .menu-option {
+      width: 50%;
+    }
+  }
+  &.three-line {
+    flex-flow: wrap;
+    .menu-option {
+      width: 32%;
+    }
+  }
+  .menu-option {
+    min-height: 24px;
+    line-height: 48px;
+    font-size: 14px;
+    color: #1a1a1a;
+    width: 100%;
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+  }
+  .check-icon {
+    width: 14px;
+    height: 14px;
+    margin-right: 5px;
+    color: #fa2c19;
+  }
+}

+ 145 - 0
src/packages/menuitem/index.vue

@@ -0,0 +1,145 @@
+<template>
+  <view
+    class="nut-menu-item"
+    :class="[{ disabled: disabled }, { 'nut-menu-active': showPanel }]"
+  >
+    <view class="nut-menu-title" @click="handleMenuPanel">
+      <view class="title-name" v-html="menuTitle"></view>
+      <i class="icon" name="arrow-down"></i>
+    </view>
+    <view class="nut-menu-panel" ref="menuPanel">
+      <view
+        class="menu-list"
+        :class="[
+          { 'bubble-line': multiLine == 2 },
+          { 'three-line': multiLine == 3 }
+        ]"
+      >
+        <view
+          class="menu-option"
+          v-for="(item, index) in menuList"
+          :key="index"
+          @click="checkMenus(item, index)"
+          ><nut-icon
+            class="check-icon"
+            v-if="currMenu == index"
+            name="dongdong"
+            size="14px"
+          ></nut-icon
+          >{{ item.value }}</view
+        >
+      </view>
+    </view>
+  </view>
+</template>
+<script lang="ts">
+import {
+  reactive,
+  toRefs,
+  onMounted,
+  ref,
+  nextTick,
+  computed,
+  watch,
+  onUnmounted
+} from 'vue';
+import { createComponent } from '@/utils/create';
+import { useParent } from '@/utils/useRelation/useParent';
+import { MENU_KEY } from './../menu/index.vue';
+import Icon from '@/packages/icon/index.vue';
+const { create } = createComponent('menu-item');
+
+export default create({
+  props: {
+    title: {
+      type: String,
+      default: ''
+    },
+    value: {
+      type: [String, Number],
+      default: ''
+    },
+    disabled: {
+      type: Boolean,
+      default: false
+    },
+    menuList: {
+      type: Array,
+      default: () => {
+        return [];
+      }
+    },
+    isVisible: {
+      //是否显示
+      type: Boolean,
+      default: false
+    },
+    multiLine: {
+      type: [String, Number],
+      default: 1 //可选值1、2、3
+    }
+  },
+  emits: ['on-checked'],
+  setup(props, { emit }) {
+    const { menuList, multiLine } = toRefs(props);
+    const menuTitle = ref(props.title);
+    const menu = useParent(MENU_KEY);
+    const parent: any = reactive(menu.parent as any);
+    const index: any = reactive(menu.index as any);
+    const state = reactive({
+      showPanel: false,
+      currMenu: 0
+      // menuTitle:''
+    });
+
+    const handleMenuPanel = () => {
+      //禁用
+      if (props.disabled) return;
+      state.showPanel = !state.showPanel;
+    };
+    //menu列表浮层展示和隐藏
+    const handleShowAndHide = (event: any) => {
+      const menuBox = document.querySelectorAll('.nut-menu-active')[0];
+      if (menuBox && state.showPanel) {
+        if (!menuBox.contains(event.target)) {
+          state.showPanel = false;
+        }
+      }
+    };
+    const checkMenus = (item: any, index: number) => {
+      console.log(item);
+      menuTitle.value = item.value;
+      state.currMenu = index;
+      emit('on-checked', menuTitle.value);
+    };
+    onMounted(() => {
+      document.addEventListener(
+        'mouseup',
+        (event: any) => {
+          handleShowAndHide(event);
+        },
+        false
+      );
+    });
+
+    onUnmounted(() => {
+      document.removeEventListener('mouseup', (event: any) => {
+        handleShowAndHide(event);
+      }),
+        false;
+    });
+    return {
+      ...toRefs(state),
+      ...toRefs(parent),
+      handleMenuPanel,
+      checkMenus,
+      menuTitle,
+      multiLine
+    };
+  }
+});
+</script>
+
+<style lang="scss" scoped>
+@import './index.scss';
+</style>