Merge remote-tracking branch '172/factory' into factory-hxy

This commit is contained in:
黄心宇 2025-12-23 17:21:11 +08:00
commit ff7bc8e428
20 changed files with 333 additions and 527 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 './index.scss';
function Banner() {
function Banner({isVisible}) {
const lines1 = [
'if(!isVisible)',
' return',
@ -35,7 +35,7 @@ function Banner() {
'})',
]
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="main-title-v2 width1600">
@ -72,10 +72,10 @@ function Banner() {
</div>
</div>
<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 className='center-right-bk'>
<TerminalAnimation lines={lines2} width={197} height={186} />
<TerminalAnimation startAnimation={isVisible} lines={lines2} width={197} height={186} />
</div>
<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'/>

View File

@ -3,6 +3,7 @@ import React, { Fragment, useEffect, useState } from 'react';
import { getSubDocList, getNewsDetail } from '../../../../Information/api';
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import moment from 'moment';
import moreContentButton from './moreContentButton';
const CommunityDynamic = () => {
const [list1, setList1] = useState([]);
@ -11,6 +12,7 @@ const CommunityDynamic = () => {
const [list3, setList3] = useState([]);
const firstDynamics = list3[0]
const firstByList1 = list1[0] || {}
const list2_home = list2.slice(0,4);
useEffect(() => {
getSubDocList(28).then(res => {
@ -43,7 +45,7 @@ const CommunityDynamic = () => {
}, [])
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='news_v2 width1600 padding25-30'>
{/* 精选新闻 */}
@ -64,7 +66,7 @@ const CommunityDynamic = () => {
{list1.slice(1,5).map((item, index) => (
<Fragment key={index}>
<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>
</div>
{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' /> */}
<span className='font-24 font-bd'>精彩活动</span>
<ul>
{list2.slice(0,5).map((item, index) => (
{list2_home.map((item, index) => (
<li key={index} className="activity-item-v2 df">
<span className="font-16">
<Link to={`/news/${firstByList1.id}`} className="title-border-to-right">{item.summary}</Link>
<span className="font-16 flex1 AlignCenter">
<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>}
</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>
))}
</ul>
</div>
<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}/>
})}
</Carousel>
</div>
</div>
<div className="padding20-30 flex1 project-section">
{/* <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>
{list3.slice(0,4).map((item, index) => (
<li key={index} className="activity-item-v2 df">
<span className="font-16">
<Link to={`/news/${firstByList1.id}`} className="title-border-to-right">{item.name}</Link>
<span className="font-16 flex1 AlignCenter">
<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>}
</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>
))}
</ul>
</div>
</div>
<div className='center pt30'>
{moreContentButton({ href: '/news' })}
</div>
</div>
);
};

View File

@ -1,13 +1,7 @@
import { Button } from 'antd';
import React, { useState, useEffect, useRef } from 'react';
import useIntersectionObserver from '../../../../../common/hooks/useIntersectionObserver';
const EcosystemAlliance = () => {
const [activeTab, setActiveTab] = useState('partners'); //
const [elementRef, isVisible] = useIntersectionObserver({
threshold: 0.2,
rootMargin: '0px 0px -50px 0px'
});
// const partners = [
// { name: '', logo: require('../image/logo1.png') },
@ -68,7 +62,7 @@ const EcosystemAlliance = () => {
};
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="tabs_v2 mb80">
@ -91,7 +85,7 @@ const EcosystemAlliance = () => {
</Button>
</div>
<div className='width1600 pt70'>
<div className='width1600 pt70 pb50'>
{partners1.map((i,key)=>{
return <div key={key} className='mb40'>
<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 React, { Fragment } from 'react';
import moreContentButton from './moreContentButton';
const ForumExchange = () => {
const forums = [
@ -51,11 +52,8 @@ const ForumExchange = () => {
];
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='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">
{forums.map((forum, index) => (
@ -85,6 +83,9 @@ const ForumExchange = () => {
</Fragment>
))}
</div>
<div className='center mt40 mt-8'>
{moreContentButton({href:"/forums", classname:"font-16 but-to-right font-bd color-white"})}
</div>
</div>
);
};

View File

@ -2,7 +2,7 @@ import { Icon } from "antd";
import React from "react";
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>
}

View File

@ -1,28 +1,10 @@
import React, { Fragment, useEffect, useState } from 'react';
import { AlignCenter } from '../../../../Component/layout';
import TerminalAnimation from './terminalAnimation1';
import useIntersectionObserver from '../../../../../common/hooks/useIntersectionObserver';
const OpenSourceCollaboration = () => {
import { Button } from 'antd';
const OpenSourceCollaboration = ({isVisible}) => {
const [selectedFeature, setSelectedFeature] = useState(null);
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 = [
{
@ -48,7 +30,7 @@ const OpenSourceCollaboration = () => {
};
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="content-wrapper-bottom width1600"></div>
<div className="content-wrapper width1600 df color-white mb35">
@ -66,10 +48,10 @@ const OpenSourceCollaboration = () => {
</div>
))}
<button className="blackBut white mt10">
<Button className="blackBut white mt10" href="/projects">
<span>了解更多</span>
<span>了解更多</span>
</button>
</Button>
</div>
<div className="right-panel">

View File

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

View File

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

View File

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

View File

@ -1,12 +1,12 @@
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({
lineI: 0,
charI: 0,
curOn: true
});
const animationRef = useRef(null);
const keyColor = {
@ -42,12 +42,12 @@ const TerminalAnimation = ({startAnimation=true, lines, width, height, class_nam
//
const sortedKeys = Object.keys(keyColor).sort((a, b) => b.length - a.length);
let remainingText = text;
while (remainingText.length > 0 && charsRendered < availableChars) {
let colored = false;
//
for (const key of sortedKeys) {
if (remainingText.startsWith(key)) {
@ -76,7 +76,7 @@ const TerminalAnimation = ({startAnimation=true, lines, width, height, class_nam
break;
}
}
//
if (!colored && remainingText.length > 0) {
const charsToRender = Math.min(1, availableChars - charsRendered);
@ -101,75 +101,85 @@ const TerminalAnimation = ({startAnimation=true, lines, width, height, class_nam
remainingText = remainingText.substring(charsToRender);
}
}
if (charsRendered >= availableChars) break;
}
return { elements, currentX, charsRendered };
};
//
const renderTextSegments = (line, charLimit, y, isCurrentLine, lineIndex) => {
let x = lineNumberWidth; //
//
const lineNumberElement = (
<text
key="line-number"
x={lineNumberWidth / 2}
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 = [...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 renderTextSegments = (line, charLimit, y, isCurrentLine, lineIndex) => {
let x = lineNumberWidth; //
//
useEffect(() => {
if (!startAnimation) {
setAnimationState({
lineI: 0,
charI: 0,
curOn: true
});
return;
//
const lineNumberElement = (
<text
key="line-number"
x={lineNumberWidth / 2}
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 = [...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 lastLineTime = Date.now();
let lastBlinkTime = Date.now();
@ -191,18 +201,22 @@ const renderTextSegments = (line, charLimit, y, isCurrentLine, lineIndex) => {
newState.charI++;
lastCharTime = now;
shouldUpdate = true;
}
}
//
else if (newState.lineI === 0 && newState.charI >= lines[0].length && now - lastLineTime > lineGap) {
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;
shouldUpdate = true;
}
}
//
else if (newState.lineI > 0 && newState.lineI < lines.length && now - lastLineTime > lineGap) {
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;
shouldUpdate = true;
}
@ -210,8 +224,11 @@ const renderTextSegments = (line, charLimit, y, isCurrentLine, lineIndex) => {
if (shouldUpdate) {
setAnimationState(newState);
}
animationRef.current = requestAnimationFrame(animate);
//
if (newState.lineI < lines.length && startAnimation) {
animationRef.current = requestAnimationFrame(animate);
}
};
animationRef.current = requestAnimationFrame(animate);
@ -221,7 +238,7 @@ const renderTextSegments = (line, charLimit, y, isCurrentLine, lineIndex) => {
cancelAnimationFrame(animationRef.current);
}
};
}, [animationState, startAnimation]);
}, [animationState]); // animationState
return (
<div className={class_name}>
@ -238,7 +255,7 @@ const renderTextSegments = (line, charLimit, y, isCurrentLine, lineIndex) => {
{renderTextSegments(line, line.length, startY + index * lineH, false, index)}
</g>
))}
{/* 渲染当前正在输入的行 */}
{animationState.lineI < lines.length && (
<g key="current-line">

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 './index.scss';
import Banner from './components/banner';
@ -11,21 +11,56 @@ import Forum from './components/forum';
import EcosystemAlliance from './components/ecosystemAlliance';
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 : '生态创新治理中心';
},[])
}, [])
return(
return (
<div className="home_v2">
<Banner {...props} />
<Banner isVisible={visibleItems[`home_v2_child1`]} />
<CommunityDynamic />
<OpenSourceCollaboration />
<OpenSourceCollaboration isVisible={visibleItems[`home_v2_child3`]}/>
<SoftwareFactory />
<Project/>
<Project />
<SupplyChainGovernance />
<Forum />
<EcosystemAlliance/>
<EcosystemAlliance />
</div>
)
}

View File

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

View File

@ -27,27 +27,7 @@ function ProjectHomePage(props) {
const [topicDetail, setTopicDetail] = useState(undefined);
const [count, setCount] = useState({});
const { countByGHM, countByOther, countByGHMType, countByMX, countByRQ } = count;
// 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 topicsByCateId = cateTopics && cateTopics[cateID];
const topicsLength = topicsByCateId && topicsByCateId.length
const LIMIT = !topicsLength ? 30 : 25;
@ -105,7 +85,7 @@ function ProjectHomePage(props) {
if (result && result.data) {
const { project_categories = [] } = result.data;
setCateList(project_categories);
// project_categories[0] && setCateID(project_categories[0].id);
project_categories[0] && setCateID(project_categories[0].id);
}
}).catch(error => { })
}
@ -127,9 +107,8 @@ function ProjectHomePage(props) {
params.language_id = topicId.split(",")
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) {
setTotal(result.data.total_count);
setProjectsList(result.data.projects);
@ -241,7 +220,20 @@ function ProjectHomePage(props) {
})}
</Carousel>
</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
placeholder="输入名称关键字"
size="middle"
@ -286,29 +278,6 @@ function ProjectHomePage(props) {
</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>
}

View File

@ -259,6 +259,18 @@
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 {
@ -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 {
0% {

View File

@ -30,7 +30,7 @@ function detail(props) {
}, [id])
function getDocListByDirId(id) {
getSubDocList(id).then(res => {
getSubDocList(id, {pageSize:10,pageNum:1}).then(res => {
if (res && res.data.rows) {
let rows = res.data.rows;
setList1(rows);

View File

@ -70,7 +70,7 @@ function List() {
<div className="font-15 opacity8 task-hide-2 lineh-24 pl8">{item.summary}</div>
<div className="FlexAJ mt20 pb10 pl8">
<Button className="black-border-button">了解更多</Button>
<Button className="black-border-button" href={`/news/${item.id}`}>了解更多</Button>
<span className="opacity8">{item.publishTime}发布</span>
</div>
</div>
@ -79,7 +79,7 @@ function List() {
</div>
<div className="others-news mt20">
{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}<i className="iconfont icon-jiantou3 ml5 font-20"></i></Link>
})}
</div>
</div>
@ -87,20 +87,20 @@ function List() {
<div className="activities-box pb80">
<div className="font-40 font-bd center mb60 pt60">精彩活动</div>
{firstActivity && <div className="first-activity width1600 FlexAJ">
<span style={{whiteSpace: 'break-spaces'}} className="font-36 font-bd lineh-50">{firstActivity.summary}</span>
<Link style={{whiteSpace: 'break-spaces'}} className="font-36 font-bd lineh-50" to={`/news/${firstActivity.id}`}>{firstActivity.summary}</Link>
<img src={firstActivity.headImg} alt="" width={186} className="headImg"/>
<span className="font-38 font-bd lineh-50">{moment(firstActivity.name).format('MM/DD')}<br/>{moment(firstActivity.name).format('HH:MM')}</span>
<RenderHtml value={firstActivity.content} className="first-activity-content"/>
<img src={require('../image/icon1.png')} style={{width: '88px', height: '88px'}}/>
</div>}
<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">
<img src={icon2} alt=""/>
<div>
<span className="font-20">{item.name}</span>
<div className="font-15 opacity8 mb40">{item.summary}</div>
<Button className="black-border-button moreheight">了解更多</Button>
<Button className="black-border-button moreheight" href={`/news/${item.id}`}>了解更多</Button>
</div>
</div>
})}
@ -114,10 +114,10 @@ function List() {
{firstDynamics && <div className="firstDynamics">
<div className="font-22">{firstDynamics.name}</div>
<div className="font-15 opacity8">{firstDynamics.summary}</div>
<Button className="black-border-button mt30">了解更多</Button>
{/* <Button className="black-border-button mt30">了解更多</Button> */}
</div>}
<div className="dynamics-list">
{list3.slice(1).map(item=>{
{list3.slice(1, 9).map(item=>{
return <div className="dynamics-item" key={item.id}>
<div className="font-22 FlexAJ">
<span>{item.name}</span>

View File

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