区块确权

This commit is contained in:
谢思 2023-03-09 14:17:31 +08:00
parent 3e2b3255c8
commit 243fbfcf04
11 changed files with 310 additions and 13 deletions

View File

@ -61,6 +61,11 @@ function Contributors({owner,projectsId,currentLogin}){
return str && str.substr(0,str.length - 1);
}
function getBlockchain(userLogin){
const user = list.filter(item =>{ return item.login === userLogin});
return user[0] && user[0].contribution_perc;
}
function setMenusFunc(data){
if(data){
let ele = (
@ -94,6 +99,10 @@ function Contributors({owner,projectsId,currentLogin}){
<span>{data.following_count}</span>
<span>关注数</span>
</Link>
{getBlockchain(data.login) && <a>
<span>{getBlockchain(data.login)}</span>
<span>贡献度</span>
</a>}
</AlignCenter>
<div className={"pb20"} style={{display:"flex",justifyContent:'center'}}>
{

View File

@ -187,8 +187,8 @@ function IssueCommentList(props){
<div>
<Link to={`/${i.user.login}`}><img src={getImageUrl(i.user.image_url)} alt="" className='commentUserImg mr8'/></Link>
<Link to={`/${i.user.login}`}>{i.user.name}</Link>
<span className='ml5 timeAgo mr3'>回复</span>
<span>{i.reply_user.name}</span>
{i.reply_user && <span className='ml5 timeAgo mr3'>回复</span>}
<span>{i.reply_user && i.reply_user.name}</span>
<span className='ml15 timeAgo font-15'>{timeAgo(i.created_at)}</span>
</div>
{login && <div>

View File

@ -85,7 +85,7 @@
justify-content: space-between;
}
.commentRenderHtml.markdown-body p{
font-size: 13px !important;
font-size: 14px !important;
}
.commentReply{
padding: 15px 0 0 20px;

View File

@ -83,6 +83,7 @@ function Details(props){
const url = `/v1/${owner}/${projectsId}/issues/${index}`;
axios.get(url).then(result=>{
if(result && result.data){
console.log('result', result);
let data = result.data;
setDetails(data);

View File

@ -8,14 +8,25 @@ import { getImageUrl } from 'educoder';
import { Link } from 'react-router-dom';
function Contribute(props){
console.log('props', props);
const [ list , setList ] = useState(undefined);
const [ page , setPage ] = useState(1);
const [ total , setTotal ] = useState(0);
const [ isSpin , setIsSpin ] = useState(true);
const {project} = props;
const owner = props.match.params.owner;
const projectsId = props.match.params.projectsId;
const LIMIT = 20;
useEffect(()=>{
if(project){
//
const {name, author} = project;
document.title = `贡献者列表-${author.name}/${name}`;
}
}, [project])
useEffect(()=>{
if(owner && projectsId){
getData();
@ -49,11 +60,11 @@ function Contribute(props){
{
list.map((item,key)=>{
return(
<AlignCenter>
<AlignCenter className='mb15'>
<img alt="" style={{borderRadius:"50%",marginRight:"10px"}} src={getImageUrl(`/${item.image_url}`)} width="50px" height="50px"/>
<div>
<Link to={`/${item.login}`} className="font-16">{item.name}</Link>
<p className="font-12 color-grey-9">提交{item.contributions}</p>
<p className="font-12 color-grey-9">提交{item.contributions}{item.contribution_perc ? ` I 贡献占比${item.contribution_perc}` : ''}</p>
</div>
</AlignCenter>
)

View File

@ -51,6 +51,9 @@ class Index extends Component {
languageFlag:false,
ignoreFlag:false,
licenseFlag:false,
// 区块链确权
tokenConTri: false
}
}
componentDidMount = () => {
@ -200,14 +203,13 @@ class Index extends Component {
isSpin: true
})
const { projectsType } = this.props.match.params;
const {
project_language_id, project_category_id, license_id, ignore_id , owners_id ,
ignoreFlag,licenseFlag,categoreFlag,languageFlag
} = this.state;
const { project_language_id, project_category_id, license_id, ignore_id, owners_id, ignoreFlag, licenseFlag, categoreFlag, languageFlag, tokenConTri} = this.state;
const decoderPass = Base64.encode(values.password);
const url = (projectsType && projectsType === "mirror") ? "/projects/migrate.json" : "/projects.json";
// 新建项目的时候,暂存数据,如果失败,返回的时候可以重新赋值
sessionStorage.newProjectValue=JSON.stringify({...values,project_language_id,project_category_id,license_id,ignore_id});
// 贡献值激励确权机制
const {blockchain_init} = values;
axios.post(url, {
...values,
auth_password:decoderPass,
@ -215,7 +217,10 @@ class Index extends Component {
project_category_id:categoreFlag ? project_category_id : undefined,
license_id:licenseFlag ? license_id : undefined,
ignore_id:ignoreFlag ? ignore_id : undefined,
user_id:owners_id
user_id:owners_id,
blockchain: tokenConTri,
blockchain_token_all: 10000,
blockchain_init_token: blockchain_init && blockchain_init/100
}).then((result) => {
if (result && result.data.id) {
projectsType && projectsType !== "mirror" && this.props.showNotification(`项目创建成功!`);
@ -343,6 +348,7 @@ class Index extends Component {
const { getFieldDecorator } = this.props.form;
// 项目类型deposit-托管项目mirror-镜像项目
const { projectsType } = this.props.match.params;
const {current_user:{open_blockchain}} = this.props;
const {
CategoryList,
@ -365,7 +371,8 @@ class Index extends Component {
ignoreFlag,
licenseFlag,
languageFlag,
categoreFlag
categoreFlag,
tokenConTri
} = this.state;
return (
<div className="main back-white" style={{padding:"0px",border:"none"}}>
@ -630,6 +637,32 @@ class Index extends Component {
)}
</Form.Item>
}
{/* 确权 仅在新建项目时 */}
{open_blockchain && projectsType === "deposit" && <div className='mt10 mb10'>
<p className='font-16'>确权</p>
<div className='mt5 mb5 color-grey3'><Checkbox checked={tokenConTri} onChange={(e)=>{this.setState({tokenConTri:e.target.checked})}}>加入贡献值激励确权机制</Checkbox></div>
<Form.Item
className="flex1"
extra="Token指本项目的虚拟货币决定用户在本仓库贡献度的比例可以通过issue悬赏或在个人主页贡献确权页进行转移。创建项目时每个项目的Token总量为10000您可以分配您可获取的Token数量未分配的Token将保留至项目Token余额"
>
{getFieldDecorator('blockchain_init', {
rules: [{
required: tokenConTri, message: '初始token值为一个不超过10000的正整数'
},
{ validator: (rule,val,callback) =>{
if(val>10000){
callback('初始token值最大值10000');
}else{
callback();
}
}}],
})(
<Input placeholder='请填写您项目创建者获取的初始Token值'/>
)}
</Form.Item>
</div>}
<div className="mt20">
<span className="ant-form-item-required"></span>
</div>

View File

@ -24,7 +24,7 @@ export default withRouter(
secondRouter = props.location.pathname.split('/')[2];
}
let userRouterArr = ['statistics', 'projects', 'notice', 'devops', 'organizes', 'info', 'following', 'followers', 'password' , "general"];
let userRouterArr = ['statistics', 'projects', 'notice', 'devops', 'organizes', 'info', 'following', 'followers', 'password' , "general", "blockchain"];
return (
<Switch>

View File

@ -278,7 +278,7 @@ ul.ant-menu.menuStyle{
.infosRightMenu{
.ant-menu-item{
padding:0px;
margin:0px 30px 0px 0px !important;
margin:0px 17px 0px 0px !important;
font-size: 17px;
height: 32px;
line-height: 0px;

View File

@ -63,6 +63,11 @@ const Personal = Loadable({
loading: Loading,
})
// 贡献确权
const Contribution = Loadable({
loader: () => import('./contribution/index'),
loading: Loading,
})
class Infos extends Component {
constructor(props) {
@ -108,6 +113,8 @@ class Infos extends Component {
this.setState({menuKey:undefined,route_type:"following"});
}else if(pathname === `/${username}/followers`){
this.setState({menuKey:undefined,route_type:"followers"});
}else if(pathname === `/${username}/blockchain`){
this.setState({menuKey:'7',route_type:undefined});
}else{
this.setState({menuKey:"6",route_type:undefined});
}
@ -394,6 +401,10 @@ class Infos extends Component {
{ user && user.user_org_count && user.user_org_count > 0 ? <span className="menuNum">({user.user_org_count})</span>:""}
</Link>
</Menu.Item>
{/* 当前用户+配置了贡献确权 */}
{current_user && user && user.login === current_user.login && current_user.open_blockchain && <Menu.Item key="7">
<Link to={`/${user && user.login}/blockchain`}><i className="iconfont icon-zuzhi"></i></Link>
</Menu.Item>}
</Menu>
}
{user && (
@ -440,6 +451,12 @@ class Infos extends Component {
return <Organize {...this.props} {...this.state} />;
}}
></Route>
<Route
path="/:username/blockchain"
render={() => {
return <Contribution {...this.props} {...this.state} />;
}}
></Route>
{/* <Route
path="/:username/info"
render={() => {

View File

@ -0,0 +1,147 @@
import React , { useEffect , useState } from 'react';
import { Button, Dropdown, Input, Menu , message, Pagination } from 'antd';
import Search from '../../Component/Search';
import Item from '../Team-item';
import Nodata from '../../Nodata';
import axios from 'axios';
import CheckProfile from '../../Component/ProfileModal/Profile';
import './index.scss';
import SearchUser from '../../Component/SearchUser';
import { FlexAJ } from '../../Component/layout';
// -
function Contribution(props){
//
const limit = 15;
const [page, setPage] = useState(1);
const [total, setTotal] = useState(undefined);
//
const [reload, setReload] = useState(undefined);
const [list, setList] = useState(undefined);
// 1 0
const [ type, setType] = useState(1);
// id/login
const [userLogin, setUserLogin] = useState({});
const [error1, setError1] = useState(undefined);
//
const [tokenCount, setTokenCount] = useState({});
const [error2, setError2] = useState(undefined);
// id
const [proId, setProId] = useState(undefined);
const { checkIfLogin , showLoginDialog , current_user:{user_id, login}, current_user } = props;
console.log('current_user', current_user);
const {user} = props;
useEffect(()=>{
// token
axios.get(`/users/blockchain/balance.json`,{params: {user_id}}).then(res=>{
if(res && res.data){
setList(res.data.projects);
setTotal(res.data.total_count);
}
})
}, [reload])
useEffect(()=>{
if(user){
//
const {username, login} = user;
document.title = `贡献确权-${username}/${login}`;
}
},[user])
//
function getUser(id, login){
const userLoginObject = Object.assign(userLogin);
userLoginObject[id] = login;
setUserLogin(userLoginObject);
}
// token
function setToken(id, token){
const tokenCountObject = Object.assign(tokenCount);
tokenCountObject[id] = token;
setTokenCount(tokenCountObject);
}
//
function confirmTransfer(id, identifier, max){
setError1(undefined);
setError2(undefined);
setProId(id);
const userLoginById = userLogin[id];
const tokenCountById = tokenCount[id];
if(!userLoginById || !tokenCountById){
!userLoginById && setError1('请确定目标转账用户');
if(!tokenCountById){
setError2('请输入token数额');
}
}else if(!/^[1-9]+[0-9]*]*$/.test(tokenCountById) || tokenCountById>max){
setError2('转账token数额必须为不超过token总数的正整数');
}else{
console.log('userLogin', userLoginById, 'tokenCount', tokenCountById);
axios.post(`/users/blockchain/transfer`, {
owner_login: login,
project_identifier: identifier,
user_id,
transfer_amount: tokenCountById,
transfer_login: userLoginById,
payer_id: user_id,
project_id: id
}).then(res=>{
console.log('res', res);
if(res && res.data && res.data.status === 2){
setReload(Math.random());
message.success('转账成功');
}
})
}
}
return(<div className='mt15 contribution'>
{/* 转账/记录切换 */}
{/* <div className='contHead'>
<a className={`itemCont ${type ? 'active' : ''}`} onClick={()=>{setType(1)}}>token转账</a>
<a className={`itemCont ${type ? '' : 'active'}`} onClick={()=>{setType(0)}}>token记录</a>
</div> */}
{/* token转账 */}
{type ? <div className='contributionTable mt25'>
{list && list.length > 0 && <div className='flexCenterCont tableHeadTri mb5'>
<div className='tableColumn1'>项目名称</div>
<div className='tableColumn2'>token总量</div>
<div className='tableColumn3'>目标转账用户</div>
<div className='tableColumn4'>转账token数额</div>
<div className='tableColumn5'>操作</div>
</div>}
{list && !list.length && <Nodata _html="暂无数据"/>}
{list && list.map(item=>{return <div key={item.id} className='flexCenterCont transferAccount'>
<div className='tableColumn1'>{item.name}</div>
<div className='tableColumn2'>{item.balance}</div>
<div className='tableColumn3'>
<SearchUser getUser={(login)=>{getUser(item.id, login)}} width={"100%"} placeholder="搜索转账目标用户"/>
{proId === item.id && error1 && <div className='errorTipTri'>{error1}</div>}
</div>
<div className='tableColumn4'>
<Input onChange={(e)=>{setToken(item.id, e.target.value)}} placeholder='请输入token数额'/>
{proId === item.id && error2 && <div className='errorTipTri'>{error2}</div>}
</div>
<div className='tableColumn5'><Button className='confirmTransfer' onClick={()=>{confirmTransfer(item.id, item.identifier, item.balance)}}>确认</Button></div>
</div>})}
</div> : <div className='contributionTable mt20 record'>
<FlexAJ className='contributionRecordItem font-15'>
<span><span className='themeSpan'>蒋宇航</span><span className='themeSpan'>GilLink/确实开源</span>给你转账了1000个tiken</span>
<span className='tableHeadTri'>6小时前</span>
</FlexAJ>
<FlexAJ className='contributionRecordItem font-15'>
<span><span className='themeSpan'>蒋宇航</span><span className='themeSpan'>GilLink/确实开源</span>给你转账了1000个tiken</span>
<span className='tableHeadTri'>6小时前</span>
</FlexAJ>
</div>}
{/* {total > limit && <div className='mt20 paginationTri'>
<Pagination simple current={page} pageSize={limit} total={total} onChange={(page)=>setPage(page)}/>
</div>} */}
</div>
)
}
export default Contribution;

View File

@ -0,0 +1,79 @@
.contHead{
&>.itemCont{
display: inline-block;
width:83px;
height:32px;
background-color:rgba(250, 251, 252, 0);
border:1px solid #d0d0d0;
border-radius:0px 4px 4px 0px;
text-align: center;
color: #333;
&:first-of-type{
border-radius:4px 0px 0px 4px;
border-right: none;
}
&:last-of-type{
border-left: none;
}
&.active{
border-color: $primary-color;
background-color: $primary-color;
color: white;
}
}
}
.contribution{
.flexCenterCont{
display: flex;
justify-content: space-between;
}
.contributionTable{
background-color:rgba(250, 252, 255, 0.89);
border-radius:4px 4px 0px 0px;
padding: 20px 20px 40px 20px;
}
.tableHeadTri{
color:#898d9d;
}
.transferAccount{
color:#40424a;
padding: 15px 0;
border-bottom: 1px solid #eee;
}
.tableColumn3, .tableColumn4{
width: 200px;
}
.tableColumn1{
width: 130px;
}
.tableColumn5{
width: 120px;
text-align: center;
}
.tableColumn2{
flex: 0.5;
}
.confirmTransfer{
border-color: $primary-color;
color: $primary-color;
}
.errorTipTri{
width: 400px;
color: red;
}
}
.record.contributionTable{
padding: 0px 20px;
.contributionRecordItem{
padding: 15px 0;
}
.contributionRecordItem+.contributionRecordItem{
border-top: 1px solid #eee;
}
.themeSpan{
color: $primary-color;
}
}
.paginationTri{
text-align: center;
}