基於elementUI使用v-model實現經緯度輸入的vue組件

  • 綁定一個 [12.34,-45.67] (東經西經,南緯北緯 正負表示) 形式的經緯度數組,可以按度分秒進行編輯,效果以下所示,點擊東經,北緯可切換。
  • 經緯度的 度轉度分秒
  • 可以獲取度分秒格式數據

Coordinates組件實現 Coordinates.vue

模板:一個span顯示東經西經,三個輸入框輸入度分秒
<template>
    <div class="coordinates">
        <!-- 經度 -->
        <div class="item">
           <span class="itude"
                 @click="itudeChange(true)">{{ longFlag | longitudeName }}</span>
            <el-input v-model.number="longitude[0]"
                      @change="change(true,0)"
                      size="mini">
                <i slot="suffix">°</i>
            </el-input>
            <el-input v-model.number="longitude[1]"
                      @change="change(true,1)"
                      size="mini">
                <i slot="suffix">′</i>
            </el-input>
            <el-input v-model.number="longitude[2]"
                      @change="change(true,2)"
                      size="mini">
                <i slot="suffix">″</i>
            </el-input>
        </div>
        <!-- 緯度 -->
        <div class="item">
           <span class="itude"
                 @click="itudeChange(false)">{{ latFlag | latitudeName }}</span>
            <el-input v-model.number="latitude[0]"
                      @change="change(false,0)"
                      size="mini">
                <i slot="suffix">°</i>
            </el-input>
            <el-input v-model.number="latitude[1]"
                      @change="change(false,1)"
                      size="mini">
                <i slot="suffix">′</i>
            </el-input>
            <el-input v-model.number="latitude[2]"
                      @change="change(false,2)"
                      size="mini">
                <i slot="suffix">″</i>
            </el-input>
        </div>
    </div>
</template>
<script>
    require('math');
    export default {
        name: 'Coordinates',
        props: {
            value: { //綁定的 value
                type: Array,
                require: true,
                validator: function (value) {
                    let len = value.length > 0 && value.length === 2;
                    let isvalid = Math.abs(value[0]) < 180 && Math.abs(value[1]) < 90;
                    return len && isvalid
                },
                default: function () {
                    return []
                }
            }
        },
        // model: { // prop爲 value 時不用實現 model 可是this.$emit(event,arg) 傳入的event須要爲 'input'
        //  prop: 'value',
        //  event: 'returnBack'
        // },
        data () {
            return {
                longitude: [], // 經度
                latitude: [],  // 緯度
                longFlag: '+', //表示東經西經
                latFlag: '+',  //表示南緯北緯
            }
        },
        created: function () {
            this.initData();
        },
        filters: {
            longitudeName (value) {
                return value === '+' ? "東經" : "西經"
            },
            latitudeName (value) {
                return value === '+' ? "南緯" : "北緯"
            }
        },
        watch: {
            /**
             * 監測父組件綁定的value
             */
            value () {
                this.initData();
            }
        },
        computed: {
            // 轉換爲  東經 XXX°XX′XX″  格式
            // 返回一個經緯度的數組
            formatString () {
                let longitude = (this.longFlag === '+' ? "東經 " : "西經 ") + this.longitude[0] + '°' + this.longitude[1] + '′' + this.longitude[2] + '″';
                let latitude = (this.latFlag === '+' ? "南緯 " : "北緯 ") + this.latitude[0] + '°' + this.latitude[1] + '′' + this.latitude[2] + '″';
                return [longitude, latitude]
            }
        },
        methods: {
            /**
             * 東經西經,南緯北緯 change事件
             */
            itudeChange (flag) {
                flag ? (this.longFlag = (this.longFlag === '+' ? '-' : '+')) : (this.latFlag = (this.latFlag === '+' ? '-' : '+'))
                this.returnBackFn()
            },
            /**
             * 初始化數據,父組件修改綁定的value時調用
             */
            initData () {
                this.longitude = this.D2Dms(Math.abs(this.value[0]));
                this.latitude = this.D2Dms(Math.abs(this.value[1]));
                this.longFlag = this.value[0] < 0 ? '-' : '+';
                this.latFlag = this.value[1] < 0 ? '-' : '+'
            },
            /**
             * 輸入框change事件,數據合法性驗證
             */
            change (flag, index) {
                let name = '', max = 0;
                flag ? [name, max] = ['longitude', 179] : [name, max] = ['latitude', 89];
                index ? max = 59 : null;
                let value = parseInt(this[name][index], 10);
                if (isNaN(value)) {
                    value = 0;
                }
                value = value < 0 ? 0 : value;
                value = value > max ? max : value;
                this.$set(this[name], index, value);
                this.returnBackFn()
            },
            /**
             * v-model 綁定事件 雙向綁定實現
             */
            returnBackFn () {
                let longitude = parseFloat(this.longFlag + this.Dms2D(this.longitude));
                let latitude = parseFloat(this.latFlag + this.Dms2D(this.latitude));
                let array = [longitude, latitude];
                this.$emit('input', array);
            },
            /**
             * 度轉度分秒
             */
            D2Dms (d_data = 0) {
                let degree = parseInt(d_data);
                let min = parseInt((d_data - degree) * 60);
                let sec = parseInt((d_data - degree) * 3600 - min * 60);
                return [degree, min, sec];
            },
            /**
             * 度分秒轉度
             */
            Dms2D (dms_data = [0, 0, 0]) {
                let d = parseFloat(dms_data[0]);
                let m = parseFloat(dms_data[1]);
                let s = parseFloat(dms_data[2]);
                return this.keepFourDecimal(d + m / 60 + s / 60 / 60);
            },
            /**
             * 保留四位小數,小於四位精度可能丟失
             */
            keepFourDecimal (num) {
                let result = parseFloat(num);
                if (isNaN(result)) {
                    return 0;
                }
                result = Math.round(num * 10000) / 10000;
                return result;
            }
        },
    }
</script>
<style lang="less" scoped>
    @color-border: #9e9e9e;
    @height: 28px;
    .coordinates {
        border: 1px solid @color-border;
        width: fit-content;
        display: inline-flex;
    }
    .item:nth-of-type(1) {
        border-right: 1px solid @color-border;
    }
    .el-input {
        width: 40px;
    }
    .itude {
        height: @height;
        line-height: @height;
        display: inline-block;
        padding-left: 5px;
        cursor: pointer;
        user-select: none;
    }
    i {
        font-size: 18px;
        color: gray;
    }
</style>
<style lang="less">
    .el-input__inner {
        text-align: center;
        border: none;
        border-radius: unset;
    }
    .el-input--suffix .el-input__inner {
        padding: 0;
    }
</style>

測試代碼 index.vue

<template>
 <div id="example">
  <Coordinates ref="coordinates"
         v-model="value"></Coordinates>
  <el-button @click="changeValue"
        type="primary">
   change value
  </el-button>
  <br>
  <span>浮點數格式:{{value.toString()}}</span>
  <br>
  <span>度分秒格式:{{formatString.toString()}}</span>
  <el-button @click="refresh"
        type="primary">
   refresh
  </el-button>
 </div>
</template>
<script>
import Coordinates from '@/components/Coordinates'
export default {
 name: 'index',
 components: {
  Coordinates
 },
 data () {
  return {
   value: [116.3881,-39.9075],
   formatString: []
  }
 },
 mounted () {
  this.refresh ()
 },
 methods: {
  changeValue () {
   this.$set(this.value, 0, (this.value[0] + 2) >= 180 ? 0 : (this.value[0] + 2))
   this.$set(this.value, 1, (this.value[1] + 2) >= 90 ? 0 : (this.value[1] + 2))
   setTimeout(() => {
    refresh ()
   }, 10);
  },
  refresh () {
   // 獲取度分秒格式
   this.formatString = this.$refs.coordinates.formatString
  }
 }
}
</script>
<style lang="less" scoped>
#example {
 padding: 20px;
}
.el-button {
 margin: 20px;
}
span {
 font-size: 17px;
}
</style>

綜上,若有疑問請留言

借鑑:https://www.jb51.net/article/161080.htmvue

相關文章
相關標籤/搜索