Browse Source

feat: address 组件初始化

yangxiaolu3 4 years ago
parent
commit
1d99af9402

+ 12 - 1
src/config.js

@@ -462,7 +462,18 @@ module.exports = {
     },
     {
       name: '业务组件',
-      packages: []
+      packages: [
+        {
+          version: '3.0.0',
+          name: 'Address',
+          type: 'component',
+          cName: '地址组件',
+          desc: '地址组件',
+          sort: 1,
+          show: true,
+          author: 'yangxiaolu3'
+        }
+      ]
     }
   ]
 };

+ 92 - 0
src/packages/address/demo.vue

@@ -0,0 +1,92 @@
+<template>
+  <div class="demo">
+    <h2>选择自定义地址</h2>
+    <nut-cell
+      title="选择地址"
+      desc="请选择地址  "
+      is-link
+      @click="showAddress"
+    ></nut-cell>
+
+    <nut-address
+      v-model:show="showPopup"
+      :province="province"
+      :city="city"
+      :country="country"
+      :town="town"
+      @onChange="onChange1"
+      @close="close1"
+      customAddressTitle="请选择所在地区"
+    ></nut-address>
+  </div>
+</template>
+
+<script lang="ts">
+import { createComponent } from '@/utils/create';
+import { reactive, ref } from 'vue';
+const { createDemo } = createComponent('address');
+export default createDemo({
+  props: {},
+  setup() {
+    const showPopup = ref(false);
+    const province = ref([
+      { id: 1, name: '北京' },
+      { id: 2, name: '广西' },
+      { id: 3, name: '江西' },
+      { id: 4, name: '四川' }
+    ]); // 省
+
+    const city = ref([
+      { id: 7, name: '朝阳区' },
+      { id: 8, name: '崇文区' },
+      { id: 9, name: '昌平区' },
+      { id: 6, name: '石景山区' }
+    ]); // 市
+
+    const country = ref([
+      { id: 3, name: '八里庄街道' },
+      { id: 9, name: '北苑' },
+      { id: 4, name: '常营乡' }
+    ]); // 县
+
+    const town = ref([]); // 镇
+
+    const text1 = ref('请选择地址');
+    const text2 = ref('请选择地址');
+    const text3 = ref('请选择地址');
+    const text4 = ref('请选择地址');
+
+    const showAddress = () => {
+      showPopup.value = !showPopup.value;
+    };
+
+    const onChange1 = cal => {
+      console.log('change1', cal);
+      if ([cal.next].length < 1) {
+        showPopup.value = false;
+      }
+    };
+    const close1 = val => {
+      console.log(val);
+      text1.value = val.data.addressStr;
+    };
+
+    return {
+      showAddress,
+      province,
+      city,
+      country,
+      town,
+      text1,
+      showPopup,
+      onChange1,
+      close1
+    };
+  }
+});
+</script>
+
+<style lang="scss" scoped>
+.demo {
+}
+</style>

+ 34 - 0
src/packages/address/doc.md

@@ -0,0 +1,34 @@
+#  address组件
+
+    ### 介绍
+    
+    基于 xxxxxxx
+    
+    ### 安装
+    
+    
+    
+    ## 代码演示
+    
+    ### 基础用法1
+    
+
+    
+    ## API
+    
+    ### Props
+    
+    | 参数         | 说明                             | 类型   | 默认值           |
+    |--------------|----------------------------------|--------|------------------|
+    | name         | 图标名称或图片链接               | String | -                |
+    | color        | 图标颜色                         | String | -                |
+    | size         | 图标大小,如 '20px' '2em' '2rem' | String | -                |
+    | class-prefix | 类名前缀,用于使用自定义图标     | String | 'nutui-iconfont' |
+    | tag          | HTML 标签                        | String | 'i'              |
+    
+    ### Events
+    
+    | 事件名 | 说明           | 回调参数     |
+    |--------|----------------|--------------|
+    | click  | 点击图标时触发 | event: Event |
+    

+ 144 - 0
src/packages/address/index.scss

@@ -0,0 +1,144 @@
+.nut-address {
+  .title {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-top: 24px;
+    margin-bottom: 17px;
+    padding: 0 20px;
+    text-align: center;
+    font-weight: bold;
+    color: #333;
+    line-height: 20px;
+    .title-main {
+      color: #262626;
+      font-size: 18px;
+    }
+  }
+
+  // 请选择
+  .custom-address {
+    .region-tab {
+      position: relative;
+      margin-top: 32px;
+      padding: 0 20px;
+      display: flex;
+      font-size: 13px;
+      color: #1d1e1e;
+
+      .tab-item {
+        margin-right: 30px;
+
+        &.active {
+          font-weight: bold;
+        }
+
+        span {
+          display: inline-block;
+          max-width: 100px;
+          white-space: nowrap;
+          overflow: hidden;
+          text-overflow: ellipsis;
+        }
+      }
+
+      .region-tab-line {
+        position: absolute;
+        bottom: -10px;
+        left: 20px;
+        display: inline-block;
+        margin-top: 5px;
+        width: 26px;
+        height: 3px;
+        background: linear-gradient(
+          90deg,
+          rgba(245, 80, 58, 1) 0%,
+          rgba(250, 209, 203, 1) 100%
+        );
+      }
+    }
+
+    .region-con {
+      margin: 20px 20px 0;
+      .region-group {
+        padding-top: 15px;
+        height: 270px;
+        overflow-y: auto;
+
+        .region-item {
+          display: flex;
+          align-items: center;
+          margin-bottom: 20px;
+          font-size: 12px;
+          color: #333;
+          &.active {
+            font-weight: bold;
+          }
+
+          .nut-icon {
+            margin-right: 6px;
+            width: 13px;
+            height: 13px;
+          }
+        }
+      }
+    }
+  }
+
+  // 配送至
+  .exist-address {
+    margin-top: 15px;
+
+    .exist-address-group {
+      padding: 15px 20px 0;
+      height: 279px;
+      overflow-y: scroll;
+
+      .exist-ul {
+        .exist-item {
+          display: flex;
+          align-items: center;
+          margin-bottom: 20px;
+          font-size: 12px;
+          line-height: 14px;
+          color: rgba(51, 51, 51, 1);
+          &.active {
+            font-weight: bold;
+          }
+          svg {
+            margin-right: 9px;
+            width: 13px;
+            height: 13px;
+          }
+          span {
+            display: inline-block;
+            flex: 1;
+          }
+        }
+      }
+    }
+
+    .choose-other {
+      width: 100%;
+      height: 54px;
+      padding: 6px 0px 0;
+      border-top: 1px solid #f2f2f2;
+      .btn {
+        width: 90%;
+        height: 42px;
+        line-height: 42px;
+        margin: auto;
+        text-align: center;
+        background: linear-gradient(
+          135deg,
+          rgba(242, 20, 12, 1) 0%,
+          rgba(242, 39, 12, 1) 70%,
+          rgba(242, 77, 12, 1) 100%
+        );
+        border-radius: 21px;
+        font-size: 15px;
+        color: rgba(255, 255, 255, 1);
+      }
+    }
+  }
+}

+ 405 - 0
src/packages/address/index.vue

@@ -0,0 +1,405 @@
+<template>
+  <nut-popup
+    position="bottom"
+    @close="close"
+    @click-overlay="clickOverlay"
+    @open="closeWay = 'self'"
+    v-model:show="showPopup"
+  >
+    <view class="nut-address">
+      <view class="title">
+        <view
+          class="arrow-back"
+          @click="switchModule"
+          v-if="showModule == 'custom' && type == 'exist' && backBtnIcon"
+          ><nut-icon name="left" color="#CCCCCC"></nut-icon
+        ></view>
+        <view class="arrow-back" v-else></view>
+
+        <view v-if="type == 'custom'">{{ customAddressTitle }}</view>
+        <view v-if="type == 'exist'">{{ existAddressTitle }}</view>
+
+        <view class="arrow-close" @click="handClose('cross')"
+          ><nut-icon name="circle-close" color="#CCCCCC" size="18px"></nut-icon
+        ></view>
+      </view>
+
+      <!-- 请选择 -->
+      <view class="custom-address" v-if="showModule == 'custom'">
+        <view class="region-tab">
+          <view
+            class="tab-item"
+            :class="[index == tabIndex ? 'active' : '']"
+            v-for="(item, key, index) in selectedRegion"
+            :key="index"
+            :ref="'tab-item-' + key"
+            @click="changeRegionTab(item, key, index)"
+            ><span>{{ getTabName(item, index) }}</span></view
+          >
+
+          <view class="region-tab-line" ref="regionLine"></view>
+        </view>
+
+        <view class="region-con">
+          <ul class="region-group">
+            <li
+              v-for="(item, index) in regionList[tabName[tabIndex]]"
+              :key="index"
+              class="region-item"
+              :class="[
+                selectedRegion[tabName[tabIndex]].id == item.id ? 'active' : ''
+              ]"
+              @click="nextAreaList(item)"
+            >
+              <nut-icon
+                type="self"
+                name="circle-close"
+                v-if="selectedRegion[tabName[tabIndex]].id == item.id"
+              ></nut-icon
+              >{{ item.name }}
+            </li>
+          </ul>
+        </view>
+      </view>
+    </view>
+  </nut-popup>
+</template>
+<script lang="ts">
+import { reactive, ref, toRefs, watch, nextTick } from 'vue';
+import { createComponent } from '@/utils/create';
+const { componentName, create } = createComponent('address');
+
+export default create({
+  props: {
+    show: {
+      type: Boolean,
+      default: false
+    },
+    type: {
+      type: String,
+      default: 'custom'
+    },
+    customAddressTitle: {
+      type: String,
+      default: '请选择所在地区'
+    },
+    province: {
+      type: Array,
+      default: () => []
+    },
+    city: {
+      type: Array,
+      default: () => []
+    }, // 市
+    country: {
+      type: Array,
+      default: () => []
+    }, // 县
+    town: {
+      type: Array,
+      default: () => []
+    }, // 镇
+    isShowCustomAddress: {
+      type: Boolean,
+      default: true
+    }, // 是否显示‘选择其他地区’按钮 type=‘exist’ 生效
+    existAddress: {
+      type: Array,
+      default: () => []
+    }, // 现存地址列表
+    existAddressTitle: {
+      type: String,
+      default: '配送至'
+    },
+    customAndExistTitle: {
+      type: String,
+      default: '选择其他地址'
+    },
+    defaultIcon: {
+      // 地址选择列表前 - 默认的图标
+      type: String,
+      default: ''
+    },
+    selectedIcon: {
+      // 地址选择列表前 - 选中的图标
+      type: String,
+      default: ''
+    },
+    closeBtnIcon: {
+      // 关闭弹框按钮 icon
+      type: String,
+      default: ''
+    },
+    backBtnIcon: {
+      // 选择其他地址左上角返回 icon
+      type: String,
+      default: ''
+    }
+  },
+  components: {},
+  emits: [
+    'update:show',
+    'onChange',
+    'on-change',
+    'selected',
+    'close',
+    'closeMask',
+    'close-mask',
+    'switchModule',
+    'switch-module'
+  ],
+
+  setup(props, { emit }) {
+    console.log('componentName', componentName);
+
+    const { existAddress, isShowCustomAddress, type, closeBtnIcon } = toRefs(
+      props
+    );
+
+    let showPopup = ref(false);
+    let showModule = ref('exist'); //展示 exist 还是 custom 主要用于’选择其他地址‘
+    let tabIndex = ref(0);
+    const tabName = ref(['province', 'city', 'country', 'town']);
+
+    let regionList = reactive({
+      province: props.province,
+      city: props.city,
+      country: props.country,
+      town: props.town
+    });
+
+    let selectedRegion = reactive({
+      province: {},
+      city: {},
+      country: {},
+      town: {},
+      addressIdStr: '',
+      addressStr: ''
+    }); //已选择的 省、市、县、镇
+
+    let selectedExistAddress = reactive({}); // 当前选择的地址
+
+    let closeWay = ref('self');
+
+    //获取已选地区列表名称
+    // const getTabName = (item, index)=>{
+    //   if (item.name) return item.name;
+
+    //   if (tabIndex.value < index) {
+    //     return item.name;
+    //   } else {
+    //     return '请选择';
+    //   }
+    // }
+    // 切换下一级列表
+    // const nextAreaList = (item) => {
+    //   // onchange 接收的参数
+    //   const calBack = {
+    //     custom: tabName.value[tabIndex.value]
+    //   };
+
+    //   selectedRegion[tabName[tabIndex.value]] = item;
+
+    //   for (let i = tabIndex.value; i < tabIndex.value - 1; i++) {
+    //     selectedRegion[tabName[i + 1]] = {};
+    //   }
+
+    //   if (tabIndex.value < 3) {
+    //     tabIndex.value = tabIndex.value + 1;
+    //     lineAnimation();
+
+    //     // 切换下一个
+    //     calBack.next = tabName[tabIndex.value];
+    //     calBack.value = item;
+    //     emit('onChange', calBack);
+    //     emit('on-change', calBack);
+    //   } else {
+    //     handClose();
+    //   }
+    // }
+    //切换地区Tab
+    // const changeRegionTab = (item, key, index) => {
+    //   tabIndex.value = index;
+    //   lineAnimation();
+    // }
+    // 移动下面的红线
+    // const lineAnimation = () => {
+    //   const name = 'tab-item-' + tabName[tabIndex.value];
+    //   nextTick(() => {
+    //     // if (this.$refs[name] && this.$refs[name][0]) {
+    //     //   const distance = this.$refs[name][0].offsetLeft;
+    //     //   TweenMax.to(this.$refs.regionLine, 0.5, { left: distance });
+    //     // }
+    //   });
+    // }
+
+    // 选择现有地址
+    // const selectedExist = (item) => {
+    //   let copyExistAdd = existAddress;
+    //   let prevExistAdd = {};
+
+    //   copyExistAdd.forEach((list, index) => {
+    //     if (list.selectedAddress) {
+    //       prevExistAdd = list;
+    //     }
+    //     list.selectedAddress = false;
+    //   });
+
+    //   item.selectedAddress = true;
+
+    //   selectedExistAddress = item;
+
+    //   emit('selected', prevExistAdd, item, copyExistAdd);
+
+    //   handClose();
+    // }
+
+    // 关闭
+    const close = () => {
+      const that = this;
+
+      const resCopy = Object.assign({}, selectedRegion);
+
+      const res = {
+        type: showModule.value,
+        data: {}
+      };
+
+      if (showModule.value == 'custom') {
+        const { province, city, country, town } = resCopy;
+
+        resCopy.addressIdStr = [
+          (province as any).id || 0,
+          (city as any).id || 0,
+          (country as any).id || 0,
+          (town as any).id || 0
+        ].join('_');
+        resCopy.addressStr = [
+          (province as any).name,
+          (city as any).name,
+          (country as any).name,
+          (town as any).name
+        ].join('');
+        res.data = resCopy;
+      } else {
+        res.data = selectedExistAddress;
+      }
+
+      // initAddress();
+
+      if (closeWay.value == 'self') {
+        emit('close', res);
+      } else {
+        emit('closeMask', { closeWay: closeWay });
+        emit('close-mask', { closeWay: closeWay });
+      }
+    };
+    // 手动关闭 点击叉号(cross),或者蒙层(mask)
+    // const handClose = (type = 'self') => {
+    //   if (!closeBtnIcon) return;
+
+    //   closeWay.value = type == 'cross' ? 'cross' : 'self';
+
+    //   showPopup.value = false;
+    // }
+    // 点击遮罩层关闭
+    const clickOverlay = () => {
+      closeWay.value = 'mask';
+    };
+    // 初始化
+    // const initAddress = () => {
+    //   for (let i = 0; i < tabName.value.length; i++) {
+    //     selectedRegion[tabName[i]] = {};
+    //   }
+    //   tabIndex.value = 0;
+    //   lineAnimation();
+    // }
+    // 选择其他地址
+    const switchModule = () => {
+      if (showModule.value == 'exist') {
+        showModule.value = 'custom';
+      } else {
+        showModule.value = 'exist';
+      }
+
+      // initAddress();
+
+      emit('switchModule', { type: showModule.value });
+      emit('switch-module', { type: showModule.value });
+    };
+
+    watch(
+      () => props.show,
+      value => {
+        showPopup.value = value;
+      }
+    );
+
+    watch(
+      () => showPopup.value,
+      value => {
+        if (!value) {
+          emit('update:show', false);
+        } else {
+          console.log('打开地址弹框');
+        }
+      }
+    );
+
+    watch(
+      () => props.province,
+      value => {
+        regionList.province = value;
+      }
+    );
+    watch(
+      () => props.city,
+      value => {
+        regionList.city = value;
+      }
+    );
+    watch(
+      () => props.country,
+      value => {
+        regionList.country = value;
+      }
+    );
+    watch(
+      () => props.town,
+      value => {
+        regionList.town = value;
+      }
+    );
+
+    watch(
+      () => props.existAddress,
+      value => {
+        //  existAddress.value = newVal;
+        // value.forEach((item, index) => {
+        //   if (item.selectedAddress) {
+        //     this.selectedExistAddress = item;
+        //   }
+        // });
+      }
+    );
+
+    return {
+      showPopup,
+      showModule,
+      tabIndex,
+      tabName,
+      regionList,
+      selectedRegion,
+      selectedExistAddress,
+      switchModule,
+      closeWay,
+      close,
+      ...toRefs(props)
+    };
+  }
+});
+</script>
+
+<style lang="scss">
+@import 'index.scss';
+</style>