CurMaterial.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513
  1. <template>
  2. <el-dialog
  3. :class="['cur-material', { 'align-left': !isAlignCenter }]"
  4. :visible="visible"
  5. :modal="false"
  6. width="900px"
  7. @close="dialogMaterialClose"
  8. >
  9. <div slot="title" class="dialog-header">
  10. <span class="dialog-header-title">
  11. {{
  12. isCurMaterial
  13. ? `当前推送资料 - ${material_name}`
  14. : `${$t('Key274')} - ${material_type === 'COURSEWARE' ? $t('Key309') : $t('Key244')} - ${material_name}`
  15. }}
  16. </span>
  17. <span @click="isAlignCenter = !isAlignCenter">
  18. <svg-icon :icon-class="isAlignCenter ? 'align-left' : 'align-center'" />
  19. </span>
  20. </div>
  21. <div v-show="!isCurMaterial && material_type === 'COURSEWARE'" class="answer-data">
  22. <span>{{ $t('Key320') }}</span>
  23. <span class="answer-data-duration">{{ answerData.duration }}{{ $t('Key321') }}</span>
  24. <span class="answer-data-label">{{ $t('Key194') }}</span>
  25. <span class="answer-data-count-right">{{ answerData.count_right }}</span>
  26. <span class="answer-data-label">{{ $t('Key195') }}</span>
  27. <span class="answer-data-count-error">{{ answerData.count_error }}</span>
  28. </div>
  29. <template v-if="material_type === 'COURSEWARE' && visible">
  30. <template v-if="category === 'OC' || category.length === 0">
  31. <bookreport
  32. v-if="isStudent"
  33. :context="context"
  34. :book-client-width="800"
  35. :book-answer-content="bookAnswerContent"
  36. />
  37. <bookquestion v-else ref="courseware" :context="context" @handleBookUserAnswer="handleBookUserAnswer" />
  38. </template>
  39. <template v-else-if="category === 'AILP'">
  40. <bookailp
  41. :context="context"
  42. :ui-type="ui_type"
  43. :preview-width="800"
  44. :preview-height="450"
  45. @handleBookUserAnswer="handleBookUserAnswer"
  46. />
  47. </template>
  48. <template v-else-if="category === 'NPC'">
  49. <booknpc v-if="context" ref="previewAnswer" :context="context" :theme-color="themeColor" />
  50. </template>
  51. <template v-if="category == 'NNPE'">
  52. <booknnpe v-if="context" :context="context" :theme-color="themeColor" />
  53. </template>
  54. </template>
  55. <template v-else>
  56. <template v-if="fileType === 'pdf'">
  57. <pdf v-for="i in numPages" :key="i" :src="pdfSrc" :page="i" />
  58. </template>
  59. <template v-else-if="isImage(fileType)">
  60. <el-image fit="contain" :src="file_url_https" />
  61. </template>
  62. <div v-else-if="fileType === 'mp3'" class="audio-file">
  63. <audio :src="file_url_https" controls />
  64. </div>
  65. <div v-else-if="fileType === 'mp4'" class="video-file">
  66. <video :src="file_url_https" controls />
  67. </div>
  68. <div v-else-if="fileType === 'txt'" class="text-file">
  69. <el-input v-model="text" type="textarea" :readonly="true" resize="none" />
  70. </div>
  71. <template v-else>
  72. <iframe
  73. :src="'https://view.officeapps.live.com/op/view.aspx?src=' + `${file_url_https}`"
  74. width="100%"
  75. height="490px"
  76. scrolling="no"
  77. />
  78. </template>
  79. </template>
  80. <div slot="footer">
  81. <el-button
  82. v-if="isCurMaterial || (!isFinished && material_type === 'COURSEWARE')"
  83. type="primary"
  84. @click="finishMyMaterial"
  85. >
  86. {{ $t('Key82') }}
  87. </el-button>
  88. </div>
  89. </el-dialog>
  90. </template>
  91. <script>
  92. import pdf from 'vue-pdf';
  93. import { GetCoursewareContent_View, GetMaterialInfo } from '@/api/course';
  94. import { GetFileStoreInfo, getContentFile } from '@/api/app';
  95. import { FinishMyMaterial, GetCurMaterialSent, GetStudentExamAnswer_FinishMaterial } from '@/api/live';
  96. import { getToken } from '@/utils/auth';
  97. export default {
  98. name: 'CurMaterial',
  99. components: {
  100. pdf
  101. },
  102. props: {
  103. taskId: {
  104. default: '',
  105. type: String
  106. },
  107. materialId: {
  108. default: '',
  109. type: String
  110. },
  111. materialType: {
  112. default: '',
  113. type: String
  114. },
  115. isFinished: {
  116. default: false,
  117. type: Boolean
  118. }
  119. },
  120. data() {
  121. return {
  122. visible: false,
  123. material_id: '',
  124. material_name: '',
  125. material_type: '',
  126. material_picture_url: '',
  127. context: null,
  128. exam_answer: '',
  129. ui_type: '',
  130. category: '',
  131. file_relative_path: '',
  132. file_url_https: '',
  133. pdfSrc: '',
  134. numPages: 1,
  135. curStudentID: '',
  136. bookAnswerContent: '',
  137. isAlignCenter: true,
  138. timer: null,
  139. isCurMaterial: true,
  140. answerData: {
  141. duration: 0,
  142. count_not_done: 0,
  143. count_right: 0,
  144. count_error: 0
  145. },
  146. text: '',
  147. themeColor: ''
  148. };
  149. },
  150. computed: {
  151. fileType() {
  152. return this.file_url_https.slice(this.file_url_https.lastIndexOf('.') + 1, this.file_url_https.length);
  153. },
  154. isStudent() {
  155. return this.curStudentID.length > 0;
  156. }
  157. },
  158. watch: {
  159. visible(newVal) {
  160. if (newVal && !this.isCurMaterial) {
  161. this.getMaterialInfo();
  162. }
  163. if (!newVal) {
  164. this.context = null;
  165. this.exam_answer = '';
  166. this.file_relative_path = '';
  167. this.file_url_https = '';
  168. this.pdfSrc = '';
  169. this.numPages = 1;
  170. this.curStudentID = '';
  171. this.material_id = '';
  172. this.material_name = '';
  173. this.material_type = '';
  174. this.material_picture_url = '';
  175. this.text = '';
  176. }
  177. },
  178. material_id(newVal) {
  179. if (newVal.length === 0) return;
  180. if (this.material_type === 'COURSEWARE') {
  181. this.getCoursewareContent_View();
  182. } else {
  183. this.getFileStoreInfo();
  184. }
  185. }
  186. },
  187. created() {
  188. this.uploadBookWriteParent();
  189. this.getCurMaterialSent();
  190. this.updateWordPack({
  191. word_key_list: ['Key274', 'Key309', 'Key244', 'Key320', 'Key321', 'Key194', 'Key195', 'Key82', 'Key333', 'Key323']
  192. });
  193. },
  194. beforeDestroy() {
  195. clearInterval(this.timer);
  196. },
  197. methods: {
  198. dialogShow() {
  199. this.isCurMaterial = false;
  200. this.visible = true;
  201. },
  202. dialogMaterialClose() {
  203. this.visible = false;
  204. if (!this.timer) this.getCurMaterialSent();
  205. this.$emit('dialogMaterialClose');
  206. },
  207. getCurMaterialSent() {
  208. this.timer = setInterval(() => {
  209. GetCurMaterialSent({ task_id: this.taskId }).then(
  210. ({ material_id, material_name, material_type, material_picture_url }) => {
  211. if (material_id !== undefined && material_id.length > 0) {
  212. this.visible = true;
  213. this.isCurMaterial = true;
  214. this.material_id = material_id;
  215. this.material_name = material_name;
  216. this.material_type = material_type;
  217. this.material_picture_url = material_picture_url;
  218. clearInterval(this.timer);
  219. this.timer = null;
  220. this.curStudentID = '-';
  221. this.$nextTick(() => {
  222. this.curStudentID = '';
  223. });
  224. }
  225. }
  226. );
  227. }, 2000);
  228. },
  229. getMaterialInfo() {
  230. GetMaterialInfo({ task_id: this.taskId, material_id: this.materialId, material_type: this.materialType }).then(
  231. ({ material_id, material_name, material_type }) => {
  232. this.material_id = material_id;
  233. this.material_name = material_name;
  234. this.material_type = material_type;
  235. }
  236. );
  237. },
  238. getCoursewareContent_View() {
  239. GetCoursewareContent_View({ id: this.material_id })
  240. .then(({ content, category, book_theme_color }) => {
  241. if (!content) {
  242. this.context = null;
  243. return;
  244. }
  245. this.category = category;
  246. if (category === 'OC' || category.length === 0) {
  247. this.context = {
  248. id: this.material_id,
  249. ui_type: JSON.parse(content).question.ui_type,
  250. content: JSON.parse(content)
  251. };
  252. this.$nextTick(() => {
  253. this.$refs.courseware.handleAnswerTimeStart();
  254. });
  255. return;
  256. }
  257. if (category === 'AILP') {
  258. const contents = JSON.parse(content);
  259. if (contents.question && contents.question.length > 0) {
  260. this.context = JSON.parse(contents.question);
  261. this.ui_type = contents.ui_type ? contents.ui_type : '';
  262. }
  263. return;
  264. }
  265. if (category === 'NPC') {
  266. this.themeColor = book_theme_color;
  267. this.context = JSON.parse(content);
  268. return;
  269. }
  270. if (category === 'NNPE') {
  271. this.themeColor = book_theme_color;
  272. this.context = JSON.parse(content);
  273. }
  274. })
  275. .then(() => {
  276. if (this.isFinished && !this.isCurMaterial) {
  277. this.getStudentExamAnswer_FinishMaterial();
  278. }
  279. });
  280. },
  281. getFileStoreInfo() {
  282. GetFileStoreInfo({ file_id: this.material_id }).then(({ file_relative_path, file_url_https }) => {
  283. this.file_relative_path = file_relative_path;
  284. this.file_url_https = file_url_https;
  285. let fileType = file_url_https.slice(file_url_https.lastIndexOf('.') + 1, file_url_https.length);
  286. if (fileType === 'pdf') {
  287. this.getNumPages(file_relative_path);
  288. }
  289. if (fileType === 'txt') {
  290. fetch(`${process.env.VUE_APP_PDF}${file_relative_path}`).then(async res => {
  291. if (!res.ok) return;
  292. this.text = await res.text();
  293. });
  294. }
  295. });
  296. },
  297. getStudentExamAnswer_FinishMaterial() {
  298. let student_id = this.$store.state.user.user_code;
  299. GetStudentExamAnswer_FinishMaterial({
  300. task_id: this.taskId,
  301. material_id: this.material_id,
  302. material_type: this.material_type,
  303. student_id
  304. }).then(({ content, duration, count_not_done, count_right, count_error }) => {
  305. this.bookAnswerContent = content;
  306. this.answerData = { duration, count_not_done, count_right, count_error };
  307. this.curStudentID = '';
  308. this.$nextTick(() => {
  309. this.curStudentID = student_id;
  310. });
  311. });
  312. },
  313. getNumPages(url) {
  314. let loadingTask = pdf.createLoadingTask(`${process.env.VUE_APP_PDF}${url}`);
  315. loadingTask.promise
  316. .then(pdf => {
  317. this.pdfSrc = loadingTask;
  318. this.numPages = pdf.numPages;
  319. })
  320. .catch(err => {
  321. console.error('pdf加载失败', err);
  322. this.$message.error(this.$i18n.t('Key323'));
  323. });
  324. },
  325. handleBookUserAnswer(data) {
  326. this.exam_answer = data;
  327. },
  328. finishMyMaterial() {
  329. if (this.material_type === 'COURSEWARE' && this.exam_answer.length === 0 && this.sys_type === 'GCLS') {
  330. this.$message.warning(this.$i18n.t('Key333'));
  331. return;
  332. }
  333. const loading = this.$loading();
  334. FinishMyMaterial({
  335. task_id: this.taskId,
  336. material_id: this.material_id,
  337. material_type: this.material_type,
  338. exam_answer: this.exam_answer
  339. })
  340. .then(() => {
  341. this.$message.success('已完成');
  342. this.dialogMaterialClose();
  343. })
  344. .finally(() => {
  345. loading.close();
  346. this.exam_answer = '';
  347. });
  348. },
  349. isImage(type) {
  350. return ['jpeg', 'gif', 'jpg', 'png', 'bmp', 'pic', 'svg'].includes(type);
  351. },
  352. /**
  353. * 课件方法
  354. */
  355. // 上传文件
  356. uploadBookWriteParent() {
  357. const { token, isHas } = getToken();
  358. let UserCode = isHas ? token.user_code : '';
  359. let UserType = isHas ? token.user_type : '';
  360. let SessionID = isHas ? token.session_id : '';
  361. this.bookuploadUrl = `${process.env.VUE_APP_BASE_API}/GCLSFileServer/WebFileUpload?UserCode=${UserCode}&UserType=${UserType}&SessionID=${SessionID}&SecurityLevel=Mid`;
  362. },
  363. // 下载文件zip
  364. downloadBookWriteParent(idList) {
  365. let MethodName = 'file_store_manager-StartCreateFileCompressPack';
  366. let data = {
  367. file_id_list: JSON.parse(idList)
  368. };
  369. getContentFile(MethodName, data).then(res => {
  370. let id = res.file_compress_task_id;
  371. this.bookDownTimer = setInterval(() => {
  372. this.checkTaskProgress(id);
  373. }, 2000);
  374. });
  375. },
  376. // 成功后调用打包进度
  377. checkTaskProgress(taskId) {
  378. const { token, isHas } = getToken();
  379. let UserCode = isHas ? token.user_code : '';
  380. let UserType = isHas ? token.user_type : '';
  381. let SessionID = isHas ? token.session_id : '';
  382. let MethodName = 'file_store_manager-GetFileCompressTaskProgress';
  383. let data = {
  384. file_compress_task_id: taskId
  385. };
  386. getContentFile(MethodName, data).then(res => {
  387. if (res.is_finish === 'true') {
  388. clearInterval(this.bookDownTimer);
  389. window.open(
  390. `${process.env.VUE_APP_BASE_API}/GCLSFileServer/WebFileDownload?UserCode=${UserCode}&UserType=${UserType}&SessionID=${SessionID}&FileID=${res.compress_pack_file_id}`
  391. );
  392. }
  393. });
  394. }
  395. }
  396. };
  397. </script>
  398. <style lang="scss">
  399. @import '~@/styles/mixin';
  400. .cur-material {
  401. @include dialog;
  402. &.align-left {
  403. .el-dialog {
  404. margin-left: 20px;
  405. }
  406. }
  407. .el-dialog__header {
  408. .dialog-header {
  409. display: flex;
  410. justify-content: space-between;
  411. padding-right: 24px;
  412. &-title {
  413. font-size: 18px;
  414. font-weight: bold;
  415. line-height: 24px;
  416. }
  417. .svg-icon {
  418. cursor: pointer;
  419. }
  420. }
  421. }
  422. .answer-data {
  423. display: flex;
  424. align-items: center;
  425. margin-bottom: 12px;
  426. font-size: 16px;
  427. &-label {
  428. margin-left: 24px;
  429. }
  430. &-duration,
  431. &-count-right,
  432. &-count-error {
  433. min-width: 60px;
  434. padding: 0 24px;
  435. text-align: center;
  436. border-right: 2px solid #dfdfdf;
  437. }
  438. &-count-error {
  439. border-right-width: 0;
  440. }
  441. }
  442. .audio-file {
  443. display: flex;
  444. justify-content: center;
  445. }
  446. .video-file {
  447. display: flex;
  448. justify-content: center;
  449. height: 100%;
  450. }
  451. .text-file {
  452. height: calc(60vh - 50px);
  453. .el-textarea,
  454. textarea {
  455. height: 100%;
  456. }
  457. }
  458. .el-dialog__body {
  459. max-height: 60vh;
  460. .el-image {
  461. width: 100%;
  462. height: calc(100% - 4px);
  463. }
  464. }
  465. }
  466. </style>