index.vue 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. <template>
  2. <div>
  3. <v-avatar
  4. ref="box"
  5. class="box point elevation-5"
  6. color="indigo"
  7. @mousedown="handleMousedown"
  8. @click="handleClick"
  9. >
  10. <v-icon dark large>
  11. mdi-face-agent
  12. </v-icon>
  13. </v-avatar>
  14. <v-navigation-drawer
  15. v-model="show"
  16. absolute
  17. temporary
  18. hide-overlay
  19. right
  20. width="500"
  21. style="z-index: 400"
  22. >
  23. <m-talk v-if="show" :title="title" :type="type" v-bind="$attrs">
  24. <template v-for="name in Object.keys(this.$scopedSlots)" v-slot:[`${name}`]="slotProps">
  25. <slot :name="name" v-bind="slotProps"></slot>
  26. </template>
  27. </m-talk>
  28. </v-navigation-drawer>
  29. </div>
  30. </template>
  31. <script>
  32. import MTalk from './talk'
  33. export default {
  34. name: 'knowledge-component',
  35. components: {
  36. MTalk
  37. },
  38. props: {
  39. // 1 手册探索 2 图表实验室 3 产品知识库
  40. type: {
  41. type: Number,
  42. default: 1
  43. },
  44. title: {
  45. type: String,
  46. default: '数据探索'
  47. },
  48. handle: Function
  49. },
  50. data () {
  51. return {
  52. show: false,
  53. lock: false,
  54. isDragging: false,
  55. info: {}
  56. }
  57. },
  58. mounted () {
  59. this.$nextTick(() => {
  60. document.addEventListener('mouseup', this.handleMouseup)
  61. document.addEventListener('mousemove', this.handleMousemove)
  62. this.$once('hook:beforeDestroy', () => {
  63. document.removeEventListener('mouseup', this.handleMouseup)
  64. document.removeEventListener('mousemove', this.handleMousemove)
  65. })
  66. })
  67. },
  68. methods: {
  69. handleClick () {
  70. if (this.isDragging) {
  71. return
  72. }
  73. if (this.handle) {
  74. this.handle()
  75. return
  76. }
  77. this.show = !this.show
  78. },
  79. // 记录当前位置
  80. handleMousedown (e) {
  81. this.lock = true
  82. const { left, top, height, width } = this.$refs.box.$el.getBoundingClientRect()
  83. this.info = {
  84. click: {
  85. x: e.clientX,
  86. y: e.clientY
  87. },
  88. left,
  89. top,
  90. height,
  91. width,
  92. clientX: e.clientX,
  93. clientY: e.clientY
  94. }
  95. },
  96. // 移动位置
  97. handleMousemove (e) {
  98. if (!this.lock) return
  99. const xLen = Math.abs(this.info.click?.x - e.clientX)
  100. const yLen = Math.abs(this.info.click?.y - e.clientY)
  101. // 拖动距离触发
  102. if (xLen < 20 && yLen < 20) {
  103. return
  104. }
  105. // 计算移动距离
  106. const clientX = e.clientX - this.info.clientX
  107. const clientY = e.clientY - this.info.clientY
  108. this.isDragging = true
  109. this.info.left += clientX
  110. this.info.top += clientY
  111. this.$refs.box.$el.style.left = this.info.left + 'px'
  112. this.$refs.box.$el.style.top = this.info.top + 'px'
  113. this.info.clientX = e.clientX
  114. this.info.clientY = e.clientY
  115. },
  116. handleMouseup (e) {
  117. this.lock = false
  118. setTimeout(() => {
  119. this.isDragging = false
  120. })
  121. }
  122. }
  123. }
  124. </script>
  125. <style lang="scss" scoped>
  126. .point {
  127. cursor: pointer;
  128. }
  129. .box {
  130. z-index: 9;
  131. position: fixed;
  132. right: 50px;
  133. bottom: 100px;
  134. }
  135. </style>