Browse Source

feat: 折叠面板适配小程序

Ymm0008 4 years ago
parent
commit
3c82e0a67d

+ 1 - 1
package.json

@@ -70,7 +70,6 @@
     "vue-router": "^4.0.4"
   },
   "devDependencies": {
-    "axios": "^0.21.0",
     "@commitlint/cli": "^10.0.0",
     "@commitlint/config-conventional": "^10.0.0",
     "@types/jest": "^26.0.22",
@@ -81,6 +80,7 @@
     "@vue/eslint-config-prettier": "^6.0.0",
     "@vue/eslint-config-typescript": "^5.0.2",
     "@vue/test-utils": "^2.0.0-rc.6",
+    "axios": "^0.21.0",
     "eslint": "^6.7.2",
     "eslint-plugin-prettier": "^3.1.3",
     "eslint-plugin-vue": "^7.0.0-0",

+ 2 - 0
src/config.json

@@ -95,6 +95,7 @@
         },
         {
           "name": "Collapse",
+          "taro": true,
           "sort": 2,
           "cName": "折叠面板",
           "type": "component",
@@ -104,6 +105,7 @@
         },
         {
           "name": "CollapseItem",
+          "taro": true,
           "sort": 3,
           "cName": "折叠面板-item",
           "type": "component",

+ 0 - 2
src/packages/__VUE/collapse/demo.vue

@@ -93,5 +93,3 @@ export default createDemo({
   }
 });
 </script>
-
-<style lang="scss" scoped></style>

+ 101 - 0
src/packages/__VUE/collapse/index.taro.vue

@@ -0,0 +1,101 @@
+<template>
+  <view>
+    <slot></slot>
+  </view>
+</template>
+<script lang="ts">
+import { provide } from 'vue';
+import { createComponent } from '@/packages/utils/create';
+import collapseitem from '@/packages/__VUE/collapseitem/index.taro.vue';
+const { create } = createComponent('collapse');
+export default create({
+  children: [collapseitem],
+  props: {
+    active: {
+      type: [String, Number, Array]
+    },
+    accordion: {
+      type: Boolean
+    },
+    // expandIconPosition: {
+    //   type: String,
+    //   default: 'right'
+    // },
+    titleIcon: {
+      type: String,
+      default: ''
+    },
+    titleIconSize: {
+      type: String,
+      default: '16px'
+    },
+    titleIconColor: {
+      type: String,
+      default: ''
+    },
+    titleIconPosition: {
+      type: String,
+      default: 'left'
+    },
+    icon: {
+      type: String,
+      default: ''
+    },
+    iconSize: {
+      type: String,
+      default: '16px'
+    },
+    iconColor: {
+      type: String,
+      default: ''
+    },
+    rotate: {
+      type: [String, Number],
+      default: 180
+    }
+  },
+  emits: ['update:active', 'change'],
+  setup(props, { emit }) {
+    const changeVal = (val: string | number | Array<string | number>) => {
+      emit('update:active', val);
+      emit('change', val);
+    };
+
+    const changeValAry = (name: string) => {
+      const activeItem: any =
+        props.active instanceof Object
+          ? Object.values(props.active)
+          : props.active;
+      let index = -1;
+      activeItem.forEach((item: string | number, idx: number) => {
+        if (String(item) == String(name)) {
+          index = idx;
+        }
+      });
+      index > -1 ? activeItem.splice(index, 1) : activeItem.push(name);
+      changeVal(activeItem);
+    };
+
+    const isExpanded = (name: string | number | Array<string | number>) => {
+      const { accordion, active } = props;
+      if (accordion) {
+        return typeof active === 'number' || typeof active === 'string'
+          ? active == name
+          : false;
+      }
+    };
+
+    provide('collapseParent', {
+      children: [],
+      props,
+      changeValAry,
+      changeVal,
+      isExpanded
+    });
+  }
+});
+</script>
+
+<style lang="scss">
+@import 'index.scss';
+</style>

+ 266 - 0
src/packages/__VUE/collapseitem/index.taro.vue

@@ -0,0 +1,266 @@
+<template>
+  <view :class="classes">
+    <view
+      :class="[
+        'collapse-item',
+        { 'item-expanded': openExpanded },
+        { 'nut-collapse-item-disabled': disabled }
+      ]"
+      @click="toggleOpen"
+    >
+      <view class="collapse-title">
+        <view>
+          <view class="collapse-title-value">
+            <nut-icon
+              v-if="titleIcon"
+              :name="titleIcon"
+              :size="titleIconSize"
+              :color="titleIconColor"
+              :class="[
+                titleIconPosition == 'left' ? 'titleIconLeft' : 'titleIconRight'
+              ]"
+            ></nut-icon>
+            <view v-html="title"></view>
+          </view>
+        </view>
+      </view>
+      <view v-if="subTitle" v-html="subTitle" class="subTitle"></view>
+      <nut-icon
+        v-if="icon"
+        :name="icon"
+        :size="iconSize"
+        :color="iconColor"
+        :class="[
+          'collapse-icon',
+          { 'col-expanded': openExpanded },
+          { 'collapse-icon-disabled': disabled }
+        ]"
+        :style="iconStyle"
+      ></nut-icon>
+    </view>
+    <view class="collapse-wrapper" ref="wrapperRef">
+      <view class="collapse-content" ref="contentRef">
+        <slot></slot>
+      </view>
+    </view>
+  </view>
+</template>
+<script lang="ts">
+import {
+  reactive,
+  inject,
+  toRefs,
+  onMounted,
+  ref,
+  nextTick,
+  computed,
+  watch,
+  getCurrentInstance,
+  ComponentInternalInstance
+} from 'vue';
+import { createComponent } from '@/packages/utils/create';
+const { create, componentName } = createComponent('collapse-item');
+
+export default create({
+  props: {
+    title: {
+      type: String,
+      default: ''
+    },
+    subTitle: {
+      type: String,
+      default: ''
+    },
+    disabled: {
+      type: Boolean,
+      default: false
+    },
+    name: {
+      type: [Number, String],
+      default: -1,
+      required: true
+    },
+    collapseRef: {
+      type: Object
+    }
+  },
+  setup(props) {
+    const collapse: any = inject('collapseParent');
+    const parent: any = reactive(collapse);
+    const classes = computed(() => {
+      const prefixCls = componentName;
+      return {
+        [prefixCls]: true,
+        // [`${prefixCls}-left`]: parent.props.classDirection === 'left',
+        [`${prefixCls}-icon`]: parent.props.icon
+      };
+    });
+    const relation = (child: ComponentInternalInstance): void => {
+      if (child.proxy) {
+        parent.children.push(child.proxy);
+      }
+    };
+    relation(getCurrentInstance() as ComponentInternalInstance);
+    const proxyData = reactive({
+      icon: parent.props.icon,
+      iconSize: parent.props.iconSize,
+      iconColor: parent.props.iconColor,
+      openExpanded: false,
+      // classDirection: 'right',
+      iconStyle: {
+        transform: 'rotate(0deg)',
+        marginTop: parent.props.iconHeght
+          ? '-' + parent.props.iconHeght / 2 + 'px'
+          : '-10px'
+      }
+    });
+
+    const titleIconStyle = reactive({
+      titleIcon: parent.props.titleIcon,
+      titleIconSize: parent.props.titleIconSize,
+      titleIconColor: parent.props.titleIconColor,
+      titleIconPosition: parent.props.titleIconPosition
+      // titleIconWH: {
+      //   width: '13px',
+      //   height: '13px'
+      // }
+    });
+
+    // 获取 Dom 元素
+    const wrapperRef: any = ref(null);
+    const contentRef: any = ref(null);
+
+    // 清除 willChange 减少性能浪费
+    const onTransitionEnd = () => {
+      const wrapperRefEle: any = document.getElementsByClassName(
+        'collapse-wrapper'
+      )[0];
+      wrapperRefEle.style.willChange = 'auto';
+    };
+
+    // 手风琴模式
+    const animation = () => {
+      const wrapperRefEle: any = wrapperRef.value;
+      const contentRefEle: any = contentRef.value;
+      if (!wrapperRefEle || !contentRefEle) {
+        return;
+      }
+      const offsetHeight = contentRefEle.offsetHeight;
+      if (offsetHeight) {
+        const contentHeight = `${offsetHeight}px`;
+        wrapperRefEle.style.willChange = 'height';
+        wrapperRefEle.style.height = !proxyData.openExpanded
+          ? 0
+          : contentHeight;
+        if (parent.props.icon && !proxyData.openExpanded) {
+          proxyData.iconStyle['transform'] = 'rotate(0deg)';
+        } else {
+          proxyData.iconStyle['transform'] =
+            'rotate(' + parent.props.rotate + 'deg)';
+        }
+      }
+      if (!proxyData.openExpanded) {
+        onTransitionEnd();
+      }
+    };
+
+    const open = () => {
+      proxyData.openExpanded = !proxyData.openExpanded;
+      animation();
+    };
+
+    const defaultOpen = () => {
+      open();
+      if (parent.props.icon) {
+        proxyData['iconStyle']['transform'] =
+          'rotate(' + parent.props.rotate + 'deg)';
+      }
+    };
+
+    const currentName = computed(() => props.name);
+    const toggleOpen = () => {
+      console.log('点击了——————-----');
+
+      if (parent.props.accordion) {
+        parent.children.forEach((item: any, index: number) => {
+          if (currentName.value == item.name) {
+            item.changeOpen(!item.openExpanded);
+          } else {
+            item.changeOpen(false);
+            item.animation();
+          }
+        });
+        nextTick(() => {
+          parent.changeVal(currentName.value);
+          animation();
+        });
+      } else {
+        parent.changeValAry(props.name);
+        open();
+      }
+    };
+    // 更改子组件展示
+    const changeOpen = (bol: boolean) => {
+      proxyData.openExpanded = bol;
+    };
+
+    const expanded = computed(() => {
+      if (parent) {
+        return parent.isExpanded(props.name);
+      }
+      return null;
+    });
+
+    watch(expanded, (value, oldValue) => {
+      if (value) {
+        proxyData.openExpanded = true;
+      }
+    });
+
+    onMounted(() => {
+      const { name } = props;
+      const active = parent && parent.props.active;
+
+      if (typeof active == 'number' || typeof active == 'string') {
+        if (name == active) {
+          defaultOpen();
+        }
+      } else if (Object.values(active) instanceof Array) {
+        const f = Object.values(active).filter(item => item == name);
+        if (f.length > 0) {
+          defaultOpen();
+        }
+      }
+
+      // proxyData.classDirection = parent.props.expandIconPosition;
+      // if (parent.props.icon && parent.props.icon != 'none') {
+      //   proxyData.iconStyle['background-image'] =
+      //     'url(' + parent.props.icon + ')';
+      // }
+      // if (parent.props.iconWidth && parent.props.icon != 'none') {
+      //   proxyData.iconStyle['width'] = parent.props.conWidth;
+      // }
+      // if (parent.props.iconHeght && parent.props.icon != 'none') {
+      //   proxyData.iconStyle['height'] = parent.props.iconHeight;
+      // }
+    });
+
+    return {
+      classes,
+      ...toRefs(proxyData),
+      ...toRefs(parent.props),
+      ...toRefs(titleIconStyle),
+      wrapperRef,
+      contentRef,
+      open,
+      toggleOpen,
+      changeOpen,
+      animation
+    };
+  }
+});
+</script>
+
+<style lang="scss">
+@import './index.scss';
+</style>

+ 4 - 1
src/packages/__VUE/collapseitem/index.vue

@@ -136,6 +136,9 @@ export default create({
         'collapse-wrapper'
       )[0];
       wrapperRefEle.style.willChange = 'auto';
+
+      // const query = wx.createSelectorQuery();
+      // query.select('#productServe').boundingClientRect();
     };
 
     // 手风琴模式
@@ -259,6 +262,6 @@ export default create({
 });
 </script>
 
-<style lang="scss" scoped>
+<style lang="scss">
 @import './index.scss';
 </style>

+ 2 - 0
src/sites/mobile-taro/vue/project.config.json

@@ -39,5 +39,7 @@
     "minifyWXSS": true
   },
   "compileType": "miniprogram",
+  "simulatorType": "wechat",
+  "simulatorPluginLibVersion": {},
   "condition": {}
 }

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

@@ -1,5 +1,6 @@
 export default {
   pages: [
+    'pages/collapse/index',
     'pages/popup/index',
     'pages/icon/index',
     'pages/inputnumber/index',

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

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

+ 93 - 0
src/sites/mobile-taro/vue/src/pages/collapse/index.vue

@@ -0,0 +1,93 @@
+<template>
+  <div class="demo full">
+    <h2>基本用法</h2>
+    <nut-collapse v-model:active="active1" icon="down-arrow">
+      <nut-collapse-item :title="title1" :name="1">
+        <view>京东“厂直优品计划”首推“政府优品馆” 3年覆盖80%镇级政府</view>
+      </nut-collapse-item>
+      <nut-collapse-item :title="title2" :name="2">
+        <view>京东到家:教师节期间 创意花束销量增长53倍</view>
+      </nut-collapse-item>
+      <nut-collapse-item :title="title3" :name="3" disabled>
+      </nut-collapse-item>
+    </nut-collapse>
+    <h2>无icon样式</h2>
+    <nut-collapse v-model:active="active4" :accordion="true">
+      <nut-collapse-item :title="title1" :name="1">
+        2020年中国数字游戏市场规模超2786亿元
+      </nut-collapse-item>
+      <nut-collapse-item :title="title2" :name="2">
+        基于区块链技术的取证APP在浙江省杭州市发布
+      </nut-collapse-item>
+    </nut-collapse>
+    <h2>手风琴</h2>
+    <nut-collapse v-model:active="active2" :accordion="true" icon="down-arrow">
+      <nut-collapse-item :title="title1" :name="1">
+        华为终端操作系统EMUI 11发布,9月11日正式开启
+      </nut-collapse-item>
+      <nut-collapse-item :title="title2" :name="2" :sub-title="subTitle">
+        中国服务机器人市场已占全球市场超1/4
+      </nut-collapse-item>
+      <nut-collapse-item :title="title3" :name="3">
+        QuestMobile:90后互联网用户规模超越80后达3.62亿
+      </nut-collapse-item>
+    </nut-collapse>
+    <h2>自定义折叠图标</h2>
+
+    <nut-collapse
+      v-model:active="active3"
+      :accordion="true"
+      icon="arrow-right2"
+      rotate="90"
+    >
+      <nut-collapse-item :title="title1" :name="1">
+        京东数科IPO将引入“绿鞋机制”
+      </nut-collapse-item>
+      <nut-collapse-item :title="title2" :name="2" sub-title="文本内容">
+        世界制造业大会开幕,阿里巴巴与安徽合作再升级
+      </nut-collapse-item>
+    </nut-collapse>
+    <h2>自定义标题图标</h2>
+    <nut-collapse
+      v-model:active="active5"
+      title-icon="issue"
+      title-icon-color="red"
+      title-icon-size="20px"
+      title-icon-position="left"
+      icon="down-arrow"
+      :accordion="true"
+    >
+      <nut-collapse-item :title="title1" :name="1">
+        “森亿智能”获4亿元D轮融资
+      </nut-collapse-item>
+      <nut-collapse-item :title="title2" :name="2" sub-title="文本内容">
+        快看漫画与全球潮玩集合店X11达成战略合作
+      </nut-collapse-item>
+    </nut-collapse>
+  </div>
+</template>
+<script lang="ts">
+import { reactive, toRefs } from 'vue';
+export default {
+  setup(props, context) {
+    const data = reactive({
+      active1: [1, '2'],
+      active2: 1,
+      active3: 1,
+      active4: 1,
+      active5: 1,
+      title1: '标题1',
+      title2: '标题2',
+      title3: '标题3',
+      subTitle: '副标题'
+    });
+    const change = (name: string) => {
+      console.log(`点击了name是${name}的面板`);
+    };
+    return {
+      change,
+      ...toRefs(data)
+    };
+  }
+};
+</script>