增加跳转 优化首页监听 其他bug

This commit is contained in:
谢思 2025-12-23 16:54:37 +08:00
parent 62828396a8
commit 68860d6be8
19 changed files with 327 additions and 522 deletions

View File

@ -1,41 +0,0 @@
import { useState, useEffect, useRef, useCallback } from 'react';
const useIntersectionObserver = (options = {}) => {
const [isIntersecting, setIsIntersecting] = useState(false);
const elementRef = useRef(null);
const observerRef = useRef(null);
useEffect(() => {
// 如果已有观察器,先断开
if (observerRef.current) {
observerRef.current.disconnect();
}
const observerOptions = {
threshold: options.threshold || 0.1,
rootMargin: options.rootMargin || '0px',
root: options.root || null
};
observerRef.current = new IntersectionObserver(([entry]) => {
// 避免在组件卸载后设置状态
if (elementRef.current) {
setIsIntersecting(entry.isIntersecting);
}
}, observerOptions);
if (elementRef.current) {
observerRef.current.observe(elementRef.current);
}
return () => {
if (observerRef.current) {
observerRef.current.disconnect();
}
};
}, [options.threshold, options.rootMargin, options.root]);
return [elementRef, isIntersecting];
};
export default useIntersectionObserver;

View File

@ -4,7 +4,7 @@ import TerminalAnimation from './terminalAnimation2';
import ActionItem from '../../../../../factory/resource/components/ActionItem'; import ActionItem from '../../../../../factory/resource/components/ActionItem';
import './index.scss'; import './index.scss';
function Banner() { function Banner({isVisible}) {
const lines1 = [ const lines1 = [
'if(!isVisible)', 'if(!isVisible)',
' return', ' return',
@ -35,7 +35,7 @@ function Banner() {
'})', '})',
] ]
return ( return (
<div className="banner_v2 color-white"> <div className="banner_v2 color-white home_v2_child" id="home_v2_child1">
{/* 主要内容区域 */} {/* 主要内容区域 */}
<div className="center"> <div className="center">
<div className="main-title-v2 width1600"> <div className="main-title-v2 width1600">
@ -72,10 +72,10 @@ function Banner() {
</div> </div>
</div> </div>
<div className='center-left-bk'> <div className='center-left-bk'>
<TerminalAnimation lines={lines1} width={210} height={280} class_name="center-left-code" /> <TerminalAnimation startAnimation={isVisible} lines={lines1} width={210} height={280} class_name="center-left-code" />
</div> </div>
<div className='center-right-bk'> <div className='center-right-bk'>
<TerminalAnimation lines={lines2} width={197} height={186} /> <TerminalAnimation startAnimation={isVisible} lines={lines2} width={197} height={186} />
</div> </div>
<img src={require('../image/img16.png')} alt='' width={1} className='center-left-2'/> <img src={require('../image/img16.png')} alt='' width={1} className='center-left-2'/>
<img src={require('../image/img17.png')} alt='' width={1} className='center-left-3'/> <img src={require('../image/img17.png')} alt='' width={1} className='center-left-3'/>

View File

@ -3,6 +3,7 @@ import React, { Fragment, useEffect, useState } from 'react';
import { getSubDocList, getNewsDetail } from '../../../../Information/api'; import { getSubDocList, getNewsDetail } from '../../../../Information/api';
import { BrowserRouter as Router, Route, Link } from "react-router-dom"; import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import moment from 'moment'; import moment from 'moment';
import moreContentButton from './moreContentButton';
const CommunityDynamic = () => { const CommunityDynamic = () => {
const [list1, setList1] = useState([]); const [list1, setList1] = useState([]);
@ -11,6 +12,7 @@ const CommunityDynamic = () => {
const [list3, setList3] = useState([]); const [list3, setList3] = useState([]);
const firstDynamics = list3[0] const firstDynamics = list3[0]
const firstByList1 = list1[0] || {} const firstByList1 = list1[0] || {}
const list2_home = list2.slice(0,4);
useEffect(() => { useEffect(() => {
getSubDocList(28).then(res => { getSubDocList(28).then(res => {
@ -43,7 +45,7 @@ const CommunityDynamic = () => {
}, []) }, [])
return ( return (
<div className="community-dynamic pb70"> <div className="community-dynamic pb70 home_v2_child" id="home_v2_child2">
<div className="title_v2 pt70">社区动态</div> <div className="title_v2 pt70">社区动态</div>
<div className='news_v2 width1600 padding25-30'> <div className='news_v2 width1600 padding25-30'>
{/* 精选新闻 */} {/* 精选新闻 */}
@ -64,7 +66,7 @@ const CommunityDynamic = () => {
{list1.slice(1,5).map((item, index) => ( {list1.slice(1,5).map((item, index) => (
<Fragment key={index}> <Fragment key={index}>
<div className="news-item-v2"> <div className="news-item-v2">
<Link to={`/news/${firstByList1.id}`}><div className='font-20 font-bd mb10 title-border-to-right task-hide'>{item.name}</div></Link> <Link to={`/news/${item.id}`}><div className='font-20 font-bd mb10 title-border-to-right task-hide'>{item.name}</div></Link>
<p className='task-hide-2 opacity8' style={{ lineHeight: 'normal' }}>{item.summary}</p> <p className='task-hide-2 opacity8' style={{ lineHeight: 'normal' }}>{item.summary}</p>
</div> </div>
{index < 3 && <Divider type="vertical" style={{ height: '60px' }} className="mt20 opacity8" />} {index < 3 && <Divider type="vertical" style={{ height: '60px' }} className="mt20 opacity8" />}
@ -81,43 +83,46 @@ const CommunityDynamic = () => {
{/* <img src={require('../image/icon2.png')} alt='' width={30} className='mb10 mr10' /> */} {/* <img src={require('../image/icon2.png')} alt='' width={30} className='mb10 mr10' /> */}
<span className='font-24 font-bd'>精彩活动</span> <span className='font-24 font-bd'>精彩活动</span>
<ul> <ul>
{list2.slice(0,5).map((item, index) => ( {list2_home.map((item, index) => (
<li key={index} className="activity-item-v2 df"> <li key={index} className="activity-item-v2 df">
<span className="font-16"> <span className="font-16 flex1 AlignCenter">
<Link to={`/news/${firstByList1.id}`} className="title-border-to-right">{item.summary}</Link> <Link to={`/news/${item.id}`} className="title-border-to-right task-hide">{item.summary}</Link>
{index === 1 && <span className="hot-tag color-white ml5">HOT</span>} {index === 1 && <span className="hot-tag color-white ml5">HOT</span>}
</span> </span>
{/* <span className="opacity8 font-15">{moment(item.publishTime).format('MM月DD日')}</span> */} {/* <span className="opacity8 font-15">{moment(item.publishTime).format('MM月DD日')}</span> */}
<span className="opacity8 font-15">{item.name}</span> <span className="opacity8 font-15 ml20">{item.name}</span>
</li> </li>
))} ))}
</ul> </ul>
</div> </div>
<Carousel autoplay className='activity-section-Carousel'> <Carousel autoplay className='activity-section-Carousel'>
{list2.slice(0,5).map((item, index)=>{ {list2_home.map((item, index)=>{
return <img src={item.headImg} alt=''key={index}/> return <img src={item.headImg} alt=''key={index}/>
})} })}
</Carousel> </Carousel>
</div> </div>
</div> </div>
<div className="padding20-30 flex1 project-section"> <div className="padding20-30 flex1 project-section">
{/* <img src={require('../image/icon3.png')} alt='' width={30} className='mb10 mr10' /> */} {/* <img src={require('../image/icon3.png')} alt='' width={30} className='mb10 mr10' /> */}
<span className='font-24 font-bd'>XX项目动态</span> <span className='font-24 font-bd'>项目动态</span>
<ul> <ul>
{list3.slice(0,4).map((item, index) => ( {list3.slice(0,4).map((item, index) => (
<li key={index} className="activity-item-v2 df"> <li key={index} className="activity-item-v2 df">
<span className="font-16"> <span className="font-16 flex1 AlignCenter">
<Link to={`/news/${firstByList1.id}`} className="title-border-to-right">{item.name}</Link> <Link to={`/news/${item.id}`} className="title-border-to-right task-hide">{item.name}</Link>
{index === 0 && <span className="hot-tag color-white ml5">NEW</span>} {index === 0 && <span className="hot-tag color-white ml5">NEW</span>}
</span> </span>
<span className="opacity8 font-15">{moment(item.publishTime).format('YYYY-MM-DD')}</span> <span className="opacity8 font-15 ml20">{moment(item.publishTime).format('YYYY-MM-DD')}</span>
</li> </li>
))} ))}
</ul> </ul>
</div> </div>
</div> </div>
<div className='center pt30'>
{moreContentButton({ href: '/news' })}
</div>
</div> </div>
); );
}; };

View File

@ -1,13 +1,7 @@
import { Button } from 'antd'; import { Button } from 'antd';
import React, { useState, useEffect, useRef } from 'react'; import React, { useState, useEffect, useRef } from 'react';
import useIntersectionObserver from '../../../../../common/hooks/useIntersectionObserver';
const EcosystemAlliance = () => { const EcosystemAlliance = () => {
const [activeTab, setActiveTab] = useState('partners'); // const [activeTab, setActiveTab] = useState('partners'); //
const [elementRef, isVisible] = useIntersectionObserver({
threshold: 0.2,
rootMargin: '0px 0px -50px 0px'
});
// const partners = [ // const partners = [
// { name: '', logo: require('../image/logo1.png') }, // { name: '', logo: require('../image/logo1.png') },
@ -68,7 +62,7 @@ const EcosystemAlliance = () => {
}; };
return ( return (
<div ref={elementRef} className={`ecosystem-alliance animate-up-box center ${isVisible ? 'animate-up' : ''}`}> <div className={`ecosystem-alliance center home_v2_child`} id="home_v2_child8">
<div className="title_v2 mb20">生态联盟</div> <div className="title_v2 mb20">生态联盟</div>
<div className="tabs_v2 mb80"> <div className="tabs_v2 mb80">
@ -91,7 +85,7 @@ const EcosystemAlliance = () => {
</Button> </Button>
</div> </div>
<div className='width1600 pt70'> <div className='width1600 pt70 pb50'>
{partners1.map((i,key)=>{ {partners1.map((i,key)=>{
return <div key={key} className='mb40'> return <div key={key} className='mb40'>
<div className='edu-txt-left font-24 font-bd mb20'>{i.title}</div> <div className='edu-txt-left font-24 font-bd mb20'>{i.title}</div>

View File

@ -1,5 +1,6 @@
import { Divider, Icon } from 'antd'; import { Divider, Icon } from 'antd';
import React, { Fragment } from 'react'; import React, { Fragment } from 'react';
import moreContentButton from './moreContentButton';
const ForumExchange = () => { const ForumExchange = () => {
const forums = [ const forums = [
@ -51,11 +52,8 @@ const ForumExchange = () => {
]; ];
return ( return (
<div className="forum-exchange"> <div className="forum-exchange home_v2_child" id="home_v2_child7">
<div className="title_v2 color-white">论坛交流</div> <div className="title_v2 color-white">论坛交流</div>
<div className='center mb60 mt-8'>
<a className='font-16 color-white but-to-right'>探索更多内容 <Icon type="right" className="font-15 icon-to-right"/></a>
</div>
<div className="forums-container width1600 color-white"> <div className="forums-container width1600 color-white">
{forums.map((forum, index) => ( {forums.map((forum, index) => (
@ -85,6 +83,9 @@ const ForumExchange = () => {
</Fragment> </Fragment>
))} ))}
</div> </div>
<div className='center mt40 mt-8'>
{moreContentButton({href:"/forums", classname:"font-16 but-to-right font-bd color-white"})}
</div>
</div> </div>
); );
}; };

View File

@ -2,7 +2,7 @@ import { Icon } from "antd";
import React from "react"; import React from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
function moreContentButton({href="/", name="探索更多内容", classname="font-16 color-white but-to-right"}){ function moreContentButton({href="/", name="探索更多内容", classname="font-16 but-to-right font-bd"}){
return <Link to={href} className={classname}>{name}<Icon type="right" className="font-15 icon-to-right"/></Link> return <Link to={href} className={classname}>{name}<Icon type="right" className="font-15 icon-to-right"/></Link>
} }

View File

@ -1,28 +1,10 @@
import React, { Fragment, useEffect, useState } from 'react'; import React, { Fragment, useEffect, useState } from 'react';
import { AlignCenter } from '../../../../Component/layout'; import { AlignCenter } from '../../../../Component/layout';
import TerminalAnimation from './terminalAnimation1'; import TerminalAnimation from './terminalAnimation1';
import useIntersectionObserver from '../../../../../common/hooks/useIntersectionObserver'; import { Button } from 'antd';
const OpenSourceCollaboration = () => { const OpenSourceCollaboration = ({isVisible}) => {
const [selectedFeature, setSelectedFeature] = useState(null); const [selectedFeature, setSelectedFeature] = useState(null);
const [active, setActive] = useState(1); const [active, setActive] = useState(1);
const [elementRef, isVisible] = useIntersectionObserver({
threshold: 0.2,
rootMargin: '0px 0px -20px 0px'
});
useEffect(() => {
if(!isVisible) return
const interval = setInterval(() => {
setActive(prevActive => {
return prevActive === 1 ? 2 : 1;
});
}, 6000);
//
return () => {
clearInterval(interval);
};
}, [isVisible]);
const features = [ const features = [
{ {
@ -48,7 +30,7 @@ const OpenSourceCollaboration = () => {
}; };
return ( return (
<div className={`open-source-collaboration-container animate-up-box ${isVisible ? 'animate-up animate-others' : ''}`} ref={elementRef} > <div className={`open-source-collaboration-container home_v2_child`} id="home_v2_child3">
<div className="title_v2">开源协作</div> <div className="title_v2">开源协作</div>
<div className="content-wrapper-bottom width1600"></div> <div className="content-wrapper-bottom width1600"></div>
<div className="content-wrapper width1600 df color-white mb35"> <div className="content-wrapper width1600 df color-white mb35">
@ -66,10 +48,10 @@ const OpenSourceCollaboration = () => {
</div> </div>
))} ))}
<button className="blackBut white mt10"> <Button className="blackBut white mt10" href="/projects">
<span>了解更多</span> <span>了解更多</span>
<span>了解更多</span> <span>了解更多</span>
</button> </Button>
</div> </div>
<div className="right-panel"> <div className="right-panel">

View File

@ -1,3 +1,4 @@
import { Button } from 'antd';
import React from 'react'; import React from 'react';
const RepositoryV2 = () => { const RepositoryV2 = () => {
@ -33,10 +34,10 @@ const RepositoryV2 = () => {
} }
]; ];
return ( return (
<div className="repository-v2"> <div className="repository-v2 home_v2_child" id="home_v2_child5">
<div className="width1600"> <div className="width1600">
{/* 标题 */} {/* 标题 */}
<div className="title_v2">XX开源软件仓</div> <div className="title_v2">开源软件仓</div>
{/* 导航标签 */} {/* 导航标签 */}
<div className="nav-tabs-v2"> <div className="nav-tabs-v2">
@ -71,10 +72,10 @@ const RepositoryV2 = () => {
))} ))}
</div> </div>
<button className="blackBut"> <Button className="blackBut" href='/explore'>
<span>了解更多</span> <span>了解更多</span>
<span>了解更多</span> <span>了解更多</span>
</button> </Button>
</div> </div>
{/* 右侧架构图 - 使用图片 */} {/* 右侧架构图 - 使用图片 */}

View File

@ -7,8 +7,6 @@ function SoftwareFactory() {
const [active, setActive] = useState(1); const [active, setActive] = useState(1);
useEffect(() => { useEffect(() => {
console.log('av', active);
const interval = setInterval(() => { const interval = setInterval(() => {
setActive(prevActive => { setActive(prevActive => {
return prevActive === 1 ? 2 : 1; return prevActive === 1 ? 2 : 1;
@ -22,7 +20,7 @@ function SoftwareFactory() {
}, []); }, []);
return ( return (
<div className="software-factory"> <div className="software-factory home_v2_child" id="home_v2_child4">
<div className="title_v2 mb30">软件工厂</div> <div className="title_v2 mb30">软件工厂</div>
<div className={`width1600 df software-factory-${active}`}> <div className={`width1600 df software-factory-${active}`}>
<div className='software-left'> <div className='software-left'>
@ -42,7 +40,7 @@ function SoftwareFactory() {
<div className='font-16'>极速模式聚焦任务 - 代码 - 制品助力软件研发快速响应<br />实现极致效率</div> <div className='font-16'>极速模式聚焦任务 - 代码 - 制品助力软件研发快速响应<br />实现极致效率</div>
</div> </div>
</div> </div>
<Button className='blackBut mt70'> <Button className='blackBut mt70' href='/softwareFactory'>
<span>了解更多</span> <span>了解更多</span>
<span>了解更多</span> <span>了解更多</span>
</Button> </Button>

View File

@ -51,7 +51,7 @@ const SupplyChainGovernance = () => {
]; ];
return ( return (
<div className="supply-chain-governance"> <div className="supply-chain-governance home_v2_child" id="home_v2_child6">
<div className="title_v2">供应链治理</div> <div className="title_v2">供应链治理</div>
<div className="width1600 governance-grid"> <div className="width1600 governance-grid">

View File

@ -1,200 +0,0 @@
import React, { useEffect, useRef } from 'react';
const TerminalAnimation = () => {
const canvasRef = useRef(null);
const animationRef = useRef(null);
const stateRef = useRef({
lineI: 0,
charI: 0,
lastChT: 0,
lastLnT: 0,
curOn: true,
blinkT: 0
});
const lines = [
'$ git clone https://gitlink.org.cn/Gitlink/forgeplus.git',
'$ cd forgeplus',
'$ git remote -v',
'$ git pull origin master',
'$ git add .',
'$ git commit -m "feat: add new feature"',
'$ git push origin master',
'$ git checkout -b feature/new-ui',
'$ git merge develop',
'$ git status ',
'$ git log --oneline -10',
// '$ git fetch upstream'
];
const keyColor = {
'git clone': '#fff700',
'git commit': '#fff700',
'git log': '#fff700',
'git merge': '#fff700',
'git pull': '#d24a6f',
'git remote': '#d24a6f',
'git push': '#d24a6f',
'git status': '#2977ff',
'git fetch': '#2977ff',
'git add': '#9aff41',
'cd forgeplus': '#9aff41',
'git checkout': '#9aff41'
};
const fontSize = 15;
const lineH = 36;
const startY = 40;
const charGap = 30;
const lineGap = 300;
//
const getPixelRatio = (context) => {
const backingStore = context.backingStorePixelRatio ||
context.webkitBackingStorePixelRatio ||
context.mozBackingStorePixelRatio ||
context.msBackingStorePixelRatio ||
context.oBackingStorePixelRatio ||
context.backingStorePixelRatio || 1;
return (window.devicePixelRatio || 1) / backingStore;
};
const drawLine = (ctx, t, y, showCur, lineIndex) => {
const prompt = '$ ';
const pW = ctx.measureText(prompt).width;
ctx.fillStyle = '#ffffff';
ctx.fillText(prompt, 30, y);
if (!t.startsWith(prompt)) return;
let seg = t.slice(prompt.length);
const fullLine = lines[lineIndex] || t;
const fullSeg = fullLine.startsWith(prompt) ? fullLine.slice(prompt.length) : fullLine;
let drawnWidth = 0;
for (let key in keyColor) {
if (fullSeg.startsWith(key)) {
const visibleKeyLength = Math.min(key.length, seg.length);
if (visibleKeyLength > 0) {
const visibleKeyText = seg.slice(0, visibleKeyLength);
ctx.fillStyle = keyColor[key];
ctx.fillText(visibleKeyText, 30 + pW + drawnWidth, y);
drawnWidth += ctx.measureText(visibleKeyText).width;
}
seg = seg.slice(visibleKeyLength);
break;
}
}
if (seg) {
ctx.fillStyle = '#ffffff';
ctx.fillText(seg, 30 + pW + drawnWidth, y);
}
if (showCur && stateRef.current.curOn) {
const curX = 30 + ctx.measureText(t + ' ').width;
ctx.fillRect(curX, y - fontSize + 4, 2, fontSize - 2);
}
};
const draw = (ctx) => {
//
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
for (let i = 0; i < stateRef.current.lineI; i++) {
drawLine(ctx, lines[i], startY + i * lineH, false, i);
}
if (stateRef.current.lineI < lines.length) {
drawLine(ctx, lines[stateRef.current.lineI].slice(0, stateRef.current.charI), startY + stateRef.current.lineI * lineH, true, stateRef.current.lineI);
}
};
const animate = (timestamp) => {
const canvas = canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext('2d');
const ratio = getPixelRatio(ctx);
ctx.font = `${fontSize * ratio}px 'Courier New', monospace`;
//
ctx.textBaseline = 'middle';
ctx.imageSmoothingEnabled = false;
const state = stateRef.current;
if (timestamp - state.blinkT > 500) {
state.curOn = !state.curOn;
state.blinkT = timestamp;
}
if (state.lineI === 0 && state.charI < lines[0].length && timestamp - state.lastChT > charGap) {
state.charI++;
state.lastChT = timestamp;
} else if (state.lineI === 0 && state.charI >= lines[0].length && timestamp - state.lastLnT > lineGap) {
state.lineI++;
state.lastLnT = timestamp;
} else if (state.lineI > 0 && state.lineI < lines.length && timestamp - state.lastLnT > lineGap) {
state.lineI++;
state.lastLnT = timestamp;
}
draw(ctx);
animationRef.current = requestAnimationFrame(animate);
};
useEffect(() => {
const canvas = canvasRef.current;
if (!canvas) return;
//
const ctx = canvas.getContext('2d');
const ratio = getPixelRatio(ctx);
//
const originalWidth = canvas.width;
const originalHeight = canvas.height;
//
canvas.width = originalWidth * ratio;
canvas.height = originalHeight * ratio;
//
canvas.style.width = originalWidth + 'px';
canvas.style.height = originalHeight + 'px';
//
ctx.scale(ratio, ratio);
ctx.font = `${fontSize * ratio}px 'Courier New', monospace`;
ctx.textBaseline = 'middle';
ctx.imageSmoothingEnabled = false;
ctx.antialiasing = 'none';
//
animationRef.current = requestAnimationFrame(animate);
//
return () => {
if (animationRef.current) {
cancelAnimationFrame(animationRef.current);
}
};
}, []);
return (
<div className='center'>
<canvas
ref={canvasRef}
width="755"
height="420"
/>
</div>
);
};
export default TerminalAnimation;

View File

@ -1,6 +1,6 @@
import React, { useEffect, useRef, useState } from 'react'; import React, { useEffect, useRef, useState } from 'react';
const TerminalAnimation = ({startAnimation=false}) => { const TerminalAnimation = ({ startAnimation = false }) => {
const [animationState, setAnimationState] = useState({ const [animationState, setAnimationState] = useState({
lineI: 0, lineI: 0,
charI: 0, charI: 0,
@ -43,9 +43,9 @@ const TerminalAnimation = ({startAnimation=false}) => {
const startY = 40; const startY = 40;
const charGap = 30; const charGap = 30;
const lineGap = 300; const lineGap = 300;
const lineNumberWidth = 40; // const lineNumberWidth = 40;
// //
const getCharWidth = (charCount) => charCount * 9.6; const getCharWidth = (charCount) => charCount * 9.6;
// //
@ -54,15 +54,12 @@ const TerminalAnimation = ({startAnimation=false}) => {
let currentX = x; let currentX = x;
let charsRendered = 0; let charsRendered = 0;
//
const sortedKeys = Object.keys(keyColor).sort((a, b) => b.length - a.length); const sortedKeys = Object.keys(keyColor).sort((a, b) => b.length - a.length);
let remainingText = text; let remainingText = text;
while (remainingText.length > 0 && charsRendered < availableChars) { while (remainingText.length > 0 && charsRendered < availableChars) {
let colored = false; let colored = false;
//
for (const key of sortedKeys) { for (const key of sortedKeys) {
if (remainingText.startsWith(key)) { if (remainingText.startsWith(key)) {
const charsToRender = Math.min(key.length, availableChars - charsRendered); const charsToRender = Math.min(key.length, availableChars - charsRendered);
@ -91,7 +88,6 @@ const TerminalAnimation = ({startAnimation=false}) => {
} }
} }
//
if (!colored && remainingText.length > 0) { if (!colored && remainingText.length > 0) {
const charsToRender = Math.min(1, availableChars - charsRendered); const charsToRender = Math.min(1, availableChars - charsRendered);
if (charsToRender > 0) { if (charsToRender > 0) {
@ -123,67 +119,72 @@ const TerminalAnimation = ({startAnimation=false}) => {
}; };
// //
const renderTextSegments = (line, charLimit, y, isCurrentLine, lineIndex) => { const renderTextSegments = (line, charLimit, y, isCurrentLine, lineIndex) => {
let x = lineNumberWidth; // let x = lineNumberWidth;
// const lineNumberElement = (
const lineNumberElement = ( <text
<text key="line-number"
key="line-number" x={lineNumberWidth / 2}
x={lineNumberWidth / 2} y={y + 1}
y={y+1}
fill="#ffffff"
fontFamily="'Courier New', monospace"
fontSize={fontSize}
dominantBaseline="middle"
textAnchor="middle"
fontWeight="bold"
>
{lineIndex + 1}.
</text>
);
//
const availableChars = charLimit;
//
const { elements: commandElements } = renderColoredText(
line,
x,
y,
availableChars
);
const allElements = [lineNumberElement, ...commandElements];
//
if (isCurrentLine && animationState.curOn) {
const cursorX = x + getCharWidth(charLimit);
allElements.push(
<rect
key="cursor"
x={cursorX + 2}
y={y - fontSize/2 + 2}
width={2}
height={fontSize - 4}
fill="#ffffff" fill="#ffffff"
/> fontFamily="'Courier New', monospace"
fontSize={fontSize}
dominantBaseline="middle"
textAnchor="middle"
fontWeight="bold"
>
{lineIndex + 1}.
</text>
); );
}
return allElements; const availableChars = charLimit;
}; const { elements: commandElements } = renderColoredText(
line,
x,
y,
availableChars
);
// const allElements = [lineNumberElement, ...commandElements];
useEffect(() => {
if (!startAnimation) { if (isCurrentLine && animationState.curOn) {
setAnimationState({ const cursorX = x + getCharWidth(charLimit);
lineI: 0, allElements.push(
charI: 0, <rect
curOn: true key="cursor"
}); x={cursorX + 2}
return; y={y - fontSize/2 + 2}
width={2}
height={fontSize - 4}
fill="#ffffff"
/>
);
} }
return allElements;
};
//
const resetAnimation = () => {
setAnimationState({
lineI: 0,
charI: 0,
curOn: true
});
};
// startAnimation
useEffect(() => {
if (startAnimation) {
resetAnimation();
}
}, [startAnimation]);
//
useEffect(() => {
if (!startAnimation) return;
let lastCharTime = Date.now(); let lastCharTime = Date.now();
let lastLineTime = Date.now(); let lastLineTime = Date.now();
let lastBlinkTime = Date.now(); let lastBlinkTime = Date.now();
@ -209,14 +210,18 @@ const renderTextSegments = (line, charLimit, y, isCurrentLine, lineIndex) => {
// //
else if (newState.lineI === 0 && newState.charI >= lines[0].length && now - lastLineTime > lineGap) { else if (newState.lineI === 0 && newState.charI >= lines[0].length && now - lastLineTime > lineGap) {
newState.lineI++; newState.lineI++;
newState.charI = lines[newState.lineI] ? lines[newState.lineI].length : 0; // if (newState.lineI < lines.length) {
newState.charI = lines[newState.lineI].length;
}
lastLineTime = now; lastLineTime = now;
shouldUpdate = true; shouldUpdate = true;
} }
// //
else if (newState.lineI > 0 && newState.lineI < lines.length && now - lastLineTime > lineGap) { else if (newState.lineI > 0 && newState.lineI < lines.length && now - lastLineTime > lineGap) {
newState.lineI++; newState.lineI++;
newState.charI = newState.lineI < lines.length ? lines[newState.lineI].length : 0; // if (newState.lineI < lines.length) {
newState.charI = lines[newState.lineI].length;
}
lastLineTime = now; lastLineTime = now;
shouldUpdate = true; shouldUpdate = true;
} }
@ -225,7 +230,10 @@ const renderTextSegments = (line, charLimit, y, isCurrentLine, lineIndex) => {
setAnimationState(newState); setAnimationState(newState);
} }
animationRef.current = requestAnimationFrame(animate); //
if (newState.lineI < lines.length && startAnimation) {
animationRef.current = requestAnimationFrame(animate);
}
}; };
animationRef.current = requestAnimationFrame(animate); animationRef.current = requestAnimationFrame(animate);
@ -235,7 +243,7 @@ const renderTextSegments = (line, charLimit, y, isCurrentLine, lineIndex) => {
cancelAnimationFrame(animationRef.current); cancelAnimationFrame(animationRef.current);
} }
}; };
}, [animationState, startAnimation]); }, [animationState]); // animationState
return ( return (
<div className='center pl15'> <div className='center pl15'>

View File

@ -1,6 +1,6 @@
import React, { useEffect, useRef, useState } from 'react'; import React, { useEffect, useRef, useState } from 'react';
const TerminalAnimation = ({startAnimation=true, lines, width, height, class_name}) => { const TerminalAnimation = ({ startAnimation = true, lines, width, height, class_name }) => {
const [animationState, setAnimationState] = useState({ const [animationState, setAnimationState] = useState({
lineI: 0, lineI: 0,
charI: 0, charI: 0,
@ -109,67 +109,77 @@ const TerminalAnimation = ({startAnimation=true, lines, width, height, class_nam
}; };
// //
const renderTextSegments = (line, charLimit, y, isCurrentLine, lineIndex) => { const renderTextSegments = (line, charLimit, y, isCurrentLine, lineIndex) => {
let x = lineNumberWidth; // let x = lineNumberWidth; //
// //
const lineNumberElement = ( const lineNumberElement = (
<text <text
key="line-number" key="line-number"
x={lineNumberWidth / 2} x={lineNumberWidth / 2}
y={y+1} y={y + 1}
fill="#ffffff" fill="#ffffff"
fontFamily="'Courier New', monospace" fontFamily="'Courier New', monospace"
fontSize={fontSize} fontSize={fontSize}
dominantBaseline="middle" dominantBaseline="middle"
textAnchor="middle" textAnchor="middle"
fontWeight="bold" fontWeight="bold"
> >
{lineIndex + 1}. {lineIndex + 1}.
</text> </text>
);
//
const availableChars = charLimit;
//
const { elements: commandElements } = renderColoredText(
line,
x,
y,
availableChars
);
const allElements = [...commandElements];
//
if (isCurrentLine && animationState.curOn) {
const cursorX = x + getCharWidth(charLimit);
allElements.push(
<rect
key="cursor"
x={cursorX + 2}
y={y - fontSize/2 + 2}
width={2}
height={fontSize - 4}
fill="#8187B9"
/>
); );
}
return allElements; //
}; const availableChars = charLimit;
// //
useEffect(() => { const { elements: commandElements } = renderColoredText(
if (!startAnimation) { line,
setAnimationState({ x,
lineI: 0, y,
charI: 0, availableChars
curOn: true );
});
return; const allElements = [...commandElements];
//
if (isCurrentLine && animationState.curOn) {
const cursorX = x + getCharWidth(charLimit);
allElements.push(
<rect
key="cursor"
x={cursorX + 2}
y={y - fontSize / 2 + 2}
width={2}
height={fontSize - 4}
fill="#8187B9"
/>
);
} }
return allElements;
};
//
const resetAnimation = () => {
setAnimationState({
lineI: 0,
charI: 0,
curOn: true
});
};
// startAnimation
useEffect(() => {
if (startAnimation) {
resetAnimation();
}
}, [startAnimation]);
//
useEffect(() => {
if (!startAnimation) return;
let lastCharTime = Date.now(); let lastCharTime = Date.now();
let lastLineTime = Date.now(); let lastLineTime = Date.now();
let lastBlinkTime = Date.now(); let lastBlinkTime = Date.now();
@ -195,14 +205,18 @@ const renderTextSegments = (line, charLimit, y, isCurrentLine, lineIndex) => {
// //
else if (newState.lineI === 0 && newState.charI >= lines[0].length && now - lastLineTime > lineGap) { else if (newState.lineI === 0 && newState.charI >= lines[0].length && now - lastLineTime > lineGap) {
newState.lineI++; newState.lineI++;
newState.charI = lines[newState.lineI] ? lines[newState.lineI].length : 0; // if (newState.lineI < lines.length) {
newState.charI = lines[newState.lineI].length;
}
lastLineTime = now; lastLineTime = now;
shouldUpdate = true; shouldUpdate = true;
} }
// //
else if (newState.lineI > 0 && newState.lineI < lines.length && now - lastLineTime > lineGap) { else if (newState.lineI > 0 && newState.lineI < lines.length && now - lastLineTime > lineGap) {
newState.lineI++; newState.lineI++;
newState.charI = newState.lineI < lines.length ? lines[newState.lineI].length : 0; // if (newState.lineI < lines.length) {
newState.charI = lines[newState.lineI].length;
}
lastLineTime = now; lastLineTime = now;
shouldUpdate = true; shouldUpdate = true;
} }
@ -211,7 +225,10 @@ const renderTextSegments = (line, charLimit, y, isCurrentLine, lineIndex) => {
setAnimationState(newState); setAnimationState(newState);
} }
animationRef.current = requestAnimationFrame(animate); //
if (newState.lineI < lines.length && startAnimation) {
animationRef.current = requestAnimationFrame(animate);
}
}; };
animationRef.current = requestAnimationFrame(animate); animationRef.current = requestAnimationFrame(animate);
@ -221,7 +238,7 @@ const renderTextSegments = (line, charLimit, y, isCurrentLine, lineIndex) => {
cancelAnimationFrame(animationRef.current); cancelAnimationFrame(animationRef.current);
} }
}; };
}, [animationState, startAnimation]); }, [animationState]); // animationState
return ( return (
<div className={class_name}> <div className={class_name}>

View File

@ -1,4 +1,4 @@
import React , { useEffect , useState } from 'react'; import React, { useEffect, useRef, useState } from 'react';
import { TPMIndexHOC } from "../../../../modules/tpm/TPMIndexHOC"; import { TPMIndexHOC } from "../../../../modules/tpm/TPMIndexHOC";
import './index.scss'; import './index.scss';
import Banner from './components/banner'; import Banner from './components/banner';
@ -11,21 +11,56 @@ import Forum from './components/forum';
import EcosystemAlliance from './components/ecosystemAlliance'; import EcosystemAlliance from './components/ecosystemAlliance';
function Index(props) { function Index(props) {
const observer = useRef();
const [visibleItems, setVisibleItems] = useState({});
useEffect(()=>{ useEffect(() => {
// IntersectionObserver
observer.current = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
const id = entry.target.id;
const element = document.getElementById(id);
if (element) {
if (entry.isIntersecting) {
element.classList.add('home_v2_visible');
setVisibleItems((prev)=>({...prev, [id]: true}))
} else {
element.classList.remove('home_v2_visible');
setVisibleItems((prev)=>({...prev, [id]: false}))
}
}
});
}, {
root: null,
rootMargin: '0px',
threshold: 0.1
});
//
document.querySelectorAll('.home_v2_child').forEach((el) => {
observer.current.observe(el);
});
return () => {
//
observer.current.disconnect();
};
}, []);
useEffect(() => {
document.title = props.mygetHelmetapi && props.mygetHelmetapi.name ? props.mygetHelmetapi.name : '生态创新治理中心'; document.title = props.mygetHelmetapi && props.mygetHelmetapi.name ? props.mygetHelmetapi.name : '生态创新治理中心';
},[]) }, [])
return( return (
<div className="home_v2"> <div className="home_v2">
<Banner {...props} /> <Banner isVisible={visibleItems[`home_v2_child1`]} />
<CommunityDynamic /> <CommunityDynamic />
<OpenSourceCollaboration /> <OpenSourceCollaboration isVisible={visibleItems[`home_v2_child3`]}/>
<SoftwareFactory /> <SoftwareFactory />
<Project/> <Project />
<SupplyChainGovernance /> <SupplyChainGovernance />
<Forum /> <Forum />
<EcosystemAlliance/> <EcosystemAlliance />
</div> </div>
) )
} }

View File

@ -86,18 +86,18 @@
} }
// 从下边滑入可见区 // 从下边滑入可见区
.animate-up-box { // .animate-up-box {
// 初始状态在下方不可见 // // 初始状态在下方不可见
transform: translateY(200px); // transform: translateY(200px);
opacity: 0; // opacity: 0;
transition: opacity 1s, transform 1s; // transition: opacity 1s, transform 1s;
// 动效状态从下方滑入 // // 动效状态从下方滑入
&.animate-up { // &.home_v2_visible {
transform: translateY(0); // transform: translateY(0);
opacity: 1; // opacity: 1;
} // }
} // }
// 标题悬停下划线从左到右滑出 // 标题悬停下划线从左到右滑出
.title-border-to-right { .title-border-to-right {
@ -392,7 +392,14 @@
.activity-section-Carousel{ .activity-section-Carousel{
width: 400px; width: 400px;
border-radius: 10px; border-radius: 10px;
max-height: 211px;
overflow: hidden;
.slick-track{
display: flex;
align-items: flex-end;
}
img{ img{
object-fit: cover;
max-width: 100%; max-width: 100%;
} }
} }
@ -411,9 +418,9 @@
background-color: #df2659; background-color: #df2659;
border-radius: 5px; border-radius: 5px;
font-size: 0.8rem; font-size: 0.8rem;
padding: 4px 5px; padding: 0px 5px;
position: relative; height: 18px;
top: -2px; line-height: 18px;
} }
} }
@ -736,7 +743,7 @@
/* ForumExchange.scss */ /* ForumExchange.scss */
.forum-exchange { .forum-exchange {
padding: 60px 20px 80px; padding: 60px 20px 50px;
background-image: url('./image/bk2.png'); background-image: url('./image/bk2.png');
background-size: 100% 100%; background-size: 100% 100%;

View File

@ -27,27 +27,7 @@ function ProjectHomePage(props) {
const [topicDetail, setTopicDetail] = useState(undefined); const [topicDetail, setTopicDetail] = useState(undefined);
const [count, setCount] = useState({}); const [count, setCount] = useState({});
const { countByGHM, countByOther, countByGHMType, countByMX, countByRQ } = count; const { countByGHM, countByOther, countByGHMType, countByMX, countByRQ } = count;
// const topicsByCateId = cateTopics && cateTopics[cateID]; const topicsByCateId = cateTopics && cateTopics[cateID];
const topicsByCateId = cateTopics && cateTopics[cateID] || [
{
"id": "1,2",
"name": "Maven",
"icon": "icon-maven",
"intro": "配置使用本站maven仓库首先请定位 **settings.xml** 文件,通常位于本地 Maven 安装目录的 **conf/settings.xml** 或用户目录下的 **m2/settings.xml**。\n\n然后在`settings.xml`​文件中` <mirrors>` 标签中添加私有仓库地址:\n\n```xml\n<mirrors>\n <mirror>\n <id>xzgd-repo-mirror</id>\n <mirrorOf>*</mirrorOf>\n <url>http://172.16.6.110:8081/repository/maven-repo/</url>\n </mirror>\n</mirrors>\n```\n\n\n在`settings.xml`​文件中`<profiles>`​标签中添加以下内容:\n\n\n```xml\n<profiles> \n <profile> \n <id>xzgd-maven-mirror-profile</id>\n <repositories>\n <repository>\n <id>xzgd-maven-mirror</id>\n <url>http://172.16.6.110:8081/repository/maven-repo/</url>\n </repository>\n </repositories>\n <pluginRepositories>\n <pluginRepository>\n <id>xzgd-maven-mirror</id>\n <url>http://172.16.6.110:8081/repository/maven-repo/</url> \n </pluginRepository> \n </pluginRepositories> \n </profile> \n</profiles>\n```\n\n在`settings.xml`​文件中`<activeProfiles>`​标签中添加以下内容:\n\n```xml\n<activeProfiles>\n <activeProfile>xzgd-maven-mirror-profile</activeProfile>\n</activeProfiles>\n```\n"
},
{
"id": "2,4",
"name": "Python",
"icon": "icon-python",
"intro": "**方法一**:通过命令配置\n\n运行以下命令:\n```xml\npip config set global.index-url https://mirrors.aliyun.com/pypi/simple\npip config set install.trusted-host mirrors.aliyun.com\n```\n\n**方法二**:手动修改配置文件\n\n根据操作系统不同修改 pip 配置文件:\n\n**Windows** 在用户目录下创建或编辑 pip.ini 文件路径如C:\\Users\\<用户名>\\pip\\pip.ini内容如下\n```xml\n[global]\nindex-url = https://mirrors.aliyun.com/pypi/simple/\n[install]\ntrusted-host = mirrors.aliyun.com\n```\n\n**Linux/Mac** 编辑或创建 ~/.config/pip/pip.conf 文件,内容如下:\n```xml\n[global]\nindex-url = https://mirrors.aliyun.com/pypi/simple\n[install]\ntrusted-host = mirrors.aliyun.com\n```\n\n验证配置运行以下命令检查是否成功\n```xml\npip config list\n```"
},
{
"id": "4,5",
"name": "Npm",
"icon": "icon-npm",
"intro": "通过使用npm config set registry命令切换npm的镜像源\n\n使用示例\n可以在命令行工具中输入以下命令\n```xml\nnpm config set registry https://registry.npm.taobao.org\n```\n\n切换镜像源后可以通过以下命令验证是否切换成功\n```xml\nnpm config get registry\n```\n\n如果输出结果显示镜像源已切换到淘宝的npm镜像源则表示切换成功。"
}
];
const topicsLength = topicsByCateId && topicsByCateId.length const topicsLength = topicsByCateId && topicsByCateId.length
const LIMIT = !topicsLength ? 30 : 25; const LIMIT = !topicsLength ? 30 : 25;
@ -105,7 +85,7 @@ function ProjectHomePage(props) {
if (result && result.data) { if (result && result.data) {
const { project_categories = [] } = result.data; const { project_categories = [] } = result.data;
setCateList(project_categories); setCateList(project_categories);
// project_categories[0] && setCateID(project_categories[0].id); project_categories[0] && setCateID(project_categories[0].id);
} }
}).catch(error => { }) }).catch(error => { })
} }
@ -127,9 +107,8 @@ function ProjectHomePage(props) {
params.language_id = topicId.split(",") params.language_id = topicId.split(",")
params.topic_id = undefined params.topic_id = undefined
} }
console.log('params', params);
axios.get(url, { params }).then(result => { cateID && axios.get(url, { params }).then(result => {
if (result && result.data) { if (result && result.data) {
setTotal(result.data.total_count); setTotal(result.data.total_count);
setProjectsList(result.data.projects); setProjectsList(result.data.projects);
@ -241,7 +220,20 @@ function ProjectHomePage(props) {
})} })}
</Carousel> </Carousel>
</div> </div>
<div className="mb20 mt20"> <div className="mb20 mt20 tipByCate_Popover_box">
{topicId && topicDetail && topicDetail.intro && <Popover
getPopupContainer={trigger => trigger.parentNode}
content={
<div style={{ overflowY: 'auto', maxHeight: '500px' }}>
<RenderHtml value={topicDetail.intro} />
</div>
}
trigger="click"
placement="bottomLeft"
overlayClassName="tipByCate_Popover"
>
<div className="tipByCate pointer">{`设置${topicDetail.name}包管理器仓库地址,`}<a>查看更多</a></div>
</Popover>}
<Search <Search
placeholder="输入名称关键字" placeholder="输入名称关键字"
size="middle" size="middle"
@ -286,29 +278,6 @@ function ProjectHomePage(props) {
</div> </div>
</div> </div>
</div> </div>
{/* 二级分类以及仓库列表(左右布局) */}
<div className="width1600 homepageMain df project_list_box">
{/* 二级分类,即标签列表 */}
{/* 项目列表 */}
<div className="flex1" style={{ position: 'relative' }}>
{/* 三方组件下的提示信息<div dangerouslySetInnerHTML={{__html: topicDetail.intro}}></div>*/}
{cateID === 39 && topicId && topicDetail && topicDetail.intro && <Popover
getPopupContainer={trigger => trigger.parentNode}
content={
<div style={{ overflowY: 'auto', maxHeight: '500px' }}>
<RenderHtml value={topicDetail.intro} />
</div>
}
trigger="click"
placement="bottomLeft"
overlayClassName="tipByCate_Popover"
>
<div className="tipByCate pointer">{`设置${topicDetail.name}包管理器仓库地址,`}<a>查看更多</a></div>
</Popover>}
</div>
</div>
</div> </div>
</div> </div>
} }

View File

@ -259,6 +259,18 @@
display: none; display: none;
} }
} }
.tipByCate {
padding: 10px 30px;
margin: 15px 0px;
background-image: linear-gradient(121.96deg, #fff6e2 0%, #fff4eb 100%);
border-radius: 8px;
color: #301403;
font-size: 16px;
}
pre.prettyprint {
margin-bottom: 16px !important;
}
} }
.project_recommend { .project_recommend {
@ -306,6 +318,21 @@
} }
} }
} }
.tipByCate_Popover_box{
position: relative;
.tipByCate_Popover.ant-popover {
width: 100%;
.ant-popover-arrow {
display: none;
}
.ant-popover-content {
margin-top: -13px;
}
}
}
@keyframes flowLight { @keyframes flowLight {
0% { 0% {

View File

@ -79,7 +79,7 @@ function List() {
</div> </div>
<div className="others-news mt20"> <div className="others-news mt20">
{list1.slice(4).map(item => { {list1.slice(4).map(item => {
return <Link key={item.id} to={`/news/${item.id}`} className="font-22 task-hide mb10 community-news-item">{item.name}{item.name}{item.name}<i className="iconfont icon-jiantou3 ml5 font-20"></i></Link> return <Link key={item.id} to={`/news/${item.id}`} className="font-22 task-hide community-news-item">{item.name}{item.name}{item.name}<i className="iconfont icon-jiantou3 ml5 font-20"></i></Link>
})} })}
</div> </div>
</div> </div>
@ -94,7 +94,7 @@ function List() {
<img src={require('../image/icon1.png')} style={{width: '88px', height: '88px'}}/> <img src={require('../image/icon1.png')} style={{width: '88px', height: '88px'}}/>
</div>} </div>}
<div className="activity-list-box width1600 pt40"> <div className="activity-list-box width1600 pt40">
{list2.map(item=>{ {list2.slice(1, 6).map(item=>{
return <div key={item.id} className="activity-item-home df"> return <div key={item.id} className="activity-item-home df">
<img src={icon2} alt=""/> <img src={icon2} alt=""/>
<div> <div>
@ -117,7 +117,7 @@ function List() {
<Button className="black-border-button mt30">了解更多</Button> <Button className="black-border-button mt30">了解更多</Button>
</div>} </div>}
<div className="dynamics-list"> <div className="dynamics-list">
{list3.slice(1).map(item=>{ {list3.slice(1, 9).map(item=>{
return <div className="dynamics-item" key={item.id}> return <div className="dynamics-item" key={item.id}>
<div className="font-22 FlexAJ"> <div className="font-22 FlexAJ">
<span>{item.name}</span> <span>{item.name}</span>

View File

@ -61,6 +61,7 @@
.community-news-item-img-box{ .community-news-item-img-box{
overflow: hidden; overflow: hidden;
border-radius: 10px; border-radius: 10px;
max-height: 80px;
} }
.icon-jiantou3{ .icon-jiantou3{
display: none; display: none;
@ -81,12 +82,13 @@
} }
} }
.others-news{ .others-news{
padding: 15px 30px 1px 30px; padding: 15px 30px 15px 30px;
background-color:#ffffff; background-color:#ffffff;
border-radius:10px; border-radius:10px;
box-shadow:0px 0px 6px rgba(92, 102, 193, 0.05); box-shadow:0px 0px 6px rgba(92, 102, 193, 0.05);
display: grid; display: grid;
grid-template-columns: repeat(2, 1fr); grid-template-columns: repeat(2, 1fr);
gap: 6px 30px;
} }
} }
.first-activity{ .first-activity{
@ -121,7 +123,7 @@
border-radius:10px; border-radius:10px;
box-shadow:0px 0px 6px rgba(92, 102, 193, 0.05); box-shadow:0px 0px 6px rgba(92, 102, 193, 0.05);
padding: 40px 30px; padding: 40px 30px;
gap: 45px; gap: 15px 45px;
background-image: url('../image/bk4.png'); background-image: url('../image/bk4.png');
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: right center; background-position: right center;