diff --git a/public/css/gitlink.min.css b/public/css/gitlink.min.css index 0f9924a83..2fbfc352f 100644 --- a/public/css/gitlink.min.css +++ b/public/css/gitlink.min.css @@ -8,6 +8,10 @@ font-family: "pangmen"; src: url("../fonts/PangMenZhengDaoBiaoTiTiMianFeiBan-2.ttf"); } +@font-face { + font-family: "sanjikai"; + src: url("../fonts/SanJiXingKaiJianTi-Cu-2.ttf"); +} @font-face { font-family: "alibaba"; src: url("../fonts/ALIBABA-PUHUITI-MEDIUM.TTF"); diff --git a/public/fonts/SanJiXingKaiJianTi-Cu-2.ttf b/public/fonts/SanJiXingKaiJianTi-Cu-2.ttf new file mode 100644 index 000000000..a7c6b6882 Binary files /dev/null and b/public/fonts/SanJiXingKaiJianTi-Cu-2.ttf differ diff --git a/src/forge/Information/Component/UserAward.jsx b/src/forge/Information/Component/UserAward.jsx index daa1b1792..3cda1bd3f 100644 --- a/src/forge/Information/Component/UserAward.jsx +++ b/src/forge/Information/Component/UserAward.jsx @@ -1,17 +1,20 @@ import React, { useEffect, useState } from 'react'; -import { Modal, Button } from 'antd'; - +import { Modal, Button, Form, Input } from 'antd'; +import cookie from 'react-cookies'; import IconImg from '../img/user/icon1.png'; +import TemplateImg from '../img/user/template.png'; import { applyAward } from '../api'; +import { httpUrl } from '../fetch'; import '../indexUser.scss'; -function UserAward({ visible , onCancel , onSure , member , id }) { +function UserAward({ visible , onCancel , onSure , member , id, form }) { const [ loading , setLoading ] = useState(false); const [ score , setScore ] = useState(0); const [ scoreLeft , setScoreLeft ] = useState(0); + const { getFieldDecorator, validateFields, getFieldsValue } = form; useEffect(()=>{ if(visible && member && member.zonePointsAccount && member.zonePointsAccount.availablePoints) { @@ -21,12 +24,116 @@ function UserAward({ visible , onCancel , onSure , member , id }) { },[visible, member]) function onOk() { - setLoading(true); - applyAward(id).then(res => { - if (res && res.data && res.data.code === 200) { - onSure(res.data.msg); + validateFields(async (err, values) => { + if (!err) { + try{ + setLoading(true); + let imgInfo = await createImg(values.realName); + + let certificateUrl = ''; + if (imgInfo && imgInfo.code == 200 && imgInfo.url) { + certificateUrl = imgInfo.url || ''; + } + + const res = await applyAward({zoneId: id, sourceData: certificateUrl}); + setLoading(false); + + if (res && res.data && res.data.code === 200) { + onSure(res.data.msg); + } + }finally{ + setLoading(false); + } } - }).finally(() => { setLoading(false) }) + }); + } + + async function waitFontLoaded() { + // 等待自定义字体加载完成 + if (document.fonts) { + await document.fonts.ready; + if(!document.fonts.check('64px sanjikai')) { + try { + const font = new FontFace('sanjikai', 'url(/fonts/SanJiXingKaiJianTi-Cu-2.ttf)'); + await font.load(); + document.fonts.add(font); + } catch (e) { + console.warn('sanjikai 字体加载失败:', e); + } + } + + if(!document.fonts.check('18px alibabaReg')) { + try { + const font = new FontFace('alibabaReg', 'url(/fonts/ALIBABA-PUHUITI-REGULAR.TTF)'); + await font.load(); + document.fonts.add(font); + } catch (e) { + console.warn('alibabaReg 字体加载失败:', e); + } + } + } + } + + + async function createImg(realName) { + const canvas = document.createElement('canvas'); + const ctx = canvas.getContext('2d'); + + const img = new Image(); + img.crossOrigin = 'anonymous'; + img.src = TemplateImg; + + await new Promise((resolve, reject) => { + img.onload = resolve; + img.onerror = reject; + }); + + // 等待字体加载完成后再绘制 + await waitFontLoaded(); + + canvas.width = img.width; + canvas.height = img.height; + ctx.drawImage(img, 0, 0); + + const memberName = realName || member?.name || member?.nickname || '学员'; + const awardDate = new Date().toLocaleDateString('zh-CN', { year: 'numeric', month: 'long', day: 'numeric' }); + + // 设置文字样式 + ctx.font = '64px sanjikai'; + ctx.fillStyle = '#04060c'; + ctx.textAlign = 'center'; + ctx.textBaseline = 'bottom'; + ctx.fillText(memberName, canvas.width / 2, 705); + + ctx.font = '18px alibabaReg'; + ctx.fillStyle = '#04060c'; + ctx.textAlign = 'left'; + ctx.textBaseline = 'middle'; + ctx.fillText(awardDate, 240, 1008); + + // TODO + const dataUrl = canvas.toDataURL('image/png'); + const link = document.createElement('a'); + link.download = `荣誉证书_${memberName}_${new Date().getTime()}.png`; + link.href = dataUrl; + link.click(); + + const blob = await new Promise(resolve => canvas.toBlob(resolve, 'image/png')); + const formData = new FormData(); + formData.append('file', blob, `member_${member.id}.png`); + formData.append('type', 'resource'); + + const uploadRes = await fetch(`${httpUrl}/file/common/upload`, { + method: 'POST', + body: formData, + headers: { + // Authorization: '6d72497d7f197eb9dc8a15db038881cd31afea7a', + Authorization: cookie.load('autologin_trustie'), + } + }); + + const uploadData = await uploadRes.json(); + return uploadData; } return( @@ -56,8 +163,22 @@ function UserAward({ visible , onCancel , onSure , member , id }) { >
您将消耗兑换「专属电子荣誉证书」,当前可用积分 {score.toLocaleString()} 分,兑换后剩余 {scoreLeft.toLocaleString()} 分。积分扣除后将自动生成证书,无法退回,请确认操作。请确认是否兑换专属电子荣誉证书?
+{zoneName} · 线下参观/沙龙报名
{item.desc}