index_new.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757
  1. <template>
  2. <div class="workPage" v-loading="loading">
  3. <questionsAndAnswers ref="questionsAndAnswersRef" v-if="workData.type === '15'" :workData="workData"
  4. @changeWorkData="changeWorkData" />
  5. <choiceQuestion ref="choiceQuestionRef" v-if="workData.type === '45'" :workData="workData"
  6. @changeWorkData="changeWorkData" />
  7. <voteQuestion ref="voteQuestionRef" v-if="workData.type === '78'" :workData="workData"
  8. @changeWorkData="changeWorkData" />
  9. <photo ref="photoRef" v-if="workData.type === '79'" :workData="workData"
  10. @changeWorkData="changeWorkData" />
  11. <kwlPage ref="kwlPageRef" v-if="workData.type === '831'" :workData="workData"
  12. @changeWorkData="changeWorkData" />
  13. <sixHatPage ref="sixHatPageRef" v-if="workData.type === '832'" :workData="workData"
  14. @changeWorkData="changeWorkData" />
  15. <file ref="fileRef" v-if="workData.type === '84'" :workData="workData"
  16. @changeWorkData="changeWorkData" />
  17. </div>
  18. </template>
  19. <script>
  20. import katex from "katex";
  21. import "katex/dist/katex.min.css";
  22. import questionsAndAnswers from "./components/questionsAndAnswers.vue";
  23. import choiceQuestion from "./components/choiceQuestion.vue";
  24. import voteQuestion from "./components/voteQuestion.vue";
  25. import photo from "./components/photo.vue";
  26. import kwlPage from "./components/kwlPage.vue";
  27. import sixHatPage from "./components/sixHatPage.vue";
  28. import file from "./components/file.vue";
  29. export default {
  30. components: {
  31. questionsAndAnswers,
  32. choiceQuestion,
  33. voteQuestion,
  34. photo,
  35. kwlPage,
  36. sixHatPage,
  37. file,
  38. },
  39. data() {
  40. return {
  41. id: this.$route.query.id,
  42. courseid: this.$route.query.courseid,
  43. userid: this.$route.query.userid,
  44. oid: this.$route.query.oid,
  45. org: this.$route.query.org,
  46. role: this.$route.query.role,
  47. stage: this.$route.query.stage,
  48. task: this.$route.query.task,
  49. tool: this.$route.query.tool,
  50. type: "",
  51. cid: this.$route.query.cid,
  52. workData: {},
  53. loadingUploadFile: false,
  54. studentWork: null,
  55. loading: false
  56. };
  57. },
  58. watch: {
  59. workData: {
  60. deep: true,
  61. handler(newValue) {
  62. if (newValue) {
  63. this.setWorkDataToWindow();
  64. }
  65. }
  66. }
  67. },
  68. computed: {
  69. renderedFormula() {
  70. return function (val) {
  71. try {
  72. // 判断是否含有HTML标签,<tag ...>...</tag>
  73. const hasTag = /<([a-zA-Z][\w\-]*)([^>]*)>([\s\S]*?)<\/\1>/g.test(
  74. val
  75. );
  76. if (!hasTag) {
  77. val = val.trim().replace(/[\u200B-\u200D\uFEFF]/g, "");
  78. // 纯文本,整体渲染
  79. try {
  80. return katex.renderToString(val.trim(), {
  81. throwOnError: false,
  82. strict: false,
  83. output: "htmlAndMathml"
  84. });
  85. } catch (e) {
  86. return val; // 渲染失败原样输出
  87. }
  88. } else {
  89. // 有标签,对每个标签内容渲染
  90. return val.replace(
  91. /<([a-zA-Z][\w\-]*)([^>]*)>([\s\S]*?)<\/\1>/g,
  92. (match, tag, attrs, inner) => {
  93. let html;
  94. val = val.trim().replace(/[\u200B-\u200D\uFEFF]/g, "");
  95. try {
  96. html = katex.renderToString(inner.trim(), {
  97. throwOnError: false,
  98. strict: false,
  99. output: "htmlAndMathml"
  100. });
  101. } catch (e) {
  102. html = inner;
  103. }
  104. return `<${tag}${attrs}>${html}</${tag}>`;
  105. }
  106. );
  107. }
  108. } catch (e) {
  109. console.error("KaTeX渲染错误:", e);
  110. return val;
  111. }
  112. };
  113. }
  114. },
  115. methods: {
  116. getWorkData() {
  117. if (!this.id) return console.log("无作业id");
  118. let params = {
  119. id: this.id
  120. };
  121. this.ajax
  122. .get(this.$store.state.api + "select_workPageById", params)
  123. .then(res => {
  124. let _data = res.data[0];
  125. if (_data.length) {
  126. _data = _data[0];
  127. _data.json = JSON.parse(_data.json);
  128. if (_data.type == 15) {
  129. _data.json.answer = "";
  130. _data.json.fileList = [];
  131. } else if (_data.type == 45) {
  132. _data.json.testJson.forEach(item => {
  133. if (item.type == "2") {
  134. item.userAnswer = [];
  135. } else if (item.type == "1") {
  136. item.userAnswer = "";
  137. }
  138. });
  139. }else if(_data.type == 79){
  140. _data.json.fileList = [];
  141. }else if(_data.type == 84){
  142. _data.json.fileList = [];
  143. }
  144. this.workData = _data;
  145. this.studentWorkToWorkData();
  146. this.getSubmitWork();
  147. }
  148. });
  149. },
  150. setWorkDataToWindow() {
  151. if (this.workData.json) {
  152. window.workData = JSON.parse(JSON.stringify(this.workData));
  153. }
  154. },
  155. uploadImage() {
  156. if (this.loadingUploadFile) return;
  157. let input = document.createElement("input");
  158. input.type = "file";
  159. input.accept = "image/jpeg,image/png";
  160. input.click();
  161. input.onchange = e => {
  162. let file = e.target.files[0];
  163. if (file) {
  164. if (file.size > 5 * 1024 * 1024) {
  165. // 5MB
  166. return this.$message.error(this.lang.ssImgTooBig);
  167. }
  168. let _type = file.type;
  169. let _size = file.size;
  170. this.loadingUploadFile = true;
  171. this.uploadFile(file).then(res => {
  172. let _file = {
  173. name: file.name,
  174. url: res.Location,
  175. type: _type,
  176. size: _size,
  177. uploadTime: new Date().getTime()
  178. };
  179. if (["15"].includes(this.workData.type)) {
  180. this.workData.json.fileList.push(_file);
  181. }
  182. this.$forceUpdate();
  183. this.loadingUploadFile = false;
  184. });
  185. }
  186. };
  187. },
  188. uploadFile(file) {
  189. return new Promise(resolve => {
  190. var credentials = {
  191. accessKeyId: "AKIATLPEDU37QV5CHLMH",
  192. secretAccessKey: "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"
  193. }; //秘钥形式的登录上传
  194. window.AWS.config.update(credentials);
  195. window.AWS.config.region = "cn-northwest-1"; //设置区域
  196. var bucket = new window.AWS.S3({ params: { Bucket: "ccrb" } }); //选择桶
  197. var _this = this;
  198. if (file) {
  199. var params = {
  200. Key:
  201. file.name.split(".")[0] +
  202. new Date().getTime() +
  203. "." +
  204. file.name.split(".")[file.name.split(".").length - 1],
  205. ContentType: file.type,
  206. Body: file,
  207. "Access-Control-Allow-Credentials": "*",
  208. ACL: "public-read"
  209. }; //key可以设置为桶的相抵路径,Body为文件, ACL最好要设置
  210. var options = {
  211. partSize: 2048 * 1024 * 1024,
  212. queueSize: 2,
  213. leavePartsOnError: true
  214. };
  215. bucket
  216. .upload(params, options)
  217. .on("httpUploadProgress", function (evt) {
  218. //这里可以写进度条
  219. // console.log("Uploaded : " + parseInt((evt.loaded * 80) / evt.total) + '%');
  220. })
  221. .send(function (err, data) {
  222. if (err) {
  223. _this.$message.error(_this.lang.ssUploadFail);
  224. resolve(null);
  225. } else {
  226. resolve(data);
  227. }
  228. });
  229. }
  230. });
  231. },
  232. //预览图片
  233. previewImage(url) {
  234. this.$hevueImgPreview(url);
  235. },
  236. deleteFile(index) {
  237. this.workData.json.fileList.splice(index, 1);
  238. this.$forceUpdate();
  239. },
  240. submitWork(task) {
  241. return new Promise(resolve => {
  242. let params = [];
  243. if (this.workData.type == "15") {
  244. //问答题
  245. params.push({
  246. uid: this.userid,
  247. cid: this.courseid,
  248. stage: 0,
  249. task: task,
  250. tool: 0,
  251. atool: "15",
  252. content: encodeURIComponent(JSON.stringify(this.workData.json)),
  253. type: "3"
  254. });
  255. } else if (this.workData.type == "45") {
  256. //选择题
  257. params.push({
  258. uid: this.userid,
  259. cid: this.courseid,
  260. stage: 0,
  261. task: task,
  262. tool: 0,
  263. atool: "45",
  264. content: encodeURIComponent(JSON.stringify(this.workData.json)),
  265. type: "8"
  266. });
  267. } else if (this.workData.type == "78") {
  268. //投票题
  269. params.push({
  270. uid: this.userid,
  271. cid: this.courseid,
  272. stage: 0,
  273. task: task,
  274. tool: 0,
  275. atool: "78",
  276. content: encodeURIComponent(JSON.stringify(this.workData.json)),
  277. type: "22"
  278. });
  279. } else if (this.workData.type == "79") {
  280. //照片题
  281. params.push({
  282. uid: this.userid,
  283. cid: this.courseid,
  284. stage: 0,
  285. task: task,
  286. tool: 0,
  287. atool: "79",
  288. content: encodeURIComponent(JSON.stringify(this.workData.json)),
  289. type: "23"
  290. });
  291. } else if (this.workData.type == "831") {
  292. //跨学科kwl题
  293. params.push({
  294. uid: this.userid,
  295. cid: this.courseid,
  296. stage: 0,
  297. task: task,
  298. tool: 0,
  299. atool: "831",
  300. content: encodeURIComponent(JSON.stringify(this.workData.json)),
  301. type: "831"
  302. });
  303. } else if (this.workData.type == "832") {
  304. //跨学科kwl题
  305. params.push({
  306. uid: this.userid,
  307. cid: this.courseid,
  308. stage: 0,
  309. task: task,
  310. tool: 0,
  311. atool: "832",
  312. content: encodeURIComponent(JSON.stringify(this.workData.json)),
  313. type: "832"
  314. });
  315. } else if (this.workData.type == "84") {
  316. //文件题
  317. params.push({
  318. uid: this.userid,
  319. cid: this.courseid,
  320. stage: 0,
  321. task: task,
  322. tool: 0,
  323. atool: "84",
  324. content: encodeURIComponent(JSON.stringify(this.workData.json)),
  325. type: "84"
  326. });
  327. }
  328. if (
  329. ["3", "8", "22", "23", "831", "832", "84"].includes(params[0].type) &&
  330. params[0].uid &&
  331. params[0].cid
  332. ) {
  333. this.loading = true;
  334. this.ajax
  335. .post(this.$store.state.api + "addCourseWorks_workPage", params)
  336. .then(res => {
  337. let _data = res.data[0];
  338. this.loading = false;
  339. if (_data.length > 0) {
  340. let _id = _data[0].id;
  341. resolve(_id);
  342. } else {
  343. resolve("");
  344. }
  345. })
  346. .catch(err => {
  347. console.log(err);
  348. resolve("");
  349. this.loading = false;
  350. });
  351. }
  352. });
  353. },
  354. //选择题选择
  355. chooseAnswer(index, index2) {
  356. if (this.workData.json.testJson[index].type == "1") {
  357. this.workData.json.testJson[index].userAnswer = index2;
  358. } else if (this.workData.json.testJson[index].type == "2") {
  359. if (this.workData.json.testJson[index].userAnswer.includes(index2)) {
  360. this.workData.json.testJson[index].userAnswer.splice(
  361. this.workData.json.testJson[index].userAnswer.indexOf(index2),
  362. 1
  363. );
  364. } else {
  365. this.workData.json.testJson[index].userAnswer.push(index2);
  366. }
  367. }
  368. },
  369. //获取已经提交的作业
  370. getSubmitWork() {
  371. if (
  372. this.userid &&
  373. this.courseid &&
  374. this.stage &&
  375. this.task &&
  376. this.tool
  377. ) {
  378. let params = {
  379. uid: this.userid,
  380. cid: this.courseid,
  381. stage: this.stage,
  382. task: this.task,
  383. tool: this.tool
  384. };
  385. this.loading = true;
  386. this.ajax
  387. .get(
  388. this.$store.state.api + "select_courseWorks_workPageData",
  389. params
  390. )
  391. .then(res => {
  392. let _data = res.data[0];
  393. this.loading = false;
  394. if (_data.length) {
  395. _data = _data[0];
  396. console.log("select_courseWorks_workPageData",_data);
  397. if (["15", "45", "78", "79", "831", "832", "84"].includes(_data.atool)) {
  398. let _work = JSON.parse(decodeURIComponent(_data.content));
  399. this.studentWork = _work;
  400. this.studentWorkToWorkData();
  401. } else {
  402. console.log("工具不一致")
  403. }
  404. }
  405. })
  406. .catch(err => {
  407. this.loading = false;
  408. console.error(err);
  409. });
  410. }
  411. },
  412. studentWorkToWorkData() {
  413. if (this.studentWork && this.workData) {
  414. console.log("studentWork", this.studentWork);
  415. console.log("workData", this.workData);
  416. let _work = JSON.parse(JSON.stringify(this.studentWork));
  417. if (this.workData.type == "15") {
  418. this.workData.json = {...this.workData.json,..._work}
  419. console.log("this.workData.json", this.workData.json);
  420. this.$refs.questionsAndAnswersRef.setWork(this.workData.json);
  421. this.$forceUpdate();
  422. } else if (this.workData.type == "45") {
  423. this.workData.json.testJson.forEach((item, index) => {
  424. if (
  425. item.type == _work.testJson[index].type &&
  426. item.teststitle == _work.testJson[index].teststitle &&
  427. JSON.stringify(item.checkList) ==
  428. JSON.stringify(_work.testJson[index].checkList)
  429. ) {
  430. this.workData.json.testJson[index].userAnswer =
  431. _work.testJson[index].userAnswer;
  432. }
  433. });
  434. } else if (this.workData.type == "78") {
  435. this.workData.json.testJson.forEach((item, index) => {
  436. if (
  437. item.type == _work.testJson[index].type &&
  438. item.teststitle == _work.testJson[index].teststitle &&
  439. JSON.stringify(item.checkList) ==
  440. JSON.stringify(_work.testJson[index].checkList)
  441. ) {
  442. this.workData.json.testJson[index].userAnswer =
  443. _work.testJson[index].userAnswer;
  444. }
  445. });
  446. }else if(this.workData.type == "79"){
  447. this.workData.json.fileList = _work.fileList;
  448. }else if(this.workData.type == "831"){
  449. this.workData.json = _work;
  450. }else if(this.workData.type == "832"){
  451. this.workData.json = _work;
  452. }else if(this.workData.type == "84"){
  453. //文件题
  454. this.workData.json.fileList = _work.fileList;
  455. }
  456. this.$forceUpdate();
  457. }
  458. },
  459. //更新workData
  460. changeWorkData(newValue) {
  461. if(JSON.stringify(newValue) == JSON.stringify(this.workData.json)) return
  462. console.log("changeWorkData", newValue)
  463. this.workData.json = JSON.parse(newValue)
  464. }
  465. },
  466. mounted() {
  467. this.getWorkData();
  468. window.submitWork = this.submitWork;
  469. }
  470. };
  471. </script>
  472. <style scoped>
  473. .workPage {
  474. width: 100%;
  475. height: 100%;
  476. min-height: 100%;
  477. background-color: #fff;
  478. overflow: auto;
  479. display: flex;
  480. flex-direction: column;
  481. align-items: center;
  482. }
  483. .wp_tool {
  484. width: 100%;
  485. height: auto;
  486. display: flex;
  487. flex-direction: column;
  488. align-items: center;
  489. padding: 40px 0;
  490. }
  491. .wp_t15_title {
  492. font-size: 40px;
  493. font-weight: 300;
  494. margin: 20px 0;
  495. max-width: 50%;
  496. min-width: 300px;
  497. text-align: left;
  498. }
  499. .wp_type {
  500. margin: 20px 0;
  501. font-size: 1em;
  502. color: #7b7b7b;
  503. }
  504. .wp_tl15_inputArea {
  505. width: 50%;
  506. min-width: 300px;
  507. margin: 20px 0;
  508. }
  509. .wp_tl15_inputArea>>>textarea {
  510. border-color: #ececec;
  511. border-radius: 10px;
  512. padding: 10px 20px;
  513. box-shadow: 0 0 10px #ececec;
  514. }
  515. .wp_tl15_inputArea>>>textarea::placeholder {
  516. color: #7f7f7f;
  517. }
  518. .wp_tl15_uploadFileArea {
  519. width: 50%;
  520. min-width: 300px;
  521. height: auto;
  522. display: flex;
  523. align-items: center;
  524. margin-bottom: 10px;
  525. }
  526. .wp_tl_btn {
  527. width: auto;
  528. height: auto;
  529. padding: 12px 16px;
  530. background-color: #f9f9f9;
  531. border-radius: 10px;
  532. border: 1px dashed #e5e5e5;
  533. font-size: 14px;
  534. display: flex;
  535. align-items: center;
  536. justify-content: center;
  537. color: #474747;
  538. cursor: pointer;
  539. margin-right: 10px;
  540. transition: 0.3s;
  541. }
  542. .wp_tl_btn:hover {
  543. background-color: #ececec;
  544. }
  545. .wp_tl_btn>svg {
  546. width: 15px;
  547. height: 15px;
  548. margin-right: 5px;
  549. }
  550. .wp_tl15_uploadFileArea>span {
  551. font-size: 14px;
  552. color: #686868;
  553. }
  554. .wp_tl15_fileList {
  555. width: 50%;
  556. min-width: 300px;
  557. height: auto;
  558. display: flex;
  559. align-items: center;
  560. flex-wrap: wrap;
  561. }
  562. .wp_tl15_fileList_item {
  563. width: 150px;
  564. height: 100px;
  565. border-radius: 10px;
  566. position: relative;
  567. margin-right: 20px;
  568. }
  569. .wp_tl15_fileList_item:hover>svg {
  570. display: block;
  571. }
  572. .wp_tl15_fileList_item>img {
  573. width: 100%;
  574. height: 100%;
  575. object-fit: cover;
  576. border-radius: 10px;
  577. box-shadow: 0 0 10px #ececec;
  578. margin: 10px;
  579. cursor: pointer;
  580. }
  581. .wp_tl15_fileList_item>svg {
  582. width: 20px;
  583. height: 20px;
  584. position: absolute;
  585. right: -20px;
  586. top: 1px;
  587. cursor: pointer;
  588. display: none;
  589. }
  590. /* 选择题 */
  591. .wp_tool45 {
  592. width: 60%;
  593. height: auto;
  594. }
  595. .wp_t45_title {
  596. font-size: 3em;
  597. font-weight: 300;
  598. margin: 20px 0 40px 0;
  599. }
  600. .s_b_m_toolItem {
  601. width: 100%;
  602. height: auto;
  603. margin-bottom: 40px;
  604. }
  605. .s_b_m_ti_option {
  606. width: 100%;
  607. height: auto;
  608. padding: 15px 15px 15px 15px;
  609. display: flex;
  610. /* flex-wrap: wrap; */
  611. background-color: #f3f7fd;
  612. border-radius: 30px;
  613. margin: 10px 0 10px 0px;
  614. box-sizing: border-box;
  615. cursor: pointer;
  616. }
  617. .s_b_m_ti_option>span>img {
  618. max-height: 150px;
  619. border-radius: 2px;
  620. cursor: pointer;
  621. }
  622. .s_b_m_ti_o_btn {
  623. width: 20px;
  624. height: 100%;
  625. min-height: 20px;
  626. display: flex;
  627. justify-content: center;
  628. align-items: center;
  629. margin-right: 10px;
  630. }
  631. .s_b_m_ti_o_btn>span {
  632. width: 20px;
  633. height: 20px;
  634. display: block;
  635. box-sizing: border-box;
  636. border: solid 1px #3681fc;
  637. overflow: hidden;
  638. }
  639. .s_b_m_ti_o_btn>span>span {
  640. width: 100%;
  641. height: 100%;
  642. display: flex;
  643. justify-content: center;
  644. align-items: center;
  645. background-color: #3681fc;
  646. position: relative;
  647. }
  648. .s_b_m_ti_o_btn1 {
  649. border-radius: 50%;
  650. }
  651. .s_b_m_ti_o_btn1>span::after {
  652. content: "";
  653. width: 8px;
  654. height: 8px;
  655. position: absolute;
  656. background-color: #fff;
  657. border-radius: 50%;
  658. }
  659. .s_b_m_ti_o_btn2 {
  660. border-radius: 2px;
  661. }
  662. .s_b_m_ti_o_btn2>span::after {
  663. content: "";
  664. width: 8px;
  665. height: 8px;
  666. position: absolute;
  667. background-color: #fff;
  668. }
  669. .s_b_m_ti_o_choice {
  670. border: solid 1px #3681fc;
  671. }
  672. .s_b_m_ti_title {
  673. display: flex;
  674. align-items: flex-start;
  675. }
  676. .s_b_m_ti_title>span:nth-of-type(1) {
  677. font-size: 30px;
  678. font-weight: bold;
  679. color: #3681fc;
  680. min-height: 30px;
  681. display: flex;
  682. align-items: center;
  683. justify-content: center;
  684. margin-top: -3px;
  685. }
  686. .s_b_m_ti_title>svg {
  687. width: 30px;
  688. height: 30px;
  689. min-width: 30px;
  690. min-height: 30px;
  691. margin: 0 10px 0 0px;
  692. }
  693. .s_b_m_ti_title>span:nth-of-type(2) {
  694. font-size: 20px;
  695. font-weight: bold;
  696. display: flex;
  697. flex-wrap: wrap;
  698. align-items: center;
  699. min-height: 30px;
  700. line-height: 30px;
  701. color: #1f1f1f;
  702. }
  703. .s_b_m_ti_title>div {
  704. font-size: 30px;
  705. font-weight: bold;
  706. }
  707. /* 选择题 */
  708. </style>