vue插件开发-可拖动城市选择插件

插件通常用来为 Vue 添加全局功能

使用插件

通过 Vue 的全局方法 Vue.use() 使用,

当然,在使用这个方法之前,你的 Vue 实例 必须已经初始化完成

// 引入自定义chooseCity 提示框
import chooseCity from './plugin/chooseCity/index'
Vue.use(chooseCity)

实际案例:

返回数据格式:

{"data":[{"name":"北京","type":"leafNode","value":110000},{"name":"上海","type":"leafNode","value":310000},{"name":"天津","type":"leafNode","value":120000},{"name":"重庆","type":"leafNode","value":500000},{"name":"安徽","type":"next","value":340000},{"name":"福建","type":"next","value":350000},{"name":"甘肃","type":"next","value":620000},{"name":"广东","type":"next","value":440000},{"name":"广西","type":"next","value":450000},{"name":"贵州","type":"next","value":520000},{"name":"海南","type":"next","value":460000},{"name":"河北","type":"next","value":130000},{"name":"河南","type":"next","value":410000},{"name":"黑龙江","type":"next","value":230000},{"name":"湖北","type":"next","value":420000},{"name":"湖南","type":"next","value":430000},{"name":"吉林","type":"next","value":220000},{"name":"江苏","type":"next","value":320000},{"name":"江西","type":"next","value":360000},{"name":"辽宁","type":"next","value":210000},{"name":"内蒙古","type":"next","value":150000},{"name":"宁夏","type":"next","value":640000},{"name":"青海","type":"next","value":630000},{"name":"山东","type":"next","value":370000},{"name":"山西","type":"next","value":140000},{"name":"陕西","type":"next","value":610000},{"name":"四川","type":"next","value":510000},{"name":"西藏","type":"next","value":540000},{"name":"新疆","type":"next","value":650000},{"name":"云南","type":"next","value":530000},{"name":"浙江","type":"next","value":330000},{"name":"港澳台","type":"next","value":800000},{"name":"海外","type":"next","value":810000},{"name":"其它","type":"leafNode","value":999999}],"state":0,"status":"SUCCESS","statusCode":0}
城市数据结构

模板部分

<template>
  <div class="chooseCity_warp" @click="closeCallBack(false,chooseCity)" id="chooseCity" v-if="show" >
        <div class="count el-dialog__header" @click.stop="" @mousedown="dowMos" @mouseup="upMos" @mousemove="move" >
            <div class="count_body">
                <!-- 头部展示区域 -->
                <div class="body_header" >
                    <div class="header_tip">{{config.title}}</div>
                    <div class="header_close"><span @click="closeCallBack(false,chooseCity)" class="close"></span></div>
                </div>
                <!-- 选中区域显示 -->
                <div class="body_cont">
                    <!-- 选中文案显示部分 -->
                    <div class="choose_cont">
                        <div class="title">已选择:</div>
                        <div class="choose_msg">
                            <div class="item" v-for="(item,index) in chooseCity" :key="index"><span class="text">{{item.name}}</span><span @click="clearCity(item)" class="delete"></span></div>
                        </div>
                    </div>
                    <!-- 选择省、市部分 -->
                    <div class="choose_list_header">
                        <div class="header_item">省份/直辖市</div>
                        <div class="header_item">城市</div>
                    </div>
                    <div class="choose_list">
                        <div class="choose_provinceList">
                            <div class="provinceList_item city_item" v-for="(itemPro,indexPro) in provinceList" :key="indexPro+'pro'"  @click="choosePro(itemPro)">
                                <div :class="{'proItem':itemPro.type != 'next','cityItem':itemPro.type == 'next','chooseActiveCity':chooseIds.includes(itemPro.value),'chooseActivePro':chooseProIds.includes(itemPro.value)}">{{itemPro.name}}</div>
                            </div>
                        </div>
                        <div class="cityList">
                            <div class="cityList_item city_item" v-for="(itemCit,indexCit) in cityList" :key="indexCit+'cit'" @click="chooseCit(itemCit)">
                                <div :class="{'proItem':itemCit.type != 'next','chooseActiveCity':chooseIds.includes(itemCit.value)}">{{itemCit.name}}</div>
                            </div>
                        </div>
                    </div>
                </div>
                <!-- 底部按钮区域 -->
                <div class="body_bot">
                    <span class="sure" v-if="config.r_btn && config.r_btn!=''"  @click="sureCallBack(true,config)">{{config.r_btn}}</span>
                    <span class="close" v-if="config.l_btn && config.l_btn!=''" @click="closeCallBack(false)">{{config.l_btn}}</span>
                </div>
                <!-- <div v-if="config.title !=''" class="confirm_header">
                    <slot name="confirm_header">{{config.title}}</slot>
                </div>
                <div class="confirm_body">
                        <div v-if="config.vnode && config.vnode!=''" v-html="config.vnode" > </div>
                        <span v-else>{{config.content}}</span>
                </div>
                <div class="confirm_footer">
                    <span class="sure" v-if="config.r_btn && config.r_btn!=''"  @click="sureCallBack(true)">{{config.r_btn}}</span>
                    <span class="close" v-if="config.l_btn && config.l_btn!=''" @click="closeCallBack(false)">{{config.l_btn}}</span>
                </div> -->
            </div>
        </div>
  </div>
</template>

<script>

export default {
  name: '',
  data() { 
    return {
        show: false,
        // 请求参数 0代表请求省级别 初始化 请求 一次
        cityParams:{
            parentId:'0'
        },
        chooseNum:1,
        proFlag:false,
        // 选中城市id集合
        chooseIds:[],
        // 选中省id集合
        chooseProIds:[],
        // 选中城市集合
        chooseCity:[],
        // 省集合
        provinceList:[],
        // 市集合
        cityList:[]
    }
  },
  props: {
      confirmOption:{
          type:Object,
          default:()=>{
              return {
                // 'title':'',
                // 'content':'',
                // 'r_btn':'继续',
                // 'l_btn':'取消',
              }
          }
      }
  },
  components:{
  },
  mounted() {
    //   console.log('this.config')
    //   console.log(this.config)
  },
  methods:{

  },
 }
</script>

<style scoped>
.chooseCity_warp{background-color: rgba(255, 255, 255, 0.5);position: fixed;top: 0px;left: 0px;width: 100%;height: 100%;z-index: 1111;}
.chooseCity_warp .count{min-width: 560px;position: absolute;top: 50%;left: 50%;transform: translateX(-50%) translateY(-50%);background-color: #ffffff;border-radius: 4px;  border-radius: 10px;background-color: rgba(102,102,102,0.2);padding: 10px;}
.chooseCity_warp .count .count_body{background-color: #ffffff;}
.chooseCity_warp .count .count_body .body_header{background-color: #555b65;color: #fff;border-radius: 3px 3px 0 0;height: 27px;display: flex;align-items: center;padding-left: 10px;} 
.chooseCity_warp .count .count_body .body_header .header_tip{flex: 4;}
.chooseCity_warp .count .count_body .body_header .header_close{flex: 1;}
.chooseCity_warp .count .count_body .body_header .header_close .close{
    background: transparent url('../../assets/dialog_close.png') left top no-repeat;
    width: 19px;
    height: 19px;
    text-indent: -9999px;
    float: right;
    cursor: pointer;
    margin: 4px;
    overflow: hidden;
}
.chooseCity_warp .count .count_body .body_header .header_close .close:hover{
    background: transparent url('../../assets/dialog_close.png') left bottom no-repeat;
}
.chooseCity_warp .count .count_body .body_cont{padding: 15px;padding-bottom: 0px;}
.chooseCity_warp .count .count_body .body_cont .choose_cont{padding: 6px 0px;display: flex;align-items: center;}
.chooseCity_warp .count .count_body .body_cont .choose_cont .title{}
.chooseCity_warp .count .count_body .body_cont .choose_cont .choose_msg{display: flex;align-items: center;}
.chooseCity_warp .count .count_body .body_cont .choose_cont .choose_msg .item{
    background: transparent url('../../assets/pickpad.gif') no-repeat scroll left top;
    cursor: default;
    float: left;
    height: 20px;
    line-height: 20px;
    margin: 4px 2px;
    padding-left: 8px;
    padding-right: 20px;
    white-space: nowrap;
    color: #333;
}
.chooseCity_warp .count .count_body .body_cont .choose_cont .choose_msg .item .delete {
    background: url('../../assets/pickpad.gif') no-repeat scroll right top transparent;
    cursor: pointer;
    display: inline-block;
    height: 20px;
    margin-left: 6px;
    margin-right: -20px;
    text-indent: -99999px;
    width: 20px;
}
.chooseCity_warp .count .count_body .body_cont .choose_cont .choose_msg .item .delete:hover{
    background: url('../../assets/pickpad.gif') no-repeat scroll right bottom transparent;
}
.chooseCity_warp .count .count_body .body_cont .choose_cont .choose_msg .item .text{vertical-align: super;}
.chooseCity_warp .count .count_body .body_cont .choose_list {display: flex;border: 1px solid #d9d9d9;}
.chooseCity_warp .count .count_body .body_cont .choose_list_header {display: flex;border: 1px solid #d9d9d9;border-bottom: 0px solid #d9d9d9;border-right: 0px solid #d9d9d9;}
.chooseCity_warp .count .count_body .body_cont .choose_list_header .header_item {flex: 1; text-align: center;  color: #999999; background: #f5f5f5;    line-height: 34px;border-right: 1px solid #d9d9d9;}
.chooseCity_warp .count .count_body .body_cont .choose_list .choose_provinceList {flex: 1;height: 340px;overflow-y: scroll;}
.chooseCity_warp .count .count_body .body_cont .choose_list .cityList {flex: 1;height: 340px;overflow-y: scroll;}
.chooseCity_warp .count .count_body .body_cont .choose_list .city_item{ line-height: 34px;border-bottom: 1px solid #d9d9d9;}
.chooseCity_warp .count .count_body .body_cont .choose_list .proItem {
    padding: 0px 10px 0px 20px;
    background-image: url("data:image/IFf;base64,R0lGODlhFQAIAIABAP///wAAACH5BAEAAAEALAAAAAAVAAgAAAIXjH8AyJ3qolFLBviqpRPbTmkfKI6deRQAOw==");
}
.chooseCity_warp .count .count_body .body_cont .choose_list .proItem:hover {
    background: #e4f7f9 right center no-repeat;
    background-image: url("data:image/IFf;base64,R0lGODlhFQAIAIABAA+puwAAACH5BAEAAAEALAAAAAAVAAgAAAIXjH8AyJ3qolFLBviqpRPbTmkfKI6deRQAOw==");
}
.chooseCity_warp .count .count_body .body_cont .choose_list .cityItem{
    padding: 0px 10px 0px 20px;
    background-image: url("data:image/IFf;base64,R0lGODlhFQAIAIABAP///wAAACH5BAEAAAEALAAAAAAVAAgAAAIXjH8AyJ3qolFLBviqpRPbTmkfKI6deRQAOw==");
}
.chooseCity_warp .count .count_body .body_cont .choose_list .cityItem:hover {
    background: #e4f7f9 right center no-repeat;
    background-image: url("data:image/IFf;base64,R0lGODlhFAALAIABAA+puwAAACH5BAEAAAEALAAAAAAUAAsAAAIVRI6pauvbngxxumpZhlt3hG1hNmYFADs=");
}
.chooseCity_warp .count .count_body .body_cont .choose_list .chooseActiveCity{
    color: #fff;
    background: #0fa9bb right center no-repeat;
    background-image: url("data:image/IFf;base64,R0lGODlhFQAIAIABAP///wAAACH5BAEAAAEALAAAAAAVAAgAAAIXjH8AyJ3qolFLBviqpRPbTmkfKI6deRQAOw==")
}
.chooseCity_warp .count .count_body .body_cont .choose_list .chooseActiveCity:hover{
    color: #fff;
    background: #0fa9bb right center no-repeat;
    background-image: url("data:image/IFf;base64,R0lGODlhFQAIAIABAP///wAAACH5BAEAAAEALAAAAAAVAAgAAAIXjH8AyJ3qolFLBviqpRPbTmkfKI6deRQAOw==")
}
.chooseCity_warp .count .count_body .body_cont .choose_list .chooseActivePro{
    color: #fff;
    background: #0fa9bb right center no-repeat;
    background-image: url("data:image/IFf;base64,R0lGODlhFAALAIABAP///wAAACH5BAEAAAEALAAAAAAUAAsAAAIVRI6pauvbngxxumpZhlt3hG1hNmYFADs=")
}
.chooseCity_warp .count .count_body .body_cont .choose_list .chooseActivePro:hover{
    color: #fff;
    background: #0fa9bb right center no-repeat;
    background-image: url("data:image/IFf;base64,R0lGODlhFAALAIABAP///wAAACH5BAEAAAEALAAAAAAUAAsAAAIVRI6pauvbngxxumpZhlt3hG1hNmYFADs=")
}
.chooseCity_warp .count .count_body .body_bot {background-color: #f6f6f6;padding: 8px 20px 8px 0;text-align: right;}
.chooseCity_warp .count .count_body .body_bot .sure{color: #333;line-height: 26px;display: inline-block;height: 26px;padding: 2px 20px;border-radius: 4px;margin-right: 10px;background-color: #fee579;border: 1px solid #c6c6c6;cursor: pointer;}
.chooseCity_warp .count .count_body .body_bot .close{color: #333;line-height: 26px;display: inline-block;height: 26px;padding: 2px 20px;border-radius: 4px;background-color: #f8f8f8;border: 1px solid #c6c6c6;cursor: pointer;}
/* .chooseCity_warp .count .count_body .body_bot .close{color: #333;} */
/* .confirm_warp .count .confirm_header{text-align: center;}
.confirm_warp .count .confirm_body{padding: 20px;min-height: 40px;}
.confirm_warp .count .confirm_footer{padding: 20px;min-height: 40px;text-align: center;}
.confirm_warp .count .confirm_footer span{display: inline-block;font-size: 14px;padding: 4px 25px;cursor: pointer;border-radius: 2px;}
.confirm_warp .count .confirm_footer .sure{background-color: #E62679;color: #ffffff;}
.confirm_warp .count .confirm_footer .close{background-color: #ffffff;color: #E62679;border: 1px solid #E62679;padding: 3px 25px;margin-left: 20px;} */

</style>
template模板

逻辑部分

import chooseCity from './index.vue'
import { cityList } from '../../api/accountaudit/talentManagement'

import Vue from 'vue'
// import { isVNode } from 'element-ui/src/utils/vdom';
/*
 *    this.$lstchooseCity({
        title:'这是什么东西~',//标题
        chooseNum:1,//选中城市数量
        draco:false,//是否允许拖动
        proFlag: false,// 是否允许获取省级别
        chooseCity:[
            {
                name: "上海",
                type: "leafNode",
                value: 310000
            }
        ],// 选中城市 默认选中
        r_btn:'确定',//左侧按钮
        l_btn:'取消',//右侧按钮
        vnode:'<span>饿哦是内容区域</span>',//内容自定义部分
        content:"我也不知道这是什么东西啊!!!"//内容文本
      }).then(res=>{
          console.log(res)//点击按钮回调函数
      })
 */

let chooseCityIndex = {};

chooseCityIndex.install = function (Vue,options) {
    const confirmIndexConstructor = Vue.extend(chooseCity);

    // 生成一个该之类的实例
    const instance = new confirmIndexConstructor();
        // $_getCitylist(){
        //     cityList(this.cityParams).then((res) => {

        //     })
        // }

    Vue.prototype.$lstchooseCity = (config) => {
        // 这里 return 一个 Promise 
        // 在项目中使用的时候,就可以进行链式调用了~
        return new Promise((resolve,reject) => {
            instance.config = config;
            instance.chooseNum = config.chooseNum || 1;
            instance.draco = config.draco || true;
            instance.proFlag = config.proFlag || false;
            instance.chooseCity = [ ...config.chooseCityArrs ] || [];
            //instance.chooseCity = config.chooseCityArrs  || [];
            instance.show = true;
            instance.moseMove = false;
            instance.mosX = 0;
            instance.mosY = 0;
            let chooseCitArr = []
            // console.log(config)
            // 拖动
            instance.dowMos = (e) => {
                instance.moseMove = true;
                instance.mosX = e.clientX;
                instance.mosY = e.clientY;
            }
            instance.upMos = () => {instance.moseMove = false;}
            instance.move = (e) => {
                // console.log(e.target)
                if(!instance.draco){
                    return false;
                }
                if(!instance.moseMove){
                    return false;
                }
                // let odiv = e.target;        //获取目标元素
                const odiv = document.querySelector('.el-dialog__header') //获取目标元素
                // console.log(odiv)
                //算出鼠标相对元素的位置
                let disX = e.clientX - odiv.offsetLeft;
                let disY = e.clientY - odiv.offsetTop;
                // 计算出 移动的距离
                let mosx = e.clientX - instance.mosX;
                let mosy = e.clientY - instance.mosY;
                document.onmousemove = (e)=>{       //鼠标按下并移动的事件
                    //用鼠标的位置减去鼠标相对元素的位置,得到元素的位置
                    // let left = e.clientX - disX;    
                    // let top = e.clientY - disY;
                    let left = odiv.offsetLeft  + mosx;    
                    let top = odiv.offsetTop + mosy;
                    //绑定元素位置到positionX和positionY上面
                    instance.mosX = e.clientX;
                    instance.mosY = e.clientY;
                    //移动当前元素
                    odiv.style.left = left + 'px';
                    odiv.style.top = top + 'px';
                };
                document.onmouseup = (e) => {
                    document.onmousemove = null;
                    document.onmouseup = null;
                };
            // }  
            }
            instance.getCity = () => {
                instance.cityList = []
                cityList(instance.cityParams).then((res) => {
                    if(instance.cityParams.parentId == 0){
                        instance.provinceList = res.data;
                    }else{
                        instance.cityList = res.data;
                    }
                })
            }
            
            // 获取当前选中城市 id集合
            instance.getCityIds = () => {
                instance.chooseIds = [];
                instance.chooseCity.forEach((ele) => {
                    instance.chooseIds.push(ele.value)
                })
            }
            // 初始化 获取城市 省级别获取后再次打开不在请求接口
            if(instance.provinceList.length  == 0){
                instance.getCity();
            }
            
            // 初始化 获取已经选中的 // 获取当前选中城市 id集合
            instance.getCityIds();
            // 选择省级别
            instance.choosePro = (val)=>{
                instance.cityParams.parentId = val.value
                if(val.type == 'next'){
                    instance.getCity();
                    instance.chooseProIds = []
                    instance.chooseProIds.push(val.value)
                    // 判断是否获取升级别数据
                    if(instance.proFlag){
                        instance.clearCity(val)
                        if(instance.chooseCity.length > instance.chooseNum){
                            instance.chooseCity = instance.chooseCity.splice(1,instance.chooseNum);
                        }
                    }
                }else{
                    instance.chooseProIds = []
                    instance.cityList = [];
                    instance.chooseIds.push(val.value);
                    instance.clearCity(val)
                    if(instance.chooseCity.length > instance.chooseNum){
                        instance.chooseCity = instance.chooseCity.splice(1,instance.chooseNum);
                    }
                    // 获取当前选中城市 id集合
                    instance.getCityIds();
                }
            }
            // 选择市级别
            instance.chooseCit = (val)=>{
                // console.log(val)
                instance.chooseIds = [];
                instance.chooseIds.push(val.value)
                instance.clearCity(val)
                if(instance.chooseCity.length > instance.chooseNum){
                    instance.chooseCity = instance.chooseCity.splice(1,instance.chooseNum);
                }
                // 获取当前选中城市 id集合
                instance.getCityIds();
            }
            // 双击取消当前选中的城市
            instance.clearCity = (val) => {
                // console.log(111)
                let flag = true;
                instance.chooseCity.forEach((ele,index) => {
                    if(ele.value == val.value){
                        flag = false;
                        instance.chooseCity.splice(index,1);
                        return false;
                    }
                });
                if(flag){
                    instance.chooseCity.push(val)
                }
                chooseCitArr = instance.chooseCity
                // 获取当前选中城市 id集合
                instance.getCityIds();
            }
            instance.sureCallBack = () => {
                instance.show = false;
                let params = {
                    type :true,
                    data:instance.chooseCity
                }
                resolve(params);
            }
            instance.closeCallBack = () => {
                instance.show = false;
                let params = {
                    type :false,
                    // data:instance.chooseCity
                }
                resolve(params);
                // resolve(false)
            }
        })
    }
    // 将这个实例挂载在我创建的div上
    // 并将此div加入全局挂载点内部

    instance.$mount(document.createElement('div'))

    document.body.appendChild(instance.$el)

}

export default chooseCityIndex;
js逻辑部分

效果截图展示

使用方式

this.$lstchooseCity({
        title:'这是什么东西~',//标题
        chooseNum:1,//选中城市数量
        draco:false,//是否允许拖动
        proFlag: false,// 是否允许获取省级别
        chooseCity:[
            {
                name: "上海",
                type: "leafNode",
                value: 310000
            }
        ],// 选中城市 默认选中
        r_btn:'确定',//左侧按钮
        l_btn:'取消',//右侧按钮
        vnode:'<span>饿哦是内容区域</span>',//内容自定义部分
        content:"我也不知道这是什么东西啊!!!"//内容文本
      }).then(res=>{
          console.log(res)//点击按钮回调函数
      })
原文地址:https://www.cnblogs.com/lst619247/p/14450873.html