Merge branch 'gitlink_ssr_head' of https://gitlink.org.cn/Gitlink/forgeplus-react into gitlink_ssr_head
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
@charset "utf-8";
|
||||
|
||||
/* 头部 */
|
||||
.header {
|
||||
width: 100%;
|
||||
|
|
|
|||
|
After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 20 KiB |
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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 >
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
|
@ -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">开源许可证是您软件源代码使用、修改及分发的法律约定,明确了他人使用项目的权利与限制,常见的有MIT、Apache 2.0、GPL等。</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
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -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: {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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/>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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}}>
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
// 如果acc中没有当前item.type的数组,则创建一个
|
||||
if (!acc[item.type]) {
|
||||
acc[item.type] = [];
|
||||
}
|
||||
// 将当前item添加到对应type的数组中
|
||||
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>
|
||||
|
|
|
|||
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 922 B |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
// 设置网页标题
|
||||
|
|
|
|||
|
|
@ -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){
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -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=>{})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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=>{})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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}`})
|
||||
}
|
||||
|
||||
// 如果项目是fork的项目,pr自动指向源仓库,否则指向本仓库默认分支
|
||||
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后台配置是否开启仓库 */}
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -513,6 +513,7 @@ class CreateMerge extends Component {
|
|||
changeCommitFunc={this.changeCommitFunc}
|
||||
comparesData={comparesData}
|
||||
pullOwnerLogin={pullOwnerLogin}
|
||||
branchParams = {getBranchParams(this.props.location.pathname)}
|
||||
></MergeFooter>
|
||||
)}
|
||||
</Spin>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
// 评论数量优先取Comment组件中列表接口返回的,其次取合并请求详情接口中的,都没有取默认值0
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
.newPanel{
|
||||
border:1px solid #eaeaea;
|
||||
border-radius: 4px;
|
||||
position: relative;
|
||||
}
|
||||
.newPanel_title{
|
||||
height: 3rem;
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -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}/>)
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)
|
||||
|
|
|
|||
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 633 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 460 B |
|
After Width: | Height: | Size: 451 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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{
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
});
|
||||
}
|
||||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||