index.html 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. <!DOCTYPE html>
  2. <html lang="zh">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <meta http-equiv="X-UA-Compatible" content="ie=edge">
  7. <script src="https://unpkg.com/vue@2.6.14/dist/vue.js"></script>
  8. <script type="text/javascript" async
  9. src="https://mingxuejingbang.oss-cn-beijing.aliyuncs.com/MathJaxFiles/2.7.5/MathJax.js?config=TeX-MML-AM_CHTML"></script>
  10. <script type="text/javascript" async src="js/globalVariable.js"></script>
  11. <script type="text/javascript" async src="js/html2canvas.min.js"></script>
  12. <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
  13. <script src="https://unpkg.com/element-ui/lib/index.js"></script>
  14. <script src="https://cdn.staticfile.org/axios/0.18.0/axios.min.js"></script>
  15. <title></title>
  16. <style>
  17. html,
  18. body {
  19. margin: 0;
  20. padding: 0
  21. }
  22. .el-radio--medium.is-bordered {
  23. height: auto;
  24. padding-bottom: 8px;
  25. }
  26. #title::after {
  27. display: table;
  28. content: "";
  29. clear: both;
  30. }
  31. </style>
  32. </head>
  33. <body>
  34. <div id="app">
  35. <div style="min-height:300px;">
  36. <div id="title" style="background:#fff;max-width:1024px">
  37. <div id="qs" style="padding-bottom: 15px" v-html="data.title"></div>
  38. <div id="ans" style="background:#fff">
  39. <template v-for="(itemOpt, idx) in data.options">
  40. <div :key="idx">
  41. <el-radio style="margin-bottom: 15px" :label="selectOpt[idx]" border size="medium">
  42. {{ selectOpt[idx] + ' 、 ' }}<span style="white-space: normal" v-html="itemOpt"></span>
  43. </el-radio>
  44. </div>
  45. </template>
  46. </div>
  47. </div>
  48. </div>
  49. <div style="margin-bottom:20px;padding: 15px;background: rgb(255, 255, 255); max-width: 1024px;">
  50. <el-input v-model="questionId" style="width: 150px;" placeholder="请输入题目编号"></el-input>
  51. <el-button type="primary" :disabled="isAuto" @click="search">搜索</el-button>
  52. <el-button type="primary" :disabled="isAuto" @click="upLoad">手动上传</el-button>
  53. <el-button type="primary" @click="auto">{{ isAuto ? '暂停' : '自动运行' }}</el-button>
  54. </div>
  55. <div v-if="imgUri" style="margin-top:30px;padding: 15px;background: rgb(255, 255, 255); max-width: 1024px;">
  56. 图片展示:
  57. <img :src="imgUri" alt="" crossorigin="anonymous">
  58. </div>
  59. </div>
  60. <script>
  61. var exampleData = {
  62. selectOpt: ['A', 'B', 'C', 'D', 'E', 'F', 'G'],
  63. data: {},
  64. time: null,
  65. domain: '',
  66. isAuto: false,
  67. questionId: '2000038',
  68. imgUri: '',
  69. times: 2
  70. }
  71. // window.onload = function(){
  72. new Vue({
  73. el: '#app',
  74. data: exampleData,
  75. created() {
  76. // if(this.domain == 'localhost:9800'){
  77. this.domain = 'https://front.mingxuejinbang.com'
  78. // }
  79. // this.getData()
  80. },
  81. methods: {
  82. convertImgToBase64(img) {
  83. return new Promise((resolve, reject) => {
  84. let canvas = document.createElement('canvas')
  85. const ctx = canvas.getContext('2d')
  86. canvas.height = img.height
  87. canvas.width = img.width
  88. ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
  89. let ext = img.src.substring(img.src.lastIndexOf('.') + 1).toLowerCase()
  90. const base64 = canvas.toDataURL('image/' + ext)
  91. canvas = null
  92. const holder = new Image()
  93. holder.setAttribute('crossOrigin', 'anonymous')
  94. holder.onload = (e) => {
  95. resolve(holder)
  96. }
  97. holder.src = base64
  98. // return base64;
  99. })
  100. },
  101. hasImg(dom) {
  102. for (let i of dom?.children || []) {
  103. if (i.nodeName === 'IMG') {
  104. return true
  105. }
  106. }
  107. return false
  108. },
  109. getCanvasWrap(width, height) {
  110. const canvasWrap = document.createElement('canvas')
  111. canvasWrap.width = this.times * width
  112. canvasWrap.height = this.times * height
  113. canvasWrap.getContext('2d').scale(this.times, this.times)
  114. return canvasWrap
  115. },
  116. getUnderline(num) {
  117. const underline = document.createElement('div')
  118. // 暂定于1个空格5像素
  119. underline.setAttribute('style', `width: ${num * 5}px;border-bottom: 1px solid #000000;display: inline-block;vertical-align: baseline;transform: translateY(2px);`)
  120. return underline
  121. },
  122. // 遍历所有子元素
  123. async findAllEle(canvasContainer, parent) {
  124. for (let i of parent.childNodes) {
  125. if (i.nodeName === 'U') {
  126. parent.insertBefore(this.getUnderline(i.innerText.length), i)
  127. parent.removeChild(i)
  128. } else if (i.nodeName === 'IMG') {
  129. return new Promise((resolve, reject) => {
  130. i.onload = async(e) => {
  131. // 如果图片存在浮动,需要手动设置容器的高度
  132. if (i.style.float) {
  133. const qs = document.querySelector('#qs')
  134. qs.style.height = Math.max(qs.getBoundingClientRect().height, i.height) + 'px'
  135. }
  136. const { width, height } = qs.getBoundingClientRect()
  137. const imgCanvas = await html2canvas(qs, {
  138. canvas: this.getCanvasWrap(width, height),
  139. useCORS: true
  140. })
  141. canvasContainer.push(imgCanvas)
  142. }
  143. i.onerror = (e) => reject(e)
  144. })
  145. }
  146. if (i.hasChildNodes()) {
  147. await this.findAllEle(canvasContainer, i)
  148. }
  149. }
  150. },
  151. async waitingLoaingImg(cb) {
  152. let doms
  153. let parent
  154. const qs = document.querySelector('#qs')
  155. // 每次重置容器高度
  156. qs.style.height = 'auto'
  157. const canvasContainer = []
  158. // await this.findAllEle(canvasContainer, qs) // 原来是为了处理图片跨域问题的无效代码
  159. if (canvasContainer.length === 0) {
  160. const { width, height } = qs.getBoundingClientRect()
  161. const topCanvas = await html2canvas(qs, {
  162. canvas: this.getCanvasWrap(width, height),
  163. useCORS: true
  164. })
  165. canvasContainer.push(topCanvas)
  166. }
  167. return canvasContainer
  168. },
  169. search() {
  170. if (this.questionId) {
  171. this.getData(this.questionId)
  172. } else {
  173. this.$message({
  174. message: '请输入题目编号',
  175. type: 'warning'
  176. })
  177. }
  178. },
  179. auto() {
  180. this.isAuto = !this.isAuto
  181. if (this.isAuto) {
  182. this.getData()
  183. }
  184. },
  185. upLoad() {
  186. if (this.questionId && this.imgUri) {
  187. this.uploadQuestionImage(this.imgUri, 1)
  188. } else {
  189. this.$message({
  190. message: '请先搜索题目',
  191. type: 'warning'
  192. })
  193. }
  194. },
  195. getData(questionId) {
  196. axios.defaults.withCredentials = true
  197. let str = ''
  198. if (questionId) {
  199. str = '?questionId=' + questionId
  200. }
  201. axios.get(this.domain + '/prod-api/front/questionCollection/getNextQuestionForImageGenerate' + str).then(response => {
  202. if (response.data.code == 200) {
  203. this.data = response.data.data
  204. this.$nextTick(() => {
  205. this.MathQueueTitle(str)
  206. })
  207. } else {
  208. this.isAuto = false
  209. this.$message({
  210. message: '获取题目信息失败',
  211. type: 'warning'
  212. })
  213. }
  214. }).catch(function(error) { // 请求失败处理
  215. this.isAuto = false
  216. this.$message.error('网络请求异常!')
  217. })
  218. },
  219. uploadQuestionImage(src, type) {
  220. axios.defaults.withCredentials = true
  221. var formData = new FormData()
  222. formData.append('questionId', this.data.questionId)
  223. formData.append('imageBase64', src)
  224. if (this.isAuto == false) {
  225. formData.append('manual', true)
  226. }
  227. axios.post(this.domain + '/prod-api/front/questionCollection/uploadQuestionImage', formData).then(response => {
  228. if (response.data.code == 200) {
  229. if (this.isAuto) {
  230. setTimeout(() => {
  231. this.getData()
  232. }, 2000)
  233. }
  234. if (type) {
  235. this.$message({
  236. message: '上传题目信息成功!',
  237. type: 'success'
  238. })
  239. }
  240. } else {
  241. this.isAuto = false
  242. this.$message({
  243. message: '上传题目信息失败!',
  244. type: 'warning'
  245. })
  246. }
  247. }).catch(function(error) { // 请求失败处理
  248. this.isAuto = false
  249. this.$message.error('网络请求异常!')
  250. })
  251. },
  252. getCookie(str) {//获取cookie
  253. let cookie = document.cookie.split('; ')
  254. let cookies = {}
  255. cookie.forEach(item => {
  256. cookies[item.split('=')[0]] = item.split('=')[1]
  257. })
  258. return cookies[str]
  259. },
  260. MathQueueTitle(str) {//初始化公式
  261. if (MathQueue) {
  262. clearInterval(this.time)
  263. console.log('math queue begin', new Date().getTime(), this.data?.questionId)
  264. MathQueue('title', async() => {
  265. console.log('math queue end', new Date().getTime(), this.data?.questionId)
  266. const canvasContainer = await this.waitingLoaingImg() // TODO: 有些题会终止运行在此,如:18343772 18343941
  267. console.log('canvas download begin', new Date().getTime(), this.data?.questionId)
  268. this.downLoad(str, canvasContainer[0]) // at least contains 1 element
  269. })
  270. } else {
  271. this.time = setInterval(() => {
  272. this.MathQueueTitle()
  273. }, 5)
  274. }
  275. },
  276. downLoad(type, canvas1) {
  277. if (!this.data.questionId) {
  278. this.$message({
  279. message: '请先搜索题目!',
  280. type: 'warning'
  281. })
  282. return
  283. }
  284. this.questionId = this.data.questionId
  285. const times = 2 // 图片放大倍数,能提升清晰度
  286. let _this = this
  287. _this.imgUri = ''
  288. document.getElementById('qs').style.display = 'none'
  289. let _canvas = document.getElementById('ans')
  290. const { width, height } = _canvas.getBoundingClientRect()
  291. html2canvas(_canvas, {
  292. canvas: this.getCanvasWrap(width, height),
  293. useCORS: true
  294. }).then((canvas2) => {
  295. document.getElementById('qs').style.display = 'block'
  296. var canvas3 = document.createElement('canvas')
  297. canvas3.width = Math.max(canvas1?.width ?? 0, canvas2.width)
  298. canvas3.height = (canvas1?.height ?? 0) + (canvas2.width && canvas2.height ? canvas2.height : 0)
  299. var ctx = canvas3.getContext('2d')
  300. // 绘制题目
  301. canvas1 && ctx.drawImage(canvas1, 0, 0, canvas1.width, canvas1.height)
  302. if (canvas2.width && canvas2.height) {
  303. // 绘制答案
  304. ctx.drawImage(canvas2, 0, canvas1?.height ?? 0, canvas2.width, canvas2.height)
  305. }
  306. _this.imgUri = canvas3.toDataURL('image/png')
  307. canvas1 = null
  308. canvas2 = null
  309. canvas3 = null
  310. if (!type) {
  311. _this.uploadQuestionImage(_this.imgUri)
  312. }
  313. })
  314. }
  315. }
  316. })
  317. // }
  318. </script>
  319. </body>
  320. </html>