Merge branch 'gitlink_ssr_head' of https://gitlink.org.cn/Gitlink/forgeplus-react into gitlink_ssr_head

This commit is contained in:
qiukai 2025-03-14 18:06:40 +08:00
commit 605d2267cc
115 changed files with 4374 additions and 656 deletions

View File

@ -44,6 +44,7 @@
"connect-timeout": "^1.9.0",
"connected-react-router": "4.4.1",
"core-js": "^3.34.0",
"crypto-js": "^4.2.0",
"dompurify": "^2.3.3",
"dotenv": "4.0.0",
"dotenv-expand": "4.2.0",

View File

@ -1,4 +1,5 @@
@charset "utf-8";
/* 头部 */
.header {
width: 100%;

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -1,3 +1,5 @@
import CryptoJS from 'crypto-js';
export function isDev() {
return window.location.port === "3007";
}
@ -6,3 +8,12 @@ export function isDev() {
export const isMobile = (/android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(navigator.userAgent.toLowerCase()));
// const isWeiXin = (/MicroMessenger/i.test(navigator.userAgent.toLowerCase()));
export function Encrypt(str){
let result = CryptoJS.AES.encrypt(str,CryptoJS.enc.Utf8.parse('59c96c3572ab8cc1'),{
iv:CryptoJS.enc.Utf8.parse('59c96c3572ab8cc1'),
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
})
return result.toString();
}

View File

@ -267,13 +267,13 @@ class Activity extends Component{
<BranchLine url={ai_shang_v1_url}/>
<p>基于信息熵围绕疑修任务的社群群智激发演化度量</p>
<span className="echartTitle">开源项目代码变更演化拓扑熵</span>
{/* <span className="echartTitle"></span>
<IssueLine url={ai_shang_v3_url}/>
<p>基于信息熵围绕代码提交的软件代码变更演化度量</p>
<span className="echartTitle">开源项目社区演化拓扑熵</span>
<SmoothLineTwo url={ai_shang_v4_url}/>
<p>基于信息熵围绕项目社群的社区分裂缩减合并和扩大演化行为度量</p>
<p>基于信息熵围绕项目社群的社区分裂缩减合并和扩大演化行为度量</p> */}
</div>

View File

@ -2,6 +2,7 @@ import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import './activity.css';
import { getImageUrl } from 'educoder';
import { truncateCommitId } from "../common/util";
class ActivityItem extends Component {
@ -25,9 +26,16 @@ class ActivityItem extends Component {
<span className="activity_type">{item.trend_type}</span>
</p >
:
// 如果是commit--CommitLog
item.trend_type === "CommitLog" ?
<p className="itemLine">
<Link to={`/${owner}/${projectsId}/commits/${item.commit_log && truncateCommitId(item.commit_log.commit_id)}`} className="font-16">{item.name}</Link>
<span className="activity_type">{item.trend_type}</span>
</p >
:
// 如果是合并请求
<p className="itemLine">
<Link to={`/${owner}/${projectsId}/pulls/${item.trend_id}`} className="font-16">{item.name}</Link>
<Link to={`/${item.project && item.project.owner&& item.project.owner.login}/${item.project && item.project.identifier}/pulls/${item.trend_id}`} className="font-16">{item.name}</Link>
<span className="activity_type">{item.trend_type}</span>
</p >
}

View File

@ -3,7 +3,7 @@ import { Input } from 'antd';
import './index.scss';
function FileName({
onChangeFile,addonBefore , name , empty , onEmpty
onChangeFile,addonBefore , name , empty , onEmpty , readOnly
}){
const [ defaultUrl , setDefaultUrl ] = useState(undefined);
const [ newUrl , setNewUrl ] = useState([]);
@ -115,6 +115,7 @@ onChangeFile,addonBefore , name , empty , onEmpty
onKeyUp={keyUpFun}
onChange={changeValue}
style={{width:"220px",borderColor:empty===true ? "red":"grey"}}
disabled={readOnly}
/>
{empty && <p className="color-red ml20">请输入文件名称</p>}
</div>

View File

@ -0,0 +1,54 @@
import React,{useEffect,useState,useRef} from 'react';
import { Input } from 'antd';
import Drop from '../../Issues/Component/drop';
import "./index.scss";
import ListItem from './list';
/***
* list:下拉内容列表
* name:所用内容名字新建页面.gitignore许可证类别语言项目基本设置类别语言
* value:选中id
* getValue:点击确定
* idname:下拉内容定位的父元素
*/
function DorpChooseMenus({list,name,value,getValue,idname}){
const dropRef = useRef(null);
const [ choose , setChoose ] = useState(undefined);
const [ defaultValue , setDefaultValue ] = useState(undefined);
useEffect(()=>{
if(value){
setChoose({name:value});
setDefaultValue(value)
}
},value)
const panel=()=>{
return(
<div>
<ListItem list={list} size="small" onChooseFunc={chooseFunc} defaultValue={value}/>
<a onClick={sureFunc} className={choose ? 'sureDrop active':"sureDrop"}>确定</a>
</div>
)
}
function chooseFunc(value){
setChoose(value);
}
function sureFunc(){
getValue(choose);
dropRef.current && dropRef.current.clearVisible(false);
}
return(
<Drop
overlay={panel()}
overlayClassName={"alllists"}
placement={"bottomLeft"}
ref={dropRef}
idname={idname}
>
<Input placeholder={name} value={defaultValue}/>
</Drop>
)
}
export default DorpChooseMenus;

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1,57 @@
import { Modal } from "antd";
import React, { useEffect, useState } from "react";
import LicenseImg from './img/license.png';
import "./index.scss";
import axios from 'axios';
import ListItem from './list';
function Index({visible,onCancel,createDefaultLicense}){
const [ list , setList ] = useState(undefined);
const [ choose , setChoose ] = useState(undefined);
useEffect(()=>{
visible && getLicenses();
},[visible])
const getLicenses = () => {
const url = `/licenses.json`;
axios.get(url).then((result) => {
if (result) {
setList(result.data.licenses);
}
}).catch((error) => { })
}
function chooseFunc(value){
setChoose(value);
}
return(
<Modal
title="请选择开源许可证"
visible={visible}
onCancel={onCancel}
footer={null}
width={870}
>
<div className="licenseBox">
<div className="licenseDesc">
<p className="font-18" style={{color:"#1B3485"}}>了解开源许可证</p>
<div className="licImg">
<img src={LicenseImg}/>
</div>
<p className="licWord mt25">开源许可证是您软件源代码使用修改及分发的法律约定明确了他人使用项目的权利与限制常见的有MITApache 2.0GPL等</p>
<p className="licWord mt20">请从推荐列表中选择适合的协议或根据高级项目需求自定义创建您的许可证</p>
<a className="licCreate" onClick={()=>createDefaultLicense(-1)}>创建自定义许可证</a>
</div>
<div className="licenseData">
<ListItem list={list} onChooseFunc={chooseFunc}/>
<a className={choose ? "licUse active":"licUse"} onClick={()=>createDefaultLicense(choose && choose.id)}>使用该许可证</a>
</div>
</div>
</Modal>
)
}
export default Index

View File

@ -0,0 +1,126 @@
.licenseBox{
display: flex;
.licenseDesc{
background: #FBFCFF;
border-radius: 11px 11px 11px 11px;
border: 1px solid rgba(70,106,255,0.33);
width: 38%;
padding:20px;
margin-right: 20px;
.licImg{
background: #F7F9FF;
border-radius: 0px 0px 0px 0px;
border: 1px dashed rgba(70,106,255,0.3);
padding:24px 30px;
text-align: center;
margin-top: 20px;
img{
height: 122px;
}
}
.licWord{
color: #4C4D5A;
line-height: 26px;
}
.licCreate{
display: block;
height: 36px;
line-height: 36px;
background: #F7F9FF;
border-radius: 7px 7px 7px 7px;
border: 1px solid #466AFF;
color: #466AFF;
text-align: center;
margin-top: 25px;
}
}
}
.licenseData{
flex: 1;
}
.licList{
margin-top: 20px;
height: 394px;
overflow-y: auto;
clear: both;
width: 100%;
li{
height: 36px;
line-height: 36px;
background: rgba(126,135,172,0.08);
border-radius: 4px 4px 4px 4px;
width: 48.5%;
margin-right: 3%;
text-align: center;
margin-bottom: 15px!important;
float: left;
cursor: pointer;
&:nth-child(2n){
margin-right: 0px;
}
&.active{
color: #fff;
background-color: #466AFF;
}
}
&.small{
height: 200px;
li{
width: 22.5%;
&:nth-child(2n){
margin-right: 3%;
}
&:nth-child(4n){
margin-right: 0px;
}
}
.none_panels{
padding:0px;
min-height: 200px;
}
}
}
.licUse{
display: block;
height: 36px;
line-height: 36px;
border-radius: 4px 4px 4px 4px;
font-size: 15px;
color: #fff!important;
text-align: center;
margin-top: 38px;
background-color: #c0c0c0;
cursor: default;
&.active{
background: #466AFF;
cursor: pointer;
}
}
.dropspan{
display: block;
}
.alllists{
background-color: #fff;
box-shadow: 2px 4px 15px rgba(0, 0, 0, 0.1);
padding:20px!important;
width: 80%;
}
.sureDrop{
width: 200px;
height: 36px;
background: #c0c0c0;
border-radius: 4px;
line-height: 36px;
color: #fff!important;
display: block;
text-align: center;
font-size: 15px;
margin:20px auto 0px;
cursor: default;
&.active{
background: #466AFF;
cursor: pointer;
}
}

View File

@ -0,0 +1,52 @@
import React, { useEffect, useState } from "react";
import Search from './img/search.png';
import Nodata from "../../Nodata";
import { Input } from 'antd';
import "./index.scss";
/**
*
* @param {list} param1 列表
* @param {onChooseFunc} param2 选中后调用父级方法
* @param {size} param3 一行显示4个默认显示2个
* @param {defaultValue} param4 默认选中的---name
* @returns
*/
function List({list,onChooseFunc,size,defaultValue}){
const [ value , setValue ] = useState(undefined);
const [ active , setActive ] = useState(undefined);
const [ copylist , setCopyList ] = useState(undefined);
useEffect(()=>{
if(list && list.length>0){
setCopyList(list);
if(defaultValue){
let getMIT = list.filter(i=>i.name === defaultValue);
getMIT && getMIT.length > 0 && setActive(getMIT[0].id);
}
}
},[list && list.length,defaultValue])
function searchFunc(search){
let filters = list && list.filter(i=>i.name.toLowerCase().indexOf(search.toLowerCase()) > -1);
setCopyList(filters);
}
return(
<React.Fragment>
<Input placeholder="输入关键词进行搜索" suffix={<img src={Search} width={"19px"} />} value={value} onChange={(e)=>{setValue(e.target.value);searchFunc(e.target.value)}}/>
<ul className={`licList ${size}`}>
{ copylist && copylist.length>0 && copylist.map((i,k)=>{
return <li className={active === i.id ?"active":""} onClick={()=>{setActive(i.id);onChooseFunc(i)}}>{i.name}</li>
})
}
{
copylist && copylist.length === 0 && <Nodata _html="暂无数据"/>
}
</ul>
</React.Fragment>
)
}
export default List;

View File

@ -140,14 +140,17 @@ function Compass({owner,projectsId}) {
projection: "perspective",
zoomSensitivity: 0,
rotateSensitivity: [1, 0],
distance: 280,
distance: 380,
alpha: 20,
beta: 25
},
top: -40,
right: -10,
boxWidth: 150,
boxDepth: 150,
height: 300,
right: -30,
top: -50,
bottom: 0,
boxWidth: 180,
boxDepth: 180,
boxHeight: 150,
environment: "none",
light: {
main: {

View File

@ -15,6 +15,8 @@ import PartnerJcc from './Component/partnerJcc';
import '../../indexZone1.scss'
import './index.scss'
import { Popover, Tabs } from 'antd';
import {getUniqueIdentifier,getUserName,getBrowserPlatform,getBrowserBrand} from '../../utils'
import {setZoneVisits} from '../../api'
let tabTimer = undefined;
@ -33,6 +35,17 @@ function HeaderPageJCC(props){
{imageUrl: visionZy, title: '资源易共享'},
{imageUrl: visionJz, title: '价值可交换'}
]
useEffect(()=>{
let uuid = getUniqueIdentifier()
let url = props.location.pathname
let platform = getBrowserPlatform()
let browser = getBrowserBrand()
let username = getUserName(props.current_user)
// if(!username) uuid = getUniqueIdentifier()
let remark = ` 操作系统:${platform};浏览器:${browser};`
setZoneVisits({url,username,uuid,remark})
},[])
useEffect(()=>{
if(id){
@ -141,7 +154,7 @@ function HeaderPageJCC(props){
</div>
{
data.introductionImage &&
<img src={data.introductionImage} alt="" width="600px" height="361px"/>
<img src={data.introductionImage.split(",")[0]} alt="" width="600px" height="361px"/>
}
</div>
}

View File

@ -7,7 +7,8 @@ import shijian from '../img/shijian.png';
import { getHomePageList , gethomePageDocList , getAllList, getPartnerList } from '../api';
import axios from 'axios';
import Partner from '../Component/partner';
import {getUniqueIdentifier,getUserName,getBrowserPlatform,getBrowserBrand} from '../utils'
import {setZoneVisits} from '../api'
function HeaderPage(props){
const [ projectList , setProjectList ] = useState(undefined);
const [ personList , setPersonList ] = useState(undefined);
@ -16,6 +17,17 @@ function HeaderPage(props){
const { deptId } = props.match.params;
const { data, id, temp } = props;
useEffect(()=>{
let uuid = getUniqueIdentifier()
let url = props.location.pathname
let platform = getBrowserPlatform()
let browser = getBrowserBrand()
let username = getUserName(props.current_user)
// if(!username) uuid = getUniqueIdentifier()
let remark = ` 操作系统:${platform};浏览器:${browser};`
setZoneVisits({url,username,uuid,remark})
},[])
useEffect(()=>{
if(id){
getProjectList();
@ -79,8 +91,8 @@ function HeaderPage(props){
<p className="z_desc task-hide-2" style={{WebkitLineClamp:data.introductionTitle?"4":"7"}} dangerouslySetInnerHTML={{ __html: data.introductionContent }} ></p>
</div>
{
data.introductionImage &&
<img src={data.introductionImage} alt="" width="300px"/>
data.introductionImage &&
<img src={data.introductionImage.split(",")[0]} alt="" width="300px"/>
}
</div>
}

View File

@ -30,7 +30,8 @@ import ra from '../img/ra.png'
import title from '../img/title.png'
import Partner from '../Component/partner';
import '../indexZone1.scss';
import { getProjectsLists, getPartnerList, getHomePageList, gethomePageDocList , getDocList , getSubDocList } from '../api'
import { getProjectsLists, getPartnerList, getHomePageList, gethomePageDocList , getDocList , getSubDocList,setZoneVisits } from '../api'
import {getUniqueIdentifier,getUserName,getBrowserPlatform,getBrowserBrand} from '../utils'
function HeaderPage(props){
@ -72,6 +73,18 @@ function HeaderPage(props){
{ title: '成长认证', icon: intro3, desc: '开发者可以通过完成开源项⽬对应的任务赢取对应能⼒成长认证开源项⽬对应的⾼级任务将获得对应等级L2或⾼级别的知识产权授权同时解锁下⼀级别成长任务' },
{ title: '顶级认证', icon: intro4, desc: '参与顶级项⽬“⾹⼭”的贡献者将获得中国科学院计算技术研究所副所长、研究员北京开源芯⽚研究院⾸席科学家RISC-V国际基⾦会理事会成员包云岗签名贡献者证书' }
]
useEffect(()=>{
let uuid = getUniqueIdentifier()
let url = props.location.pathname
let platform = getBrowserPlatform()
let browser = getBrowserBrand()
let username = getUserName(props.current_user)
// if(!username) uuid = getUniqueIdentifier()
let remark = ` 操作系统:${platform};浏览器:${browser};`
setZoneVisits({url,username,uuid,remark})
},[])
useEffect(()=>{
if(id){
// getProjectList();

View File

@ -15,6 +15,9 @@ import "slick-carousel/slick/slick-theme.css";
import Slider from 'react-slick';
import Left from '../../../home/Img/left.png';
import Right from '../../../home/Img/right.png';
import {getUniqueIdentifier,getUserName,getBrowserPlatform,getBrowserBrand} from '../utils'
import {setZoneVisits} from '../api'
function HeaderPageCCF(props) {
@ -42,6 +45,17 @@ function HeaderPageCCF(props) {
afterChange: (index)=> setTag(index)
}
useEffect(()=>{
let uuid = getUniqueIdentifier()
let url = props.location.pathname
let platform = getBrowserPlatform()
let browser = getBrowserBrand()
let username = getUserName(props.current_user)
// if(!username) uuid = getUniqueIdentifier()
let remark = ` 操作系统:${platform};浏览器:${browser};`
setZoneVisits({url,username,uuid,remark})
},[])
useEffect(() => {
if (id||cateId) {
getProjectList();
@ -237,7 +251,7 @@ function HeaderPageCCF(props) {
</div>
{
data.introductionImage &&
<img src={data.introductionImage} alt="" width="300px" />
<img src={data.introductionImage.split(",")[0]} alt="" width="300px" />
}
</div>
}

View File

@ -9,6 +9,8 @@ import RenderHtml from '../../../components/render-html';
import { Base64 } from 'js-base64';
import { getTocContent } from '../../../common/marked';
import TreeByWikiSidebar from '../../Wiki/components/treeByWikiSidebar';
import {getUniqueIdentifier,getUserName,getBrowserPlatform,getBrowserBrand} from '../utils';
import {setZoneVisits} from '../api'
function ProjectSource(props) {
const [loading, setLoading] = useState(false);
@ -102,7 +104,17 @@ function ProjectSource(props) {
})
}, [wikiDetail])
useEffect(() =>{
//
let uuid = getUniqueIdentifier()
let url = props.location.pathname
let platform = getBrowserPlatform()
let browser = getBrowserBrand()
let username = getUserName(props.current_user)
// if(!username) uuid = getUniqueIdentifier()
let remark = ` 操作系统:${platform};浏览器:${browser};`
setZoneVisits({url,username,uuid,remark})
//
const handleClick = (event) => {
let target = event.target;

View File

@ -1,6 +1,7 @@
import React , { useEffect, useState } from 'react';
import { getZoneStatistics } from '../../../api';
import '../index.scss';
import { Carousel } from 'antd';
//
// statistics:
@ -28,11 +29,15 @@ function Introduction({data, statistics=true}) {
{firstTitle}
<span className='firstTitle_back font-34'>ABOUT US</span>
</div>
<div>
{introductionImage && <img src={introductionImage} alt="" width="526px" align="right" hspace="35" vspace="10"/>}
{introductionContent && <span dangerouslySetInnerHTML={{ __html: introductionContent }} className='font-16' style={{lineHeight: '40px'}}></span>}
<div className='clearfix'>
{introductionImage && <Carousel className='intro_1_carousel zone_1_carousel' autoplay={true}>
{introductionImage.split(",").map((item, index) => {
return <img key={index} src={item} alt="" className='intro_image'/>;
})}
</Carousel>}
{introductionContent && <span dangerouslySetInnerHTML={{ __html: introductionContent }} className='font-16 intro_1_I' style={{lineHeight: '40px'}}></span>}
</div>
{statistics && <div className='df wrap mt50 between'>
{statistics && <div className='df wrap pt50 between width1200'>
{Object.keys(label).map(item=>{
return <div className='align font-16'>
<span className='font-34 color-blue font-bd'>{data1[item]}</span><br/>

View File

@ -131,8 +131,8 @@ function News({data, subTitle, firstDirIntro, secondDirIntro}) {
{/* 第一个顺序栏目 */}
{firstDir && <div className='firstDir clearfix'>
<p className='font-16 task-hide'>
{firstDir.name}
<Link style={{color: 'white'}} className='fr font-15' to={`/zone/uos/news/${firstDir.id}`}>更多<Icon type="arrow-right" className="ml5"/></Link>
<Link style={{color: 'white'}} to={`/zone/uos/news/${firstDir.id}`} onClick={()=>{window.scrollTo(0,450)}}>{firstDir.name}</Link>
<Link style={{color: 'white'}} className='fr font-15' to={`/zone/uos/news/${firstDir.id}`} onClick={()=>{window.scrollTo(0,450)}}>更多<Icon type="arrow-right" className="ml5"/></Link>
</p>
<div style={{fontFamily:"FangSong", height: 200}} className='font-24 ml85 mr80 flexCenter font-bd'>{firstDirIntro}</div>
{docListByFirstDir[0] && <div className='pt10 pb10 pl20 docByFirstDir task-hide'>
@ -147,7 +147,9 @@ function News({data, subTitle, firstDirIntro, secondDirIntro}) {
<img src={dirpng}/>
</div>
<div>
<p className='font-17 font-bd task-hide'>{secondDir.name}</p>
<p className='font-17 font-bd task-hide'>
<Link to={`/zone/uos/news/${secondDir.id}`} onClick={()=>{window.scrollTo(0,450)}}>{secondDir.name}</Link>
</p>
<p className='color627'>{secondDirIntro}</p>
</div>
</div>
@ -164,9 +166,9 @@ function News({data, subTitle, firstDirIntro, secondDirIntro}) {
</Row>
</div>}
</div>
<div className='df wrap backWhite pt15 pb15 pl10 pr10'>
{dirs && !!dirs.length && <div className='df wrap backWhite pt30 pb15 pl10 pr10'>
{renderItems()}
</div>
</div>}
</div>
</div>
}

View File

@ -1,6 +1,7 @@
import React , { useEffect, useState } from 'react';
import { getPartnerList } from '../../../api';
import '../index.scss';
import Nodata from '../../../../Nodata';
function Partner({data}) {
const {id, key, homepagePartnersTitle} = data || {};
@ -42,10 +43,11 @@ function Partner({data}) {
);
}
return <div className='partner_1_bg'>
return list && !!list.length && <div className='partner_1_bg'>
<div className='partner_1 width1200 pt50 pb60'>
<p className='font-bd font-30 mb10 task-hide center mb50'>{homepagePartnersTitle}</p>
<div>{gridItems}</div>
{/* {list && !list.length && <Nodata _html="暂无数据"/>} */}
</div>
</div>
}

View File

@ -3,6 +3,43 @@ import '../index.scss';
import { getProjectListByScore, getProjectsTypeLists } from '../../../api';
import { Carousel, Divider, Spin } from 'antd';
import Nodata from '../../../../Nodata';
import Slider from 'react-slick';
export function TypeCarousel({pageSize=4, list=[], typeId, onClick}) {
const setting = {
prevArrow: <img src={require('../image/left.png')} alt=''/>,
nextArrow: <img src={require('../image/right.png')} alt=''/>,
afterChange: (currentSlide)=>{onClick(list[currentSlide*pageSize].id)}
}
const pages = []
for (let i = 0; i < Math.ceil(list.length / pageSize); i++) {
const start = i * pageSize;
const end = start + pageSize;
pages.push(list.slice(start, end));
}
return <Slider {...setting} className='mb60'>
{pages.map((page, index)=>{
return <div key={index}>
<div className='flexCenter align'>
{page.map((item, i) => {
const order = index*pageSize+i
return <div key={i} className={`proTypeBox flexCenter ml15 mr15 pl10 pr10 pointer ${typeId === item.id && "active"}`} onClick={() => { onClick(item.id) }}>
{/* 项目 */}
<span className={`p_1_icon icon${i} mr5`}></span>
{/* 专项项目 */}
<span className={`s_p_order font-25 mr10 color-blue`}>
<span className='s_p_order_1'>{order < 9 ? `0${order+1}` : order+1}</span>
<span className={`s_p_order_icon s_p_order_icon${i}`}></span>
<img src={require('../image/s_project3.png')} width="18"/>
</span>
<span className='font-17 font-bd task-hide' title={item.name}>{item.name}</span>
</div>
})}
</div>
</div>
})}
</Slider>
}
function Project({ data, subTitle }) {
const { id, homepageProjectTitle } = data || {};
@ -15,7 +52,10 @@ function Project({ data, subTitle }) {
const pages = [];
useEffect(() => {
id && getProjectsTypeLists(id).then(result => {
id && getProjectsTypeLists(id, {
pageNum: 1,
pageSize: 100,
}).then(result => {
if (result) {
const rows = result.data.rows;
rows[0] && setTypeId(rows[0].id)
@ -52,19 +92,12 @@ function Project({ data, subTitle }) {
<div className='project_1 width1200 pt50 pb60'>
<p className='font-bd font-30 mb15 align task-hide'>{homepageProjectTitle}</p>
<p className='font-16 align color212 mb30'>{subTitle}</p>
<div className='flexCenter align'>
{typeList.slice(0, 3).map((item, index) => {
return <div className={`proTypeBox flexCenter font-17 mr30 pointer ${typeId === item.id && "active"}`} onClick={() => { setTypeId(item.id); setPage(1) }}>
<span className={`icon${index} mr5`}></span>
{item.name}
</div>
})}
</div>
<TypeCarousel pageSize={5} list={typeList} typeId={typeId} onClick={(id)=>{setTypeId(id); setPage(1)}}/>
<Spin spinning={loading}>
<Carousel className='zone_1_carousel' afterChange={(current)=>{setLoading(true);setPage(current+1)}}>
{pages.map((page1, index) => (
<div key={index}>
<div className='df wrap between mt60 pl10 pr10 mb25'>
<div className='df wrap between pl10 pr10 mb25'>
{lists.map((i, index) => {
const { extra: { commitCount, contributorsCount, lastWeekScore, scoreChange } } = i || {}
const order = page >= 2 ? (page-1)*6+index+1 : index+1;

View File

@ -56,26 +56,28 @@ function Source({data, subTitle}) {
<div className='flex1'>
<Spin spinning={isSpin}>
<div className='df wrap between'>
{sourceList.map(i=>{
{sourceList.map(i => {
const time = i.updateTime || i.createTime
return <div className='s_1_souce mr25 mt30'>
<div className='flexCenter between m15' style={{minHeight: 90}}>
<div style={{width: '0'}} className='flex1'>
<p className='task-hide'><Link to={`/zone/${key}/source/${i.id}`} className=" font-17 font-bd">{i.name}</Link></p>
<p className="task-hide-2 color212">{i.summary}</p>
return <Link to={`/zone/${key}/source/${i.id}`}>
<div className='s_1_souce mr25 mt30'>
<div className='flexCenter between m15' style={{ minHeight: 90 }}>
<div style={{ width: '0' }} className='flex1'>
<p className='task-hide font-17 font-bd'>{i.name}</p>
<p className="task-hide-2 color212">{i.summary}</p>
</div>
<img src={require("../image/new3.png")} width={48} className='ml10' />
</div>
<img src={require("../image/new3.png")} width={48} className='ml10'/>
<div className='flexCenter between'>
<span className='color848'>{time && time.split(" ")[0].replace(/-/g, '.')}</span>
<Link className='color-blue' to={`/zone/${key}/source/${i.id}`}>
<img src={require("../image/source4.png")} width={25} className='normalImg' />
<img src={require("../image/source5.png")} width={25} className='hoverImg' />
<span className='pl5'>下载</span>
</Link>
</div>
</div>
<div className='flexCenter between'>
<span className='color848'>{time && time.split(" ")[0].replace(/-/g, '.')}</span>
<Link className='color-blue' to={`/zone/${key}/source/${i.id}`}>
<img src={require("../image/source4.png")} width={25} className='normalImg'/>
<img src={require("../image/source5.png")} width={25} className='hoverImg'/>
<span className='pl5'>下载</span>
</Link>
</div>
</div>
</Link>
})}
</div>
<div style={{marginLeft: 150}}>

View File

@ -1,8 +1,9 @@
import React , { useEffect, useState } from 'react';
import { getSpecialProjectDetail, getSpecialProjectList, getSpecialProjectTypeList } from '../../../api';
import { Col, Divider, Row, Tag } from 'antd';
import { getSpecialProjectDetail, getSpecialProjectList, getSpecialProjectTypeList, getImage, getSpecialReportList } from '../../../api';
import { Col, Divider, Icon, Row, Tag } from 'antd';
import Nodata from '../../../../Nodata';
import '../index.scss';
import { TypeCarousel } from './project';
function SpecialProjects({data, subTitle}) {
const {key, homepageSpecialProjectTitle, id} = data || {};
@ -11,11 +12,14 @@ function SpecialProjects({data, subTitle}) {
const [projectList, setProjectList] = useState([]);
const [selectedItem, setSelectedItem] = useState(null);
const [projectDetail, setProjectDetail] = useState({});
const relevancyTypes = ["PAPER", "PATENT", "PROJECT"]
const itemsPerRow = 3;
useEffect(()=>{
id && getSpecialProjectTypeList(id, {
pageNum: 1,
pageSize: 4,
pageSize: 100,
}).then(res=>{
if(res){
const rows = res.data.rows;
@ -35,61 +39,96 @@ function SpecialProjects({data, subTitle}) {
})
}, [typeId])
useEffect(()=>{
selectedItem && getSpecialProjectDetail(selectedItem).then(res=>{
if(res){
const rows = res.data.rows;
//
const rowsByType = rows.reduce((acc, item) => {
// accitem.type
if (!acc[item.type]) {
acc[item.type] = [];
}
// itemtype
acc[item.type].push(item);
return acc;
}, {});
setProjectDetail({
detail: projectList.find(item => item.id === selectedItem),
...rowsByType
});
}
})
}, [selectedItem])
async function getRelevancyList(id, type, pageSize=5) {
const res = await getSpecialProjectDetail(id, {
type,
pageNum: 1,
pageSize
});
return res.data;
}
const itemsPerRow = 3;
async function getRelevancyListByReport(id, pageSize=5) {
const res = await getSpecialReportList({
specialProjectId: id,
pageNum: 1,
pageSize
});
return res.data;
}
const handleItemClick = (id) => {
const handleItemClick = async (id) => {
setSelectedItem(selectedItem === id ? null : id);
if(id === selectedItem) return
const data = await Promise.all(relevancyTypes.map(async (item) => {
return {[item]: await getRelevancyList(id, item)};
}));
data.push({
REPORT: await getRelevancyListByReport(id)
})
const [a,b,c,d] = data
setProjectDetail({
detail: projectList.find(item => item.id === id),
...a,
...b,
...c,
...d
});
};
const list = (title, table1, table2, list)=>{
const showMore = (type, isShowAll) => {
return <p className='center color-blue mb10'>
<a onClick={async () => {
const data = type === "REPORT" ? await getRelevancyListByReport(selectedItem, isShowAll ? 5 : 1000): await getRelevancyList(selectedItem, type, isShowAll ? 5 : 1000);
const detail = {
...projectDetail
}
detail[type] = data
setProjectDetail(detail);
}}>{isShowAll ? '收起' : '展开'}<Icon type={isShowAll ? "up" : "down"} className="ml5" /></a>
</p>
}
const listItem = (title, table1, table2, table3, res)=>{
const typekey = {
"论文": "PAPER",
"专利": "PATENT",
"会议报告": "REPORT"
}
const type = typekey[title];
const {rows:list, total} = res;
const isShowAll = list.length === total
return !!list.length && <React.Fragment>
<p className='leftBox font-16 font-bd'>{title}</p>
<Divider dashed className="margin0-20"/>
<Row className='s_p_rowList mb30 font-15'>
<Col span={14} className='s_p_rowList_t font-bd'>{table1}</Col>
<Col span={10} className='s_p_rowList_t font-bd'>{table2}</Col>
<Row className='s_p_rowList mb20 font-15'>
<Col span={12} className='s_p_rowList_t font-bd'>{table1}</Col>
<Col span={6} className='s_p_rowList_t font-bd'>{table2}</Col>
<Col span={6} className='s_p_rowList_t font-bd'>{table3}</Col>
{list.map(item=>{
const {author} = item;
const detail = item.relevancyObject;
const {author, company, remark, link} = item;
const detail = item.relevancyObject || item;
const {name, createUser, id} = detail
return <React.Fragment>
<Col span={14} className='task-hide'>
<a href={`/zone/${key}/source/${id}`}>
{title === "论文" ? <img src={require('../image/s_project1.png')} width={24} className='mr10'/> : title === "专利" ? <img src={require('../image/s_project2.png')} width={24} className='mr10'/> : <img src={createUser.avatar}/>}
<Col span={12} className='task-hide'>
{title !== "会议报告" ? <a href={`/zone/${key}/source/${id}`}>
{title === "论文" ? <img src={require('../image/s_project1.png')} width={24} className='mr10' /> : title === "专利" ? <img src={require('../image/s_project2.png')} width={24} className='mr10' /> : <img src={createUser.avatar} />}
{name}
</a>
</a> : link ? <a href={link}>{name}</a> : <span>{name}</span>}
</Col>
<Col span={10} className='task-hide'>{author}</Col>
<Col span={6} className='task-hide'>{author}</Col>
<Col span={6} className='task-hide' title={company || remark}>{company || remark || '-'}</Col>
</React.Fragment>
})}
</Row>
{total > 5 && showMore(type, isShowAll)}
{!list.length && <Nodata _html="暂无数据" small/>}
</React.Fragment>
}
const list2 = (title, table1, table2, list)=>{
const list2Item = (title, table1, table2, res)=>{
const {rows:list, total} = res;
const isShowAll = list.length === total
return !!list.length && <React.Fragment>
<p className='leftBox font-16 font-bd'>{title}</p>
<Divider dashed className="margin0-20"/>
@ -107,6 +146,7 @@ function SpecialProjects({data, subTitle}) {
</React.Fragment>
})}
</Row>
{total > 5 && showMore("PROJECT", isShowAll)}
{!list.length && <Nodata _html="暂无数据" small/>}
</React.Fragment>
}
@ -120,13 +160,13 @@ function SpecialProjects({data, subTitle}) {
currentRow.push(
<div
key={item.id}
className={`s_p_detail ${selectedItem === item.id ? 'active' : ''}`}
className={`s_p_detail df between ${selectedItem === item.id ? 'active' : ''} ${(index+1)%3 === 0 ? "" : 'mr30'}`}
onClick={() => handleItemClick(item.id)}
>
<span className={`font-bd font-22 s_p_d_order align s_p_d_order${index+1}`}>{index+1}</span>
<div className='font-bd font-17 task-hide-2 mb5'>{item.name}</div>
<div className='task-hide-2 color6B7' style={{minHeight: '56px'}}>摘要{item.description}</div>
<p style={{color: '#75798A'}} className='mt3'><i className='iconfont icon-chengyuan2 font-15 mr5'></i>{item.projectLeader}</p>
{ item.responsibleCompany && <div className='task-hide-2 color6B7 breadk-all'>承担单位{item.responsibleCompany}</div> }
<p style={{color: '#75798A'}} className='mt6'><i className='iconfont icon-chengyuan2 font-15 mr5'></i>{item.projectLeader}</p>
</div>
);
@ -137,29 +177,29 @@ function SpecialProjects({data, subTitle}) {
//
if (currentRow.some(item => item.key === selectedItem?.toString())) {
const {detail, PAPER=[], PATENT=[], PROJECT=[]} = projectDetail;
const {name, projectLeader, awards, description} = detail || {};
const {detail, PAPER={rows:[]}, PATENT={rows:[]}, PROJECT={rows:[]}, REPORT={rows:[]}} = projectDetail;
const {name, projectLeader, awards, introductionImageIdentifier} = detail || {};
rendered.push(
<div key={`details-${selectedItem}`} className="item-details">
<div className="details-content">
<div className='s_p_head'>
<Tag color='#466AFF' style={{padding: '0 2px'}}>项目</Tag><span className='font-20 font-bd'>{name}</span>
<p className='mt10 font-15 pb5'>项目负责人<span className='color212'>{projectLeader}</span></p>
<p className='font-15 pb5'>奖项<span className='color-blue'>{awards}</span></p>
<p className='font-15 pb5'>摘要<span className='color212'>{description}</span></p>
</div>
<div style={{padding: '22px 33px'}}>
{list("论文", "论文标题", "作者", PAPER)}
{list("专利", "专利标题", "作者", PATENT)}
{list2("开源项目", "项目名称", "参与者", PROJECT)}
{!PAPER.length && !PATENT.length && !PROJECT.length && <Nodata _html="暂无数据" small/>}
{ awards && <p className='font-15 pb5'>奖项<span className='color-blue'>{awards}</span></p> }
{ introductionImageIdentifier &&<p className='font-15 pb5 intro imageLayerParent'>简介<img className="intro-img pointer" src={ getImage(introductionImageIdentifier) } alt="" /></p>}
</div>
{
PAPER.rows.length + PATENT.rows.length + PROJECT.rows.length > 0 + REPORT.rows.length > 0 && <div style={{padding: '22px 33px'}}>
{listItem("论文", "论文标题", "作者", "单位", PAPER)}
{listItem("专利", "专利标题", "作者", "单位", PATENT)}
{list2Item("开源项目", "项目名称", "参与者", PROJECT)}
{listItem("会议报告", "报告名称", "报告人", "备注", REPORT)}
</div>
}
</div>
</div>
);
}
//
currentRow = [];
}
@ -171,19 +211,8 @@ function SpecialProjects({data, subTitle}) {
<div className='specialProjects_1 width1200 pt50 pb60'>
<p className='font-bd font-30 mb15 align task-hide'>{homepageSpecialProjectTitle}</p>
<p className='font-16 align color212 mb50'>{subTitle}</p>
<div className='flexCenter align mb50'>
{typeList.map((item, index)=>{
return <div className={`proTypeBox flexCenter mr30 color212 pointer pl5 pr5 ${typeId === item.id && "active"}`} onClick={()=>{setTypeId(item.id)}}>
<span className={`s_p_order font-25 mr10 color-blue`}>
<span className='s_p_order_1'>{index < 9 ? `0${index+1}` : index+1}</span>
<span className={`s_p_order_icon s_p_order_icon${index}`}></span>
<img src={require('../image/s_project3.png')} width="18"/>
</span>
<span className='font-17 font-bd task-hide'>{item.name}</span>
</div>
})}
</div>
<div className='df wrap between'>
<TypeCarousel pageSize={3} list={typeList} typeId={typeId} onClick={(id)=>{setTypeId(id);}}/>
<div className='df wrap'>
{renderItems()}
<div style={{margin: '0 auto'}}>{!projectList.length && <Nodata _html="暂无数据"/>}</div>
</div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 922 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -65,6 +65,20 @@
color: #D9D9D9;
}
}
.intro_1_carousel{
width: 526px;
float: right;
margin-left: 35px;
.slick-dots li button:before{
font-size: 10px;
top: -12px;
}
}
.intro_image{
width: 526px;
height: 272px;
object-fit: scale-down;
}
}
.news_1_bg{
background-image: url('./image/new1.png');
@ -147,8 +161,9 @@
}
}
.pd_desc{
min-height: 55px;
// min-height: 55px;
color: #6B707C;
margin-bottom: 10px;
}
.pd_tag{
display: flex;
@ -186,7 +201,7 @@
}
}
.project_1_bg .proTypeBox, .specialProjects_1_bg .proTypeBox{
width: 280px;
min-width: 275px;
height: 63px;
background: #F5F6F7;
border-radius: 8px;
@ -207,6 +222,12 @@
.icon2{
background-image: url('./image/proType3_active.png');
}
.icon3{
background-image: url('./image/proType4_active.png');
}
.icon4{
background-image: url('./image/proType5_active.png');
}
.s_p_order_1, .s_p_order>img{
display: none;
}
@ -225,7 +246,7 @@
background-image: url('./image/s_project7.png');
}
}
.icon1, .s_p_order_icon, .icon0, .icon2{
.s_p_order_icon, .p_1_icon{
width: 22px;
height: 22px;
background-size: 100% 100%;
@ -239,12 +260,30 @@
background-image: url('./image/proType2.png');
}
.icon2{
width: 25px;
height: 19px;
height: 18px;
background-image: url('./image/proType3.png');
}
.icon3{
width: 24px;
height: 24px;
background-image: url('./image/proType4.png');
}
.icon4{
background-image: url('./image/proType5.png');
}
}
.project_1_bg .proTypeBox{
min-width:200px;
.s_p_order{
display: none;
}
}
.specialProjects_1_bg .proTypeBox{
.p_1_icon{
display: none;
}
}
.source_1_bg{
background-color: white;
background-image: url('./image/source1.png');
@ -347,9 +386,10 @@
background-size: 100% 100%;
.s_p_detail+div:not(.item-details){
flex: 0 0 calc(33.333% - 20px);
margin: 0 0 35px 0 !important;
margin-bottom: 35px !important;
}
.s_p_detail{
flex-direction: column;
flex: 0 0 calc(33.333% - 20px);
background: #FFFFFF;
box-shadow: 0px 0px 10px 1px rgba(28,48,175,0.08);
@ -359,6 +399,9 @@
margin-bottom: 35px;
cursor: pointer;
transition: all 0.3s ease;
.breadk-all {
word-break: break-all;
}
.s_p_d_order{
width: 36px;
height: 30px;
@ -421,6 +464,12 @@
background: #FFFFFF;
box-shadow: 0px 0px 10px 1px rgba(88,116,255,0.18);
border-radius: 4px;
.intro {
display: flex;
}
.intro-img {
width: 200px;
}
}
.leftBox{
position: relative;

View File

@ -1,4 +1,4 @@
import React,{ } from 'react';
import React,{ useEffect , useState } from 'react';
import Introduction from './component/introduction';
import News from './component/news';
import Project from './component/project';
@ -8,8 +8,21 @@ import Partner from './component/partner';
import SpecialProjects from './component/specialProjects';
import '../../indexZone1.scss'
import './index.scss'
import {getUniqueIdentifier,getUserName,getBrowserPlatform,getBrowserBrand} from '../../utils'
import {setZoneVisits} from '../../api'
function HeaderPageUos(props){
useEffect(()=>{
let uuid = getUniqueIdentifier()
let url = props.location.pathname
let platform = getBrowserPlatform()
let browser = getBrowserBrand()
let username = getUserName(props.current_user)
// if(!username) uuid = getUniqueIdentifier()
let remark = ` 操作系统:${platform};浏览器:${browser};`
setZoneVisits({url,username,uuid,remark})
},[])
const { data } = props;
return(

View File

@ -13,6 +13,8 @@ import DefaultImg from '../Component/defaultImg';
import { getNewsAllList , getNewsHotList } from '../api';
import { tempConfig, tempEnum } from '../tempInfo';
import { httpUrl } from '../fetch';
import {getUniqueIdentifier,getUserName,getBrowserPlatform,getBrowserBrand} from '../utils';
import {setZoneVisits} from '../api'
function Main(props){
@ -26,7 +28,16 @@ function Main(props){
const { deptId , cateId } = props.match.params;
const { id, temp, role, history, sectionCmsTitle } = props;
useEffect(()=>{
let uuid = getUniqueIdentifier()
let url = props.location.pathname
let platform = getBrowserPlatform()
let browser = getBrowserBrand()
let username = getUserName(props.current_user)
// if(!username) uuid = getUniqueIdentifier()
let remark = ` 操作系统:${platform};浏览器:${browser};`
setZoneVisits({url,username,uuid,remark})
},[])
const menu = (
<Menu>
<Menu.Item>

View File

@ -12,6 +12,8 @@ import CommentList from '../Component/comments/list'
import { connect } from 'react-redux';
import { withRouter } from "react-router";
import { setZoneDetail, setNewsDetail } from '../../../redux/actions/server';
import {getUniqueIdentifier,getUserName,getBrowserPlatform,getBrowserBrand} from '../utils';
import {setZoneVisits} from '../api'
function NewsDetail(props){
@ -28,6 +30,16 @@ function NewsDetail(props){
setMeta()
}
useEffect(()=>{
let uuid = getUniqueIdentifier()
let url = props.location.pathname
let platform = getBrowserPlatform()
let browser = getBrowserBrand()
let username = getUserName(props.current_user)
// if(!username) uuid = getUniqueIdentifier()
let remark = ` 操作系统:${platform};浏览器:${browser};`
setZoneVisits({url,username,uuid,remark})
},[])
useEffect(()=>{
let initialContent = undefined;
//

View File

@ -8,7 +8,8 @@ import axios from 'axios';
import { getProjectsLists , getProjectsTypeLists } from '../api';
import nodata from '../img/nodata.png';
import { tempEnum } from '../tempInfo';
import {getUniqueIdentifier,getUserName,getBrowserPlatform,getBrowserBrand} from '../utils'
import {setZoneVisits} from '../api'
const { Search } = Input;
function ProjectSource(props){
@ -23,6 +24,16 @@ function ProjectSource(props){
const [ value , setValue ] = useState(undefined);
const [ loading , setLoading ] = useState(false);
const { id, temp, sectionProjectTitle } = props;
useEffect(()=>{
let uuid = getUniqueIdentifier()
let url = props.location.pathname
let platform = getBrowserPlatform()
let browser = getBrowserBrand()
let username = getUserName(props.current_user)
// if(!username) uuid = getUniqueIdentifier()
let remark = ` 操作系统:${platform};浏览器:${browser};`
setZoneVisits({url,username,uuid,remark})
},[])
useEffect(()=>{
if(id){

View File

@ -8,6 +8,8 @@ import Nodata from '../../Nodata';
import ImgGreen from '../img/green_nodata.png';
import '../indexZone1.scss';
import { FlexAJ } from '../../Component/layout';
import {getUniqueIdentifier,getUserName,getBrowserPlatform,getBrowserBrand} from '../utils';
import {setZoneVisits} from '../api'
const { Search } = Input;
@ -22,9 +24,20 @@ function Source(props){
const [ total , setTotal ] = useState(0);
const [ search , setSearch ] = useState(undefined);
const [ isSpin , setIsSpin ] = useState(true);
const limit = 20;
const limit = 21;
const { id , temp, history, role, sectionResourceTitle } = props;
useEffect(()=>{
let uuid = getUniqueIdentifier()
let url = props.location.pathname
let platform = getBrowserPlatform()
let browser = getBrowserBrand()
let username = getUserName(props.current_user)
// if(!username) uuid = getUniqueIdentifier()
let remark = ` 操作系统:${platform};浏览器:${browser};`
setZoneVisits({url,username,uuid,remark})
},[])
useEffect(()=>{
id && getLists();
},[id,chooseTypeId,chooseZoneId,page,search])

View File

@ -7,6 +7,8 @@ import { httpUrl } from '../fetch';
import { AlignCenter, FlexAJ } from '../../Component/layout';
import SourceIcon from '../img/sourceIcon.png';
import { addMeta, setSeoMeta } from 'educoder';
import {getUniqueIdentifier,getUserName,getBrowserPlatform,getBrowserBrand} from '../utils';
import {setZoneVisits} from '../api'
function SourceDetail(props){
@ -15,6 +17,17 @@ function SourceDetail(props){
const zonedetail = props.data;
const {temp} = props;
useEffect(()=>{
let uuid = getUniqueIdentifier()
let url = props.location.pathname
let platform = getBrowserPlatform()
let browser = getBrowserBrand()
let username = getUserName(props.current_user)
// if(!username) uuid = getUniqueIdentifier()
let remark = ` 操作系统:${platform};浏览器:${browser};`
setZoneVisits({url,username,uuid,remark})
},[])
useEffect(()=>{
if(sourceid){
getDetails();
@ -71,7 +84,7 @@ function SourceDetail(props){
{detail.auditStatus === '0' && <Badge color={temp === "zone" ? "#466aff" : "#089f7f"} text="待审核" className='pass'/> }
{detail.auditStatus === '2' && <Badge color="#eb1212" text="未通过" className='failed'/> }
</div>
<div className='pb20'>{detail.summary}</div>
<div className='pb20' dangerouslySetInnerHTML={{ __html: detail.summary }} ></div>
{
detail.fileList && detail.fileList.length > 0 &&
<ul className="files_box">

View File

@ -8,6 +8,8 @@ import MemberApply from '../Component/memberApply';
import { getVIPLists, getAuditStatus, applyJoin } from '../api';
import nodata from '../img/nodata.png';
import MemberList from '../Component/memberList';
import {getUniqueIdentifier,getUserName,getBrowserPlatform,getBrowserBrand} from '../utils';
import {setZoneVisits} from '../api'
import '../indexZone1.scss';
@ -25,6 +27,17 @@ function ZoneVIP(props){
const [ memberStatus , setMemberStatus] = useState(memberStatusEnum.notMember);
const { id, showNotification, checkIfLogin, showLoginDialog, temp, sectionMemberTitle } = props;
useEffect(()=>{
let uuid = getUniqueIdentifier()
let url = props.location.pathname
let platform = getBrowserPlatform()
let browser = getBrowserBrand()
let username = getUserName(props.current_user)
// if(!username) uuid = getUniqueIdentifier()
let remark = ` 操作系统:${platform};浏览器:${browser};`
setZoneVisits({url,username,uuid,remark})
},[])
useEffect(()=>{
if(id){
setIsSpin(true);

View File

@ -1,5 +1,9 @@
import fetch from './fetch';
import fetch, { httpUrl } from './fetch';
export function getImage(identifier) {
return `${ httpUrl }/file/open/downloadByIdentifier/${ identifier }`
}
/**入口页接口***/
export function getMainInfos(deptId, params) {
@ -370,6 +374,14 @@ export function getSpecialProjectDetail(specialProjectId,params) {
})
}
export function getSpecialReportList(params) {
return fetch({
url: `/zone/open/ZoneSpecialProjectReport/list`,
method: 'GET',
params
})
}
// 专区统计数据
export function getZoneStatistics(id) {
return fetch({
@ -427,3 +439,11 @@ export function getRoleList(){
})
}
// 组织升级为企业
export function setZoneVisits(data) {
return fetch({
url: `/zone/open/zoneVisits`,
method: 'POST',
data
})
}

View File

@ -120,7 +120,6 @@ function Index(props){
const sourcedetail = pathname.indexOf(`/zone/${deptId}/newdetail/`)>-1 && isPhone();
const {current_user} = props;
console.log('deptId', deptId);
useEffect(()=>{
@ -154,8 +153,9 @@ function Index(props){
}
setData(data);
document.title= data && data.mainTitle;
setId(data.id);
if(data.id){
if(data && data.id){
setId(data.id);
getAdminUrl(data.id);
getRole(data.id)
}

View File

@ -0,0 +1,68 @@
export const generateUUID = () => { //生成uuid
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0,
v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
export const getUniqueIdentifier = () => { //获取uuid
let uniqueId = localStorage.getItem('uniqueId');
if (!uniqueId) {
uniqueId = generateUUID();
localStorage.setItem('uniqueId', uniqueId);
}
return uniqueId;
}
export const getUserName = (user) => { //获取用户名
if(user && user.login) return user.login
return ''
}
export const getBrowserPlatform = () => {
const userAgent = navigator.userAgent;
let platform;
if (userAgent.includes("Mac")) {
platform = "MacOS";
} else if (userAgent.includes("X11") || userAgent.includes("Linux")) {
platform = "Linux";
} else if (userAgent.includes("Android")) {
platform = "Android";
} else if (userAgent.includes("iPhone") || userAgent.includes("iPad")) {
platform = "iOS";
} else if (userAgent.includes("Win")) {
platform = "Windows";
} else {
platform = "Unknown";
}
return platform;
}
export const getBrowserBrand = () => { //获取浏览器信息
const userAgent = navigator.userAgent;
let brand = "Unknown";
// 检测浏览器品牌
if (userAgent.includes("Firefox")) {
brand = "Firefox";
} else if (userAgent.includes("Safari") && !userAgent.includes("Chrome")) {
brand = "Safari";
} else if (userAgent.includes("MSIE") || userAgent.includes("Trident")) {
brand = "Internet Explorer";
} else if (userAgent.includes("Edge") || userAgent.includes("Edg")) {
brand = "Edge";
}else if (userAgent.includes("QQBrowser")) {
brand = "QQBrowser";
} else if (userAgent.includes("LBBrowser") ) {
brand = "liebao";
}else if (userAgent.includes("360")) {
brand = "360Browser";
} else if (userAgent.includes("Opera") || userAgent.includes("OPR")) {
brand = "Opera";
} else if (userAgent.includes("Chrome")) {
brand = "Chrome";
}
return brand;
}

View File

@ -1,8 +1,9 @@
import React,{useState, useEffect, useRef, useImperativeHandle,forwardRef} from 'react';
import { Dropdown } from 'antd';
import { findDOMNode } from 'react-dom';
import "../index.scss";
function Drop({overlay , children , placement, overlayClassName},ref){
function Drop({overlay , children , placement, overlayClassName,idname},ref){
const [ visible , setVisible ] = useState(false);
const refFa = useRef(null);
const refBox = useRef(null);
@ -35,6 +36,7 @@ function Drop({overlay , children , placement, overlayClassName},ref){
placement={placement}
visible={visible}
overlay={<div ref={refFa}>{overlay}</div>}
getPopupContainer={(trigger) => idname ? document.getElementById(idname) :trigger.parentNode}
trigger={['click']}
overlayClassName={overlayClassName}
>

View File

@ -59,7 +59,7 @@ function Details(props){
const permission = props && props.projectDetail && props.projectDetail.permission;
let colors = ["#1abcb1","#28be6c","#e67e22","#db3d1d"];
const {projectDetail , open_blockchain ,current_user } = props;
const {projectDetail , open_blockchain ,current_user, showNpsModal } = props;
useEffect(()=>{
if(pathname === `/${owner}/${projectsId}/issues/${index}/copy`){
setEdit(true);
@ -284,6 +284,7 @@ function Details(props){
setEdit(false);
//
setCommentReload(Math.random());
showNpsModal("submitIssue", 1);
}
}).catch(error=>{})
}else{
@ -310,6 +311,7 @@ function Details(props){
window.scrollTo(0,0);
props.showNotification("疑修复制成功!");
props.history.push(`/${owner}/${projectsId}/issues/${result.data.project_issues_index}`);
showNpsModal("createIssue", 1);
}
}).catch(error=>{})
}

View File

@ -10,7 +10,7 @@ import axios from 'axios';
function New(props){
// history search
const {location:{search}, projectDetail} = props;
const {location:{search}, projectDetail, showNpsModal} = props;
const feedBack = search && search.indexOf('type=feedback') !== -1;
// id
const milepostId = props.match.params.milepostId;
@ -200,6 +200,7 @@ function New(props){
window.scrollTo(0,0);
props.showNotification("疑修创建成功!");
props.history.push(`/${owner}/${projectsId}/issues/${result.data.project_issues_index}`);
showNpsModal("createIssue", 1);
}
}).catch(error=>{})
}

View File

@ -5,7 +5,7 @@ import { Link } from 'react-router-dom';
import { connect } from 'react-redux';
import { truncateCommitId } from "../common/util";
import CloneAddress from '../Branch/CloneAddress';
import CheckProfile from '../Component/ProfileModal/Profile';
import SelectBranch from '../Branch/Select';
import User from '../Component/User';
import axios from 'axios';
@ -21,15 +21,19 @@ import DrawerPanel from '../Component/DrawerPanel';
import UpdateDescModal from './sub/UpdateDescModal';
import Nodata from '../Nodata';
import Invite from './sub/Invite';
import CheckProfile from '../Component/ProfileModal/Profile';
import RenderHtml from '../../components/render-html';
import Loadable from 'react-loadable';
import Loading from '../../Loading';
import ProjectPortrait from '../Component/projectPortrait';
import ProjectCompass from '../Component/projectCompass';
import imNoneImg from './img/importNone.png';
import LicenseModal from '../Component/licenseModal/index';
import moment from 'moment';
const ProjectCompass = Loadable({
loader: () => import('../Component/projectCompass'),
loading: Loading,
})
const format = "YYYY-MM-DD HH:mm"
const CoderRootFileDetail = Loadable({
loader: () => import("./CoderRootFileDetail"),
@ -73,6 +77,7 @@ function CoderDepot(props){
const [ pullsFlag , setPullsFlag ] = useState(true);
const [ issuesFlag , setIssuesFlag ] = useState(true);
const [ releaseVersions , setReleaseVersions] = useState(undefined);
const [ licenseVisible ,setLicenseVisible] = useState(false);
const owner = props.match.params.owner;
const projectsId = props.match.params.projectsId;
@ -304,6 +309,7 @@ function CoderDepot(props){
)
}
function getPathUrl(array,index){
if(array && array.length>0 && index){
let str = "";
@ -400,6 +406,16 @@ function CoderDepot(props){
props.showLoginDialog(`/${owner}/${projectsId}/issues/new`);
}
}
//
function createDefaultLicense(id){
if(props.checkIfLogin()===false){
props.showLoginDialog()
return false;
}
let b = branchName || defaultBranch;
let checkvalue = turnbar(b);
props.history.push({pathname:`/${owner}/${projectsId}/${checkvalue}/newfile${treeValue === undefined ? "" : `/${treeValue}`}`,state:`${id}`})
}
// forkpr
function compare() {
@ -419,6 +435,7 @@ function CoderDepot(props){
return(
<div className='WhiteBack'>
{mirror_status ===0 && <UpdateDescModal desc={desc} website={website} lesson_url={lesson_url} topicsInfo={topics} visible={openModal} onCancel={()=>setOpenModal(false)} onOk={okUpdate}/> }
<LicenseModal visible={licenseVisible} onCancel={()=>{setLicenseVisible(false)}} createDefaultLicense={createDefaultLicense}/>
<Spin spinning={isSpin}>
{
((dirInfo || fileInfo) && mirror_status===0 ) &&
@ -659,10 +676,15 @@ function CoderDepot(props){
<span>{projectDetail && projectDetail.size}</span>
</div>
{
projectDetail && projectDetail.license_name &&
<div className="pinfos">
projectDetail && projectDetail.license_id !== 0 &&
<div className={projectDetail.license_name ? "pinfos" :"color-grey-6"}>
<i className="iconfont icon-xieyiicon font-16 mr10"></i>
<Link to={`/${owner}/${projectsId}/tree/${turnbar(branchName || defaultBranch)}/LICENSE`} className="color-grey-6">{projectDetail.license_name}</Link>
{
projectDetail.license_name ?
<Link to={`/${owner}/${projectsId}/tree/${turnbar(branchName || defaultBranch)}/LICENSE`} className="color-grey-6">{projectDetail.license_name}</Link>
:
<a className='color-blue' onClick={()=>{setLicenseVisible(true)}}>点击选择许可证(LICENSE)</a>
}
</div>
}
</div>
@ -695,7 +717,7 @@ function CoderDepot(props){
/>
</React.Fragment>
}
{__CLIENT__ && <ProjectCompass owner={owner} projectsId={projectsId} />}
{__CLIENT__ && projectDetail && projectDetail.project_id && !projectDetail.private && <ProjectCompass owner={owner} projectsId={projectsId} />}
{/* 贡献者 */}
{mirror_status ===0 && <Contributors owner={owner} projectsId={projectsId} /> }
{/* 项目画像: 导入/fork项目不展示, open_portrait后台配置是否开启仓库 */}

View File

@ -162,9 +162,9 @@ class CoderRootFileDetail extends Component {
};
deleteFile = () => {
const { detail } = this.props;
const { detail ,projectDetail , getDetail } = this.props;
const { projectsId, owner ,branchName} = this.props.match.params;
console.log(detail,this.props);
const url = `/${owner}/${projectsId}/delete_file.json`;
axios.delete(url, {
params: {
@ -176,8 +176,18 @@ class CoderRootFileDetail extends Component {
})
.then((result) => {
if (result) {
this.props.showNotification("删除成功!");
this.props.history.push(`/${owner}/${projectsId}`);
if(detail.path && detail.path==="LICENSE" && projectDetail){
const url1 = `/${owner}/${projectsId}.json`;
const { name , identifier } = projectDetail;
axios.put(url1,{name,identifier,license_id:""}).then(res=>{
getDetail && getDetail();
this.props.showNotification("删除成功!");
this.props.history.push(`/${owner}/${projectsId}`);
}).catch(error=>{})
}else{
this.props.showNotification("删除成功!");
this.props.history.push(`/${owner}/${projectsId}`);
}
}
})
.catch((error) => {

View File

@ -1,4 +1,4 @@
import React, { useEffect, useState } from "react";
import React, { useEffect, useState , useRef } from "react";
import styled from "styled-components";
import { Button ,Spin } from "antd";
import { timeFormat, truncateCommitId } from '../common/util';
@ -57,6 +57,24 @@ export default (props) => {
const [isSpin, setIsSpin] = useState(true);
const { sha , projectsId, owner } = match.params;
const [ dropLoading, setDropLoading] = useState(false); //loading
const limit = 100;
const fRef = useRef();
useEffect(()=>{
window.addEventListener("scroll",scrollListener);
return ()=>{window.removeEventListener("scroll",scrollListener);}
},[])
const scrollListener=()=>{
let scrollHeight = document.documentElement.scrollHeight;
let clientHeight = document.documentElement.clientHeight;
let scrollTop = document.documentElement.scrollTop;
if(Math.ceil(scrollTop+clientHeight) >= scrollHeight-5){
InitDiffData();
}
}
useEffect(()=>{
if(projectDetail){
const { author, name} = projectDetail;
@ -66,17 +84,18 @@ export default (props) => {
useEffect(() => {
if (projectsId && owner && sha) {
InitDiffData();
const url = `/${owner}/${projectsId}/commits/${sha}.json`;
axios
.get(url)
.then(result => {
if (result) {
setData(result.data);
// setData(result.data);
setCommit(result.data.commit);
setParents(result.data.parents);
setCommitter(result.data.committer || (result.data.commit && result.data.commit.committer));
setIsSpin(false);
}
})
.catch(error => {
@ -84,6 +103,28 @@ export default (props) => {
});
}
}, [projectsId , owner, sha]);
async function InitDiffData (){
let f = fRef.current ? fRef.current.files : [];
let p = fRef.current ? fRef.current.page : 1;
let hm = fRef.current ? fRef.current.hasMore : true;
if (!hm || dropLoading || !isSpin) {
return;
}
setDropLoading(true);
const url = `/v1/${owner}/${projectsId}/commits/${sha}/files.json`;
await axios.get(url,{
params:{page:p,limit}
}).then(res=>{
if(res?.status === 200){
let arr = p === 1 ? res.data.files : [...f,...res.data.files];
setData(res.data);
fRef.current = {files:arr,page:p+1,hasMore:arr.length === limit};
setDropLoading(false);
}
})
}
return (
<div className="main" style={{padding:"0px",border:"none"}}>
<Spin spinning={isSpin}>
@ -132,10 +173,13 @@ export default (props) => {
<Files
history={history}
data={data}
filesData={fRef.current && fRef.current.files}
owner={owner}
projectsId={projectsId}
parentsSha={parents && parents.length > 0 && parents[0].sha}
mergeId={`commits/${sha}`}
/>
{ dropLoading && <p style={{textAlign:"center",color:"#999",padding:"10px"}}>文件加载中...</p>}
</Spin>
</div>
);

View File

@ -513,6 +513,7 @@ class CreateMerge extends Component {
changeCommitFunc={this.changeCommitFunc}
comparesData={comparesData}
pullOwnerLogin={pullOwnerLogin}
branchParams = {getBranchParams(this.props.location.pathname)}
></MergeFooter>
)}
</Spin>

View File

@ -1,61 +1,40 @@
import React ,{useEffect,useState } from 'react';
import { truncateCommitId } from '../common/util';
import { AlignCenter , FlexAJ } from '../Component/layout';
import { Tooltip,Progress } from 'antd';
import { Tooltip , Progress } from 'antd';
import './merge.css';
import './Index.scss';
import FileDrop from './components/fileDrop';
function Files({ data,history,owner,projectsId , parentsSha }){
const [ files , setFiles ] = useState(data && data.files);
const [ copyfileTipTitle, setCopyfileTipTitle] = useState("复制文件路径");
function Files({ data , filesData , history,owner,projectsId , parentsSha , mergeId }){
const [ files , setFiles ] = useState(filesData);
const [ isOpen, setIsOpen] = useState(false);
useEffect(()=>{
if(data){
setFiles(data.files);
if(filesData){
setFiles(filesData);
}
},[data]);
},[filesData]);
useEffect(()=>{
document.addEventListener('click',()=>{setIsOpen(false)})
},[])
function showDown(flag,index,isBin){
if(!isBin){
var lists = files.concat();
lists[index].flag = !flag ? true : false;
lists.splice();
setFiles(lists);
}
}
function copyFileName(fileName){
var copyCont = document.createElement('input');
copyCont.defaultValue = fileName;
document.body.appendChild(copyCont);
copyCont.select(); //
document.execCommand("Copy"); //
copyCont.className = 'copyCont';
copyCont.style.display='none';
setCopyfileTipTitle("复制成功");
}
const folderOpen = (
<div className="folders">
<div className="folderList">
{files && files.map((item, key) => {
return (
<a href={`#value${key}`}>
<FlexAJ className="filesInfo" key={key} onClick={() => {item.flag && showDown(item.flag, key, item.isBin);setIsOpen(false);}}>
<FlexAJ className="filesInfo" key={key} onClick={() => {item.flag && showDown(item.flag, key, item.is_bin);setIsOpen(false);}}>
<AlignCenter>
<i className="iconfont icon-wenjianicon mr4"></i>
<span className="cursor-pointer" data-clipboard-text={item.name}>{item.name}</span>
<span className="cursor-pointer" data-clipboard-text={item.filename}>{item.filename}</span>
</AlignCenter>
<div className="see-file">
<Tooltip placement="top" title={`${item.addition+item.deletion}处更改${item.addition + item.deletion > 0 ? "":""}${item.addition>0?item.addition+"处添加":""}${item.addition>0 && item.deletion>0 ?"和":""}${item.deletion>0?item.deletion+"处删除":""}`}>
<Progress showInfo = {false} strokeColor = "#2DB44D" size="small" percent={item.addition/(item.addition+item.deletion)*100} />
{item.addition >0 && <span className="color-green ml10">+{item.addition}</span>}
{item.deletion >0 && <span className="color-red ml10">-{item.deletion}</span>}
<Tooltip placement="top" title={`${item.additions+item.deletions}处更改${item.additions + item.deletions > 0 ? "":""}${item.additions>0?item.additions+"处添加":""}${item.additions>0 && item.deletions>0 ?"和":""}${item.deletions>0?item.deletions+"处删除":""}`}>
<Progress showInfo = {false} strokeColor = "#2DB44D" size="small" percent={item.additions/(item.additions+item.deletions)*100} />
{item.additions >0 && <span className="color-green ml10">+{item.additions}</span>}
{item.deletions >0 && <span className="color-red ml10">-{item.deletions}</span>}
</Tooltip>
</div>
</FlexAJ>
@ -66,22 +45,17 @@ function Files({ data,history,owner,projectsId , parentsSha }){
</div>
)
function subStrContent(content){
return content ? " " + content.slice(1) :"";
}
return(
<div onClick={(e)=>{e.nativeEvent.stopImmediatePropagation()}}>
<AlignCenter className="color-grey-9" style={{position:'relative'}}>
<div onClick={()=>{setIsOpen(!isOpen)}}>
<i className={`iconfont mr5 ${isOpen? "font-18 icon-sanjiaoxing-down":"font-16 icon-triangle"}`}></i>
<span className="color-grey-6 update-file-count">
共有<span className="color-grey-3"> {data && data.files_count} 个文件 </span>被更改
{ data && data.total_addition ? <span>包括 <span className="color-green">{data && data.total_addition} 次插入</span></span>:"" }
{ data && data.total_addition && data.total_deletion ? " 和 ":""}
{ data && data.total_deletion ? <span className="color-red"> {data && data.total_deletion} 次删除</span>:""}
</span>
</div>
<AlignCenter className="color-grey-9" style={{position:'relative'}} onClick={()=>{setIsOpen(!isOpen)}}>
<div style={{width:20}}><i className={`iconfont mr5 ${isOpen? "font-18 icon-sanjiaoxing-down":"font-16 icon-triangle"}`}></i></div>
<span className="color-grey-6 update-file-count">
共有<span className="color-grey-3"> {data && data.file_nums} 个文件 </span>被更改
{ data && data.total_addition ? <span>包括 <span className="color-green">{data && data.total_addition} 次插入</span></span>:"" }
{ data && data.total_addition && data.total_deletion ? " 和 ":""}
{ data && data.total_deletion ? <span className="color-red"> {data && data.total_deletion} 次删除</span>:""}
</span>
{isOpen && folderOpen}
</AlignCenter>
{
@ -92,57 +66,7 @@ function Files({ data,history,owner,projectsId , parentsSha }){
return(
<div className="files" key={key}>
<a id= {`value${key}`} className="anchorPoint"></a>
<FlexAJ className="filesInfo">
<AlignCenter>
{!item.isBin ? <i className={!item.flag?"iconfont icon-sanjiaoxing-down color-grey-9":"iconfont icon-triangle font-15 color-grey-9"} onClick={()=>showDown(item.flag,key,item.isBin)}></i>:""}
<span className="cursor-pointer" data-clipboard-text={item.name} onClick={()=>showDown(item.flag,key,item.isBin)}>
{ item.isRenamed && item.old_name}
{ item.isRenamed && <i className="iconfont icon-youjiang font-12 color-grey-8 ml5 mr5"></i> }
{item.name}
</span>
<Tooltip
title={copyfileTipTitle}
onVisibleChange={()=>setCopyfileTipTitle("复制文件路径")}
>
<i className="iconfont icon-fuzhiicon ml6" onClick={()=>copyFileName(item.name)}></i>
</Tooltip>
</AlignCenter>
<div className="see-file">
<Tooltip placement="top" title={`${item.addition + item.deletion}处更改${item.addition + item.deletion > 0 ? "":""} ${item.addition > 0 ? item.addition + "处添加" : ""}${item.addition > 0 && item.deletion > 0 ? "和" : ""}${item.deletion > 0 ? item.deletion + "处删除" : ""}`}>
<Progress showInfo = {false} strokeColor = "#2DB44D" size="small" percent={item.addition/(item.addition+item.deletion)*100} />
<span className="ml10">{item.addition+item.deletion}</span>
</Tooltip>
{
!item.isSubmodule &&
<span className="see-file-btn" onClick={()=>{history.push(`/${owner}/${projectsId}${item.isDeleted ? `/commits/${truncateCommitId(parentsSha)}`:`/tree/${truncateCommitId(item.sha)}/${item.name}`}`)}}>查看文件</span>
}
</div>
</FlexAJ>
{
item.sections && item.sections.length >= 1 && !item.flag &&
<div className="filesContent">
{
item.sections.map((i,k)=>{
return(
i.lines && i.lines.length>0 && i.lines.map((item,key)=>{
return(
<div key={k+key} className={(item.type === 2) ? "linesContent add" : item.type === 3 ? "linesContent reduce": item.type===4?"linesContent translate":"linesContent"}>
<span className="lines">
<span>{item.leftIdx && item.leftIdx !=="0" ? item.leftIdx :"" }</span>
<span>{item.rightIdx && item.rightIdx !=="0" ? item.rightIdx :"" }</span>
</span>
<div style={{display:"flex"}}>
<span className="linetype">{item.type===2 ? "+" : item.type===3 ? "-" :""}</span>
<div><span style={{whiteSpace:"pre-wrap"}}>{(item.type===3 || item.type===2) ? subStrContent(item.content) : item.content}</span></div>
</div>
</div>
)
})
)
})
}
</div>
}
<FileDrop item={item} prekey={key} projectsId={projectsId} owner={owner} history={history} parentsSha={parentsSha} mergeId={mergeId}/>
</div>
)
})

View File

@ -117,4 +117,11 @@
width:30px;
text-align: center;
display: inline-block;
}
.diffDesc{
padding:30px 0px;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}

View File

@ -22,14 +22,21 @@ class MergeFooter extends Component {
activeKey: '1',
commitCount: 0,
filesCount: 0,
unitData:undefined,
//
commentsTotalCount: 0,
page:1,
hasMore:true,
dropLoading:false,
limit:100
};
}
componentDidMount() {
this.Init();
// 便
this.props.bindFootRef && this.props.bindFootRef(this);
window.addEventListener("scroll",this.scrollListener);
return ()=>{window.removeEventListener("scroll",this.scrollListener);}
}
componentDidUpdate(prevProps) {
@ -41,6 +48,18 @@ class MergeFooter extends Component {
}
}
scrollListener=()=>{
let scrollHeight = document.documentElement.scrollHeight;
let clientHeight = document.documentElement.clientHeight;
let scrollTop = document.documentElement.scrollTop;
if(Math.ceil(scrollTop+clientHeight) >= scrollHeight-5){
const { match } = this.props;
const { projectsId, owner, mergeId } = match.params;
this.getFile(owner, projectsId, mergeId);
}
}
Init = (isTabChange) => {
const { data, location, match } = this.props;
const { pathname } = location;
@ -60,7 +79,7 @@ class MergeFooter extends Component {
this.setState({
activeKey: activeKey,
commitCount: data && data.commits_count,
filesCount: data && data.files_count,
filesCount:data && data.files_count
});
};
@ -81,7 +100,7 @@ class MergeFooter extends Component {
if (result) {
this.setState({
commitsData: result.data.commits,
commitCount: result.data.commits_count,
commitCount: result.data.commits_count
});
}
this.setState({ isSpin: false });
@ -91,29 +110,38 @@ class MergeFooter extends Component {
});
};
getFile = (owner, projectsId, mergeId) => {
this.setState({ isSpin: true });
const url = `/${owner}/${projectsId}/pulls/${mergeId}/files.json`;
axios
.get(url)
.then((result) => {
if (result) {
this.setState({
filesData: result.data,
filesCount: result.data.files_count,
});
}
this.setState({ isSpin: false });
})
.catch((error) => {
this.setState({ isSpin: false });
});
getFile = async(owner, projectsId, mergeId) => {
const { page,limit , hasMore , dropLoading , filesData } = this.state;
if(!hasMore || dropLoading){
return;
}
this.setState({ isSpin: page === 1 , dropLoading:page > 1 });
const url = `/v1/${owner}/${projectsId}/pulls/${mergeId}/files.json`;
await axios.get(url,{
params:{page,limit}
}).then((result) => {
if (result) {
let datas = result.data;
let files = page === 1 ? datas.files : filesData.concat(datas.files);
this.setState({
unitData:datas,
filesData: files,
hasMore:datas.files && datas.files.length === limit,
page:page+1
});
}
this.setState({ dropLoading:false,isSpin: false });
})
.catch((error) => {
this.setState({ isSpin: false });
});
};
render() {
const { projectsId, owner, mergeId } = this.props.match.params;
const { order_id, data = {} } = this.props;
const { order_id, data = {} , pullOwnerLogin } = this.props;
const {
isSpin,
activeKey,
@ -121,6 +149,8 @@ class MergeFooter extends Component {
commitCount,
filesData,
commitsData = [],
unitData,
dropLoading
} = this.state;
// Comment0
@ -128,7 +158,6 @@ class MergeFooter extends Component {
this.state.commentsTotalCount || data.comments_total_count || 0,
10
);
return (
<div className="main mergeRequest" style={{ paddingTop: '0px' }}>
<Spin spinning={isSpin}>
@ -193,12 +222,17 @@ class MergeFooter extends Component {
}
key="3"
>
<Files
{...this.props}
data={filesData}
projectsId={projectsId}
owner={owner}
/>
<div>
<Files
{...this.props}
data={unitData}
filesData={filesData}
projectsId={projectsId}
owner={owner}
mergeId={`pulls/${mergeId}`}
/>
{ dropLoading && <p style={{textAlign:"center",color:"#999",padding:"10px"}}>文件加载中...</p>}
</div>
</TabPane>
)}
</Tabs>

View File

@ -407,7 +407,7 @@ class MessageCount extends Component {
{
(data.pull_request.fork_project_user || data.issue.author_name!=="已注销") ?
<Link
to={`/${data.pull_request.is_original ? data.pull_request.fork_project_user : data.issue.author_name}/${data.pull_request.is_original?data.project_identifier:projectsId}/tree/${turnbar(data.pull_request && data.pull_request.head)}`}
to={`/${data.pull_request.is_original ? data.pull_request.fork_project_user : data.issue.project_author}/${data.pull_request.is_original?data.project_identifier:projectsId}/tree/${turnbar(data.pull_request && data.pull_request.head)}`}
className="ver-middle task-hide" style={{maxWidth:"300px"}} title={`${data.pull_request.fork_project_user}: ${data.pull_request && data.pull_request.head}`}
>
{data.pull_request && (data.pull_request.is_original ? data.pull_request.fork_project_user:data.issue.author_name)}: {data.pull_request && data.pull_request.head}
@ -652,7 +652,7 @@ class MessageCount extends Component {
{...this.props}
{...this.state}
bindFootRef={this.bindFootRef}
pullOwnerLogin={data.pull_request && data.pull_request.fork_project_user}
pullOwnerLogin={data.pull_request && (data.pull_request.is_original ? data.pull_request.fork_project_user:data.issue.project_author)}
></MergeLinkFooter>
</div>
) : (

View File

@ -281,7 +281,6 @@ class NewMerge extends Component {
id,
comparesData
} = this.state;
const renderBrances = (list, type) => {
if (list && list.length > 0) {
return list.map((item, key) => {
@ -313,7 +312,9 @@ class NewMerge extends Component {
return <div dangerouslySetInnerHTML={{ __html: html }}></div>;
};
let { project } = this.props;
// 源仓库所有者login
const pullOwnerLogin = projects_names && projects_names.filter(item=>{return item.id === id})[0].project_user_login;
console.log("-------------",pullOwnerLogin,projects_names);
return (
<div>
<div className="main">

View File

@ -4,7 +4,6 @@ import axios from "axios";
import "../Order/order.scss";
import "./merge.css";
import MergeForm from "./merge_form";
import MergeFooter from "./merge_footer";
const Option = Select.Option;
class UpdateMerge extends Component {
constructor(props) {

View File

@ -0,0 +1,117 @@
import React ,{useEffect,useState } from 'react';
import { truncateCommitId } from '../../common/util';
import { AlignCenter , FlexAJ } from '../../Component/layout';
import { Tooltip , Progress , Spin } from 'antd';
import "../Index.scss";
import axios from 'axios';
function FileDrop({prekey,item,projectsId,owner , history , mergeId,parentsSha}){
const [ copyfileTipTitle, setCopyfileTipTitle] = useState("复制文件路径");
const [ sections, setSections ] = useState(undefined);
const [ show, setShow ] = useState(true);
const [ isSpin, setIsSpin ] = useState(false);
useEffect(()=>{
if(prekey < 3 && item && item.filename){
showDown(item.filename);
}
},[prekey,item])
function copyFileName(fileName){
var copyCont = document.createElement('input');
copyCont.defaultValue = fileName;
document.body.appendChild(copyCont);
copyCont.select(); //
document.execCommand("Copy"); //
copyCont.className = 'copyCont';
copyCont.style.display='none';
setCopyfileTipTitle("复制成功");
}
function subStrContent(content){
return content ? " " + content.slice(1) :"";
}
function showDown(filename){
if(!sections){
setIsSpin(true);
const url = `/v1/${owner}/${projectsId}/${mergeId}/files.json`;
axios.get(url,{
params:{filepath:filename}
}).then(res=>{
if(res){
let files = res.data && res.data.files && res.data.files.length>0 && res.data.files[0];
setSections(files.sections);
setShow(true);
setIsSpin(false);
}
})
}else{
setShow(!show);
}
}
return(
<div key={prekey}>
<Spin spinning={isSpin}>
<FlexAJ className="filesInfo">
<AlignCenter onClick={()=>setShow(!show)} style={{cursor:!item.is_bin && item.changes !== 0?"pointer":"default"}}>
{(!item.is_bin && item.changes !== 0) ? <div style={{width:20}}><i className={show ?"iconfont icon-sanjiaoxing-down color-grey-9":"iconfont icon-triangle font-15 color-grey-9"}></i></div>:""}
<span className="cursor-pointer" data-clipboard-text={item.name}>
{ item.is_renamed && item.old_name}
{ item.is_renamed && <i className="iconfont icon-youjiang font-12 color-grey-8 ml5 mr5"></i> }
{item.filename}
</span>
<Tooltip
title={copyfileTipTitle}
onVisibleChange={()=>setCopyfileTipTitle("复制文件路径")}
>
<i className="iconfont icon-fuzhiicon ml6" onClick={(e)=>{e.stopPropagation();copyFileName(item.filename);}}></i>
</Tooltip>
</AlignCenter>
<div className="see-file">
<Tooltip placement="top" title={`${item.additions + item.deletions}处更改${item.additions + item.deletions > 0 ? "":""} ${item.additions > 0 ? item.additions + "处添加" : ""}${item.additions > 0 && item.deletions > 0 ? "和" : ""}${item.deletions > 0 ? item.deletions + "处删除" : ""}`}>
<Progress showInfo = {false} strokeColor = "#2DB44D" size="small" percent={item.additions/(item.additions+item.deletions)*100} />
<span className="ml10">{item.additions+item.deletions}</span>
</Tooltip>
{
!item.is_submodule &&
<span className="see-file-btn" onClick={()=>{history.push(`/${owner}/${projectsId}${item.is_deleted ? `/commits/${truncateCommitId(parentsSha)}`:`/tree/${truncateCommitId(item.sha)}/${item.filename}`}`)}}>查看文件</span>
}
</div>
</FlexAJ>
{
(!item.is_bin && item.changes !== 0) && show &&
<div className="filesContent">
{
(sections && sections.length > 0) ? sections.map((i,k)=>{
return(
i.lines && i.lines.length>0 && i.lines.map((item,keys)=>{
return(
<div key={k+keys} className={(item.type === 2) ? "linesContent add" : item.type === 3 ? "linesContent reduce": item.type===4?"linesContent translate":"linesContent"}>
<span className="lines">
<span>{item.left_index && item.left_index !=="0" ? item.left_index :"" }</span>
<span>{item.right_index && item.right_index !=="0" ? item.right_index :"" }</span>
</span>
<div style={{display:"flex"}}>
<span className="linetype">{item.type===2 ? "+" : item.type===3 ? "-" :""}</span>
<div>
<span style={{whiteSpace:"pre-wrap"}}>{(item.type===3 || item.type===2) ? subStrContent(item.content) : item.content}</span>
</div>
</div>
</div>
)
})
)
})
:
<div className='diffDesc'>
<a onClick={()=>showDown(item.filename)} className='color-blue'>加载差异</a>差异被折叠
</div>
}
</div>
}
</Spin>
</div>
)
}
export default FileDrop;

View File

@ -198,6 +198,12 @@ form .ant-cascader-picker, form .ant-select {
.linesContent .lines > span:first-child{
margin-right: 0px;
}
.linesContent .lines,.linesContent .linetype,.no-select,.filesInfo{
-webkit-user-select: none; /* Safari */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* IE/Edge */
user-select: none; /* 标准语法 */
}
.linesContent .lines > span{
width: 50%;
text-align: right;

View File

@ -1,10 +1,12 @@
import React, { Component } from 'react';
import { Tabs } from 'antd';
import { Tabs , Spin } from 'antd';
import Commits from './Commits';
import Files from './Files';
import { returnbar , turnbar } from 'educoder';
import '../Order/order.scss';
import './merge.css';
import axios from 'axios';
const { TabPane } = Tabs;
@ -13,9 +15,30 @@ class MergeFooter extends Component {
super(props);
this.state = {
activeKey: '1',
diff:undefined,
filesData: undefined,
page:1,
hasMore:true,
dropLoading:false,
limit:100
};
}
componentDidMount(){
this.getFilesInfo();
window.addEventListener("scroll",this.scrollListener);
return ()=>{window.removeEventListener("scroll",this.scrollListener);}
}
scrollListener=()=>{
let scrollHeight = document.documentElement.scrollHeight;
let clientHeight = document.documentElement.clientHeight;
let scrollTop = document.documentElement.scrollTop;
if(Math.ceil(scrollTop+clientHeight) >= scrollHeight-5){
this.getFilesInfo();
}
}
changeTab = (index) => {
this.setState({
activeKey: index,
@ -27,11 +50,58 @@ class MergeFooter extends Component {
changeCommitFunc&& changeCommitFunc(page);
}
componentDidUpdate(prevProps) {
// 解决切换tab后浏览器回退不刷新的问题、点击tab后url变化但tab未切换的问题
const newPathname = this.props.location.pathname;
const prevPathname = prevProps.location.pathname;
if (newPathname !== prevPathname) {
this.getFilesInfo();
}
}
getFilesInfo=()=>{
const { hasMore , dropLoading , filesData , page , limit } = this.state;
if(!hasMore || dropLoading){
return;
}
this.setState({dropLoading:page>1})
const { branchParams = {} } = this.props;
const { mergeOwner, projectId } = branchParams;
let url = `/v1/${mergeOwner}/${projectId}/${this.getUrl()}/files.json`;
axios.get(url,{
params:{page,limit}
}).then(res=>{
if(res){
let datas = res.data;
let files = page === 1 ? datas.files : filesData.concat(datas.files);
this.setState({
diff:res.data ,
filesData: files,
hasMore:datas.files && datas.files.length === limit,
dropLoading:false,page:page+1
})
}
})
}
getUrl = ()=>{
const { branchParams = {} } = this.props;
const { pullOwner, pullBranch, mergeOwner, mergeBranch, projectId , pullIdentity } = branchParams;
let url = `compare`;
if (mergeOwner === pullOwner) {
url += `/${Base64.encode(returnbar(pullBranch))}...${Base64.encode(returnbar(mergeBranch))}`;
} else {
url += `/${Base64.encode(returnbar(mergeBranch))}...${pullOwner}/${pullIdentity || projectId}:${Base64.encode(returnbar(pullBranch))}`;
}
return url;
}
render() {
const { projectsId, owner } = this.props.match.params;
const { comparesData = {} ,limit } = this.props;
const { commits, diff, commits_count } = comparesData;
const { activeKey } = this.state;
const { comparesData = {} ,limit ,branchParams } = this.props;
const { commits,commits_count } = comparesData;
const { activeKey , diff , filesData , dropLoading } = this.state;
return (commits && commits.length === 0) || !diff ? (
''
@ -71,19 +141,24 @@ class MergeFooter extends Component {
tab={
<span>
<span className="font-16">文件</span>
{diff.files_count > 0 && (
<span className="tabNum">{diff.files_count}</span>
{diff.file_nums > 0 && (
<span className="tabNum">{diff.file_nums}</span>
)}
</span>
}
key="3"
>
<Files
{...this.props}
data={diff}
projectsId={projectsId}
owner={owner}
/>
<div>
<Files
{...this.props}
data={diff}
filesData={filesData}
projectsId={projectsId}
owner={owner}
mergeId={this.getUrl()}
/>
{ dropLoading && <p style={{textAlign:"center",color:"#999",padding:"10px"}}>文件加载中...</p>}
</div>
</TabPane>
)}
</Tabs>

View File

@ -3,6 +3,7 @@ import { Input , Form , Select , Checkbox , Button , Spin , AutoComplete, Modal
import { Base64 } from 'js-base64';
import { AlignCenter } from '../Component/layout';
import { withTranslation } from 'react-i18next';
import DorpChooseMenus from '../Component/licenseModal/dorpChooseMenus';
import '../css/index.scss';
import './new.scss'
@ -76,6 +77,8 @@ class Index extends Component {
this.getOwner();
//判断是否为删除新建项目失败后返回,并执行对应逻辑
this.isDeleteProjectBack();
// 获取开源许可证
this.getLicenses();
}
componentDidUpdate=(prevPros)=>{
this.updateDocumentTitle();
@ -123,7 +126,7 @@ class Index extends Component {
const url = `/project_categories.json`
axios.get(url).then((result) => {
if (result) {
this.setOptionsList(result.data.project_categories, 'project_category');
// this.setOptionsList(result.data.project_categories, 'project_category');
this.setState({
CategoryList: result.data.project_categories
})
@ -135,7 +138,7 @@ class Index extends Component {
const url = `/project_languages.json`
axios.get(url).then((result) => {
if (result) {
this.setOptionsList(result.data.project_languages, 'project_language')
// this.setOptionsList(result.data.project_languages, 'project_language')
this.setState({
LanguageList: result.data.project_languages
})
@ -147,7 +150,7 @@ class Index extends Component {
const url = `/ignores.json`
axios.get(url).then((result) => {
if (result) {
this.setOptionsList(result.data.ignores, 'ignore');
// this.setOptionsList(result.data.ignores, 'ignore');
this.setState({
GitignoreList: result.data.ignores
})
@ -159,14 +162,13 @@ class Index extends Component {
const url = `/licenses.json`
axios.get(url).then((result) => {
if (result) {
this.setOptionsList(result.data.licenses, 'license');
let data = result.data.licenses;
this.setState({
LicensesList: result.data.licenses
LicensesList: data
})
}
}).catch((error) => { })
}
isDeleteProjectBack = () => {
let mirror_status = this.props.history.location.mirror_status;
if (mirror_status === 2 && sessionStorage.newProjectValue) {
@ -264,28 +266,6 @@ class Index extends Component {
})
}
ChangePlatform = (value, e, name, list) => {
this.setOptionsList(list, name, value)
this.setState({
[name + "_id"]: e.key,
[name + "_name"]: value,
})
}
blurCategory = (value, list, name) => {
let filter = list && list.filter(item => item.name === value);
if (!filter || filter.length === 0) {
this.props.form.setFieldsValue({
[name]: undefined
})
this.setState({
[name + "_name"]: undefined,
[name + "_id"]: undefined
})
this.setOptionsList(list, name);
}
}
checkId = (rule, value, callback, list, title) => {
let filter = list.filter(item => item.name === value);
if(!value){
@ -358,14 +338,6 @@ class Index extends Component {
this.getGitignore();
}
}
showLicenseFunc =(e)=>{
const { LicensesList } = this.state;
this.setState({licenseFlag:e.target.checked})
if(e.target.checked && (!LicensesList || (LicensesList && LicensesList.length===0))){
// 获取开源许可证
this.getLicenses();
}
}
blurFunc=(rule, value, callback)=>{
@ -430,6 +402,24 @@ class Index extends Component {
}
}
showLicenseFunc =(e)=>{
const { LicensesList } = this.state;
this.setState({
licenseFlag:e.target.checked
})
}
getValue=(values,typeName)=>{
const {id,name} = values
if(id){
this.setState({
[typeName + "_id"]: id
})
this.props.form.setFieldsValue({
[`${typeName}`]:name
})
}
}
render() {
const { getFieldDecorator } = this.props.form;
// 项目类型deposit-托管项目mirror-镜像项目
@ -465,7 +455,7 @@ class Index extends Component {
} = this.state;
return (
<div className="main back-white" style={{padding:"0px",border:"none"}}>
<div className="newPanel">
<div className="newPanel" id="newPanel">
<div className="newPanel_title">{projectsType && projectsType === "mirror" ? "导入" : "新建"}项目</div>
<Spin spinning={isSpin}>
<Form>
@ -655,14 +645,7 @@ class Index extends Component {
validator: (rule, value, callback) => this.checkId(rule, value, callback, GitignoreList, 'gitignore')
}],
})(
<AutoComplete
placeholder="请选择gitignore用来定义哪些文件不需要添加到版本管理中"
onChange={(value, e) => this.ChangePlatform(value, e, 'ignore', GitignoreList)}
className="plateAutoComplete"
onBlur={(value) => this.blurCategory(value, GitignoreList, "ignore")}
>
{ignore_list}
</AutoComplete>
<DorpChooseMenus idname={"newPanel"} name={"请选择gitignore用来定义哪些文件不需要添加到版本管理中"} list={GitignoreList} getValue={(value)=>this.getValue(value,"ignore")}/>
)}
</Form.Item>
}
@ -674,7 +657,7 @@ class Index extends Component {
)}
</Form.Item>
{ licenseFlag &&
<Form.Item>
<Form.Item className="privatePart">
{getFieldDecorator('license', {
rules: [{
required: licenseFlag, message: '请选择开源许可证'
@ -682,14 +665,7 @@ class Index extends Component {
validator: (rule, value, callback) => this.checkId(rule, value, callback, LicensesList, '开源许可证')
}],
})(
<AutoComplete
placeholder="请选择开源许可证"
onChange={(value, e) => this.ChangePlatform(value, e, 'license', LicensesList)}
className="plateAutoComplete"
onBlur={(value) => this.blurCategory(value, LicensesList, "license")}
>
{license_list}
</AutoComplete>
<DorpChooseMenus idname={"newPanel"} name={"请选择开源许可证"} list={LicensesList} getValue={(value)=>this.getValue(value,"license")}/>
)}
</Form.Item>
}
@ -721,9 +697,7 @@ class Index extends Component {
)}
</Form.Item>
{categoreFlag &&
<Form.Item
className="privatePart"
>
<Form.Item className="privatePart">
{getFieldDecorator('project_category', {
rules: [{
required: categoreFlag, message: '请选择项目类别',
@ -731,20 +705,11 @@ class Index extends Component {
validator: (rule, value, callback) => this.checkId(rule, value, callback, CategoryList, '项目类别')
}],
})(
<AutoComplete
placeholder="请选择项目类别"
onChange={(value, e) => this.ChangePlatform(value, e, 'project_category', CategoryList)}
className="plateAutoComplete"
onBlur={(value) => this.blurCategory(value, CategoryList, "project_category")}
>
{project_category_list}
</AutoComplete>
<DorpChooseMenus idname={"newPanel"} name={"请选择项目类别"} list={CategoryList} getValue={(value)=>this.getValue(value,"project_category")}/>
)}
</Form.Item>
}
<Form.Item
className="privatePart"
>
<Form.Item className="privatePart">
{getFieldDecorator('languageFlag')(
<Checkbox checked={languageFlag} onChange={(e)=>this.showLanguageFunc(e)}>项目语言</Checkbox>
)}
@ -758,14 +723,7 @@ class Index extends Component {
validator: (rule, value, callback) => this.checkId(rule, value, callback, LanguageList, '项目语言')
}],
})(
<AutoComplete
placeholder="请选择项目语言"
onChange={(value, e) => this.ChangePlatform(value, e, 'project_language', LanguageList)}
className="plateAutoComplete"
onBlur={(value) => this.blurCategory(value, LanguageList, "project_language")}
>
{project_language_list}
</AutoComplete>
<DorpChooseMenus idname={"newPanel"} name={"请选择项目语言"} list={LanguageList} getValue={(value)=>this.getValue(value,"project_language")}/>
)}
</Form.Item>
}

View File

@ -1,6 +1,7 @@
.newPanel{
border:1px solid #eaeaea;
border-radius: 4px;
position: relative;
}
.newPanel_title{
height: 3rem;

View File

@ -2,18 +2,25 @@ import React, { useEffect , useState } from "react";
import Meditor from "./m_editor";
import "./index.css";
import { returnbar } from 'educoder';
import { Select } from "antd";
import FileLanguage from '../Component/FileLanguage';
import FP from '../Component/FileName/index';
import axios from 'axios';
const { Option } = Select;
function Index(props){
const [ filename , setFileName ] = useState("");
const [ language , setLanguage ] = useState("");
const [ checkName , setCheckName ] = useState(false);
const [ licenseList ,setLicenseList ] = useState([])
const [ licenseId ,setLicenseId ] = useState([])
const [ content ,setContent ] = useState("");
const [ chooseName ,setChooseName ] = useState("");
const [ empty , setEmpty ] = useState(false);
const {projectDetail} = props;
const { pathname } = props && props.location;
const { pathname , state } = props && props.location;
const urlroot = pathname.split("newfile/")[1];
const file_path = urlroot ? `/${urlroot}/` :"/";
@ -21,6 +28,55 @@ function Index(props){
updateDocumentTitle();
},[projectDetail])
useEffect(()=>{
if(state){
getLicenseList();
setLicenseId(parseInt(state))
if(parseInt(state) !== -1){
getLinceseById(state);
}
}
},[state])
useEffect(()=>{
if(licenseList && licenseList.length>0 && licenseId){
let filters = licenseList.filter(i=>i.id === licenseId);
let name = filters && filters.length === 1 ? filters[0].name : -1
setChooseName(name);
}
},[licenseId,licenseList.splice()])
// 根据license id查询详情
const getLinceseById=(id)=>{
let url =`/licenses/${id}.json`;
axios.get(url).then(res=>{
if(res && res.data){
setContent(res.data.content)
}
}).catch(error=>{})
}
// 获取license列表
const getLicenseList=()=>{
let url =`/licenses.json`;
axios.get(url).then(res=>{
if(res && res.data){
setLicenseList(res.data.licenses)
}
}).catch(error=>{})
}
// 选择许可证
const selectFunc=(e,item)=>{
const { key } = item;
setLicenseId(parseInt(key));
props.history.push({state:key})
if(key && parseInt(key) !== -1){
getLinceseById(key);
}else{
setContent(undefined)
}
}
// 更新网页标题
function updateDocumentTitle(){
@ -57,11 +113,34 @@ function Index(props){
addonBefore={`${ projectDetail && projectDetail.identifier }${returnbar(file_path)}`}
onChangeFile={onChangeFile}
empty={empty}
name={(Boolean(state) ? "LICENSE" :"")}
onEmpty={(e)=>setEmpty(e)}
readOnly={Boolean(state)}
/>
</div>
<div className="text-right">
<FileLanguage language={language} select_language={select_language}></FileLanguage>
{
(licenseList && licenseList.length>0 && state) ?
<Select
style={{width:200}}
showSearch
placeholder="请选择许可证"
filterOption={(input, option) =>
option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
onSelect={selectFunc}
value={chooseName}
>
<Option value={-1} key={-1}>创建自定义许可证</Option>
{
licenseList.map((i,k)=>{
return <Option value={i.name} key={i.id}>{i.name}</Option>
})
}
</Select>
:
<FileLanguage language={language} select_language={select_language}></FileLanguage>
}
</div>
</div>
<div className="editorBorder">
@ -69,13 +148,14 @@ function Index(props){
{...props}
filepath={`${file_path}`}
language = {language}
content={""}
content={content}
readOnly={false}
editor_type="new"
descName={filename ? `Add ${filename}`:""}
checkName={checkName}
onEmpty={(e)=>setEmpty(e)}
empty={empty}
state={state}
filename={filename}
></Meditor>
</div>

View File

@ -69,7 +69,7 @@ class UserSubmitComponent extends Component {
// 提交变更
subMitFrom = () => {
const { filepath, content, editor_type , checkName , onEmpty , checkContent} = this.props;
const { filepath, content, editor_type , checkName , onEmpty , checkContent , state , projectDetail} = this.props;
const { branch, projectsId , owner } = this.props.match.params;
const { submitType, filename , empty } = this.state;
if(editor_type==="upload" && checkContent && !content){
@ -99,13 +99,25 @@ class UserSubmitComponent extends Component {
this.setState({ isSpin: false });
if (result.data && result.data.name) {
this.props.showNotification("文件新建成功!");
const { getDetail } = this.props;
if(submitType === "1"){
const { getDetail } = this.props;
getDetail && getDetail();
}
window.scrollTo(0,0);
let url = `/${owner}/${projectsId}${values.branchname ? `/tree/${turnbar(values.branchname)}`: (branch ? `/tree/${turnbar(branch)}` : "")}`;
this.props.history.push(url);
// 存在state是代表创建自定义许可证
if(state){
const url = `/${owner}/${projectsId}.json`;
const { name , identifier } = projectDetail;
axios.put(url,{name,identifier,license_id:parseInt(state)===-1?0:state }).then(res=>{
getDetail && getDetail();
window.scrollTo(0,0);
let url = `/${owner}/${projectsId}${values.branchname ? `/tree/${turnbar(values.branchname)}`: (branch ? `/tree/${turnbar(branch)}` : "")}`;
this.props.history.push(url);
}).catch(error=>{})
}else{
window.scrollTo(0,0);
let url = `/${owner}/${projectsId}${values.branchname ? `/tree/${turnbar(values.branchname)}`: (branch ? `/tree/${turnbar(branch)}` : "")}`;
this.props.history.push(url);
}
}
})
.catch((error) => {

View File

@ -6,7 +6,6 @@ import 'codemirror/mode/clike/clike';
import 'codemirror/mode/css/css';
import UserSubmitComponent from "./UserSubmitComponent";
import CloudIDE from "./cloudIDE";
import { Base64 } from "js-base64";
import "./index.css";
import "./editor.css";
@ -35,7 +34,7 @@ class m_editor extends Component {
render() {
const { editorValue, changeValue } = this.state;
const { readOnly, editorType,onEmpty,filename,empty, currentBranch, descName, checkName, detail, match: { params },filepath, ideTheme , md } = this.props;
const { readOnly, editorType,onEmpty,filename,empty, currentBranch, descName, checkName, detail, match: { params },filepath, state , projectDetail} = this.props;
const editor_options = {
lineNumbers: "on",
lineWrapping: true, //强制换行
@ -87,6 +86,8 @@ class m_editor extends Component {
empty={empty}
filename={filename}
changeValueFlag={this.props.content !== changeValue}
state={state}
projectDetail={projectDetail}
></UserSubmitComponent>
</div>
)}

View File

@ -23,6 +23,15 @@ const Pages = Loadable({
loader: () => import('./pages'),
loading: Loading,
})
//
const Measurement = Loadable({
loader: () => import('./measurement'),
loading: Loading,
})
const MeasurementDetail = Loadable({
loader: () => import('./measurement/dimension'),
loading: Loading,
})
function ServerIndex(props){
const { projectDetail } = props;
const { owner , projectsId } = props.match.params;
@ -37,11 +46,21 @@ function ServerIndex(props){
return(
<div className="panels">
<Switch {...props}>
<Route path="/:owner/:projectsId/service/measurement/:type"
render={
(p) => (<MeasurementDetail {...props} {...p}/>)
}
></Route>
<Route path="/:owner/:projectsId/service/reposyncer"
render={
() => (<Reposyncer {...props}/>)
}
></Route>
<Route path="/:owner/:projectsId/service/measurement"
render={
(p) => (<Measurement {...props} {...p}/>)
}
></Route>
<Route path="/:owner/:projectsId/service/codeanalysis"
render={
() => (<Data {...props}/>)

View File

@ -8,6 +8,7 @@ function Main(props){
const [ has_trace_user , setHas_trace_user ] = useState(false);
const { current_user , resetUserInfo, projectDetail, showNotification, isDeveloper, isManager, isReporter } = props;
const privateStatus = projectDetail && projectDetail.private;
useEffect(()=>{
//
@ -79,6 +80,19 @@ function Main(props){
<Link to={`/${owner}/${projectsId}/service/pages`} className="btnhover">查看详情</Link>
</span>
</li>}
{
!privateStatus &&
<li>
<span className="servername">
<img src={require('./img/logo4.png')} alt=""/>
<a onClick={openDetail} style={{marginLeft:"-8px"}}>开源软件健康度量服务</a>
</span>
<p className="task-hide-2 serverdesc">支持项目健康度量分析一键生成协助维护项目健康生态</p>
<span className="serverbtn">
<Link to={`/${owner}/${projectsId}/service/measurement`} className="btnhover">查看详情</Link>
</span>
</li>
}
</ul>
</div>
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 633 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 451 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -461,4 +461,35 @@
.timeBox{
color: rgba(70, 106, 255, 1)
}
}
.nullStoreBox{
background-color:#fafcff;
border-radius:4px 4px 0px 0px;
text-align: center;
color:#333333;
padding-bottom: 65px;
.loBox{width: 68px;}
.introBox{
color:#666666;
width: 57%;
margin: 15px auto;
word-break: break-all;
}
.borBox{
width: 45%;
margin: 0 auto 20px;
border-bottom: 1px solid rgba(90, 117, 193, 0.23);
}
}
.headBox{
height:60px;
line-height: 60px;
background-color:#fafcff;
border:1px solid rgba(42, 97, 255, 0.23);
border-radius:3px 3px 0px 0px;
color:#333333;
position: relative;
em{
font-family: YouSheBiaoTiHei;
}
}

View File

@ -0,0 +1,169 @@
import React, { useEffect, useState } from 'react';
import StackImg from './stackImg';
import Pie from './pie';
import { Select } from 'antd';
import Nodata from '../../../Nodata';
function CompanyData({ data }){
const [ keys , setKeys ] = useState([]);
const [ values , setValues ] = useState([]);
const [ userDrop , setUserDrop ] = useState([]);
const [ userValue , setUserValue ] = useState([]);
const [ c1keys , setC1Keys ] = useState([]);
const [ c1values , setC1Values ] = useState([]);
const [ c1userDrop , setC1UserDrop ] = useState([]);
const [ c1userValue , setC1UserValue ] = useState([]);
const [ c3keys , setC3Keys ] = useState([]);
const [ c3values , setC3Values ] = useState([]);
const [ c3userDrop , setC3UserDrop ] = useState([]);
const [ c3userValue , setC3UserValue ] = useState([]);
const [ pieValues , setPieValues ] = useState([]);
const { c1 ,c2,c3,c4 } = data;
useEffect(()=>{
if(c1 && c1.length > 0){
const values = Array.from(new Set(c1.map(i => i.Module || "")));
setC1UserDrop(values);
setC1UserValue(values[0]);
}
},[c1])
useEffect(()=>{
if(c1userValue && c1 && c1.length > 0){
let fil = c1.filter(i=> i.Module === c1userValue);
const companyArray = Array.from(new Set(fil.map(i => i.Company)));
let monthList = fil.map(obj => obj.Month);
let valueList = fil.map(obj => obj['Weight Ratio']);
setC1Keys(monthList);
if(companyArray && companyArray.length>0 && fil.length>0){
const arr = companyArray.map(item=>{
let v = fil.filter(i=> i.Company == item);
let valueList = v.map(obj => obj['Weight Ratio']);
return {name :item , type: 'line',stack: 'Total',data:valueList}
})
setC1Values(arr);
}else{
setC1Values(valueList);
}
}
},[c1,c1userValue])
useEffect(()=>{
if(c2 && c2.length > 0){
const values = Array.from(new Set(c2.map(i => i.Company || "")));
setUserDrop(values);
setUserValue(values[0]);
}
},[c2])
useEffect(()=>{
if(userValue && c2 && c2.length > 0){
let fil = c2.filter(i=>i.Company === userValue);
let monthList = fil.map(obj => obj.Month);
let valueList = fil.map(obj => obj['Total Weight']);
setKeys(monthList);
setValues(valueList);
}
},[c2,userValue])
useEffect(()=>{
if(c3 && c3.length > 0){
const values = Array.from(new Set(c3.map(i => i.Company || "")));
setC3UserDrop(values);
setC3UserValue(values[0]);
}
},[c3])
useEffect(()=>{
if(c3userValue && c3 && c3.length > 0){
let fil = c3.filter(i=>i.Company === c3userValue);
let monthList = fil.map(obj => obj.Month);
let valueList = fil.map(obj => parseInt(obj['Percentage']));
setC3Keys(monthList);
setC3Values(valueList);
}
},[c3,c3userValue])
useEffect(()=>{
if(c4 && c4.length > 0){
let pieValues = c4.map(i=>{return {name:i['Company'],value:i["Commit Count"]}});
setPieValues(pieValues)
}
},[c4])
return(
(!c1 && !c2 && !c3 && !c4) ? <div>
<div className='dropEchartPanel'>
{
c1userValue && c1userDrop && c1userDrop.length>0 &&
<div style={{textAlign:"left",paddingLeft:20}}>
<span className='mr10'>公司名:</span>
<Select
value={c1userValue}
style={{ width: 220 }}
onChange={(e)=>{setC1UserValue(e);}}
>
{c1userDrop.map(value => {
return <Option key={value}>{value}</Option>
})}
</Select>
</div>
}
<StackImg x={c1keys} values={c1values} title={'每月公司贡献占比'} id={"c1"}/>
</div>
<div className='dropEchartPanel'>
{
userValue && userDrop && userDrop.length>0 &&
<div style={{textAlign:"left",paddingLeft:20}}>
<span className='mr10'>公司名:</span>
<Select
value={userValue}
style={{ width: 220 }}
onChange={(e)=>{setUserValue(e);}}
>
{userDrop.map(value => {
return <Option key={value}>{value}</Option>
})}
</Select>
</div>
}
<StackImg x={keys} values={[{name:"", type: 'line',stack: 'Total',data:values}]} title={'每月公司总贡献'} id={"c2"}/>
</div>
<div className='dropEchartPanel'>
{
c3userValue && c3userDrop && c3userDrop.length>0 &&
<div style={{textAlign:"left",paddingLeft:20}}>
<span className='mr10'>公司名:</span>
<Select
value={c3userValue}
style={{ width: 220 }}
onChange={(e)=>{setC3UserValue(e);}}
>
{c3userDrop.map(value => {
return <Option key={value}>{value}</Option>
})}
</Select>
</div>
}
<StackImg x={c3keys} values={[{name:"", type: 'line',stack: 'Total',data:c3values}]} title={'每月公司贡献最高模块中总贡献'} id={"c3"}/>
</div>
<div className='dropEchartPanel'>
<Pie title={'公司总提交数'} values={pieValues}/>
</div>
</div>
:
<Nodata _html="暂无数据~"/>
)
}
export default CompanyData;

View File

@ -0,0 +1,51 @@
import React, { useEffect, useState } from 'react';
import StackImg from './stackImg';
import { Select } from 'antd';
function DataStack({ data }){
const [ keys , setKeys ] = useState([]);
const [ values , setValues ] = useState([]);
const [ userDrop , setUserDrop ] = useState([]);
const [ userValue , setUserValue ] = useState([]);
useEffect(()=>{
if(data){
let drop = Object.keys(data);
setUserDrop(drop);
setUserValue(drop[0]);
}
},[data])
useEffect(()=>{
if(data && userValue){
let arr = data[userValue];
let ks = arr && Object.keys(arr);
let vs = arr && Object.values(arr);
setKeys(ks);
setValues(vs);
}
},[data,userValue])
return(
<div>
{
userValue && userDrop && userDrop.length>0 &&
<div style={{textAlign:"left",paddingLeft:20}}>
<span className='mr10'>公司名:</span>
<Select
value={userValue}
style={{ width: 220 }}
onChange={(e)=>{setUserValue(e);}}
>
{userDrop.map(value => {
return <Option key={value}>{value}</Option>
})}
</Select>
</div>
}
<StackImg x={keys} values={[{name:"", type: 'line',stack: 'Total',data:values}]} id={"companyNum"}/>
</div>
)
}
export default DataStack;

View File

@ -0,0 +1,54 @@
import React, { useEffect , useState } from 'react';
import { Table } from 'antd';
import { Link } from 'react-router-dom';
import "../index.scss";
import { fieldData , thirdData , subField } from '../config';
function Indicator({owner , projectsId , data , subCardIndex}){
const [ dataSource , setDataSource ] = useState([]);
useEffect(()=>{
if(data && data.length > 0 && subCardIndex){
let name = thirdData[subField[subCardIndex]];
let s = data.filter(i=>name.includes(i.name));
setDataSource(s);
}
},[subCardIndex,data])
const column = [
{
title: "指标名称",
dataIndex: "name",
render:(value)=>{
return(
(value !=="branchNum" && value !== "browseNum") ?
<Link to={`/${owner}/${projectsId}/service/measurement/${value}`} className='color-blue'>{fieldData[value]}</Link>
:
<span>{fieldData[value]}</span>
)
// return fieldData[value]
}
},
{
title: "度量得分",
dataIndex: "InitialScore",
align:"center",
render:(v)=>{
return `${Math.round(v)}`
}
},
{
title: "权重占比",
align:"center",
dataIndex: "Weight",
render:(v)=>{
return `${v ? parseFloat(v*100).toFixed(1) : 0}%`
}
},
]
return(
<Table columns={column} dataSource={dataSource} pagination={false} size='small' className='smalltable'/>
)
}
export default Indicator;

View File

@ -0,0 +1,62 @@
import React, { useEffect, useState } from 'react';
import * as echarts from 'echarts';
// title:
function Pie({values,title}){
useEffect(()=>{
if(values && values.length>0){
Init(values)
}
},[values])
function Init(data){
var huan_val = document.getElementById("Pie");
var myEcharts = echarts.init(huan_val);
let option = {
title: {
text: title || "",
left: 20,
top:20
},
tooltip: {
trigger: 'item'
},
color:['#5871FF',"#FFBE38","#00bf9a"],
legend: {
orient: 'horizontal',
right: 20,
top:20,
formatter: function (name) {
//
var data = option.series[0].data;
for (var i = 0; i < data.length; i++) {
if (data[i].name == name) {
return data[i].value === 0 ? name + ' (0)' : name;
}
}
return name;
}
},
series: [
{
name: '',
type: 'pie',
radius: '50%',
data: data,
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
},
center: ['50%', '60%']
}
]
};
myEcharts.setOption(option);
}
return(
<div id="Pie" style={{height:"300px"}}></div>
)
}
export default Pie;

View File

@ -0,0 +1,82 @@
import React, { useEffect , useState } from 'react';
import * as echarts from 'echarts';
import { fieldData } from '../config';
function Radar({data, subCardIndex}){
const [ values , setValues ] = useState([]);
const [ titles , setTitles ] = useState([]);
useEffect(()=>{
if(data && data.length > 0 && subCardIndex){
let d = [...data];
let v = [];
d = d.map(i=>{
v = v.concat(i.InitialScore)
return {name:fieldData[i.name],max:100}
})
setValues(v);
setTitles(d);
}
},[subCardIndex,data])
useEffect(()=>{
if(titles && values && values.length>0 &&titles.length>0)
Init();
},[values,titles])
function Init(){
var huan_val = document.getElementById("radar");
var myEcharts = echarts.init(huan_val);
let option = {
tooltip: {
trigger: 'item'
},
title: {
text: ''
},
color:['#5871FF',"#FFBE38"],
radar: {
// shape: 'circle',
indicator: titles,
radius:'50%',
// splitLine:{
// lineStyle:{
// color:'#11B0D3',
// opacity: 0.2
// },
// },
// axisLine:{
// lineStyle:{
// color:'#11B0D3',
// opacity: 0.2
// },
// },
// splitArea:{
// areaStyle:{
// color: 'none',
// }
// },
nameGap: 3,
},
series: [
{
name: '',
type: 'radar',
data: [
{
value: values,
name: ''
}
]
}
]
};
myEcharts.setOption(option);
}
return(
<div id="radar" style={{height:"300px",width:"45%"}}></div>
)
}
export default Radar;

View File

@ -0,0 +1,137 @@
import React, { useEffect, useState } from 'react';
import * as echarts from 'echarts';
import { detailByName } from '../config';
import Pie from './pie';
import { Select } from 'antd';
function Stack({name,data , type , pieData}){
const [ obj , setObj ] = useState({});
const [ list , setList ] = useState(data);
const [ userDrop , setUserDrop ] = useState([]);
const [ userValue , setUserValue ] = useState([]);
const [pieEchartData,setPieEchartData] = useState([]);
useEffect(()=>{
if(name){
let obj = detailByName[name.replace(/\s/g, '')];
setObj(obj);
}
},[name])
useEffect(()=>{
if(data && data.length > 0){
if(obj && obj.user_login){
const values = Array.from(new Set(data.map(i => i.user_login || "")));
setUserDrop(values);
setUserValue(values[0]);
}else{
setList(data);
}
}
},[data,obj && obj.user_login])
useEffect(()=>{
if(userValue){
let valueData = data && data.filter(i=>i.user_login === userValue);
setList(valueData);
}
},[userValue])
useEffect(()=>{
if(list && list.length > 0){
let values = [];
const monthList = list.map(obj => obj.month);
values = list.map(obj => parseFloat(obj[type] || obj["count"] || obj['commits_number'] || 0).toFixed(2));
Init(monthList,values);
}
},[list])
function Init(x,values){
var huan_val = document.getElementById("line");
var myEcharts = echarts.init(huan_val);
let option = {
title: {
text: ''
},
tooltip: {
trigger: 'axis'
},
color:['#5871FF',"#FFBE38"],
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
// toolbox: {
// feature: {
// saveAsImage: {}
// }
// },
xAxis: {
type: 'category',
boundaryGap: false,
data: x
},
yAxis: {
type: 'value'
},
series: [
{
name: name,
type: 'line',
stack: 'Total',
data: values
}
]
};
myEcharts.setOption(option);
}
useEffect(()=>{
if(pieData){
let newData = new Array;
for (var i in pieData) {
var json = {};
if (pieData[i] != "") {
json.name = i;
json.value = pieData[i];
newData.push(json);
}
}
setPieEchartData(newData)
}
},[pieData])
return(
<div>
<div className='dropEchartPanel'>
{
obj && obj.user_login && userValue && userDrop && userDrop.length>0 &&
<div style={{textAlign:"left",paddingLeft:20}}>
<span className='mr10'>贡献者:</span>
<Select
value={userValue}
style={{ width: 220 }}
onChange={(e)=>{setUserValue(e);}}
>
{userDrop.map(value => {
return <Option key={value}>{value}</Option>
})}
</Select>
</div>
}
<div id="line" style={{height:"350px"}}></div>
</div>
{
pieData &&
<div className='dropEchartPanel'>
<Pie values={pieEchartData} title="Commit Message质量分布"/>
</div>
}
</div>
)
}
export default Stack;

View File

@ -0,0 +1,58 @@
import React, { useEffect, useState } from 'react';
import * as echarts from 'echarts';
function StackImg({x,values,title,id}){
useEffect(()=>{
if(x && values && x.length>0 && values.length>0 && id){
Init(x,values)
}
},[x,values,id])
function Init(xData,data){
var huan_val = document.getElementById(id);
var myEcharts = echarts.init(huan_val);
let option = {
title: {
text: title || '',
textStyle:{
fontSize: 17 ,
fontWeight: 'normal',
},
left:20,
top:15
},
tooltip: {
trigger: 'axis'
},
color:['#5871FF',"#FFBE38"],
// legend: {
// data: ['Email', 'Union Ads']
// },
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
// toolbox: {
// feature: {
// saveAsImage: {}
// }
// },
xAxis: {
type: 'category',
boundaryGap: false,
data: xData
},
yAxis: {
type: 'value'
},
series: data
};
myEcharts.setOption(option);
}
return(
<div id={id} style={{height:"350px"}}></div>
)
}
export default StackImg;

View File

@ -0,0 +1,83 @@
export const firstData = ['开发活动', '社区协作','项目影响']
export const secondData = {
开发活动:["开发质量","开发效率"],
社区协作:['个体开发者','商业公司'],
项目影响:["项目影响"]
}
export const field={
社区协作:"communityCooperation",
开发活动:"developmentActivity",
项目影响:"influence"
}
export const subField={
开发质量:"developmentQuality",
开发效率:"developmentEfficiency",
个体开发者:"individual",
商业公司:"company",
项目影响:"influence"
}
export const thirdData = {
developmentEfficiency:["PRNum","PRTime","commitNum","issueTime","ProjectActivity"],
developmentQuality:["commitMessageQuality","issueNum"],
individual:["individualLoss","individualNum","longTermIndividualNum"],
company:["companyLead","companyLoss","companyNum","longTermCompanyNum"],
influence:["branchNum","browseNum","forkNum","praiseNum","storeNum"]
}
export const fieldData = {
PRNum:"PR数量",
PRTime:"PR处理间隔",
commitNum:"Commit数量",
issueTime:"Issue处理间隔",
commitMessageQuality:"Commit message质量",
issueNum:"Issue数量",
ProjectActivity:"项目活跃度",
individualLoss:"长期贡献者稳定性",
individualNum:"活跃开发者占比",
longTermIndividualNum:"长期贡献者占比",
companyLead:"公司主导",
companyLoss:"公司贡献者流失预测",
companyNum:"公司贡献者数量",
longTermCompanyNum:"长期公司贡献者数量",
browseNum:"浏览次数",
forkNum:"Fork次数",
praiseNum:"点赞次数",
storeNum:"关注数量",//followNum
branchNum:"分支数量"
}
export const detailByName={
Commitmessage质量:{desc:"可以通过每月新增Poor Message数量和Commit Message质量分布这两个维度对Commit message质量进行度量",radar:true},
Issue数量:{desc:"通过分析项目每月提交的Issue数量对Issue数量指标进行度量"},
Commit数量:{desc:"通过分析项目每月提交的三种Commit数量对Commit数量指标进行度量"},
PR数量:{desc:"通过分析项目每月提交的PR数量对PR数量指标进行度量"},
PR处理间隔:{desc:"通过分析每个月新增PR的平均处理间隔对PR处理间隔(小时)指标进行度量"},
Issue处理间隔:{desc:"通过分析每个月新增Issue的平均处理间隔对Issue处理间隔(小时)指标进行度量"},
活跃开发者占比:{desc:"通过分析每个月活跃的个体贡献者数量对个体贡献者数量指标进行度量",descTitle:"个体贡献者数量"},//个体贡献者数量
项目活跃度:{desc:"不同类型的贡献者每月/周给master分支的贡献次数",month:true,user_login:true,descTitle:"所有个体开发者贡献历史"},//所有个体开发者贡献历史
长期贡献者占比:{desc:"通过分析每个月活跃的长期个体贡献者数量对长期个体贡献者数量指标进行度量",descTitle:"长期个体贡献者数量"},//长期个体贡献者数量
长期贡献者稳定性:{desc:"通过分析具体的即将流失的长期贡献者流失概率对个体贡献者流失预测指标进行度量",user_login:true,bar:true,descTitle:"个体贡献者流失预测"},//个体贡献者流失预测
公司贡献者数量:{desc:"通过分析每月做出了贡献的公司贡献者数量对公司贡献者数量指标进行度量"},
长期公司贡献者数量:{desc:"通过分析每月的长期对项目做出贡献的公司贡献者数量对长期公司贡献者数量指标进行度量"},
公司主导:{desc:"从每月公司贡献占比、每月公司总贡献、每月公司贡献最高模块中总贡献、公司总提交数四个方面对公司主导指标进行度量",radar:true},
公司贡献者流失预测:{desc:"通过分析各个公司的流失概率对公司贡献者流失预测指标进行度量"},
浏览次数:{desc:"通过分析每月项目的浏览次数对浏览次数指标进行度量"},
点赞次数:{desc:"通过分析每月项目的点赞次数对点赞次数指标进行度量"},
关注数量:{desc:"通过分析每月项目的关注数量对关注数量指标进行度量"},
分支数量:{desc:"通过分析每月项目的分支数量对分支数量指标进行度量"},
Fork次数:{desc:"通过分析每月项目的新增Fork数量对Fork数量指标进行度量"},
}
export const scoreComp = (score)=>{
let value = Math.round(score);
return <span className="faYou" style={{color:value >= 80 ? "#466aff" : (value<80 && value>=60) ? "#E18419" :"#F70202"}}>{value}</span>
}
export const colorsField={
社区协作:"#F58400",
开发活动:"#466AFF",
项目影响:"#00BF9A"
}

View File

@ -0,0 +1,152 @@
import React ,{useEffect, useState} from 'react';
import { Breadcrumb , Select } from 'antd';
import { Link } from 'react-router-dom';
import "./index.scss";
import LineStack from './componet/stack';
import DataStack from './componet/dataStack';
import CompanyData from './componet/companyData';
import { detailByName , secondData , thirdData , subField ,fieldData , scoreComp} from './config';
import axios from 'axios';
import Nodata from '../../Nodata';
const dimensionData = {
开发质量: ['Commit message质量', 'Issue数量'],
开发效率: ['PR数量', 'PR处理间隔', 'Commit数量', 'Issue处理间隔','项目活跃度'],
个体开发者:['活跃开发者占比','长期贡献者占比','长期贡献者稳定性'],
商业公司:['公司主导','公司贡献者流失预测','公司贡献者数量','长期公司贡献者数量'],
项目影响:['Fork次数','点赞次数','关注数量']//'','',
};
function Dimension(props){
const { type , owner , projectsId } = props.match.params;
const [ data , setData ] = useState([]);
const [ pieData , setPieData ] = useState([]);
const [ score , setScore ] = useState(0);
const [title, setTitle] = useState('');
const [seconds, setSeconds] = useState([]);
const [sValue, setSValue] = useState('');
const [ dimensions , setDimensions] = useState([]);
const [dValue, setDValue] = useState('');
const findObjKeyByValue = (obj, value) => {
return Object.keys(obj).find(key => obj[key].includes(value));
};
const findKeyByValue = (obj, value) => {
return Object.keys(obj).find(key => obj[key].includes(value));
};
useEffect(()=>{
if(type){
Init(type==="storeNum" ?"followNum":type);
let find = findObjKeyByValue(thirdData,type);
if(find){
let sub = findKeyByValue(subField,find);
setSValue(sub);
}else{
setSValue(seconds[0])
}
setDValue(fieldData[type])
}
},[type])
function Init(t){
const url = `/v1/${owner}/${projectsId}/oss_health_measuring/keyid`;
axios(url,{params:{
key_id:t
}}).then(res=>{
if(!res){
props.history.push(`/${owner}/${projectsId}/service/measurement`)
return;
}
let data = res && res.data;
if(data){
let d = data[t] ? data[t].value : data.value;
let s = data.score !== undefined ? data.score : data[t].score;
setPieData(data.distribution);
setData(d);
if(t==="companyLoss"){
d = d.map(i=>{return { month:i.Company , count : (i["Predicted State"] || 0.0)}});
setData(d);
}else if(t === "companyNum" || t === "longTermCompanyNum" || t==="companyLead"){
d = Object.keys(data.value).length === 0 ?[]:[Object.assign(data.value)];
setData(d);
}
setScore(s);
}
})
}
useEffect(()=>{
if(sValue){
let arr = dimensionData[sValue];
setDimensions(arr);
let find = findObjKeyByValue(secondData,sValue);
setTitle(find);
let subList = secondData[find];
setSeconds(subList);
}
},[sValue])
return(
<div>
<Breadcrumb separator=">">
<Breadcrumb.Item><Link className="color-blue" to={`/${owner}/${projectsId}/service/measurement`}>首页</Link></Breadcrumb.Item>
<Breadcrumb.Item>{title}</Breadcrumb.Item>
</Breadcrumb>
<div className='headBox font-16 pl15 mt20 df pr30 mb30'>
{title}
<span style={{marginLeft:"auto"}}>
<span>度量值<em className='color-blue font-26'>{scoreComp(score)}</em></span>
{/* <span className='ml30'>健康度权重比:<em className='color-blue font-26'>20%</em></span> */}
</span>
</div>
<div className='dropTeam'>
<Select
value={sValue}
style={{ width: 120 }}
onChange={(e)=>{setSValue(e);props.history.push(`/${owner}/${projectsId}/service/measurement/${findObjKeyByValue(fieldData,dimensionData[e][0])}`)}}
border={null}
>
{seconds.map(province => (
<Option key={province}>{province}</Option>
))}
</Select>
<Select
value={dValue}
style={{ width: 190 }}
onChange={(e)=>{setDValue(e);props.history.push(`/${owner}/${projectsId}/service/measurement/${findObjKeyByValue(fieldData,e)}`)}}
border={null}
>
{dimensions.map(province => (
<Option key={province}>{province}</Option>
))}
</Select>
</div>
<div className='dropEcharts'>
<p className='dropName'>{dValue && (detailByName[dValue.replace(/\s/g, '')].descTitle ? detailByName[dValue.replace(/\s/g, '')].descTitle : dValue)}</p>
<p className='dropDesc'>{dValue && detailByName[dValue.replace(/\s/g, '')].desc}</p>
{
data && data.length > 0 ?
<div>
{
type === "companyNum" || type === "longTermCompanyNum" ?
<div className='dropEchartPanel'><DataStack data={data[0]} /></div>
:
type === "companyLead" ?
<CompanyData data={data[0]} />
:
<LineStack name={dValue} data={data} type={type==="storeNum" ?"followNum":type} pieData={pieData}/>
}
</div>
:<Nodata _html={`暂无数据~`}/>
}
</div>
</div>
)
}
export default Dimension;

View File

@ -0,0 +1,260 @@
import React, {useState, useEffect , useRef} from "react";
import './index.scss';
import Logo from '../img/logo4.png';
import { Button , Spin } from 'antd';
import Large from '../img/large.png';
import Middle from '../img/middle.png';
import Small from '../img/small.png';
import team from '../img/team.png';
import person from '../img/person.png';
import Rader from './componet/radar';
import Indicator from './componet/indicator';
import { scoreComp , field , firstData , colorsField, secondData , subField , thirdData } from './config';
import axios from "axios";
import cookie from 'react-cookies';
// import res from './xiuos.json';
function Index(props){
const [ dataLoading , setDataLoading ] = useState(true);
const [cardIndex, setCardIndex] = useState(firstData[0]);
const [ subCardIndex, setSubCardIndex] = useState(1);
const [ loading , setLoading ] = useState(false);
const { owner , projectsId } = props.match.params;
const [ datas , setDatas ] = useState();
const [ scoreNormal , setScoreNormal ] = useState();
const [ score , setScore ] = useState([]);
const [ scoreNum , setScoreNum ] = useState(0);
const [ subMenus , setSubMenus ] = useState([]);
const [ subDatas , setSubDatas ] = useState([]);
const [ fieldDatas , setFieldDatas ] = useState([]);
let interval = undefined;
const count = useRef();
useEffect(()=>{
if(owner && projectsId){
console.log(cookie.load("opensource"));
if(cookie.load("opensource") && cookie.load("opensource")===`${owner}_${projectsId}`){
getReportStart();
}else{
checkData();
}
}
},[owner,projectsId])
//
async function checkData(){
const url = `/v1/${owner}/${projectsId}/oss_health_measuring/can_get`;
axios(url).then(res=>{
if(res && res.data && res.data.statue === "YES"){
Init();
clearInterval(interval);
cookie.remove("opensource");
}else if(res && res.data && res.data.statue === "NO"){
setDataLoading(false);
}
})
}
//
function Init(){
const url = `/v1/${owner}/${projectsId}/oss_health_measuring/keyid`;
axios(url,{params:{
key_id:"score"
}}).then(res=>{
if(res && res.data){
let data = res.data;
setTimeout(()=>{setDataLoading(false)},300);
setDatas(data);
setScoreNormal(data);
getlist(data);
}
})
}
//
function getReportStart(){
setLoading(true);
count.current = 20;
checkData();
interval = setInterval(() => {
if (count.current > 1) {
let old = count.current;
count.current = old - 1;
console.log(count);
checkData();
} else {
//
clearInterval(interval);
setLoading(false);
props.showNotification("当前服务繁忙,请稍后再试");
}
}, 30000)
}
// tab
useEffect(()=>{
if(score && cardIndex){
//
let sub = secondData[cardIndex];
sub && sub.length > 0 && setSubCardIndex(sub[0]);
let subData = score.filter(i=>i.name === cardIndex);
setSubMenus(sub);
//
let value = subData && subData.length > 0 && subData[0];
if(value){
setSubDatas(value);
let name = value.name;
let subname = secondData[name];
let list = subname.map(i=>{
return subField[i]
})
let listData = [];
for(var i =0;i<list.length;i++){
let l = value[list[i]] || value;
let d = thirdData[list[i]];
if(d){
let items = d.map(j=>{
return {name:j,...l[j],InitialScore:l[j] ? Math.round(l[j].InitialScore) : 0};
})
listData = listData.concat(items);
}
}
setFieldDatas(listData);
}
}
},[cardIndex,score])
//
function getlist(values){
let d = firstData.map(i=>{
return {name:i,...values[field[i]],color:colorsField[i]};
})
setScore(d);
let sn = d.reduce((sum, item) => Math.round(sum + Math.round(item.WeightedScore)), 0);
setScoreNum(sn);
}
console.log(subDatas);
return(
<div>
<div class="headBox font-16 pl15">开源软件健康度量服务</div>
{ dataLoading && <Spin><div style={{minHeight:400,width:"100%"}}></div></Spin>}
{
!dataLoading && !datas &&
<div class="nullStoreBox mt25">
<img src={Logo} class="loBox mt50" />
<p class="font-22 mt10">欢迎使用开源软件健康度量服务</p>
<div class="introBox font-15">开源软件健康度量服务是一种代码库分析工具旨在通过深入分析代码库的关键活动数据来评估开源项目的健康状况该服务通过收集和分析代码库中的贡献者提交commit合并请求pullRequest疑修issue等数据指标为项目开发者维护者提供了一个全面的项目健康报告</div>
<div class="borBox"></div>
{!loading && <Button type="primary" onClick={()=>{cookie.save("opensource",`${owner}_${projectsId}`,{ expires: new Date(new Date().getTime() + 24 * 3600 * 1000),path:`/` });getReportStart();}}>开始体验</Button> }
{ loading && <div><Spin /><span className="ml5 color-blue">服务报告生成中...</span></div> }
</div>
}
{
!dataLoading && datas &&
<div className="scorePanel">
{
scoreNormal &&
<div className="scoreMain">
<div className="scoreimg">
<span className="scoreValue">
<img src={scoreNum >= 80 ? Large : scoreNum < 60 ? Small : Middle} alt="" width={"100%"}/>
<div className="s_content">
<span>健康值</span>
<span className="s_num">{scoreComp(scoreNum)}</span>
</div>
</span>
<p className="font-18 fw500 mt10 mb15">本项目开源生态健康度</p>
<p className="s_detail">开源生态健康度由开发活动社区协作项目影响三个总体维度对开源项目的健康状况进行分析并加权求和用于反馈项目的总体健康状况您可点击查询每一个维度的度量指标及可视化图表</p>
</div>
{
score &&
<ul className="scoreProgress">
{
<li>
<span className="index">{Math.round(scoreNum)}</span>
<span className="name">开源生态健康度</span>
<div className="progress">
{
score.map((i,k)=>{
return <p style={{width:`${i.WeightedScore}%`,backgroundColor:`${i.color}`}}></p>
})
}
</div>
<span><span className="color-blue">{Math.round(scoreNum)}</span><span className="ml3 mr3">/</span>100</span>
</li>
}
{
score.map((i,k)=>{
return(
<li>
<span className="index">{Math.round(i.WeightedScore)}</span>
<span className="name">{i.name}</span>
<div className="progress"><span style={{width:`${i.WeightedScore}%`,backgroundColor:`${i.color}`}}></span></div>
<span><span className="color-blue">{Math.round(i.WeightedScore)}</span><span className="ml3 mr3">/</span>{i.Weight*100}</span>
</li>
)
})
}
</ul>
}
</div>
}
<div className="scoreDetail">
<ul className="typeLi">
{
firstData.map((i,k)=>{
return <li className={cardIndex === i ?"active":""} onClick={()=>{setCardIndex(i)}}><span>{k<10 && `0${k+1}`}</span>{i}</li>
})
}
</ul>
{
subDatas &&
<div className="typeContent">
<p className="typeValue fw500">
{cardIndex}总分{scoreComp(subDatas.InitialScore)}<span className="mr40 faYou">/100</span>
{cardIndex}加权得分{scoreComp(subDatas.WeightedScore)}<span className="mr40 faYou">/{subDatas.Weight*100}</span>
</p>
<div className="typeEchart">
<ul className="echartCate">
{
subMenus && subMenus.length > 0 &&
subMenus.map((i,k)=>{
let detail = subDatas[subField[i]];
return(
<li className={`${subCardIndex === i ?"active":""}`} onClick={()=>setSubCardIndex(i)}>
<div className="echartNum">
<span className="fw500 font-16">{i}</span>
<span><span className="font-17">{i}总分</span>{scoreComp(detail ? detail.InitialScore : subDatas.InitialScore)}</span>
<span><span className="font-17">健康度权重比</span><span className="faYou">{(detail ? detail.Weight : subDatas.Weight) *100}%</span></span>
</div>
<img src={team} width={76} alt="" />
</li>
)
})
}
</ul>
<div className="echartData">
<Rader data={fieldDatas} subCardIndex={subCardIndex}/>
<div style={{width:"50%"}}>
<Indicator data={fieldDatas} subCardIndex={subCardIndex} owner={owner} projectsId={projectsId}/>
</div>
</div>
</div>
</div>
}
</div>
</div>
}
</div>
)
}
export default Index;

View File

@ -0,0 +1,325 @@
.scorePanel{
.scoreMain{
display: flex;
padding:35px 0px;
.scoreimg{
width: 47%;
padding-right: 80px;
text-align: center;
.s_detail{
font-weight: 400;
font-size: 15px;
color: #131415;
line-height: 30px;
text-align: left;
}
.scoreValue{
position: relative;
display: block;
width: 270px;
text-align: center;
margin:0px auto;
.s_content{
position: absolute;
width: 100%;
bottom: 0px;
text-align: center;
font-weight: 400;
font-size: 13px;
color: #B2C6D9;
display: flex;
flex-direction: column;
.s_num{
font-family: "YouSheBiaoTiHei";
font-weight: 400;
font-size: 41px;
color: #466AFF;
height: 50px;
line-height: 50px;
}
}
}
}
.scoreProgress{
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
padding:20px 0px;
li{
height: 36px;
width: 100%;
background: #F8F8F8;
padding-right: 28px;
display: flex;
align-items: center;
color: #61708B;
position: relative;
&::before{
width: 0;
height: 0;
border: 26px solid;
border-color: transparent transparent transparent #F8F8F8;
transform: rotate(45deg);
position: absolute;
left: -26px;
content: "";
top: 10px;
}
.index{
width: 56px;
background-image: url(../img/index.png);
background-repeat: no-repeat;
background-size: 100% 100%;
display: block;
text-align: center;
line-height: 36px;
font-family: YouSheBiaoTiHei;
font-weight: 400;
font-size: 16px;
color: #466AFF;
margin-right: 15px;
}
.name{
width: 120px;
margin-right: 14px;
font-weight: 500;
font-size: 16px;
color: #181818;
}
.progress{
width: 330px;
height: 16px;
background: #F3F3F3;
position: relative;
display: flex;
margin-right: 15px;
&>span{
position: absolute;
height: 100%;
left: 0px;
top:0px;
}
&>p{
border-right: 2px solid #fff;
&:last-child{
border:none;
}
}
}
}
}
}
}
.typeLi{
display: flex;
align-items: center;
justify-content: center;
padding:44px 0px;
li{
height: 62px;
background: #F5F6F7;
border-radius: 8px 8px 8px 8px;
border: 1px solid #DBE0E9;
width: 280px;
font-weight: 500;
font-size: 16px;
color: #212F54;
line-height: 60px;
text-align: center;
margin:0px 25px;
cursor: pointer;
span{
font-family: YouSheBiaoTiHei;
font-weight: 400;
font-size: 25px;
margin-right: 15px;
position: relative;
&::after{
content: "";
right: -8px;
bottom: -3px;
width: 19px;
height: 15px;
background-image: url('../img/trans1.png');
background-repeat: no-repeat;
position: absolute;
}
}
&.active{
background-image: url('../img/blue.png');
border:none;
line-height: 62px;
color: #fff;
span{
&::after{
background-image: url('../img/trans.png');
}
}
}
&:hover{
border-color: #6F93D4;
}
}
}
.fw500{
font-weight: 500;
}
.typeContent{
background: rgba(102,102,102,0.03);
border-radius: 0px 0px 0px 0px;
border: 1px solid rgba(70,106,255,0.13);
padding:40px 30px;
.typeValue{
font-size: 17px;
color: #181818;
text-align: center;
margin-bottom: 30px!important;
span{
font-weight: 400;
font-size: 26px;
}
}
.typeEchart{
display: flex;
}
.echartCate{
width: 298px;
margin-right: 30px;
.echartNum{
display: flex;
flex-direction: column;
flex:1;
font-weight: 400;
font-size: 22px;
color: #212F54;
span{
display: flex;
justify-content: space-between;
}
}
li{
background-image: url('../img/back.png');
background-size: 100% 100%;
width: 100%;
height: 170px;
box-shadow: 0px 0px 10px 1px rgba(28,48,175,0.04);
border-radius: 8px 8px 8px 8px;
display: flex;
align-items: center;
padding-left: 30px;
font-weight: 400;
font-size: 15px;
color: #181818;
line-height: 40px;
border: 1px solid #fff;
cursor: pointer;
&.active{
border-color: #466AFF;
position: relative;
&::after{
content: "";
position: absolute;
right: 0px;
top: 0px;
background-image: url("../img/true.png");
width: 56px;
height: 44px;
}
}
&:first-child{
margin-bottom: 30px!important;
}
}
}
.echartData{
background: #FFFFFF;
box-shadow: 0px 0px 10px 1px rgba(28,48,175,0.08);
border-radius: 0px 0px 0px 0px;
flex: 1;
min-height: 370px;
padding:25px;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: space-between;
}
}
.smalltable{
border-color: rgba(65,84,241,0.04)!important;
.ant-table-body{
margin:0px!important;
.ant-table-thead th {
background-color:rgba(65,84,241,0.04)!important;
}
.ant-table-tbody tr td{
background: #F5F6F7;
}
.ant-table-tbody tr:hover{
background: #F5F6FA;
}
}
}
.dropTeam{
background-image: url('../img/blue-large.png');
background-size: 100% 100%;
background-repeat: no-repeat;
width: 330px;
height: 50px;
margin:40px auto;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
.ant-select-selection{
border:none;
color: #fff;
background-color: transparent;
box-shadow: none;
.ant-select-arrow .ant-select-arrow-icon svg{
color: #fff;
}
}
.ant-select{
position: relative;
&:last-child::before{
content: "";
position: absolute;
width: 1px;
height: 14px;
border: 1px solid rgba(255,255,255,0.36);
left: 0px;
top:9px;
}
}
}
.dropEcharts{
background-image: url('../img/echartBack.png');
text-align: center;
padding: 30px;
.dropName{
font-weight: 500;
font-size: 20px;
color: #181818;
}
.dropDesc{
font-weight: 400;
font-size: 15px;
color: #212F54;
width: 75%;
margin:0px auto;
margin-top: 15px;
}
.dropEchartPanel{
background: #FFFFFF;
box-shadow: 0px 0px 10px 1px rgba(28,48,175,0.08);
border-radius: 6px 6px 6px 6px;
padding:25px;
width: 75%;
margin:0px auto;
margin-top: 20px;
}
}
.faYou{
font-family: YouSheBiaoTiHei;
}

File diff suppressed because it is too large Load Diff

View File

@ -30,15 +30,6 @@
border-color:#1a47ff;
}
}
.reposyncerBox .headBox, .storeListBox .headBox{
height:60px;
line-height: 60px;
background-color:#fafcff;
border:1px solid rgba(42, 97, 255, 0.23);
border-radius:3px 3px 0px 0px;
color:#333333;
position: relative;
}
.reposyncerBox{
padding-bottom: 60px;
.helpBox1{
@ -53,24 +44,6 @@
align-items: center;
cursor: default;
}
.nullStoreBox{
background-color:#fafcff;
border-radius:4px 4px 0px 0px;
text-align: center;
color:#333333;
padding-bottom: 65px;
.loBox{width: 68px;}
.introBox{
color:#666666;
width: 57%;
margin: 15px auto;
}
.borBox{
width: 45%;
margin: 0 auto 20px;
border-bottom: 1px solid rgba(90, 117, 193, 0.23);
}
}
.rightContentBox{
display: flex;
.leftNav{

View File

@ -7,6 +7,8 @@ import { Link } from 'react-router-dom';
import axios from "axios";
import "./setting.scss";
import { unInstallMarketBot } from "../../softbot/api";
import DorpChooseMenus from '../Component/licenseModal/dorpChooseMenus';
const { TextArea } = Input;
const { Option } = Select;
@ -46,6 +48,7 @@ class Setting extends Component {
return;
}
}
componentDidMount = () => {
this.updateDocumentTitle();
this.getCategory();
@ -63,18 +66,39 @@ class Setting extends Component {
}
getLanguage = () => {
this.getInfo();
};
getLanguage = (id) => {
const url = `/project_languages.json`;
axios
.get(url)
.then((result) => {
if (result) {
let LanguageList = this.setOptionsList(result.data.project_languages);
this.setState({
LanguageList,
});
axios.get(url).then((result) => {
if (result) {
let data = result.data.project_languages;
this.setState({LanguageList:data});
if(id){
let filters = data.filter(i=>i.id === id);
if(filters && filters.length > 0){
this.props.form.setFieldsValue({project_language_id:filters[0].name})
}
}
})
.catch((error) => {});
}
})
.catch((error) => {});
};
getCategory = (id) => {
const url = `/project_categories.json`;
axios.get(url).then((result) => {
if (result) {
let data = result.data.project_categories;
this.setState({CategoryList:data});
if(id){
let filters = data.filter(i=>i.id === id);
if(filters && filters.length > 0){
this.props.form.setFieldsValue({project_category_id:filters[0].name})
}
}
}
}).catch((error) => {});
};
getInfo = () => {
@ -85,13 +109,15 @@ class Setting extends Component {
.then((result) => {
if (result) {
const { project_units } = this.state;
const { project_name ,project_description , project_identifier } = result.data;
let units = result.data.project_units;
units.push(...project_units);
this.props.form.setFieldsValue({
...result.data,
project_name ,project_description , private:result.data.private , project_identifier ,
project_units:units
});
this.getCategory(result.data.project_category_id);
this.getLanguage(result.data.project_language_id);
this.setState({
projectName:result.data.project_name,
private_check: result.data.private,
@ -108,23 +134,6 @@ class Setting extends Component {
});
};
getCategory = () => {
const url = `/project_categories.json`;
axios
.get(url)
.then((result) => {
if (result) {
let CategoryList = this.setOptionsList(
result.data.project_categories
);
this.setState({
CategoryList,
});
}
})
.catch((error) => {});
};
setOptionsList = (data) => {
let list = undefined;
if (data && data.length > 0) {
@ -163,17 +172,17 @@ class Setting extends Component {
update=(values)=>{
const { projectsId , owner } = this.props.match.params;
const { private_check , project_units } = this.state;
const {project_language_id, project_category_id, pr_view_admin} = values;
const { private_check , project_units , project_language_id , project_category_id} = this.state;
const {pr_view_admin} = values;
const url = `/${owner}/${projectsId}.json`;
axios.patch(url, {
name: values.project_name,
description: values.project_description,
private: private_check,
identifier:values.project_identifier,
project_language_id,
project_category_id,
pr_view_admin
pr_view_admin,
...values,
project_language_id , project_category_id
}).then((result) => {
if (result) {
this.props.showNotification(`仓库信息修改成功!`);
@ -272,6 +281,17 @@ class Setting extends Component {
})
}
getValue=(values,typeName)=>{
const {id,name} = values;
this.setState({
[`${typeName}`]:id
})
this.props.form.setFieldsValue({
[`${typeName}`]:name
})
}
render() {
const { getFieldDecorator } = this.props.form;
const { projectsId , owner } = this.props.match.params;
@ -295,7 +315,7 @@ class Setting extends Component {
<Spin spinning={loading}>
<WhiteBack>
<Title>基本设置</Title>
<Form className="baseForm" style={{marginLeft: '30px'}}>
<Form className="baseForm" id="baseForm" style={{marginLeft: '30px'}}>
<Form.Item label="项目名称">
{getFieldDecorator("project_name", {
rules: [
@ -358,20 +378,22 @@ class Setting extends Component {
{getFieldDecorator("project_category_id", {
rules: [],
})(
<Select>
<Option key={0} value={""}>未选择项目类别</Option>
{CategoryList}
</Select>
// <Select>
// <Option key={0} value={""}>未选择项目类别</Option>
// {CategoryList}
// </Select>
<DorpChooseMenus idname={"baseForm"} name={"请选择项目类别"} list={CategoryList} getValue={(value)=>this.getValue(value,"project_category_id")}/>
)}
</Form.Item>
<Form.Item label="项目语言">
{getFieldDecorator("project_language_id", {
rules: [],
})(
<Select>
<Option key={0} value={""}>未选择项目语言</Option>
{LanguageList}
</Select>
// <Select>
// <Option key={0} value={""}>未选择项目语言</Option>
// {LanguageList}
// </Select>
<DorpChooseMenus idname={"baseForm"} name={"请选择项目类别"} list={LanguageList} getValue={(value)=>this.getValue(value,"project_language_id")}/>
)}
</Form.Item>
<span className="mb15">访问权限:</span>

View File

@ -3,7 +3,7 @@ import fetch from '../Information/fetch'
//今日工作态势
export function todayWorkSituation() {
return fetch({
url: `/pms/specialArea/softwareFact/statis/todayWorkSituation`,
url: `/pms/open/specialArea/softwareFact/statis/todayWorkSituation`,
method: 'get',
});
}
@ -11,7 +11,7 @@ export function todayWorkSituation() {
//月、周工作态势
export function weekMonthWorkSituation(type) {
return fetch({
url: `/pms/specialArea/softwareFact/statis/weekMonthWorkSituation?type=${type}`,
url: `/pms/open/specialArea/softwareFact/statis/weekMonthWorkSituation?type=${type}`,
method: 'get',
});
}
@ -19,7 +19,7 @@ export function weekMonthWorkSituation(type) {
//项目研发状态
export function projectSituation() {
return fetch({
url: `/pms/specialArea/softwareFact/statis/projectSituation`,
url: `/pms/open/specialArea/softwareFact/statis/projectSituation`,
method: 'get',
});
}

View File

@ -5,50 +5,9 @@ import shidu from "../../img/shidu.png";
import ziwaixian from "../../img/ziwaixian.png";
function CenterPanel(props) {
let { timeObj } = props;
const [weatherObj, setWetherObj] = useState({});
useEffect(() => {
const timer = setInterval(() => {
//5
// getWether();
}, 300000);
// getWether();
return () => {
clearInterval(timer);
};
}, []);
function getWether() {
// fxLink 便
// now.obsTime
// now.temp
// now.feelsLike
// now.icon
// now.text
// now.wind360 360
// now.windDir
// now.windScale
// now.windSpeed /
// now.humidity 湿
// now.precip 1
// now.pressure
// now.vis
// now.cloud
// now.dew
fetch(
`https://devapi.qweather.com/v7/weather/now?location=112.87,28.22&key=18a193299e4944a7bf3ccf23b38474c2`
)
.then((response) => response.json())
.then((data) => {
setWetherObj({
text: data.now.text,
temp: data.now.temp,
windDir: data.now.windDir,
humidity: data.now.humidity,
});
})
.catch((err) => {});
}
let { timeObj,weatherObj } = props;
return (
<div className="center-panel">
<div className="first-section">

View File

@ -10,8 +10,7 @@ import "slick-carousel/slick/slick.css";
import "slick-carousel/slick/slick-theme.css";
import Slider from "react-slick";
import RightContent from "./rightContent";
import { getLunar } from "../../utils";
import { todayWorkSituation,weekMonthWorkSituation} from "../../api";
// let focuseWorkList = [
// {
// name: "page1",
@ -59,7 +58,7 @@ const setting = {
slidesToShow: 1,
slidesToScroll: 1,
pauseOnDotsHover: true,
autoplaySpeed: Number(localStorage.getItem('homeLoopSpeed')) || 5000,
autoplaySpeed: Number(localStorage.getItem('homeLoopSpeed')) || 8000,
pauseOnFocus: true,
autoplay: true,
arrows: true,
@ -69,24 +68,12 @@ const setting = {
};
function Home(props) {
const { newsList } = props;
const [currentMonth, setCurrentMonth] = useState("");
const [timeObj, setTimeObj] = useState({});
const { newsList,timeObj,dayMap,weekMap,monthMap,weatherObj,currentMonth } = props;
const [firstNews, setFirstNews] = useState({});
const [focuseWorkList, setFocuseWorkList] = useState([]);
const rightContentDom = React.useRef(null)
// const [focuseWorkList, setFocuseWorkList] = useState([]);
useEffect(() => {
getDay()
const timer = setInterval(() => {
//10
getDay();
}, Number(localStorage.getItem('apiTime')) || 300000);
return () => {
clearInterval(timer);
};
}, []);
useEffect(() => {
@ -129,30 +116,7 @@ function Home(props) {
setFocuseWorkList(finalList);
}
}, newsList);
function getDay() {
//
const days = [
"星期日",
"星期一",
"星期二",
"星期三",
"星期四",
"星期五",
"星期六",
];
let time = new Date();
let year = time.getFullYear();
let month = time.getMonth() + 1;
let day = time.getDate();
let obj = {
month: month < 10 ? "0" + month : month.toString(),
day: day < 10 ? "0" + day : day.toString(),
week: days[time.getDay()],
lunar: getLunar(time),
};
setCurrentMonth(`${year}${month}`);
setTimeObj(obj);
}
return (
<div className="home-wrap">
@ -209,7 +173,7 @@ function Home(props) {
</div>
<div className="soft-title">软件工厂工作态势</div>
</div>
<RightContent timeObj={timeObj} ref={rightContentDom}/>
<RightContent dayMap={dayMap} weekMap={weekMap} monthMap={monthMap} timeObj={timeObj} ref={rightContentDom} weatherObj={weatherObj}/>
</div>
</div>
);

View File

@ -7,7 +7,7 @@ import SecondPanel from "./secondPanel";
import ThirdPanel from "./thirdPanel";
import ForthPanel from "./forthPanel";
import CenterPanel from "./centerPanel";
import { todayWorkSituation, weekMonthWorkSituation } from "../../api";
const setting = {
dots: false,
infinite: true,
@ -15,7 +15,7 @@ const setting = {
slidesToShow: 1,
slidesToScroll: 1,
pauseOnDotsHover: false,
autoplaySpeed: Number(localStorage.getItem("pageLoopSpeed")) || 8000,
autoplaySpeed: Number(localStorage.getItem("homeLoopSpeed")) || 8000,
pauseOnFocus: false,
autoplay: true,
arrows: false,
@ -26,10 +26,7 @@ const setting = {
// afterChange: (index) => setTag(index),
};
function RightContent(props) {
const { timeObj } = props;
const [dayMap, setDayMap] = useState({});
const [weekMap, setWeekMap] = useState({});
const [monthMap, setMonthMap] = useState({});
const { timeObj,dayMap,weekMap,monthMap,weatherObj } = props;
const firstDayDom = React.useRef(null);
const firstweekDom = React.useRef(null);
const secondDayDom = React.useRef(null);
@ -40,44 +37,7 @@ function RightContent(props) {
const forthmonthDom = React.useRef(null);
const centerDom = React.useRef(null);
useEffect(() => {
getTodayData();
getWeekData();
getMonthData();
const timer = setInterval(() => {
//5
getTodayData();
getWeekData();
getMonthData();
}, Number(localStorage.getItem("apiTime")) || 300000);
return () => {
clearInterval(timer);
};
}, []);
function getTodayData() {
todayWorkSituation().then((res) => {
if (res.data.code === 200) {
setDayMap(res.data.data);
}
});
}
function getWeekData() {
weekMonthWorkSituation("week").then((res) => {
if (res.data.code === 200) {
setWeekMap(res.data.data);
}
});
}
function getMonthData() {
weekMonthWorkSituation("month").then((res) => {
if (res.data.code === 200) {
setMonthMap(res.data.data);
}
});
}
return (
<div style={{ width: 1330, height: "100%" }}>
<Slider {...setting}>
@ -93,7 +53,7 @@ function RightContent(props) {
<ThirdPanel cardPage="1" dayMap={dayMap} ref={thirdDayDom}/>
<ForthPanel cardPage="1" dayMap={dayMap} ref={forthDayDom} />
</div>
<CenterPanel timeObj={timeObj} />
<CenterPanel timeObj={timeObj} weatherObj={weatherObj} ref={centerDom}/>
</div>
<div className="right-content" key="card2">
<div className="top-section">
@ -104,7 +64,7 @@ function RightContent(props) {
<ThirdPanel cardPage="2" monthMap={monthMap} ref={thirdmonthDom} />
<ForthPanel cardPage="2" monthMap={monthMap} ref={forthmonthDom} />
</div>
<CenterPanel timeObj={timeObj} ref={centerDom}/>
<CenterPanel timeObj={timeObj} weatherObj={weatherObj} ref={centerDom}/>
</div>
</Slider>
</div>

View File

@ -4,38 +4,10 @@ import { withRouter } from "react-router";
import centerBg from "../../img/center_title_bg.png";
import "./index.scss";
import ProjectCard from "./ProjectCard";
import { projectSituation } from "../../api";
function ProjectStatus(props) {
const {} = props;
const [projectList, setProjectList] = useState([]);
const {projectList} = props;
const ProjectCardDom = React.useRef(null)
useEffect(() => {
getProjectList();
//
let timer = setInterval(()=>{
getProjectList();
}, Number(localStorage.getItem('apiTime')) || 300000)
return ()=>{
clearInterval(timer)
}
}, []);
function getProjectList() {
projectSituation().then((res) => {
if (res.data.code === 200) {
let list =
res.data.data.length > 10
? res.data.data.slice(0, 10)
: res.data.data;
setProjectList(list);
}
});
}
function getShadowCls(idx) {
switch (idx) {
case 5:

Some files were not shown because too many files have changed in this diff Show More