ModuleBase.vue 5.9 KB

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