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

This commit is contained in:
谢思 2024-11-07 14:54:24 +08:00
commit 33975bf64f
19 changed files with 385 additions and 54 deletions

View File

@ -48,6 +48,7 @@
"dotenv": "4.0.0",
"dotenv-expand": "4.2.0",
"echarts": "^4.9.0",
"echarts-gl": "^1.1.2",
"echarts-wordcloud": "^2.0.0",
"editor.md": "^1.5.0",
"express": "^4.18.2",

View File

@ -0,0 +1,229 @@
import React ,{ Fragment, useEffect, useState } from 'react';
import echarts from 'echarts/lib/echarts'
import 'echarts-gl';
import { getmaturityAnalyse } from '../api/project'
import { Icon, Popover } from 'antd';
function Compass({owner,projectsId}) {
const [mainScore, setMainScore] = useState({})
useEffect(()=>{
getmaturityAnalyse({ owner, repository: projectsId }).then(res => {
if (res.data && res.data.main_score && res.data.main_score.score > 0) {
setMainScore(res.data.main_score)
Init(res.data)
}
})
},[])
function Init(data) {
let huan_val = document.getElementById("compass");
let myEcharts = echarts.init(huan_val);
const a = 3;
const n = 3;
const o = false;
const l = o ? "white" : "#333"
, r = o ? "rgba(255,255,255,0.8)" : "#aaa";
let option = {
tooltip: {
textStyle: {
fontWeight: "bolder"
},
formatter: e => "mock" === e.data.validity ? e.name + ": \u5373\u5c06\u4e0a\u7ebf" : e.data.description + ": " + e.data.value[2]
},
xAxis3D: {
name: " ",
type: "category",
data: data.main_score.x_axis,
interval: 2,
axisLine: {
lineStyle: {
width: 1,
color: l
}
},
splitLine: {
show: !0,
interval: a - 1
},
axisLabel: {
interval: a - 1,
textStyle: {
color: r,
padding: [10, 10, 0, 0]
}
},
axisTick: {
interval: a - 1,
length: 1
}
},
yAxis3D: {
name: " ",
type: "category",
data: data.main_score.y_axis,
axisLine: {
lineStyle: {
width: 1,
color: l
}
},
axisTick: {
interval: n - 1,
length: 0
},
splitArea: {
show: !0,
areaStyle: {
color: ["rgba(255,247,207,0.5)", "rgba(255,231,231,0.5)", "rgba(226,226,226,0.5)"]
}
},
axisLabel: {
interval: n - 1,
textStyle: {
color: r,
padding: [10, 0, 0, 0]
}
},
splitLine: {
show: !0,
interval: n - 1
}
},
zAxis3D: {
name: " ",
type: "value",
splitNumber: 4,
axisLine: {
lineStyle: o ? {
width: 1,
opacity: .2,
color: l
} : {
width: .1,
opacity: .5,
color: l
}
},
splitArea: {
show: !0,
areaStyle: {
color: ["#ffffff", "#ffffff", "#ffffff"]
}
},
axisLabel: {
show: !1
},
axisTick: {
show: !1
}
},
grid3D: {
axisLine: {
interval: 1
},
axisPointer: {
lineStyle: {
opacity: .2
},
label: {
show: !1
}
},
splitLine: {
interval: 2
},
viewControl: {
projection: "perspective",
zoomSensitivity: 0,
rotateSensitivity: [1, 0],
distance: 280,
alpha: 20,
beta: 25
},
top: -40,
right: -10,
boxWidth: 150,
boxDepth: 150,
environment: "none",
light: {
main: {
intensity: .8,
alpha: 50
},
ambient: {
intensity: .5
}
}
},
series: [{
type: "bar3D",
data: data.scores,
shading: "realistic",
bevelSize: .1,
bevelSmoothness: 2,
barSize: 13,
label: {
show: !1,
fontSize: 12,
fontWeight: 500,
borderWidth: 1,
color: "#333",
distance: -20,
formatter: function (e) {
for (let t = e.data.description, i = 2, a = Math.ceil(t.length / i), n = "\n", o = 0; o < a; o++) {
let l = o * i
, r = l + i;
n += t.substring(l, r) + "\n"
}
return n
}
},
itemStyle: {
opacity: .95
},
emphasis: {
label: {
show: !1
},
itemStyle: {
color: "#FFB800",
opacity: .7
}
}
}]
};
myEcharts.setOption(option);
}
const content = () => {
return <div>
开源健康成熟度评分评估围绕以下维度对项目展开评估<br/>
稳健性由安全警告开发者数量版本数评分反映<br/>
扩展性由许可证评分fork数评分点赞数评分关注数评分反映<br/>
维护性由未解决 issue 和已解决 issuecommit数评分反映<br/>
<br/>
总体成熟度评分<br/>
80-100: 项目成熟维护良好<br/>
60-80: 项目稳定有待改进<br/>
0-60: 项目成长期需要加强维护<br/>
</div>
}
return(
mainScore.score && mainScore.score > 0 ? <div className='halfs'>
<div className="font-16 color-ooo mb20">
开源健康成熟度评分 ({ mainScore.score && mainScore.score.toFixed(2) })
<Popover content={content()}>
<Icon type="question-circle" theme="filled" className="color-blue ml10 pointer"/>
</Popover>
</div>
<div id="compass" style={{height:"250px"}}></div>
</div> : null
)
}
export default Compass;

View File

@ -141,7 +141,7 @@ function HeaderPageJCC(props){
</div>
{
data.introductionImage &&
<img src={data.introductionImage} alt="" width="600px" height="361px"/>
<img src={data.introductionImage.split(",")[0]} alt="" width="600px" height="361px"/>
}
</div>
}

View File

@ -79,8 +79,8 @@ function HeaderPage(props){
<p className="z_desc task-hide-2" style={{WebkitLineClamp:data.introductionTitle?"4":"7"}} dangerouslySetInnerHTML={{ __html: data.introductionContent }} ></p>
</div>
{
data.introductionImage &&
<img src={data.introductionImage} alt="" width="300px"/>
data.introductionImage &&
<img src={data.introductionImage.split(",")[0]} alt="" width="300px"/>
}
</div>
}

View File

@ -237,7 +237,7 @@ function HeaderPageCCF(props) {
</div>
{
data.introductionImage &&
<img src={data.introductionImage} alt="" width="300px" />
<img src={data.introductionImage.split(",")[0]} alt="" width="300px" />
}
</div>
}

View File

@ -1,6 +1,7 @@
import React , { useEffect, useState } from 'react';
import { getZoneStatistics } from '../../../api';
import '../index.scss';
import { Carousel } from 'antd';
//
// statistics:
@ -28,9 +29,13 @@ 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 pt50 between width1200'>
{Object.keys(label).map(item=>{

View File

@ -131,7 +131,7 @@ function News({data, subTitle, firstDirIntro, secondDirIntro}) {
{/* 第一个顺序栏目 */}
{firstDir && <div className='firstDir clearfix'>
<p className='font-16 task-hide'>
{firstDir.name}
<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>
@ -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,7 +166,7 @@ function News({data, subTitle, firstDirIntro, secondDirIntro}) {
</Row>
</div>}
</div>
{dirs && !!dirs.length && <div className='df wrap backWhite pt15 pb15 pl10 pr10'>
{dirs && !!dirs.length && <div className='df wrap backWhite pt30 pb15 pl10 pr10'>
{renderItems()}
</div>}
</div>

View File

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

View File

@ -1,8 +1,9 @@
import React , { useEffect, useState } from 'react';
import { getSpecialProjectDetail, getSpecialProjectList, getSpecialProjectTypeList } from '../../../api';
import { getSpecialProjectDetail, getSpecialProjectList, getSpecialProjectTypeList, getImage } from '../../../api';
import { Col, Divider, Row, Tag } from 'antd';
import Nodata from '../../../../Nodata';
import '../index.scss';
import { TypeCarousel } from './project';
function SpecialProjects({data, subTitle}) {
const {key, homepageSpecialProjectTitle, id} = data || {};
@ -15,7 +16,7 @@ function SpecialProjects({data, subTitle}) {
useEffect(()=>{
id && getSpecialProjectTypeList(id, {
pageNum: 1,
pageSize: 4,
pageSize: 100,
}).then(res=>{
if(res){
const rows = res.data.rows;
@ -63,25 +64,27 @@ function SpecialProjects({data, subTitle}) {
setSelectedItem(selectedItem === id ? null : id);
};
const list = (title, table1, table2, list)=>{
const list = (title, table1, table2,table3, list)=>{
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>
<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 {author, company} = item;
const detail = item.relevancyObject;
const {name, createUser, id} = detail
return <React.Fragment>
<Col span={14} className='task-hide'>
<Col span={12} 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}/>}
{name}
</a>
</Col>
<Col span={10} className='task-hide'>{author}</Col>
<Col span={6} className='task-hide'>{author}</Col>
<Col span={6} className='task-hide'>{company}</Col>
</React.Fragment>
})}
</Row>
@ -120,13 +123,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' : ''}`}
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>
);
@ -139,19 +142,19 @@ 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 {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>
{ 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>
<div style={{padding: '22px 33px'}}>
{list("论文", "论文标题", "作者", PAPER)}
{list("专利", "专利标题", "作者", PATENT)}
{list("论文", "论文标题", "作者", "单位", PAPER)}
{list("专利", "专利标题", "作者", "单位", PATENT)}
{list2("开源项目", "项目名称", "参与者", PROJECT)}
{!PAPER.length && !PATENT.length && !PROJECT.length && <Nodata _html="暂无数据" small/>}
</div>
@ -171,18 +174,7 @@ 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>
<TypeCarousel pageSize={3} list={typeList} typeId={typeId} onClick={(id)=>{setTypeId(id);}}/>
<div className='df wrap between'>
{renderItems()}
<div style={{margin: '0 auto'}}>{!projectList.length && <Nodata _html="暂无数据"/>}</div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 922 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

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

View File

@ -1,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) {

View File

@ -29,6 +29,11 @@ import ProjectPortrait from '../Component/projectPortrait';
import imNoneImg from './img/importNone.png';
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"),
@ -694,6 +699,7 @@ function CoderDepot(props){
/>
</React.Fragment>
}
{/* {__CLIENT__ && <ProjectCompass owner={owner} projectsId={projectsId} />} */}
{/* 贡献者 */}
{mirror_status ===0 && <Contributors owner={owner} projectsId={projectsId} /> }
{/* 项目画像: 导入/fork项目不展示, open_portrait后台配置是否开启仓库 */}

10
src/forge/api/project.js Normal file
View File

@ -0,0 +1,10 @@
import javaFetch from '../javaFetch';
const service = javaFetch();
export function getmaturityAnalyse(params) {
return service({
url: `https://testbd.trustie.net/maturityAnalyse/`,
method: 'get',
params: window.location.hostname === 'testforgeplus.trustie.net' ? { ...params, env_url: 'testforgeplus' } : params
});
}