index.vue 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. <template>
  2. <Teleport to="body" :disabled="!appendToBody">
  3. <transition name="fade-pupop">
  4. <div class="m-drawer-pupop" v-if="modelValue" @click="handleWrapperClick"></div>
  5. </transition>
  6. <transition
  7. :name="animateName"
  8. @after-enter="afterEnter"
  9. @after-leave="afterLeave">
  10. <div
  11. class="m-drawer__container"
  12. v-show="modelValue"
  13. :style="drawerStyle"
  14. >
  15. <div class="m-drawer" :class="customClass">
  16. <div class="m-drawer__header" v-if="withHeader">
  17. <slot name="header">
  18. <span class="m-drawer__title">{{ title }}</span>
  19. </slot>
  20. <i class="iconfont icon-close" @click="handleClose" v-if="showClose"></i>
  21. </div>
  22. <div class="m-drawer__body">
  23. <slot></slot>
  24. </div>
  25. <div class="m-drawer__footer">
  26. <slot name="footer"></slot>
  27. </div>
  28. </div>
  29. </div>
  30. </transition>
  31. </Teleport>
  32. </template>
  33. <script>
  34. export default {
  35. name: "mDrawer"
  36. };
  37. </script>
  38. <script setup>
  39. import { computed,watch } from 'vue-demi';
  40. const emit = defineEmits(['update:modelValue','close','open','opened','closed'])
  41. const props = defineProps({
  42. modelValue:Boolean,
  43. title:{
  44. type:String,
  45. default:"标题"
  46. },
  47. width:{
  48. type:String,
  49. default:"30%"
  50. },
  51. height:{
  52. type:String,
  53. default:"40%"
  54. },
  55. direction:{
  56. type:String,
  57. default:'right'
  58. },
  59. wrapperClosable: {
  60. type: Boolean,
  61. default: true
  62. },
  63. withHeader: {
  64. type: Boolean,
  65. default: true
  66. },
  67. showClose: {
  68. type: Boolean,
  69. default: true
  70. },
  71. customClass: {
  72. type: String,
  73. default: ''
  74. },
  75. appendToBody: {
  76. type: Boolean,
  77. default: false
  78. },
  79. })
  80. const drawerStyle = computed(() => {
  81. return [
  82. props.direction=="left"?{"top":0,"left":0,"width":props.width,"height":"100%"}:
  83. props.direction=="top"?{"top":0,"left":0,"width":"100%","height":props.height}:
  84. props.direction=="right"?{"top":0,"right":0,"width":props.width,"height":"100%"}:
  85. props.direction=="bottom"?{"bottom":0,"left":0,"width":"100%","height":props.height}:{}
  86. ]
  87. })
  88. const animateName = computed(()=>{
  89. return props.direction=="left"?"slide-left":
  90. props.direction=="top"?"slide-top":
  91. props.direction=="right"?"slide-right":
  92. props.direction=="bottom"?"slide-bottom":""
  93. })
  94. watch(() =>props.modelValue,(val) => {
  95. if(val) {
  96. emit('open')
  97. }
  98. })
  99. const handleClose = () => {
  100. emit('update:modelValue',false)
  101. emit('close')
  102. }
  103. const handleWrapperClick = (e) => {
  104. if(props.wrapperClosable){
  105. if(e.target.className=='m-drawer-pupop'){
  106. handleClose()
  107. }
  108. }
  109. }
  110. const afterEnter = () => {
  111. emit('opened')
  112. }
  113. const afterLeave = () => {
  114. emit('closed')
  115. }
  116. </script>
  117. <style lang="scss" scoped>
  118. @import '../../styles/components/drawer.scss';
  119. .fade-pupop-enter-active, .fade-pupop-leave-active {
  120. opacity: 1;
  121. transition: opacity .2s ease;
  122. }
  123. .fade-pupop-enter-from, .fade-pupop-leave-to {
  124. opacity: 0;
  125. transition: opacity .5s ease;
  126. }
  127. .slide-left-enter-active, .slide-left-leave-active{
  128. transition: all .3s ease;
  129. transform: translateX(0);
  130. }
  131. .slide-left-enter-from, .slide-left-leave-to {
  132. transform: translateX(-100%);
  133. }
  134. .slide-top-enter-active, .slide-top-leave-active{
  135. transition: all .3s ease;
  136. transform: translateY(0);
  137. }
  138. .slide-top-enter-from, .slide-top-leave-to {
  139. transform: translateY(-100%);
  140. }
  141. .slide-right-enter-active, .slide-right-leave-active{
  142. transition: all .3s ease;
  143. transform: translateX(0);
  144. }
  145. .slide-right-enter-from, .slide-right-leave-to {
  146. transform: translateX(100%);
  147. }
  148. .slide-bottom-enter-active, .slide-bottom-leave-active{
  149. transition: all .3s ease;
  150. transform: translateY(0);
  151. }
  152. .slide-bottom-enter-from, .slide-bottom-leave-to {
  153. transform: translateY(100%);
  154. }
  155. </style>