杨小璐 5 years ago
parent
commit
4d4b696006

+ 1 - 0
package.json

@@ -96,6 +96,7 @@
         "folder-hash": "^2.1.2",
         "friendly-errors-webpack-plugin": "1.7.0",
         "google-code-prettify": "1.0.5",
+        "gsap": "^3.2.6",
         "has": "1.0.3",
         "highlight.js": "^9.13.1",
         "html-webpack-plugin": "3.2.0",

File diff suppressed because it is too large
+ 17 - 0
src/assets/svg/address-location.svg


File diff suppressed because it is too large
+ 13 - 0
src/assets/svg/arrows-back.svg


File diff suppressed because it is too large
+ 13 - 0
src/assets/svg/hook-red.svg


File diff suppressed because it is too large
+ 15 - 0
src/assets/svg/tick-red.svg


+ 10 - 0
src/config.json

@@ -622,6 +622,16 @@
       "sort": "5",
       "showDemo": true,
       "author": "张宇"
+    },
+    {
+      "version": "1.0.0",
+      "name": "Address",
+      "chnName": "地址选择",
+      "desc": "业务功能-地址选择",
+      "type": "component",
+      "sort": "6",
+      "showDemo": true,
+      "author": "yangxiaolu"
     }
   ]
 }

+ 4 - 1
src/nutui.js

@@ -120,6 +120,8 @@ import SideNavBarItem from "./packages/sidenavbaritem/index.js";
 import "./packages/sidenavbaritem/sidenavbaritem.scss";
 import Drag from "./packages/drag/index.js";
 import "./packages/drag/drag.scss";
+import Address from "./packages/address/index.js";
+import "./packages/address/address.scss";
 
 const packages = {
   Cell,
@@ -180,7 +182,8 @@ const packages = {
   SideNavBar: SideNavBar,
   SubSideNavBar: SubSideNavBar,
   SideNavBarItem: SideNavBarItem,
-  Drag: Drag
+  Drag: Drag,
+  Address: Address
 };
 
 const components = {};

+ 152 - 0
src/packages/address/address.scss

@@ -0,0 +1,152 @@
+.nut-address{
+    .title{
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        margin-top: 20px;
+        padding: 0 20px;
+        text-align: center;
+        font-size: 16px;
+        font-weight: bold;
+        color: #333;
+        line-height: 20px;
+        svg{
+            width: 20px;
+            height: 20px;
+        }
+        .arrow{
+            display: inline-block;
+            width: 20px;
+            height: 20px;
+            svg{
+                width: 20px;
+                height: 20px;
+            }
+        }
+    }
+    // 请选择
+    .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: scroll;
+    
+                .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);
+            }
+        }
+        
+    }
+
+    .nut-icon{
+        display: flex;
+        align-items: center;
+        svg{
+            margin-top: -1px;
+        }
+    }
+}

+ 290 - 0
src/packages/address/address.vue

@@ -0,0 +1,290 @@
+<template>
+    <div class="nut-address">
+        <nut-popup v-model="showPopup" round position="bottom" class="choose-address" @close="close">
+            <div class="title">
+                <span class="arrow" @click="switchModule">
+                    <nut-icon v-if="showModule == 'custom' && type == 'exist'" type="self" :url="require('../../assets/svg/arrows-back.svg')"></nut-icon>
+                   
+                </span>
+
+                <span v-if="type == 'custom'">请选择所在地区</span>
+                <span v-if="type == 'exist'">配送至</span>
+
+                <span @click="close('close')"><nut-icon type="circle-cross" size="18px"></nut-icon></span>
+            </div>
+
+            <!-- 请选择 -->
+            <div class="custom-address" v-if="showModule == 'custom' ">
+                <div class="region-tab">
+                    <div 
+                        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></div>
+
+                    <span class="region-tab-line" ref="regionLine"></span>
+                </div>
+
+                <div 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" :url="require('../../assets/svg/hook-red.svg')" v-if="selectedRegion[tabName[tabIndex]].id == item.id"></nut-icon>{{item.name}}</li>
+                    </ul>
+                </div>
+            </div>
+            <!-- 配送至 -->
+            <div class="exist-address" v-if="showModule == 'exist'">
+                <div class="exist-address-group">
+                    <ul class="exist-ul">
+                        <li 
+                            class="exist-item" 
+                            :class="[item.selectedAddress ?'active':'']"
+                            v-for="(item,index) in existAddress" 
+                            :key='index'
+                            @click="selectedExist(item)"
+                        >
+                            <nut-icon  type="self" :url="item.selectedAddress?require('../../assets/svg/tick-red.svg'):require('../../assets/svg/address-location.svg')"></nut-icon>
+                            <!-- <nut-icon v-if="item.selectedAddress == false" type="self" :url="require('../../assets/svg/address-location.svg')"></nut-icon> -->
+                            
+                            <span>{{item.provinceName + item.cityName + item.countyName + item.townName + item.addressDetail }}</span>
+                        </li>
+                    </ul>
+                </div>
+
+                <div class="choose-other" @click="switchModule">
+                    <div class="btn">选择其他地址</div>
+                </div>
+            </div>
+        </nut-popup>
+    </div>
+</template>
+<script>
+import Popup from "./../popup/popup.vue";
+import Icon from "./../icon/icon.vue";
+import { TweenMax } from 'gsap'
+export default {
+    name:'nut-address',
+    props: {
+        value: {
+            type: Boolean,
+            default: false
+        },
+        type: {
+            type: String,
+            default: 'custom'
+        },
+        province:{
+            type: Array,
+            default: () => []
+        }, // 省
+        city:{
+            type: Array,
+            default: () => []
+        },// 市
+        country:{
+            type: Array,
+            default: () => []
+        },// 县
+        town:{
+            type: Array,
+            default: () => []
+        }, // 镇
+
+        existAddress:{
+            type: Array,
+            default: () => []
+        }, // 现存地址列表
+    },
+    data() {
+        return {
+            showPopup:false,
+            showModule:'exist', //展示 exist 还是 custom 主要用于‘选择其他地址’
+            tabIndex: 0 ,
+            tabName:['province', 'city', 'country', 'town'],
+            regionList:{
+                'province': [],
+                'city': [],
+                'country': [],
+                'town': []
+            }, //省、市、县、镇列表,地址id字符串,地址字符串
+            selectedRegion:{
+                'province': {},
+                'city': {},
+                'country': {},
+                'town': {}
+            }, //已选择的 省、市、县、镇
+
+            selectedExistAddress:{}, // 当前选择的地址
+        }
+    },
+    components: {
+        "nut-popup": Popup,
+        "nut-icon":Icon
+    },
+    watch:{
+        value(newVal,oldVal){
+            this.showPopup = newVal
+        },
+        showPopup(newVal,oldVal){
+            if (newVal == false) this.$emit('input', false)
+            if (newVal == true) {
+                if (this.type == 'custom') {
+                    // this.getProvinceId('province')
+                } else {
+                    console.log(this.existAddress)
+                }
+
+                this.showModule = this.type
+            }
+        },
+        province(newVal,oldVal){
+            this.regionList.province = newVal
+        },
+        city(newVal,oldVal){
+            this.regionList.city = newVal
+        },
+        country(newVal,oldVal){
+            this.regionList.country = newVal
+        },
+        town(newVal,oldVal){
+
+            if(newVal.length > 0){
+                this.regionList.town = newVal
+            }else{
+                
+                // this.close()
+                this.showPopup = false
+            }
+            
+        }
+    },
+    mounted(){
+        
+    },
+    methods: {
+
+        //获取已选地区列表名称
+        getTabName(item, index) {
+
+            if (item.name) return item.name
+
+            if (this.tabIndex < index) {
+                return item.name
+            } else {
+                return '请选择'
+            }
+        },
+        // 切换下一级列表
+        nextAreaList(item) {
+            // onchange 接收的参数
+            const calBack = {
+                custom:this.tabName[this.tabIndex]
+            }
+
+            this.selectedRegion[this.tabName[this.tabIndex]] = item
+
+            for (let i = this.tabIndex; i < this.tabName.length - 1; i++) {
+                this.selectedRegion[this.tabName[i + 1]] = {}
+            }
+            
+            if (this.tabIndex < 3) {
+
+                this.tabIndex = this.tabIndex + 1
+                this.lineAnimation()
+
+                // 切换下一个
+                calBack.next = this.tabName[this.tabIndex]
+                calBack.value = item
+                this.$emit('onChange', calBack)
+
+            } else {
+                // this.close()
+                this.showPopup = false
+            }
+        },
+        //切换地区Tab
+        changeRegionTab(item, key, index) {
+            this.tabIndex = index
+            this.lineAnimation()
+        },
+        // 移动下面的红线
+        lineAnimation() {
+            const name = 'tab-item-' + this.tabName[this.tabIndex]    
+            this.$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 })
+                }
+            })
+        },
+
+        // 选择现有地址
+        selectedExist(item) {
+
+            this.selectedExistAddress = item
+
+            this.$emit('selected',item)
+            // this.close()
+            this.showPopup = false
+        },
+
+        // 关闭
+        close(type){
+            this.showPopup = false
+            const that = this
+
+            const resCopy = Object.assign({}, this.selectedRegion) 
+        
+            const res = {
+                type: this.showModule,
+                data: {}
+            }
+            
+            if (this.showModule == 'custom') {
+                const { province, city, country, town } = resCopy
+                resCopy.addressIdStr = [province.id || 0, city.id || 0, country.id || 0, town.id || 0].join('_')
+                resCopy.addressStr = [province.name, city.name, country.name, town.name].join('')
+                res.data = resCopy
+            } else {
+                res.data = this.selectedExistAddress
+            }
+            
+            
+            this.initAddress()
+
+            if(type == 'close'){
+                // this.$emit('close',res)
+            }else{
+                this.$emit('close',res)
+            }
+            // console.log('关闭')
+            setTimeout(()=>{
+                that.showModule = 'type'
+            },500)
+            
+        },
+        // 初始化
+        initAddress(){
+            for (let i = 0; i < this.tabName.length; i++) {
+                this.selectedRegion[this.tabName[i]] = {}
+            }
+            this.tabIndex = 0
+            this.lineAnimation()
+        },
+        // 选择其他地址
+        switchModule() {
+            this.showModule = this.showModule == 'exist' ? 'custom' : 'exist'
+            this.initAddress()
+        }
+    }
+}
+</script>

File diff suppressed because it is too large
+ 15 - 0
src/packages/address/data.js


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

@@ -0,0 +1,126 @@
+<template>
+    <div class="address-box">
+      <div class="address-list init" @click="showAddress">
+
+        <div class="titile">选择地址</div>
+        <div class="choose">{{text1}}</div>
+
+      </div>
+
+      <div class="address-list other" @click="showAddressOther">
+        <div class="titile">选择地址</div>
+        <div class="choose">{{text2}}</div>
+      </div>
+
+
+      <nut-address v-model="showPopup" :province="province" :city="city" :country="country" :town="town" @onChange="onChange1" @close="close1"></nut-address>
+
+      <nut-address v-model="showPopupOther" type="exist" :existAddress="existAddress" :province="province" :city="city" :country="country" :town="town" @onChange="onChange2" @close="close2" @selected="selected"></nut-address>
+    </div>
+</template>
+
+<script>
+import Address from './data'
+export default {
+  components: {
+
+  },
+  data() {
+    return {
+      showPopup:false,
+      province:[], // 省
+      city:[],// 市
+      country:[],// 县
+      town:[], // 镇
+
+      showPopupOther:false,
+      existAddress:[],
+
+      text1:'省市区县、乡镇等',
+      text2:''
+    };
+  },
+  methods: {
+      showAddress(){
+        this.showPopup = true
+        const that = this
+        setTimeout(()=>{
+          that.province = Address.province
+        },1000)
+      },
+
+      showAddressOther(){
+        this.showPopupOther = true
+        const that = this
+        this.existAddress = [].concat(Address.addressList)
+        setTimeout(()=>{
+          that.province = Address.province
+        },1000)
+      },
+
+      onChange1(cal){
+        console.log('change1',cal)
+        this[cal.next] = [].concat(Address[cal.next])
+      },
+      onChange2(cal){
+        console.log('change2',cal)
+        this[cal.next] = [].concat(Address[cal.next])
+      },
+      close1(val){
+        console.log(val)
+        this.text1 = val.data.addressStr
+      },
+      close2(val){
+        console.log(val)
+        if(val.type == 'exist'){
+          this.text2 = val.data.fullAddress
+        }else{
+          this.text2 = val.data.addressStr
+        }
+        
+      },
+      selected(val){
+        this.existAddress.forEach((item)=>{
+         
+          this.$set(item,'selectedAddress',false)
+          if(item.id == val.id){
+            this.$set(item,'selectedAddress',true)
+          }
+        })
+      }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.address-box{
+  margin-top: 10px;
+
+  .address-list{
+    margin-bottom: 10px;
+    background: #fff;
+    border-radius: 4px;
+    font-size: 14px;
+    padding: 10px;
+
+    &.init{
+      display: flex;
+      .titile{
+        margin-right: 15px;
+      }
+      .choose{
+        color: #999;
+      }
+    }
+    &.other{
+      display: flex;
+      .titile{
+        margin-right: 15px;
+      }
+      .choose{
+        color: #999;
+      }
+    }
+  }
+}
+</style>

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

@@ -0,0 +1,2 @@
+# Address 地址选择
+

+ 8 - 0
src/packages/address/index.js

@@ -0,0 +1,8 @@
+import Address from './address.vue';
+import './address.scss';
+
+Address.install = function(Vue) {
+  Vue.component(Address.name, Address);
+};
+
+export default Address

+ 1 - 0
types/nutui.d.ts

@@ -80,3 +80,4 @@ export declare class SubSideNavBar extends UIComponent { }
 export declare class SideNavBarItem extends UIComponent { }
 export declare class Qart extends UIComponent { }
 export declare class Drag extends UIComponent { }
+export declare class Address extends UIComponent {}