CoursewarePreview.vue 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. <template>
  2. <div
  3. class="courserware"
  4. :style="[
  5. {
  6. backgroundImage: background.background_image_url ? `url(${background.background_image_url})` : '',
  7. backgroundSize: background.background_image_url
  8. ? `${background.background_position.width}% ${background.background_position.height}%`
  9. : '',
  10. backgroundPosition: background.background_image_url
  11. ? `${background.background_position.left}% ${background.background_position.top}%`
  12. : '',
  13. },
  14. ]"
  15. >
  16. <template v-for="(row, i) in data.row_list">
  17. <div :key="i" class="row" :style="getMultipleColStyle(i)">
  18. <!-- 列 -->
  19. <template v-for="(col, j) in row.col_list">
  20. <div :key="j" :class="['col', `col-${i}-${j}`]" :style="computedColStyle(col)">
  21. <!-- 网格 -->
  22. <template v-for="(grid, k) in col.grid_list">
  23. <component
  24. :is="previewComponentList[grid.type]"
  25. ref="preview"
  26. :key="k"
  27. :content="computedColContent(grid.id)"
  28. :class="[grid.id]"
  29. :style="{
  30. gridArea: grid.grid_area,
  31. height: grid.height,
  32. }"
  33. />
  34. </template>
  35. </div>
  36. </template>
  37. </div>
  38. </template>
  39. </div>
  40. </template>
  41. <script>
  42. import { previewComponentList } from '@/views/book/courseware/data/bookType';
  43. export default {
  44. name: 'CoursewarePreview',
  45. provide() {
  46. return {
  47. getDragStatus: () => false,
  48. bookInfo: this.bookInfo,
  49. };
  50. },
  51. props: {
  52. data: {
  53. type: Object,
  54. default: () => ({}),
  55. },
  56. background: {
  57. type: Object,
  58. default: () => ({}),
  59. },
  60. componentList: {
  61. type: Array,
  62. required: true,
  63. },
  64. },
  65. data() {
  66. return {
  67. previewComponentList,
  68. bookInfo: {
  69. theme_color: '',
  70. },
  71. };
  72. },
  73. methods: {
  74. /**
  75. * 计算组件内容
  76. * @param {string} id 组件id
  77. * @returns {string} 组件内容
  78. */
  79. computedColContent(id) {
  80. if (!id) return '';
  81. return this.componentList.find((item) => item.component_id === id)?.content || '';
  82. },
  83. getMultipleColStyle(i) {
  84. let row = this.data.row_list[i];
  85. let col = row.col_list;
  86. if (col.length <= 1) {
  87. return {
  88. gridTemplateColumns: '100fr',
  89. };
  90. }
  91. let gridTemplateColumns = row.width_list.join(' ');
  92. return {
  93. gridAutoFlow: 'column',
  94. gridTemplateColumns,
  95. gridTemplateRows: 'auto',
  96. };
  97. },
  98. /**
  99. * 分割整数为多个 1的倍数
  100. * @param {number} num
  101. * @param {number} parts
  102. */
  103. splitInteger(num, parts) {
  104. let base = Math.floor(num / parts);
  105. let arr = Array(parts).fill(base);
  106. let remainder = num - base * parts;
  107. for (let i = 0; remainder > 0; i = (i + 1) % parts) {
  108. arr[i] += 1;
  109. remainder -= 1;
  110. }
  111. return arr;
  112. },
  113. computedColStyle(col) {
  114. const grid = col.grid_list;
  115. let maxCol = 0; // 最大列数
  116. let rowList = new Map();
  117. grid.forEach(({ row }) => {
  118. rowList.set(row, (rowList.get(row) || 0) + 1);
  119. });
  120. let curMaxRow = 0; // 当前数量最大 row 的值
  121. rowList.forEach((value, key) => {
  122. if (value > maxCol) {
  123. maxCol = value;
  124. curMaxRow = key;
  125. }
  126. });
  127. // 计算 grid_template_areas
  128. let gridTemplateAreas = '';
  129. let gridArr = [];
  130. grid.forEach(({ grid_area, row }) => {
  131. if (!gridArr[row - 1]) {
  132. gridArr[row - 1] = [];
  133. }
  134. if (curMaxRow === row) {
  135. gridArr[row - 1].push(`${grid_area}`);
  136. } else {
  137. let filter = grid.filter((item) => item.row === row);
  138. let find = filter.findIndex((item) => item.grid_area === grid_area);
  139. let needNum = maxCol - filter.length; // 需要的数量
  140. let str = '';
  141. if (filter.length === 1) {
  142. str = ` ${grid_area} `.repeat(needNum + 1);
  143. } else {
  144. let arr = this.splitInteger(needNum, filter.length);
  145. str = arr[find] === 0 ? ` ${grid_area} ` : ` ${grid_area} `.repeat(arr[find] + 1);
  146. }
  147. gridArr[row - 1].push(`${str}`);
  148. }
  149. });
  150. gridArr.forEach((item) => {
  151. gridTemplateAreas += `'${item.join(' ')}' `;
  152. });
  153. // 计算 grid_template_columns
  154. let gridTemplateColumns = '';
  155. let max = { row: 0, num: 0 };
  156. grid.forEach(({ row }) => {
  157. // 计算出 row 的哪个值最多
  158. let len = grid.filter((item) => item.row === row).length;
  159. if (max.num < len) {
  160. max.num = len;
  161. max.row = row;
  162. }
  163. });
  164. grid.forEach((item) => {
  165. if (item.row === max.row) {
  166. gridTemplateColumns += `${item.width} `;
  167. }
  168. });
  169. // 计算 grid_template_rows
  170. let gridTemplateRows = '';
  171. // 将 grid 按照 row 分组
  172. let gridMap = new Map();
  173. grid.forEach((item) => {
  174. if (!gridMap.has(item.row)) {
  175. gridMap.set(item.row, []);
  176. }
  177. gridMap.get(item.row).push(item.height);
  178. });
  179. gridMap.forEach((value) => {
  180. if (value.length === 1) {
  181. gridTemplateRows += `${value[0]} `;
  182. } else {
  183. let isAllAuto = value.every((item) => item === 'auto'); // 是否全是 auto
  184. gridTemplateRows += isAllAuto ? 'auto ' : `max(${value.join(', ')}) `;
  185. }
  186. });
  187. return {
  188. width: col.width,
  189. gridTemplateAreas,
  190. gridTemplateColumns,
  191. gridTemplateRows,
  192. };
  193. },
  194. },
  195. };
  196. </script>
  197. <style lang="scss" scoped>
  198. .courserware {
  199. display: flex;
  200. flex-direction: column;
  201. row-gap: 6px;
  202. width: 100%;
  203. height: 100%;
  204. min-height: 500px;
  205. padding: 24px;
  206. background-color: #fff;
  207. background-repeat: no-repeat;
  208. border-bottom-right-radius: 12px;
  209. border-bottom-left-radius: 12px;
  210. .row {
  211. display: grid;
  212. gap: 16px;
  213. .col {
  214. display: grid;
  215. gap: 16px;
  216. overflow: hidden;
  217. }
  218. }
  219. }
  220. </style>