ecert.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676
  1. <!--
  2. 电子证书
  3. http://localhost:5173/card/#/pages/achievement/ecert/ecert
  4. https://oss-mbh5.colormaprun.com/card/#/pages/achievement/ecert/ecert
  5. -->
  6. <template>
  7. <view class="main">
  8. <my-topbar :mcName="ecertConfig.title" class="mytopbar" :showRule="false" @btnBackClick="btnBack"></my-topbar>
  9. <!-- <text class="title" v-html="ecertConfig.title"></text> -->
  10. <image v-if="canvasReady" ref="ecertImg" class="ecertImg" :style="getEcertImageStyle()" mode="aspectFit" :src="ecertUrl" @click="previewCert"></image>
  11. <div v-else class="loading-animation"></div>
  12. <view class="btnlist uni-row uni-jcse">
  13. <text v-if="isCertCreate == 0" class="btnlist-item" @click="modify">修改姓名</text>
  14. <text class="btnlist-item" @click="previewCert">预览证书</text>
  15. <text class="btnlist-item" @click="downloadCert">下载证书</text>
  16. </view>
  17. <!-- <br>
  18. <a :href="'action://down_file?filename='+encodeURIComponent('图片下载_xfl.png')+'&url='+encodeURIComponent('https://oss-mbh5.colormaprun.com/card/static/logo/xfl.png')">下载图片</a>
  19. &nbsp;
  20. <a :href="'action://down_file?filename='+encodeURIComponent('文件下载_index.html')+'&url='+encodeURIComponent('https://oss-mbh5.colormaprun.com/card/index.html')">下载文件</a>
  21. &nbsp;
  22. <a :href="'action://down_file?filename='+encodeURIComponent('音频下载_1.mp3')+'&url='+encodeURIComponent('https://oss-mbh5.colormaprun.com/card/static/audio/1.mp3')">下载音频</a>
  23. &nbsp;
  24. <br>
  25. <a :href="'action://down_file?filename='+encodeURIComponent('小视频下载_Walk_to_image_room.mp4')+'&url='+encodeURIComponent('https://oss-mbh5.colormaprun.com/card/Walk_to_image_room.mp4')">下载小视频</a>
  26. &nbsp;
  27. <a :href="'action://down_file?filename='+encodeURIComponent('大视频下载_wifidigger.mp4')+'&url='+encodeURIComponent('https://oss-mbh5.colormaprun.com/card/wifidigger.mp4')">下载大视频</a>
  28. &nbsp; -->
  29. <!-- <a href="tel:13335119550">拨打电话</a> &nbsp; -->
  30. <!-- <view class="info uni-column uni-ais">
  31. <text class="info-text">姓名:{{nickName}}</text>
  32. <text class="info-text">院系:{{coiName}}</text>
  33. <text class="info-memo">(注:姓名只能修改一次,院系部分无法修改)</text>
  34. </view>
  35. <button class="btnCert" @click="btnCert">{{btnCertText}}</button> -->
  36. <canvas v-if="canvasReady" canvas-id="ecert" id="ecert" class="ecert" :style="getEcertCanvasStyle()"></canvas>
  37. <uni-popup ref="modifyDialog" type="dialog">
  38. <uni-popup-dialog type="warn" mode="base" title="修改姓名" @confirm="modifyDialogConfirm">
  39. <view class="dialogView uni-column uni-jcse">
  40. <input class="uni-input" maxlength="12" placeholder="请输入姓名" placeholder-style="font-size: 14px;" v-model="newName" />
  41. <view class="dialogView-memo">( 注:姓名<span style="color: red;">只能修改一次</span> )</view>
  42. </view>
  43. </uni-popup-dialog>
  44. </uni-popup>
  45. <uni-popup ref="confirmDialog" type="dialog">
  46. <uni-popup-dialog type="warn" cancelText="取消" confirmText="确定" title="提示" :content="confirmContent"
  47. @confirm="confirmDialogConfirm"></uni-popup-dialog>
  48. </uni-popup>
  49. <!-- <movable-area :scale-area="true" class="movable-area">
  50. <movable-view class="movable-view" direction="all" :inertia="true" :scale-min="0.5" :scale-max="10" :scale="true" :out-of-bounds="true">
  51. <canvas canvas-id="ecert" id="ecert" class="ecert" :style="getEcertStyle()"></canvas>
  52. </movable-view>
  53. </movable-area> -->
  54. </view>
  55. </template>
  56. <script>
  57. import tools from '/common/tools';
  58. import {
  59. token,
  60. apiCertStyleQuery,
  61. apiUserBaseQueryInCertificate,
  62. apiCertificateCreateByUserAi,
  63. checkResCode
  64. } from '/common/api';
  65. export default {
  66. data() {
  67. return {
  68. pageName: "ecert",
  69. queryObj: {},
  70. queryString: "",
  71. token: "",
  72. certStyleType: "", // 证书样式类型 比如 "shanda1"
  73. oarId: 0, // 成就记录id
  74. dpr: 1, // 设备像素比
  75. windowWidth: uni.getSystemInfoSync().windowWidth, // 可视区域宽度
  76. // windowHeight: uni.getSystemInfoSync().windowHeight, // 可视区域高度
  77. ecertWidth: 0,
  78. ecertHeight: 0,
  79. ecertUrl: "",
  80. btnCertText: "确认生成证书",
  81. nickName: '',
  82. coiId: 0, // 报名单位id
  83. coiName: "", // 报名单位名称
  84. teamNum: 0, // 队伍
  85. compName: "", // 赛事名称
  86. certificateName: "", // 奖项名称
  87. compBt: 0, // 赛事开始时间戳
  88. compEt: 0, // 赛事结束时间戳
  89. totalSysPoint: 0, // 个人总积分
  90. totalDistance: 0, // 个人总里程,单位米
  91. isCertCreate: 0, // 是否已经生成证书(生成后不能修改昵称) 0 未生成 1 已生成
  92. ecertConfig: {},
  93. canvasReady: false,
  94. newName: "",
  95. confirmContent: ""
  96. }
  97. },
  98. onLoad(query) {
  99. // console.log(query);
  100. this.queryObj = query;
  101. this.queryString = tools.objectToQueryString(this.queryObj);
  102. // console.log(queryString);
  103. this.token = query["token"] ?? token;
  104. this.certStyleType = query["certStyleType"] ?? "";
  105. this.oarId = query["oarId"] ?? 0;
  106. this.init();
  107. },
  108. onReady() {
  109. // this.test();
  110. },
  111. methods: {
  112. init() {
  113. let that = this;
  114. uni.getSystemInfo({
  115. success: function(res) {
  116. that.dpr = res.pixelRatio;
  117. // console.log('设备像素比:', that.dpr);
  118. that.userBaseQueryInCertificate();
  119. }
  120. });
  121. },
  122. // 格式化 距离
  123. fmtDistanct(val) {
  124. return Math.round(val * 100 / 1000) / 100;
  125. },
  126. getEcertImageStyle() {
  127. if (this.ecertWidth == 0) {
  128. return "";
  129. }
  130. const ratio = 0.9;
  131. const width = this.windowWidth * ratio;
  132. const height = Math.round(this.windowWidth / this.ecertWidth * this.ecertHeight) * ratio;
  133. let style = "";
  134. style += "width: " + width + "px;";
  135. style += "height: " + height + "px;";
  136. // console.log("[getEcertImageStyle]", style);
  137. return style;
  138. },
  139. getEcertCanvasStyle() {
  140. let style = "";
  141. style += "width: " + this.ecertWidth + "px;";
  142. style += "height: " + this.ecertHeight + "px;";
  143. // console.log("[getEcertCanvasStyle]", style);
  144. return style;
  145. },
  146. getParamValue(paramName) {
  147. let value = null;
  148. if (paramName == "nickName") {
  149. value = this.nickName;
  150. } else if (paramName == "coiName") {
  151. value = "(" + this.coiName + ")";
  152. } else if (paramName == "totalDistance") {
  153. value = this.fmtDistanct(this.totalDistance);
  154. } else if (paramName == "totalSysPoint") {
  155. value = this.totalSysPoint;
  156. }
  157. return value;
  158. },
  159. drawText(param, text, context) {
  160. if (param.font != undefined) {
  161. const font = param.font;
  162. if (font.align != undefined && font.align.length > 0) {
  163. context.setTextAlign(font.align);
  164. }
  165. if (font.size != undefined && font.size > 0) {
  166. const font_family = (font.family != undefined && font.family.length > 0) ? font.family : "Arial";
  167. const font_size = Math.round(font.size / this.dpr);
  168. let style = `${font_size}px ${font_family}`;
  169. if (font.preStyle != undefined && font.preStyle.length > 0) {
  170. style = `${font.preStyle} ${style}`;
  171. }
  172. // console.log(style);
  173. context.font = style;
  174. }
  175. // if (font.size != undefined && font.size > 0) {
  176. // context.setFontSize(font.size / this.dpr);
  177. // }
  178. if (font.color != undefined && font.color.length > 0) {
  179. context.fillStyle = font.color;
  180. }
  181. }
  182. context.fillText(text, param.position.x / this.dpr, param.position.y / this.dpr);
  183. },
  184. makeCert() {
  185. let that = this;
  186. // console.log("[makeCert]", that.ecertConfig.tplUrl);
  187. const ctx = uni.createCanvasContext('ecert');
  188. ctx.drawImage(that.ecertConfig.tplUrl, 0, 0, that.ecertWidth, that.ecertHeight);
  189. for (var i = 0; i < that.ecertConfig.paramList.length; i++) {
  190. const param = that.ecertConfig.paramList[i];
  191. const text = that.getParamValue(param.paramName);
  192. that.drawText(param, text, ctx);
  193. }
  194. ctx.draw(true, () => {
  195. // 在这里调用uni.canvasToTempFilePath,确保绘制完成
  196. uni.canvasToTempFilePath({
  197. canvasId: 'ecert',
  198. success: function(res) {
  199. // 在H5平台下,tempFilePath 为 base64
  200. // console.log(res.tempFilePath);
  201. let imageData = res.tempFilePath;
  202. that.ecertUrl = imageData;
  203. }
  204. });
  205. });
  206. },
  207. // 查询电子证书样式
  208. certStyleQuery() {
  209. uni.request({
  210. url: apiCertStyleQuery,
  211. header: {
  212. "Content-Type": "application/x-www-form-urlencoded",
  213. "token": this.token,
  214. },
  215. method: "POST",
  216. data: {
  217. "certStyleType": this.certStyleType
  218. },
  219. success: (res) => {
  220. // console.log("certStyleQuery", res);
  221. if (res.data.code == 0) {
  222. const data = res.data.data;
  223. const ecertConfig = data.configJson != "" ? JSON.parse(data.configJson) : "";
  224. this.ecertConfig = ecertConfig;
  225. /* this.ecertConfig = {
  226. "title": "完赛证书",
  227. "tplUrl": "/static/ecert/shanda/youxiujiang.jpg",
  228. "width": 2000,
  229. "height": 2828,
  230. "paramList": [{
  231. "paramName": "nickName",
  232. "font": {
  233. "preStyle": "bold",
  234. "size": 56,
  235. "family": "",
  236. "color": "#000000",
  237. "align": "center"
  238. },
  239. "position": {
  240. "x": 1000,
  241. "y": 2120
  242. }
  243. },
  244. {
  245. "paramName": "coiName",
  246. "font": {
  247. "preStyle": "",
  248. "size": 46,
  249. "family": "",
  250. "color": "#646363",
  251. "align": "center"
  252. },
  253. "position": {
  254. "x": 1000,
  255. "y": 2200
  256. }
  257. },
  258. {
  259. "paramName": "totalDistance",
  260. "font": {
  261. "preStyle": "",
  262. "size": 40,
  263. "family": "",
  264. "color": "#fe0000",
  265. "align": "center"
  266. },
  267. "position": {
  268. "x": 910,
  269. "y": 1704
  270. }
  271. },
  272. {
  273. "paramName": "totalSysPoint",
  274. "font": {
  275. "preStyle": "",
  276. "size": 40,
  277. "family": "",
  278. "color": "#fe0000",
  279. "align": "left"
  280. },
  281. "position": {
  282. "x": 1320,
  283. "y": 1704
  284. }
  285. }
  286. ]
  287. }; */
  288. // console.log("ecertConfig:", this.ecertConfig);
  289. this.ecertWidth = Math.round(this.ecertConfig.width / this.dpr);
  290. this.ecertHeight = Math.round(this.ecertConfig.height / this.dpr);
  291. // console.log("[certStyleQuery] ecertWidth ecertHeight:", this.ecertWidth, this.ecertHeight);
  292. this.canvasReady = true;
  293. this.$nextTick(() => {
  294. this.makeCert();
  295. });
  296. }
  297. },
  298. fail: (err) => {
  299. console.log("certStyleQuery err", err);
  300. },
  301. });
  302. },
  303. // 查询电子证书成就对应用户基本信息
  304. userBaseQueryInCertificate() {
  305. uni.request({
  306. url: apiUserBaseQueryInCertificate,
  307. header: {
  308. "Content-Type": "application/x-www-form-urlencoded",
  309. "token": this.token,
  310. },
  311. method: "POST",
  312. data: {
  313. "oarId": this.oarId
  314. },
  315. success: (res) => {
  316. // console.log("userBaseQueryInCertificate", res);
  317. if (res.data.code == 0) {
  318. const data = res.data.data;
  319. this.nickName = data.nickName;
  320. this.coiId = data.coiId;
  321. this.coiName = data.coiName;
  322. this.teamNum = data.teamNum;
  323. this.compName = data.compName;
  324. this.certificateName = data.certificateName;
  325. this.compBt = data.compBt;
  326. this.compEt = data.compEt;
  327. this.totalSysPoint = data.totalSysPoint;
  328. this.totalDistance = data.totalDistance;
  329. this.isCertCreate = data.isCertCreate;
  330. this.certStyleQuery();
  331. }
  332. },
  333. fail: (err) => {
  334. console.log("userBaseQueryInCertificate err", err);
  335. },
  336. });
  337. },
  338. // 根据成就信息确认生成电子证书
  339. certificateCreateByUserAi() {
  340. uni.request({
  341. url: apiCertificateCreateByUserAi,
  342. header: {
  343. "Content-Type": "application/x-www-form-urlencoded",
  344. "token": this.token,
  345. },
  346. method: "POST",
  347. data: {
  348. "oarId": this.oarId,
  349. "coiName": this.coiName,
  350. "coiId": this.coiId,
  351. "teamNum": this.teamNum,
  352. "nickName": this.nickName,
  353. },
  354. success: (res) => {
  355. // console.log("certificateCreateByUserAi", res);
  356. if (checkResCode(res)) {
  357. uni.showToast({
  358. icon: "none",
  359. title: "姓名修改成功",
  360. duration: 3000
  361. });
  362. }
  363. this.userBaseQueryInCertificate();
  364. },
  365. fail: (err) => {
  366. console.log("certificateCreateByUserAi err", err);
  367. uni.showToast({
  368. icon: "none",
  369. title: "姓名修改失败:" + err.message
  370. });
  371. this.userBaseQueryInCertificate();
  372. },
  373. });
  374. },
  375. modify() {
  376. if (this.isCertCreate) {
  377. uni.showToast({
  378. icon: "none",
  379. title: "不能再次修改姓名"
  380. });
  381. return;
  382. }
  383. this.newName = this.nickName;
  384. this.$refs.modifyDialog.open();
  385. },
  386. modifyDialogConfirm() {
  387. this.newName = this.newName.trim();
  388. if (this.newName.length == 0) {
  389. uni.showToast({
  390. icon: "none",
  391. title: "请输入姓名"
  392. });
  393. return;
  394. }
  395. if (this.newName != this.nickName) {
  396. this.confirmContent = `姓名:${this.newName}\r\n\r\n只能修改一次,确定要修改吗?`;
  397. this.$refs.confirmDialog.open();
  398. } else {
  399. uni.showToast({
  400. icon: "none",
  401. title: "姓名未修改,操作取消"
  402. });
  403. return;
  404. }
  405. },
  406. confirmDialogConfirm() {
  407. this.nickName = this.newName.trim();
  408. this.certificateCreateByUserAi();
  409. },
  410. previewCert() {
  411. uni.previewImage({
  412. showmenu: true,
  413. urls: [this.ecertUrl] // 需要预览的图片 HTTP 链接列表
  414. });
  415. },
  416. downloadCert() {
  417. let data = {
  418. name: `${this.compName}_${this.certificateName}_ecert.png`,
  419. content: this.ecertUrl,
  420. type: "image"
  421. };
  422. data = JSON.stringify(data);
  423. console.log(data);
  424. save_base64.postMessage(data);
  425. /* let filename = `${this.compName}_${this.certificateName}_ecert`;
  426. // console.log("filename:", filename);
  427. filename = encodeURIComponent(filename);
  428. let fileurl = this.ecertUrl;
  429. // console.log("fileurl:", fileurl);
  430. fileurl = encodeURIComponent(fileurl);
  431. const url = `action://down_pic_base64?filename=${filename}&url=${fileurl}`;
  432. tools.appAction(url); */
  433. /* uni.downloadFile({
  434. // url: this.ecertConfig.tplUrl,
  435. // url: this.$refs.ecertImg.src,
  436. url: this.ecertUrl,
  437. success: function(downloadResult) {
  438. if (downloadResult.statusCode === 200) {
  439. // 下载成功后,尝试保存图片到本地相册
  440. uni.saveImageToPhotosAlbum({
  441. filePath: downloadResult.tempFilePath,
  442. success: function() {
  443. console.log('图片保存成功');
  444. uni.showToast({
  445. icon: "none",
  446. title: "图片保存成功",
  447. duration: 2000
  448. });
  449. },
  450. fail: function(err) {
  451. console.log('保存图片失败', err);
  452. uni.showToast({
  453. icon: "none",
  454. title: "保存图片失败:" + err.errMsg,
  455. duration: 5000
  456. });
  457. }
  458. });
  459. }
  460. },
  461. fail: function(err) {
  462. console.log('下载图片失败', err);
  463. uni.showToast({
  464. icon: "none",
  465. title: "下载图片失败:" + err.errMsg,
  466. duration: 5000
  467. });
  468. }
  469. }); */
  470. },
  471. btnCert() {
  472. this.makeCert();
  473. },
  474. btnBack() {
  475. window.history.back();
  476. /* const url = `action://to_home/`;
  477. tools.appAction(url); */
  478. },
  479. test() {
  480. // var ctx = uni.createCanvasContext('firstCanvas')
  481. var ctx = uni.createCanvasContext('ecert')
  482. // ctx.scale(this.dpr, this.dpr);
  483. ctx.setStrokeStyle("#00ff00")
  484. ctx.setLineWidth(5)
  485. ctx.rect(0, 0, 200, 200)
  486. ctx.stroke()
  487. ctx.setStrokeStyle("red");
  488. // ctx.setLineWidth(5);
  489. ctx.moveTo(150, 20);
  490. ctx.lineTo(150, 170);
  491. ctx.stroke();
  492. ctx.setFontSize(15);
  493. ctx.setTextAlign('left');
  494. ctx.fillText('textAlign=left', 150, 60);
  495. ctx.setTextAlign('center');
  496. ctx.fillText('textAlign=center', 150, 80);
  497. ctx.setTextAlign('right');
  498. ctx.fillText('textAlign=right', 150, 100);
  499. ctx.draw()
  500. },
  501. }
  502. }
  503. </script>
  504. <style>
  505. .main {
  506. width: 100vw;
  507. height: 100vh;
  508. overflow: hidden;
  509. text-align: center;
  510. }
  511. .mytopbar {
  512. padding-top: 40px;
  513. padding-bottom: 20px;
  514. margin-left: auto;
  515. margin-right: auto;
  516. }
  517. /* .title {
  518. display: block;
  519. margin-top: 35px;
  520. margin-bottom: 15px;
  521. font-size: 22px;
  522. font-weight: 550;
  523. color: #333333;
  524. } */
  525. .loading-animation {
  526. margin: 200px auto;
  527. border: 6px solid #f3f3f3;
  528. border-top: 6px solid #3498db;
  529. border-radius: 50%;
  530. width: 50px;
  531. height: 50px;
  532. animation: spin 1s linear infinite;
  533. }
  534. @keyframes spin {
  535. 0% {
  536. transform: rotate(0deg);
  537. }
  538. 100% {
  539. transform: rotate(360deg);
  540. }
  541. }
  542. .ecertImg {
  543. margin-top: 6px;
  544. margin-bottom: 20px;
  545. border: #bfbfbf solid 1px;
  546. box-shadow: 3px 3px 8px #b9b9b9;
  547. /* -webkit-touch-callout: default; */
  548. }
  549. .btnlist {
  550. width: 80%;
  551. /* height: 30px; */
  552. margin: 0px auto;
  553. /* background-color: #3498db; */
  554. }
  555. .btnlist-item {
  556. font-size: 14px;
  557. font-weight: 500;
  558. color: #383838;
  559. text-decoration: underline;
  560. }
  561. .ecert {
  562. visibility: hidden;
  563. /* width: 100vw;
  564. height: 100vh; */
  565. /* transform: scale(0.5); */
  566. }
  567. .dialogView {
  568. width: 90%;
  569. }
  570. .dialogView-memo {
  571. font-size: 12px;
  572. font-weight: 400;
  573. color: #808080;
  574. }
  575. .uni-input {
  576. width: 100%;
  577. height: 34px;
  578. margin-bottom: 10px;
  579. border: 1px solid #dcdfe6;
  580. border-radius: 4px;
  581. font-size: 14px;
  582. }
  583. ::v-deep #u-a-p > div {
  584. top: 30px !important;
  585. }
  586. /* .info {
  587. max-width: fit-content;
  588. margin-left: auto;
  589. margin-right: auto;
  590. margin-top: 30px;
  591. }
  592. .info-text {
  593. font-size: 16px;
  594. font-weight: 500;
  595. color: #333333;
  596. line-height: 30px;
  597. }
  598. .info-memo {
  599. font-size: 12px;
  600. font-weight: 400;
  601. color: #808080;
  602. line-height: 25px;
  603. }
  604. .btnCert {
  605. width: 289px;
  606. height: 53px;
  607. margin-top: 30px;
  608. margin-bottom: 20px;
  609. border-radius: 27px;
  610. background: #a43a07;
  611. color: #ffffff;
  612. line-height: 53px;
  613. } */
  614. /* .movable-area {
  615. width: 100vw;
  616. height: 80vh;
  617. background-color: #ddd;
  618. overflow: hidden;
  619. align-items: center;
  620. }
  621. .movable-view {
  622. width: 100%;
  623. height: 50%;
  624. transform: scale(0.1) !important;
  625. } */
  626. </style>