[vue]自行實做一個彈跳小視窗的元件,並關閉其他同樣的元件

  • 3060
  • 0
  • vue
  • 2018-02-07

[vue]自行實做一個彈跳小視窗的元件,並關閉其他同樣的元件

這次想要來寫下我如何自行製作一個類似tooltip,但是裡面可以自行決定放任何內容的元件,有興趣的人可以看看我的做法,或者你也可以提供更好的作法給筆者。

做完的效果會類似如下。

而component會使用slot來放兩塊可取代的,一個是中間筆者想自行實做的,而另一個則是footer固定的button按鈕,此元件的原始碼大約如下

<template>
  <div ref="editable">
    <a style="display:block;color:blue" @click="click">{{value}}</a>
    <div v-if="isPopup" class="vue-popUpSmall" :style="popPosition">
      <slot/>
      <slot name="footer">
        <div class="text-center">
          <div class="button">
            <input type="button" value="submit" class="btn_refresh refresh_roll tool_goal_submit" @click="$emit('submit')"
            />
          </div>
          <div class="button">
            <input type="button" value="Cancel" class="btn_refresh refresh_roll" @click="cancel" />
          </div>
        </div>
      </slot>
    </div>
  </div>
</template>

<script>

export default {
  name: 'editable',
  props: {
    value: {
      type: [Number, String]
    },
    position: {
      type: String,
      default: '0'
    }
  },
  data() {
    return {
      isPopup: false
    }
  },
  computed: {
    popPosition() {
      return `margin-left:${this.position}`
    }
  },
  methods: {
    close() {
      this.isPopup = false
    },
    click() {
      this.isPopup = !this.isPopup
    },
    cancel() {
      this.isPopup = false
    }
  }
}
</script>

<style lang="scss" scoped>
.vue-popUpSmall {
  position: absolute;
  background-color: #e6e6e6;
  border: 4px solid #b9b9b9;
  color: #000000;
  padding: 5px;
  max-height: 300px;
  z-index: 1;
}

td {
  border: 0;
  padding: 2px;
}
.button {
  display: inline;
  margin: 2px;
}
</style>

而這元件的使用方式,大約會如下程式碼

<ul>
  <li v-for="item in viewModel.events" :key="item.eventId">
    <editable v-model="item.eventId">
      <table>
        <tr>
          <td class="text-right">
            <span>Test 1 :</span>
          </td>
          <td>
            <input type="text">
          </td>
        </tr>
        <tr>
          <td class="text-right">
            <span>Test 2 :</span>
          </td>
          <td>
            <input type="text">
          </td>
        </tr>
      </table>
    </editable>
  </li>
</ul>

但事情還沒完,因為此元件當點了的時候,其實要關閉畫面上所有其他的元件,但目前卻可以一直點出不同位置的元件

接著再來把此元件修改一下,說明直接註解寫在元件裡面。

<template>
  <div ref="editable">
    <a style="display:block;color:blue" @click="click">{{value}}</a>
    <div v-if="isPopup" class="vue-popUpSmall" :style="popPosition">
      <slot/>
      <slot name="footer">
        <div class="text-center">
          <div class="button">
            <input type="button" value="submit" class="btn_refresh refresh_roll tool_goal_submit" @click="$emit('submit')"
            />
          </div>
          <div class="button">
            <input type="button" value="Cancel" class="btn_refresh refresh_roll" @click="cancel" />
          </div>
        </div>
      </slot>
    </div>
  </div>
</template>

<script>

export default {
  name: 'editable',
  props: {
    value: {
      type: [Number, String]
    },
    position: {
      type: String,
      default: '0'
    }
  },
  data() {
    return {
      isPopup: false
    }
  },
  computed: {
    popPosition() {
      return `margin-left:${this.position}`
    }
  },
  methods: {
    close() {
      this.isPopup = false
    },
    click() {
      this.$emit('click')
      this.isPopup = !this.isPopup
    },
    cancel() {
      this.isPopup = false
    },
    documentClick(e) {
      let el = this.$refs.editable
      // 下面最主要目的是為了比對如果el和e.target取得的dom有不相同,或不包含的話,就把視窗關閉起來
      if (el !== e.target && !el.contains(e.target)) {
        this.isPopup = false
      }
    }
  },
  mounted() {
    //監聽dom的click事件
    document.addEventListener('click', this.documentClick)
  },
  destroyed() {
    //銷毀元件的時候,記得要remove以防memory leak
    document.removeEventListener('click', this.documentClick)
  }
}
</script>

<style lang="scss" scoped>
.vue-popUpSmall {
  position: absolute;
  background-color: #e6e6e6;
  border: 4px solid #b9b9b9;
  color: #000000;
  padding: 5px;
  max-height: 300px;
  z-index: 1;
}

td {
  border: 0;
  padding: 2px;
}
.button {
  display: inline;
  margin: 2px;
}
</style>

最後完成的簡單範例就會如下圖示例

如果有更好的做法,再請告知筆者哦。