PreviewMixin.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. import SerialNumberPosition from './SerialNumberPosition.vue';
  2. import PinyinText from '@/components/PinyinText.vue';
  3. import AnswerAnalysis from '@/views/book/courseware/preview/common/AnswerAnalysis.vue';
  4. import PreviewOperation from '@/views/book/courseware/preview/common/PreviewOperation.vue';
  5. import { isEnable } from '@/views/book/courseware/data/common';
  6. import { ContentGetCoursewareComponentContent } from '@/api/book';
  7. import { sanitizeHTML } from '@/utils/common';
  8. const mixin = {
  9. data() {
  10. return {
  11. sanitizeHTML,
  12. typeList: ['interaction'],
  13. answer: { answer_list: [], is_right: false }, // 用户答案
  14. isJudgingRightWrong: false, // 是否判断对错
  15. isShowRightAnswer: false, // 是否显示正确答案
  16. disabled: false, // 是否禁用
  17. isShowParse: false, // 是否显示解析
  18. isEnable,
  19. loader: false,
  20. visibleAnswerAnalysis: false, // 是否显示答案解析弹窗
  21. answerAnalysisState: null, // 答案解析弹窗前的状态快照
  22. };
  23. },
  24. inject: ['getLang', 'getChinese', 'convertText', 'getTitleList', 'getPermissionControl'],
  25. props: {
  26. id: {
  27. type: String,
  28. default: '',
  29. },
  30. content: {
  31. type: String,
  32. default: '',
  33. },
  34. coursewareId: {
  35. type: String,
  36. default: '',
  37. },
  38. type: {
  39. type: String,
  40. default: '',
  41. },
  42. isMobile: {
  43. type: Boolean,
  44. default: false,
  45. },
  46. },
  47. computed: {
  48. showLang() {
  49. return this.getLang() !== 'ZH';
  50. },
  51. permissionControl() {
  52. return this.getPermissionControl();
  53. },
  54. },
  55. watch: {
  56. content: {
  57. handler(newVal) {
  58. if (this.type === 'edit') return;
  59. if (!newVal) return;
  60. this.data = JSON.parse(newVal);
  61. },
  62. immediate: true,
  63. },
  64. },
  65. components: {
  66. SerialNumberPosition,
  67. PinyinText,
  68. AnswerAnalysis,
  69. PreviewOperation,
  70. },
  71. created() {
  72. // 这里分为 预览 和 编辑调整位置、视频互动组件 三种情况
  73. // 预览时,content 直接传入
  74. // 编辑调整位置时,content 需要通过接口获取
  75. if (this.type === 'edit') {
  76. this.getCoursewareComponentContent();
  77. }
  78. },
  79. methods: {
  80. getCoursewareComponentContent() {
  81. ContentGetCoursewareComponentContent({ courseware_id: this.coursewareId, component_id: this.id }).then(
  82. ({ content }) => {
  83. if (content) {
  84. let oldData = JSON.parse(content);
  85. if (oldData.type === 'audio') {
  86. let p = oldData.property || {};
  87. if (!p.file_name_display_mode) p.file_name_display_mode = 'true';
  88. if (p.view_method === 'independent' && !p.style_mode) {
  89. p.style_mode = 'middle';
  90. }
  91. if (!p.style_mode) p.style_mode = 'big';
  92. if (p.view_method === 'icon') {
  93. p.file_name_display_mode = 'false';
  94. p.view_method = 'independent';
  95. p.style_mode = 'small';
  96. }
  97. }
  98. this.data = oldData;
  99. }
  100. this.loader = true;
  101. },
  102. );
  103. },
  104. /**
  105. * 获取用户答案
  106. * @returns {array} 用户答案
  107. */
  108. getAnswer() {
  109. return this.answer;
  110. },
  111. /**
  112. * 显示答案
  113. * @param {boolean} isJudgingRightWrong 是否判断对错
  114. * @param {boolean} isShowRightAnswer 是否显示正确答案
  115. * @param {object} userAnswer 用户答案
  116. * @param {boolean} disabled 是否禁用
  117. */
  118. showAnswer(isJudgingRightWrong, isShowRightAnswer, userAnswer, disabled) {
  119. // if (this.loader === false) {
  120. // return setTimeout(() => this.showAnswer(isJudgingRightWrong, isShowRightAnswer, userAnswer, disabled), 100);
  121. // }
  122. this.isJudgingRightWrong = isJudgingRightWrong;
  123. this.isShowRightAnswer = isShowRightAnswer;
  124. this.disabled = disabled;
  125. if (userAnswer) this.answer = userAnswer;
  126. },
  127. /**
  128. * 得到序号外部样式
  129. */
  130. getAreaStyle() {
  131. if (!isEnable(this.data.property.sn_display_mode)) {
  132. return {
  133. grid: `"main" / 1fr`,
  134. };
  135. }
  136. const position = this.data.property.sn_position;
  137. let grid = '';
  138. if (position.includes('right')) {
  139. grid = `"main position" / 1fr auto`;
  140. } else if (position.includes('left')) {
  141. grid = `"position main" / auto 1fr`;
  142. } else if (position.includes('top')) {
  143. grid = `"position" auto "main" 1fr`;
  144. } else if (position.includes('bottom')) {
  145. grid = `"main" 1fr "position" auto`;
  146. }
  147. return {
  148. grid,
  149. };
  150. },
  151. /**
  152. * 得到背景图、背景色及边框样式
  153. */
  154. getComponentStyle() {
  155. let backgroundData = {};
  156. if (Object.hasOwn(this.data.property, 'background_image_url')) {
  157. // 保护性读取位置/大小值,避免 undefined 导致字符串 "undefined%"
  158. const { background_position: pos } = this.data.property;
  159. const widthPct = typeof pos.width === 'undefined' ? '' : pos.width;
  160. const heightPct = typeof pos.height === 'undefined' ? '' : pos.height;
  161. const leftPct = typeof pos.left === 'undefined' ? '' : pos.left;
  162. const topPct = typeof pos.top === 'undefined' ? '' : pos.top;
  163. backgroundData.backgroundImage = `url(${this.data.property.background_image_url})`;
  164. let bcSize = '';
  165. if (pos.image_mode === 'fill') {
  166. bcSize = '100% 100%';
  167. } else if (pos.image_mode === 'repeat') {
  168. bcSize = 'auto';
  169. } else {
  170. bcSize = `${widthPct}% ${heightPct}%`;
  171. }
  172. backgroundData.backgroundSize = bcSize;
  173. backgroundData.backgroundPosition = pos.image_mode === 'fill' ? '0% 0%' : `${leftPct}% ${topPct}%`;
  174. backgroundData.backgroundRepeat = pos.image_mode === 'repeat' ? 'repeat' : 'no-repeat';
  175. }
  176. let borderData = {};
  177. if (Object.hasOwn(this.data.property, 'is_border')) {
  178. const isBorder = isEnable(this.data.property.is_border);
  179. borderData.borderWidth = isBorder ? (this.data.property.border_style === 'double' ? '3px' : '1px') : '0px';
  180. borderData.borderStyle = isBorder ? this.data.property.border_style : 'none';
  181. borderData.borderColor = isBorder ? this.data.property.border_color : 'transparent';
  182. }
  183. return {
  184. backgroundColor: this.data.property?.background_color,
  185. ...backgroundData,
  186. ...borderData,
  187. };
  188. },
  189. showAnswerAnalysis() {
  190. if (!this.answerAnalysisState) {
  191. this.answerAnalysisState = {
  192. disabled: this.disabled,
  193. isJudgingRightWrong: this.isJudgingRightWrong,
  194. };
  195. }
  196. this.visibleAnswerAnalysis = true;
  197. this.disabled = true;
  198. this.isJudgingRightWrong = this.permissionControl.can_judge_correct;
  199. this.isShowRightAnswer = this.permissionControl.can_show_answer;
  200. },
  201. closeAnswerAnalysis() {
  202. if (this.answerAnalysisState) {
  203. this.disabled = this.answerAnalysisState.disabled;
  204. this.isJudgingRightWrong = this.answerAnalysisState.isJudgingRightWrong;
  205. this.isShowRightAnswer = false;
  206. this.answerAnalysisState = null;
  207. } else {
  208. this.disabled = false;
  209. this.isJudgingRightWrong = false;
  210. this.isShowRightAnswer = false;
  211. }
  212. },
  213. },
  214. };
  215. export default mixin;