index.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  1. // pages/index/component/PicturePanel/index.js
  2. const MAX_PICTURE = 6
  3. const app = getApp()
  4. const API = app.globalData.api
  5. const SUFFIX = ["jpg", "jpeg", "png"]
  6. const MASK = false
  7. const MD5 = require('js-md5')
  8. Component({
  9. /**
  10. * 组件的属性列表
  11. */
  12. properties: {
  13. paper: {
  14. type: Object
  15. },
  16. cancelAll: {
  17. type: Boolean,
  18. observer: function(isCancelAll) {
  19. console.log("isCancelAll",isCancelAll)
  20. if (this.data.willSubmit && isCancelAll) {
  21. wx.hideLoading()
  22. }
  23. }
  24. }
  25. },
  26. /**
  27. * 组件的初始数据
  28. */
  29. data: {
  30. size: 0,
  31. maxPicture: MAX_PICTURE,
  32. pictures: [],
  33. enableChoose: true,
  34. gridLoading: false,
  35. willSubmit: false, // 确定按钮是否已点击
  36. mmd: MD5
  37. },
  38. timeoutHandler: null,
  39. /**
  40. * 组件的方法列表
  41. */
  42. methods: {
  43. reset: function() {
  44. this.setData({
  45. pictures: [],
  46. willSubmit: false
  47. })
  48. },
  49. del: function(e) {
  50. if (this.data.willSubmit) {
  51. return false
  52. }
  53. var index = e.currentTarget.dataset.index;
  54. console.log(index);
  55. var pictures = this.data.pictures
  56. pictures.splice(index, 1)
  57. this.setData({
  58. pictures: pictures
  59. })
  60. },
  61. choosePic: function(e) {
  62. if (this.data.willSubmit) {
  63. return false
  64. }
  65. if (this.data.pictures.length >= MAX_PICTURE) {
  66. return false
  67. }
  68. console.log('choosePic', this.data.enableChoose)
  69. if (!this.data.enableChoose) {
  70. return false
  71. }
  72. this.setData({
  73. enableChoose: false,
  74. })
  75. var that = this
  76. wx.showActionSheet({
  77. itemList: ['拍照'],
  78. success: chooseResult => {
  79. console.log('showActionSheet',this.data.pictures.length)
  80. console.log('chooseResult.tapIndex1',chooseResult.tapIndex)
  81. if (this.data.pictures.length >= MAX_PICTURE) {
  82. wx.showToast({
  83. title: '一次最多只能选择6张',
  84. icon: 'none',
  85. mask: MASK
  86. })
  87. return
  88. }
  89. console.log('chooseResult.tapIndex2',chooseResult.tapIndex)
  90. that.setData({
  91. gridLoading: true
  92. })
  93. console.log('chooseResult.tapIndex3',chooseResult.tapIndex)
  94. switch (chooseResult.tapIndex) {
  95. case 0:
  96. wx.chooseMedia({
  97. sourceType: ['camera'],
  98. sizeType: ['original'],
  99. mediaType: ['image'],
  100. camera: 'back',
  101. fail: e => {
  102. console.log('chooseMedia:fail:' + e.errMsg)
  103. },
  104. success: res => {
  105. console.log(res);
  106. var pictures = that.data.pictures
  107. res.tempFiles.forEach(function (item, idx) {
  108. console.log(idx, item)
  109. item['suffix'] = item.tempFilePath.substring(item.tempFilePath.lastIndexOf(".") + 1, item.tempFilePath.length)
  110. item['err'] = SUFFIX.indexOf(item['suffix'].toLowerCase()) > -1 ? '' : '图片格式错误'
  111. if (item['err'] == '') {
  112. item['err'] = item.size > 1024 * 1024 * 10 ? '图片超过10M' : ''
  113. }
  114. console.log(item)
  115. })
  116. console.log(res.tempFiles)
  117. Array.prototype.push.apply(pictures, res.tempFiles);
  118. console.log('pictures::' + pictures)
  119. that.setData({
  120. pictures: pictures,
  121. })
  122. },
  123. complete: () => {
  124. that.setData({
  125. gridLoading: false
  126. })
  127. }
  128. })
  129. break;
  130. case 1:
  131. wx.chooseMedia({
  132. count: MAX_PICTURE - that.data.pictures.length,
  133. sizeType: ['original'],
  134. sourceType: ['album'],
  135. mediaType: ['image'],
  136. camera: 'back',
  137. success: res => {
  138. console.log(res);
  139. var pictures = that.data.pictures
  140. var pic = []
  141. res.tempFiles.forEach(function (item, idx) {
  142. console.log(idx, item)
  143. if (that.data.pictures.length + idx + 1 <= MAX_PICTURE) {
  144. item['suffix'] = item.tempFilePath.substring(item.tempFilePath.lastIndexOf(".") + 1, item.tempFilePath.length)
  145. item['err'] = SUFFIX.indexOf(item['suffix'].toLowerCase()) > -1 ? '' : '图片格式错误'
  146. if (item['err'] == '') {
  147. item['err'] = item.size > 1024 * 1024 * 10 ? '图片超过10M' : ''
  148. }
  149. pic.push(item)
  150. console.log(item)
  151. }
  152. })
  153. console.log(pic)
  154. Array.prototype.push.apply(pictures, pic);
  155. that.setData({
  156. pictures: pictures,
  157. })
  158. },
  159. complete: () => {
  160. that.setData({
  161. gridLoading: false
  162. })
  163. }
  164. })
  165. break;
  166. }
  167. },
  168. complete: () => {
  169. that.setData({
  170. enableChoose: true
  171. })
  172. }
  173. })
  174. },
  175. submit: function(e) {
  176. const pictures = this.data.pictures
  177. var valid = true
  178. pictures.forEach(function(item, index) {
  179. pictures[index]['retry'] = 0
  180. if (item.err) {
  181. valid = false
  182. return
  183. }
  184. })
  185. if (!valid) {
  186. wx.showToast({
  187. title: '仅支持10M以下的jpg、png、jpeg格式图片',
  188. icon: 'none',
  189. duration: 3000,
  190. mask: MASK
  191. })
  192. return
  193. }
  194. if (this.data.willSubmit) {
  195. return false
  196. }
  197. this.setData({
  198. willSubmit: true
  199. })
  200. wx.showLoading({
  201. title: '正在上传(0/' + this.data.pictures.length + ')',
  202. mask: MASK
  203. })
  204. var that = this
  205. this.timeoutHandler = setTimeout(function() {
  206. console.log('timeout', new Date())
  207. wx.hideLoading()
  208. that.setData({
  209. willSubmit: false
  210. })
  211. if (that.data.pictures.length > 0) {
  212. wx.showToast({
  213. title: '上传失败',
  214. icon: 'none',
  215. duration: 2500,
  216. mask: MASK
  217. })
  218. }
  219. }, 60000)
  220. this.checkUploadComplete()
  221. //TODO 断点上传
  222. for (var index = 0; index < pictures.length; index++) {
  223. console.log('uploadImage:' + index)
  224. var retryTimes = 3
  225. this.uploadImage(index, retryTimes)
  226. }
  227. },
  228. checkFailed() {
  229. var _pics = this.data.pictures
  230. var retryFailedCount = _pics.filter(function (item) {
  231. return item != null && item.status == 'FAIL' && item.retry <= 0
  232. }).length
  233. var failedCount = _pics.filter(function (item) {
  234. return item != null && item.status == 'FAIL'
  235. }).length
  236. console.log('checkFailed', new Date, retryFailedCount)
  237. var that = this
  238. if (failedCount > 0 && retryFailedCount == failedCount) {
  239. clearTimeout(this.timeoutHandler)
  240. wx.hideLoading()
  241. wx.showToast({
  242. title: '上传失败',
  243. icon: 'none',
  244. duration: 2500,
  245. mask: MASK
  246. })
  247. that.setData({
  248. willSubmit: false
  249. })
  250. }
  251. },
  252. checkUploadComplete() {
  253. if (this.data.willSubmit && !this.data.cancelAll && !this.checkFailed()) {
  254. var _pics = this.data.pictures
  255. var uploadCount = _pics.filter(function(item) {
  256. return item != null && item.status == 'SUCCESS'
  257. }).length
  258. console.log('checkUploadComplete',_pics)
  259. if (uploadCount == this.data.pictures.length) {
  260. clearTimeout(this.timeoutHandler)
  261. this.timeoutHandler = setTimeout(function () {
  262. console.log('timeout', new Date())
  263. wx.hideLoading()
  264. that.setData({
  265. willSubmit: false
  266. })
  267. if (that.data.pictures.length > 0) {
  268. wx.showToast({
  269. title: '更新答案超时',
  270. icon: 'none',
  271. duration: 2500,
  272. mask: MASK
  273. })
  274. }
  275. }, 30000)
  276. wx.showLoading({
  277. title: '正在更新答案',
  278. mask: MASK
  279. })
  280. // TODO 合并图片地址,保存地址到服务端,轮询获取保存状态
  281. const imagePaths = this.data.pictures.map(function(item) {
  282. return item.url
  283. }).join(',')
  284. const that = this
  285. console.log('notifyServer')
  286. API.notifyServer(that.data.paper.examRecordDataId, that.data.paper.examStudentId,
  287. that.data.paper.questionOrder, imagePaths, 'PIC')
  288. .then(notifyResult => {
  289. console.log(notifyResult)
  290. const _startTime = new Date().getTime()
  291. var times = 10 // 轮询10次
  292. const handler = setInterval(function() { // 10秒超时
  293. API.getNotifyResult(notifyResult)
  294. .then(res => {
  295. console.log(res)
  296. if (res == 'CONFIRMED') {
  297. clearTimeout(that.timeoutHandler)
  298. clearInterval(handler)
  299. wx.hideLoading()
  300. wx.showToast({
  301. title: '上传成功\n继续扫描下一题',
  302. icon: 'success',
  303. duration: 2500,
  304. mask: MASK
  305. })
  306. setTimeout(function () {
  307. wx.reLaunch({
  308. url: '/pages/index/index'
  309. })
  310. }, 2500)
  311. } else if (res == 'DISCARDED') {
  312. clearTimeout(that.timeoutHandler)
  313. clearInterval(handler)
  314. wx.hideLoading()
  315. wx.showToast({
  316. title: '题目已切换,请重新扫码',
  317. icon: 'none',
  318. duration: 2500,
  319. mask: MASK
  320. })
  321. setTimeout(function () {
  322. wx.reLaunch({
  323. url: '/pages/index/index'
  324. })
  325. }, 2500)
  326. } else {
  327. throw '答案未同步'
  328. }
  329. }).catch(exceptions => {
  330. console.log('getNotifyResult:catch:', exceptions)
  331. times--
  332. if (new Date().getTime() - _startTime >= 1000 * 10 || times < 0) {
  333. clearInterval(handler)
  334. clearTimeout(that.timeoutHandler)
  335. wx.hideLoading()
  336. wx.showToast({
  337. title: '答案上传失败,请点击重试',
  338. icon: 'none',
  339. duration: 2500,
  340. mask: MASK
  341. })
  342. that.setData({
  343. willSubmit:false
  344. })
  345. }
  346. })
  347. }, 1000)
  348. }).catch(function(error) {
  349. clearTimeout(that.timeoutHandler)
  350. wx.hideLoading()
  351. wx.showToast({
  352. title: error,
  353. icon: 'none',
  354. duration: 2500,
  355. mask: MASK
  356. })
  357. that.setData({
  358. willSubmit: false
  359. })
  360. })
  361. return true
  362. } else {
  363. console.log('正在上传(' + uploadCount + '/' + this.data.pictures.length + ')')
  364. wx.showLoading({
  365. title: '正在上传(' + uploadCount + '/' + this.data.pictures.length + ')',
  366. mask: MASK
  367. })
  368. return false
  369. }
  370. }
  371. },
  372. uploadFail(that, filepath, md5, formParams, formUrl, accessUrl, index, retry) {
  373. var pic = that.data.pictures
  374. pic[index]['status'] = 'FAIL'
  375. pic[index]['retry'] = 3 - retry
  376. that.setData({
  377. pictures: pic
  378. })
  379. that.checkFailed()
  380. retry--
  381. if (retry > 0) {
  382. that.uploadFile(filepath, md5, formParams, md5, formUrl, accessUrl, index, retry)
  383. }
  384. },
  385. //上传文件,支持配置重试次数
  386. uploadFile(filepath, md5, formParams, formUrl, accessUrl, index, retry) {
  387. console.log('uploadFile')
  388. if (this.data.cancelAll) {
  389. return
  390. }
  391. if (retry <= 0) {
  392. return
  393. }
  394. const that = this
  395. const c_md5 = wx.arrayBufferToBase64(new Uint8Array(md5.match(/[\da-f]{2}/gi).map(function (h) {
  396. return parseInt(h, 16)
  397. })))
  398. console.log('uploadFile::start upload')
  399. const task = wx.uploadFile({
  400. url: formUrl,
  401. filePath: filepath,
  402. name: 'file',
  403. formData: formParams,
  404. success: taskRes => {
  405. console.log('uploadFile::upload success', taskRes)
  406. var pic = that.data.pictures
  407. pic[index]['url'] = accessUrl
  408. pic[index]['status'] = 'SUCCESS'
  409. that.setData({
  410. pictures: pic
  411. })
  412. that.checkUploadComplete()
  413. },
  414. fail: error => {
  415. console.log('uploadFile:uploadFile:catch', error)
  416. that.uploadFail(that, filepath, md5, formParams, formUrl, accessUrl, index, retry)
  417. }
  418. })
  419. task.onHeadersReceived(res => {
  420. console.log('task header::', res, res.header['Content-MD5'], c_md5)
  421. if (c_md5 != res.header['Content-MD5']) {
  422. that.uploadFail(that, filepath, md5, formParams, formUrl, accessUrl, index, retry)
  423. }
  424. })
  425. },
  426. uploadImage(imageIndex, retry) {
  427. if (this.data.cancelAll) {
  428. return;
  429. }
  430. const img = this.data.pictures[imageIndex]
  431. console.log(img)
  432. if (img.status == 'SUCCESS') { // 已上传
  433. return
  434. }
  435. const that = this
  436. if (retry > 0) {
  437. wx.compressImage({
  438. src: img.tempFilePath,
  439. quality: 80,
  440. success: function(cf) {
  441. console.log('uploadImage:compressImage:success',cf)
  442. wx.getFileInfo({
  443. filePath: cf.tempFilePath,
  444. success: function (fileRes) {
  445. console.log('uploadImage:compressImage:success>>getFileInfo:success', fileRes)
  446. // 获取上传签名
  447. API.getSign(that.data.paper.examRecordDataId, that.data.paper.examStudentId,
  448. that.data.paper.questionOrder, fileRes.digest, 'jpeg')
  449. .then(signResult => {
  450. // 上传文件
  451. var _retry = 3
  452. that.uploadFile(cf.tempFilePath, fileRes.digest, signResult.formParams, signResult.formUrl, signResult.accessUrl, imageIndex, _retry)
  453. }).catch(exceptions => {
  454. var pic = that.data.pictures
  455. pic[imageIndex]['status'] = 'FAIL'
  456. pic[imageIndex]['retry'] = 3 - retry
  457. that.setData({
  458. pictures: pic
  459. })
  460. that.checkFailed()
  461. console.log('uploadImage:exceptions:', new Date(), pic, exceptions)
  462. that.uploadImage(imageIndex, --retry)
  463. })
  464. }
  465. })
  466. },
  467. error: function() {
  468. var pic = that.data.pictures
  469. pic[imageIndex]['status'] = 'FAIL'
  470. pic[imageIndex]['retry'] = 3 - retry
  471. that.setData({
  472. pictures: pic
  473. })
  474. that.checkFailed()
  475. console.log('压缩失败')
  476. },
  477. complete: function () {
  478. console.log('压缩complete')
  479. }
  480. })
  481. } else {
  482. var pic = that.data.pictures
  483. pic[imageIndex]['status'] = 'FAIL'
  484. pic[imageIndex]['retry'] = 3 - retry
  485. that.setData({
  486. pictures: pic
  487. })
  488. that.checkFailed()
  489. }
  490. },
  491. },
  492. attached() {
  493. var result = wx.getSystemInfoSync();
  494. this.setData({
  495. size: (result.screenWidth - 20 * 2 - 10 * 4) / 3
  496. })
  497. }
  498. })