积分 证书

积分 证书
This commit is contained in:
amypeng 2026-06-04 13:53:50 +08:00
parent fbb19687ff
commit 65973d3b75
7 changed files with 187 additions and 21 deletions

View File

@ -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");

Binary file not shown.

View File

@ -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 }) {
>
<div className="info">
<p className="award">您将消耗<label> 200 积分 </label>兑换专属电子荣誉证书当前可用积分 {score.toLocaleString()} 兑换后剩余 {scoreLeft.toLocaleString()} 积分扣除后将自动生成证书无法退回请确认操作请确认是否兑换专属电子荣誉证书</p>
<Form form={form}>
<Form.Item label="真实姓名">
{getFieldDecorator('realName', {
rules: [
{
required: true,
message: '请输入真实姓名'
}
],
})(
<Input placeholder="请输入真实姓名" maxLength={20} />,
)}
</Form.Item>
</Form>
</div>
</Modal>
)
}
export default UserAward;
export default Form.create()(UserAward);

View File

@ -45,7 +45,7 @@ function ApplyActivity({ visible, onCancel, onSure, form, member, zoneName, }) {
<img alt="头像" src={IconImg} style={{width: '48px', height: '48px'}}></img>
<div className="txt">
<h4>
专家一对一答疑
线下参观/沙龙报名
</h4>
<p>{zoneName}<span> · </span>线下参观/沙龙报名</p>
</div>

View File

@ -50,6 +50,7 @@ function ZoneUser(props){
const [ qaVisible , setQaVisible ] = useState(false);
const [ successVisible , setSuccessVisible ] = useState(false);
const [ timeNum, setTimeNum] = useState(3);
const [ memberAccountInfo, setMemberAccountInfo ] = useState([]);
const { id, showNotification, checkIfLogin, showLoginDialog, temp, sectionMemberTitle } = props;
@ -71,6 +72,36 @@ function ZoneUser(props){
}
},[id])
useEffect(() => {
let temp = awardsInfoData.map(item => {
let tempData = Object.assign({}, item);
if(!memberInfo) return tempData;
if(item.key === 'certificate' && memberInfo.pointsEvent?.id) {
tempData.exist = true;
tempData.txt = '下载证书';
}
if(item.key === 'acitvity' && Array.isArray(memberInfo.applyList)) {
let index = memberInfo.applyList.findIndex(item => item.applyType === 'offlineActivityFreeze');
if(index !== -1) {
tempData.disabled = true;
tempData.txt = '等待审核';
}
}
if(item.key === 'qa' && Array.isArray(memberInfo.applyList)) {
let index = memberInfo.applyList.findIndex(item => item.applyType === 'expertQaFreeze');
if(index !== -1) {
tempData.disabled = true;
tempData.txt = '等待审核';
}
}
return tempData;
})
setMemberAccountInfo(temp);
}, [memberInfo])
useEffect(() => {
if (successVisible) {
const pid = setInterval(() => {
@ -225,8 +256,16 @@ function ZoneUser(props){
}
function pointsCustom(item) {
if(item.disabled) return;
console.log('iii', item)
if(item.key == 'certificate') {
setAwardVisible(true);
if(!item.exist) {
setAwardVisible(true);
} else {
let url = memberInfo.pointsEvent?.sourceData;
console.log('uuu', url)
window.open(url, '_blank');
}
} else if(item.key == 'doc') {
setDocVisible(true);
} else if(item.key == 'acitvity') {
@ -305,16 +344,16 @@ function ZoneUser(props){
</div>
<div className="awards">
{
awardsInfoData.map((item, index) => (
memberAccountInfo.map((item, index) => (
<div className="award">
<h3><img src={item.img} alt="" style={{marginRight: 16}}/> {item.title}</h3>
<p>{item.desc}</p>
<div className="action">
<span><label>{item.score}积分</label>/{item.unit}</span>
{memberInfo?.zonePointsAccount?.availablePoints >= item.score ? (
<div className="btn" onClick={() => pointsCustom(item)}>
{item.txt}
</div>
{memberInfo?.zonePointsAccount?.availablePoints >= item.score ? (
<div className={`btn ${item.disabled ? 'disabled' : ''}`} onClick={() => pointsCustom(item)}>
{item.txt}
</div>
) : (
<div className="btn disabled">
积分不足
@ -342,10 +381,10 @@ function ZoneUser(props){
onCancel={()=>setDocVisible(false)} onSure={() => { setDocVisible(false); setSuccessVisible(true);getInfo();}}
/>
<ApplyAcitvity visible={acitvityVisible} member={memberInfo} zoneName={zoneInfo?.name}
onCancel={()=>setAcitvityVisible(false)} onSure={() => { setQaVisible(false); getInfo();}}
onCancel={()=>setAcitvityVisible(false)} onSure={() => { showNotification(msg); setAcitvityVisible(false); getInfo();}}
/>
<ApplyQA visible={qaVisible} member={memberInfo} zoneName={zoneInfo?.name}
onCancel={()=>setQaVisible(false)} onSure={() => { setQaVisible(false); getInfo();}}
onCancel={()=>setQaVisible(false)} onSure={() => { showNotification(msg); setQaVisible(false); getInfo();}}
/>
<Modal

View File

@ -642,10 +642,11 @@ export function deleteMemberProject(ids){
})
}
export function applyAward(id) {
export function applyAward(data) {
return fetch({
url: `/zone/points/account/certificate/redeem/${id}`,
method: 'post'
url: `/zone/points/account/certificate/redeem`,
method: 'post',
data
});
}
export function doApplyQA(data) {

View File

@ -764,6 +764,7 @@
line-height:32px;
color:#4a5377;
font-size:15px;
margin-bottom: 30px;
label{
font-family: alibaba;
color: #f5383b;