ModuleBase.vue 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. <template>
  2. <div class="module-wrapper">
  3. <div
  4. class="horizontal-line top"
  5. :style="{ backgroundColor: bgColor }"
  6. @mousedown="dragStart($event, 'ns-resize', 'top')"
  7. ></div>
  8. <div
  9. class="vertical-line left"
  10. :style="{ backgroundColor: bgColor }"
  11. @mousedown="dragStart($event, 'ew-resize', 'left')"
  12. ></div>
  13. <div class="module" draggable="false">
  14. <div class="module-top">
  15. <span class="title">{{ componentNameList[type] }}</span>
  16. <div class="module-icon">
  17. <span><SvgIcon icon-class="copy" size="10" /></span>
  18. <span :class="[{ active: getCurSettingId() === id }]" @click="showSetting">
  19. <SvgIcon icon-class="setup" size="10" />
  20. </span>
  21. <span @click="deleteComponent"><SvgIcon icon-class="delete" size="10" /></span>
  22. </div>
  23. </div>
  24. <div class="module-content">
  25. <slot name="content"></slot>
  26. </div>
  27. </div>
  28. <div
  29. class="vertical-line right"
  30. :style="{ backgroundColor: bgColor }"
  31. @mousedown="dragStart($event, 'ew-resize', 'right')"
  32. ></div>
  33. <div
  34. class="horizontal-line bottom"
  35. :style="{ backgroundColor: bgColor }"
  36. @mousedown="dragStart($event, 'ns-resize', 'bottom')"
  37. ></div>
  38. </div>
  39. </template>
  40. <script>
  41. import { componentNameList } from '@/views/book/courseware/data/bookType.js';
  42. export default {
  43. name: 'ModuleBase',
  44. inject: ['id', 'showSetting', 'getCurSettingId', 'deleteComponent', 'handleComponentMove'],
  45. props: {
  46. type: {
  47. type: String,
  48. default: 'text',
  49. },
  50. },
  51. data() {
  52. return {
  53. componentNameList,
  54. drag: {
  55. dragging: false,
  56. startX: 0,
  57. startY: 0,
  58. type: '',
  59. },
  60. bgColor: '#ebebeb',
  61. };
  62. },
  63. created() {
  64. document.addEventListener('mousemove', this.dragMove);
  65. document.addEventListener('mouseup', this.dragEnd);
  66. },
  67. beforeDestroy() {
  68. document.removeEventListener('mousemove', this.dragMove);
  69. document.removeEventListener('mouseup', this.dragEnd);
  70. },
  71. methods: {
  72. /**
  73. * 拖拽开始
  74. * @param {MouseEvent} event
  75. * @param {string} cursor
  76. * @param {string} type
  77. */
  78. dragStart(event, cursor, type) {
  79. const { clientX, clientY } = event;
  80. this.drag = {
  81. dragging: true,
  82. startX: clientX,
  83. startY: clientY,
  84. type,
  85. };
  86. this.bgColor = '#272727';
  87. document.body.style.cursor = cursor;
  88. },
  89. /**
  90. * 拖拽移动
  91. * @param {MouseEvent} event
  92. */
  93. dragMove(event) {
  94. if (!this.drag.dragging) return;
  95. const { clientX, clientY } = event;
  96. const { startX, startY, type } = this.drag;
  97. const offsetX = clientX - startX;
  98. const offsetY = clientY - startY;
  99. this.handleComponentMove({ type, offsetX, offsetY });
  100. this.drag.startX = clientX;
  101. this.drag.startY = clientY;
  102. },
  103. /**
  104. * 拖拽结束
  105. */
  106. dragEnd() {
  107. this.drag = {
  108. dragging: false,
  109. startX: 0,
  110. startY: 0,
  111. type: '',
  112. };
  113. this.bgColor = '#ebebeb';
  114. document.body.style.cursor = 'auto';
  115. },
  116. },
  117. };
  118. </script>
  119. <style lang="scss" scoped>
  120. .module-wrapper {
  121. display: grid;
  122. grid-template:
  123. 'top top top' 1px
  124. 'left module right' auto
  125. 'bottom bottom bottom' 1px
  126. / 1px auto 1px;
  127. .horizontal-line {
  128. width: 100%;
  129. cursor: ns-resize;
  130. &.top {
  131. grid-area: top;
  132. }
  133. &.bottom {
  134. grid-area: bottom;
  135. }
  136. }
  137. .vertical-line {
  138. cursor: ew-resize;
  139. &.left {
  140. grid-area: left;
  141. }
  142. &.right {
  143. grid-area: right;
  144. }
  145. }
  146. .module {
  147. grid-area: module;
  148. padding: 8px;
  149. overflow: auto;
  150. background-color: #fff;
  151. &-top {
  152. display: flex;
  153. justify-content: space-between;
  154. margin-bottom: 3px;
  155. .title {
  156. font-size: 12px;
  157. color: $label-color;
  158. }
  159. }
  160. &-icon {
  161. display: flex;
  162. column-gap: 8px;
  163. span {
  164. display: flex;
  165. align-items: center;
  166. justify-content: center;
  167. width: 16px;
  168. height: 16px;
  169. cursor: pointer;
  170. background-color: #fff;
  171. border-radius: 20px;
  172. &.active {
  173. background-color: #c9c9c9;
  174. }
  175. .svg-icon.setup {
  176. color: #000;
  177. }
  178. .svg-icon.delete {
  179. color: #ed4646;
  180. }
  181. }
  182. }
  183. &-content {
  184. position: relative;
  185. padding: 8px;
  186. background-color: #fff;
  187. .option-list {
  188. .rich-wrapper {
  189. flex: 1;
  190. min-height: 32px;
  191. :deep .rich-text {
  192. &.mce-content-body {
  193. padding-top: 4px;
  194. }
  195. &:not(.mce-edit-focus) {
  196. p {
  197. margin: 0;
  198. }
  199. }
  200. &.mce-content-body[data-mce-placeholder]:not(.mce-visualblocks)::before {
  201. top: 6px;
  202. }
  203. }
  204. }
  205. }
  206. }
  207. }
  208. }
  209. </style>