封装message组件和dialog组件

1,313 阅读1分钟

message

在main.js中引入message.js import { createMessage } from '@/components/message',然后绑定到原型上Vue.prototype.$message = createMessage

// message.js
import Vue from 'vue'
import Message from './message.vue'
const instances = []
let seed = 1
export function createMessage ({ msg = '', type = '' }) {
  const MessageConstructor = Vue.extend(Message)
  const MessageInstance = new MessageConstructor({
    propsData: {
      msg,
      type
    }
  })
  let top = 20
  instances.forEach(item => {
    top += item.$el.offsetHeight + 16
  })
  MessageInstance.topStyle = top
  MessageInstance.id = ++seed
  MessageInstance.onClose = () => {
    createMessage.close(MessageInstance.id)
  }
  MessageInstance.visible = true
  MessageInstance.$mount()
  document.body.appendChild(MessageInstance.$el)
  instances.push(MessageInstance)
  return MessageInstance
}

['success', 'error', 'info', 'warning'].forEach(type => {
  createMessage[type] = function (msg) {
    createMessage({ msg, type })
  }
})

createMessage.close = function (id) {
  let index = null
  let offsetHeight = null
  instances.forEach((item, i) => {
    if (item.id === id) {
      index = i
      offsetHeight = item.$el.offsetHeight
    }
  })
  instances.splice(index, 1)
  instances.forEach((item, i) => {
    console.log(i, index, i >= index)
    if (i >= index) {
      item.topStyle = item.topStyle - 16 - offsetHeight
    }
  })
}

Message组件
<template>
  <div
    class="message"
    :class="[type]"
    :style="{top: topStyle + 'px'}"
    v-show="visible"
    @mouseenter="endTime"
    @mouseleave="startTime"
  >
    {{msg}}
    <span class="close-button" @click="handleClose">x</span>
  </div>
</template>

<script>
export default {
  props: {
    msg: {
      type: String,
      default: ''
    },
    type: {
      type: String,
      default: ''
    }
  },
  data () {
    return {
      id: 0,
      topStyle: 20,
      visible: false,
      timer: null,
      onClose: null
    }
  },
  mounted () {
    this.startTime()
  },
  methods: {
    handleClose () {
      if (this.$el && this.$el.parentNode) {
        this.onClose && this.onClose()
        this.$destroy()
        this.$el.parentNode.removeChild(this.$el)
      }
    },
    startTime () {
      this.timer = setTimeout(() => {
        this.handleClose()
      }, 1000)
    },
    endTime () {
      clearTimeout(this.timer)
      this.timer = null
    }
  }
}
</script>
<style lang="less" scoped>
.message {
  min-width: 200px;
  height: 30px;
  position: fixed;
  z-index: 999;
  left: 50%;
  transform: translateX(-50%);
  padding: 0 20px;
  line-height: 30px;
  border-radius: 5px;
  border: 1px solid #ccc;
  &.success {
    background: yellowgreen;
    color: #fff;
    border: 0 none;
  }
  &.error {
    background: red;
    color: #fff;
    border: 0 none;
  }
  &.info {
    background: gray;
    color: #fff;
    border: 0 none;
  }
  &.warning {
    background: yellow;
    color: #fff;
    border: 0 none;
  }
}
.close-button {
  position: absolute;
  right: 20px;
  cursor: pointer;
}
</style>

使用
<template>
  <div class="hello">
    <button @click="handleClick">点击</button>
    <button @click="successClick">success</button>
    <button @click="failClick">fail</button>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  methods: {
    handleClick () {
      this.$message({
        msg: '空白',
        type: ''
      })
    },
    successClick () {
      this.$message.success('成功')
    },
    failClick () {
      this.$message.error('失败')
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="less">
button + button {
  margin-left: 20px;
}
</style>

dialog

dialog组件
//dialog.vue
<template>
  <div class="mask" @click="handleClose" v-if="dialogVisible">
    <div class="dialog" @click.stop.prevent>
      <div class="title">
        <slot name="title"></slot>
      </div>
      <div class="content">
        <slot></slot>
      </div>
      <div class="footer">
        <slot name="footer"></slot>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'Dialog',
  props: {
    visible: Boolean,
    appendToBody: {
      type: Boolean,
      default: true
    }
  },
  data () {
    return {
      dialogVisible: false
    }
  },
  watch: {
    visible (value) {
      if (value) {
        if (this.appendToBody) {
          this.dialogVisible = value
          document.body.append(this.$el)
        } else {
          this.dialogVisible = value
        }
      } else {
        this.dialogVisible = value
      }
    }
  },
  methods: {
    handleClose () {
      this.$emit('update:visible', false)
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="less">
.mask {
  position: fixed;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  background: rgba(0, 0, 0, 0.2);
  overflow: auto;
  .dialog {
    min-width: 500px;
    min-height: 100px;
    background: #fff;
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    display: flex;
    flex-direction: column;
    .title {
      height: 50px;
      line-height: 50px;
      text-align: left;
      border-bottom: 1px solid #ccc;
      padding-left: 20px;
    }
    .footer {
      height: 50px;
      text-align: right;
      border-top: 1px solid #ccc;
      padding: 10px;
      box-sizing: border-box;
      button + button {
        margin-left: 20px;
      }
    }
    .content {
      overflow: auto;
      // max-height: 700px;
      flex: 1;
    }
  }
}
</style>
使用
<template>
  <div class="hello">
    <button @click="handleClick">点击</button>
    <my-dialog :visible.sync="visible">
      <template v-slot:title>
        <div>
          标题
        </div>
      </template>
       内容
      <template v-slot:footer>
        <button @click="handleCancel">取消</button>
        <button @click="handleCancel">确定</button>
      </template>
    </my-dialog>
  </div>
</template>

<script>
import myDialog from './dialog.vue'
export default {
  name: 'HelloWorld',
  components: {
    myDialog
  },
  data () {
    return {
      visible: false
    }
  },
  methods: {
    handleClick () {
      this.visible = !this.visible
    },
    handleCancel () {
      this.visible = false
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="less">
button + button {
  margin-left: 20px;
}
</style>