文案、首页优化,资源部分图片替换,首页滚动效果,首页课程取值等
|
|
@ -1,6 +1,3 @@
|
|||
import imageBack1 from '../images/factory/back1.png'
|
||||
import imageBack2 from '../images/factory/back2.png'
|
||||
import imageBack3 from '../images/factory/back3.png'
|
||||
|
||||
export const factoryZoneId = 102
|
||||
export const dirId = 9
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ gsap.registerPlugin(ScrollTrigger);
|
|||
|
||||
const resourceInfo = [
|
||||
{ name: '项目', title: '软件项目', icon: 'icon-xiangmu', desc: '汇聚用户需求,形成开发任务,设置质量门槛,提升软件质量,快速构建软件版本,高效应对市场变化。', path: '/softwareFactory/resource/softwareProject' },
|
||||
{ name: '版本', title: '工厂版本', icon: 'icon-banben', desc: '按照敏捷开发快速迭代的思路,形成解决微小问题的小版本,提供长期支持版本(LTS版)。', path: '/softwareFactory/resource/factoryVersion' },
|
||||
{ name: '版本', title: '软件工厂基座', icon: 'icon-banben', desc: '按照敏捷开发快速迭代的思路,形成解决微小问题的小版本,提供长期支持版本(LTS版)。', path: '/softwareFactory/resource/factoryVersion' },
|
||||
{ name: '工具', title: '开发工具', icon: 'icon-gongju', desc: '提供开发者所需的各类软件开发工具,包括IDE、Git工具、命令行终端工具、数据库设计工具、图像编辑工具等。', path: '/softwareFactory/resource/developmentTools' },
|
||||
]
|
||||
|
||||
|
|
@ -156,12 +156,14 @@ const Resource = forwardRef(({ zoneId }, ref) => {
|
|||
{
|
||||
resourceList1 && resourceList1.length > 0 && resourceList1.map((item, index) => {
|
||||
const { projectProperties } = item;
|
||||
let imageUrl = projectProperties.authorImageUrl.split(':4000/') && projectProperties.authorImageUrl.split(':4000/')[1]
|
||||
const authorImageUrl = imageUrl ? `${window.location.origin}/${ imageUrl }` : projectProperties.authorImageUrl
|
||||
// projectProperties.praisesCount = Math.floor(Math.random(0, 1) * 50)
|
||||
// projectProperties.forkedCount = Math.floor(Math.random(0, 1) * 50)
|
||||
const tags = projectProperties.topics && projectProperties.topics.split(',');
|
||||
return <div key={index} className="content-item1">
|
||||
<div className="item-head">
|
||||
<img src={projectProperties.authorImageUrl} alt="" />
|
||||
<img src={authorImageUrl} alt="" />
|
||||
<Link to={`/${projectProperties.fullName}`}><div className="item-name">{projectProperties.name.split('/')[1]}</div></Link>
|
||||
</div>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -245,6 +245,7 @@
|
|||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
h3 {
|
||||
|
|
|
|||
|
|
@ -4,17 +4,17 @@ import lessonsRight1 from '../../../images/factory/lessons-right-1.webp'
|
|||
import { Link } from 'react-router-dom';
|
||||
import KFIcon from '../../../components/KFIcon';
|
||||
import back1 from '../../../images/factory/back1.png'
|
||||
import back2 from '../../../images/factory/back2.png'
|
||||
import tagIcon from '../../../images/factory/lessons-tag-icon.webp'
|
||||
import '../../common.scss'
|
||||
import { getSourceList } from '../../api'
|
||||
import { factoryZoneId, manualId, standardId } from '../../../constants/factory'
|
||||
import { factoryZoneId, manualId, standardId, lessonId } from '../../../constants/factory'
|
||||
import { downloadFunc } from '../../../forge/Utils/utils'
|
||||
|
||||
const Resource2 = forwardRef(({ zoneId }, ref) => {
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
const [standard, setStandard] = useState({});
|
||||
const [manualList, setManualList] = useState([]);
|
||||
const [chapterList, setChapterList] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
|
|
@ -25,7 +25,7 @@ const Resource2 = forwardRef(({ zoneId }, ref) => {
|
|||
}, { threshold: 0.1 });
|
||||
getStandard()
|
||||
getManualList()
|
||||
|
||||
getLessonList()
|
||||
|
||||
if (ref.current) {
|
||||
observer.observe(ref.current);
|
||||
|
|
@ -61,6 +61,35 @@ const Resource2 = forwardRef(({ zoneId }, ref) => {
|
|||
});
|
||||
}
|
||||
|
||||
const getLessonList = () => {
|
||||
getSourceList({ id: factoryZoneId, keywords: lessonId }).then(res => {
|
||||
const rows = []
|
||||
res.data.rows.forEach(e => {
|
||||
let extendData = {}
|
||||
if (e.extendData) {
|
||||
extendData = JSON.parse(e.extendData)
|
||||
}
|
||||
rows.push({ ...e, extendDataUrl: extendData.url, extendDataHeadImg: extendData.headImg })
|
||||
});
|
||||
// setLesson(rows[0]);
|
||||
getChapterList(rows[0].name)
|
||||
});
|
||||
};
|
||||
|
||||
const getChapterList = (id) => {
|
||||
getSourceList({ id: factoryZoneId, keywords: id }).then(res => {
|
||||
const rows = []
|
||||
res.data.rows.forEach(e => {
|
||||
let extendData = {}
|
||||
if (e.extendData) {
|
||||
extendData = JSON.parse(e.extendData)
|
||||
}
|
||||
rows.push({ ...e, extendDataUrl: extendData.url, extendDataHeadImg: extendData.headImg })
|
||||
});
|
||||
setChapterList(rows);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`factory-resource2 factory-module ${isVisible ? 'animate-active' : ''}`}
|
||||
|
|
@ -72,22 +101,18 @@ const Resource2 = forwardRef(({ zoneId }, ref) => {
|
|||
<h3>在线课程</h3>
|
||||
<p>全景智治贯通需求到交付,敏捷可视管控风险,打造高效可信软件生产新范式,赋能研发跃迁</p>
|
||||
<div className="left-content">
|
||||
<div className="left-item">
|
||||
<img src={back1} alt="" />
|
||||
<div className="item-info">
|
||||
<h4>Numpy科学计算</h4>
|
||||
<p>本AI核心通识课课程是一门全面且深入的入门课程,旨在为学生提供一个从基础到高级的全方位...</p>
|
||||
<button className="shiny-button">查看详情</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="left-item">
|
||||
<img src={back2} alt="" />
|
||||
<div className="item-info">
|
||||
<h4>探索数据类型</h4>
|
||||
<p>本AI核心通识课课程是一门全面且深入的入门课程,旨在为学生提供一个从基础到高级的全方位...</p>
|
||||
<button className="shiny-button">查看详情</button>
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
chapterList.slice(0,2).map((item, index) => {
|
||||
return <div className="left-item" key={index}>
|
||||
<img src={ item.extendDataHeadImg || back1} alt="" />
|
||||
<div className="item-info">
|
||||
<h4>{ item.name }</h4>
|
||||
<p>{item.summary}</p>
|
||||
<button className="shiny-button" onClick={() => { window.open(item.extendDataUrl) }}>查看详情</button>
|
||||
</div>
|
||||
</div>
|
||||
})
|
||||
}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -13,23 +13,34 @@ import { factoryZoneId } from '../constants/factory';
|
|||
import bg1 from '../images/factory/bg-1.mov'
|
||||
import bg2 from '../images/factory/bg-2.mp4'
|
||||
import mouse from '../images/factory/mouse.png'
|
||||
|
||||
import DelayedScroll from '../forge/Utils/delayScroll'
|
||||
gsap.registerPlugin(ScrollTrigger);
|
||||
|
||||
const bannerInfo = {
|
||||
title: `软件工厂,开启XX软件研发新时代`,
|
||||
title: `软件工厂,开启xx软件研发新时代`,
|
||||
desc: '软件开发过程数字化,软件开发活动智能化,软件开发链路敏捷化',
|
||||
link: '/'
|
||||
link: '/',
|
||||
forums: 'forums',
|
||||
forumsName: '软件工厂'
|
||||
}
|
||||
|
||||
let delayScroll
|
||||
|
||||
function Index(props) {
|
||||
const contentRef = useRef(null);
|
||||
const bannerRef = useRef(null);
|
||||
const resourceRef = useRef(null);
|
||||
const resource2Ref = useRef(null);
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
const [banner2isVisible, setBanner2IsVisible] = useState(false);
|
||||
const [firstV, setFirstV] = useState(true)
|
||||
const isAnimating = useRef(false)
|
||||
const lastScrollY = useRef(false)
|
||||
const firstScreenRef = useRef(false)
|
||||
const ticking = useRef(false)
|
||||
|
||||
useEffect(() => {
|
||||
delayScroll = new DelayedScroll()
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
const entry = entries[0];
|
||||
// 如果元素进入视口
|
||||
|
|
@ -46,8 +57,27 @@ function Index(props) {
|
|||
observer.observe(contentRef.current);
|
||||
}
|
||||
|
||||
const observer2 = new IntersectionObserver((entries) => {
|
||||
const entry = entries[0];
|
||||
if (entry.isIntersecting) {
|
||||
setBanner2IsVisible(true);
|
||||
observer2.disconnect();
|
||||
}
|
||||
}, {
|
||||
threshold: 0.1
|
||||
});
|
||||
|
||||
if (bannerRef.current) {
|
||||
observer2.observe(bannerRef.current);
|
||||
}
|
||||
|
||||
setupScrollListeners()
|
||||
|
||||
return () => {
|
||||
if (contentRef.current) observer.disconnect();
|
||||
if (bannerRef.current) observer2.disconnect();
|
||||
if (delayScroll) delayScroll.cleanup()
|
||||
window.removeEventListener('scroll', helpScroll)
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
|
@ -77,24 +107,108 @@ function Index(props) {
|
|||
};
|
||||
}, [resourceRef, resource2Ref]);
|
||||
|
||||
const setupScrollListeners = () => {
|
||||
window.addEventListener('scroll', helpScroll);
|
||||
// 检测第一屏是否离开
|
||||
// const observer = new IntersectionObserver(
|
||||
// (entries) => {
|
||||
// entries.forEach(entry => {
|
||||
// if (!entry.isIntersecting) {
|
||||
// // 第一屏离开视口,禁用snap
|
||||
// // this.disableSnap();
|
||||
// // this.isFirstScreenActive = false;
|
||||
// }
|
||||
// });
|
||||
// },
|
||||
// {
|
||||
// threshold: 0.1,
|
||||
// rootMargin: '0px'
|
||||
// }
|
||||
// );
|
||||
// observer.observe(firstScreenRef.current);
|
||||
}
|
||||
|
||||
const helpScroll = () => {
|
||||
if (!ticking.current) {
|
||||
requestAnimationFrame(() => {
|
||||
handleScroll();
|
||||
ticking.current = false;
|
||||
});
|
||||
ticking.current = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const scrollToNextSection = () => {
|
||||
if (isAnimating.current) return;
|
||||
isAnimating.current = true;
|
||||
const targetScroll = window.innerHeight;
|
||||
// 平滑滚动
|
||||
window.scrollTo({
|
||||
top: targetScroll,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
|
||||
// 滚动完成后标记
|
||||
setTimeout(() => {
|
||||
isAnimating.current = false;
|
||||
}, 800);
|
||||
}
|
||||
|
||||
const scrollToTop = () => {
|
||||
if (isAnimating.current) return;
|
||||
isAnimating.current = true;
|
||||
window.scrollTo({
|
||||
top: 0,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
setTimeout(() => {
|
||||
isAnimating.current = false;
|
||||
}, 800);
|
||||
}
|
||||
|
||||
const handleScroll = () => {
|
||||
const currentScrollY = window.scrollY;
|
||||
const firstScreenHeight = window.innerHeight;
|
||||
// if (this.isFirstScreenActive) {
|
||||
if (currentScrollY > 50 && currentScrollY < firstScreenHeight && currentScrollY > lastScrollY.current) {
|
||||
scrollToNextSection();
|
||||
}
|
||||
if ( currentScrollY < firstScreenHeight - 50 && currentScrollY < lastScrollY.current) {
|
||||
scrollToTop()
|
||||
}
|
||||
// }
|
||||
lastScrollY.current = currentScrollY;
|
||||
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="factory">
|
||||
<div className="factory-banner">
|
||||
<video className={ `factory-banner-video ${ firstV ? 'video-show' : '' }` } src={bg1} autoPlay muted onEnded={() => { setFirstV(false) }}></video>
|
||||
<video className={ `factory-banner-video ${ !firstV ? 'video-show' : '' }` } src={bg2} autoPlay muted loop></video>
|
||||
<div className="video-wpapper" ref={firstScreenRef}>
|
||||
<video className={`factory-banner-video ${firstV ? 'video-show' : ''}`} src={bg1} autoPlay muted onEnded={() => { setFirstV(false) }}></video>
|
||||
<video className={`factory-banner-video ${!firstV ? 'video-show' : ''}`} src={bg2} autoPlay muted loop></video>
|
||||
</div>
|
||||
<div className="banner-content">
|
||||
<h1>{bannerInfo.title}</h1>
|
||||
<p>{bannerInfo.desc}</p>
|
||||
<Link to={`${bannerInfo.link}`}>
|
||||
<div className="banner-content-button">立即体验</div>
|
||||
</Link>
|
||||
<div className="banner-content-more">
|
||||
<div className="h-wrapper delayed-element"><h1>{bannerInfo.title}</h1></div>
|
||||
<div className="p-wrapper delayed-element"><p>{bannerInfo.desc}</p></div>
|
||||
|
||||
<div className="b-wrapper delayed-element">
|
||||
<Link to={`${bannerInfo.link}`}>
|
||||
<div className="banner-content-button">立即体验</div>
|
||||
</Link>
|
||||
<Link to={{ pathname: `${bannerInfo.forums}`, state: { name: bannerInfo.forumsName } }}>
|
||||
<div className="banner-content-button">论坛交流</div>
|
||||
</Link>
|
||||
</div>
|
||||
<div className={`banner-content-more delayed-element ${!banner2isVisible ? '' : 'hide'}`} onClick={() => { scrollToNextSection() }}>
|
||||
<div>
|
||||
<img src={ mouse } alt="" ></img>
|
||||
<img src={mouse} alt="" ></img>
|
||||
</div>
|
||||
<span>下滑了解更多</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="banner-2" ref={bannerRef}></div>
|
||||
{/* <div className="banner-data">
|
||||
{
|
||||
bannerData.map(e => {
|
||||
|
|
@ -108,10 +222,10 @@ function Index(props) {
|
|||
}
|
||||
</div> */}
|
||||
</div>
|
||||
<div className={`factory-video factory-module ${isVisible ? 'animate-active' : ''}`} ref={contentRef}>
|
||||
<h1 className="module-title">软件工厂 宣传视频</h1>
|
||||
{/* <div className={`factory-video factory-module ${isVisible ? 'animate-active' : ''}`} ref={contentRef}>
|
||||
<h1 className="module-title">软件工厂 精彩呈现</h1>
|
||||
<video controls autoPlay muted src="http://172.20.32.203:4000/api/attachments/598dbdfe-a767-46d6-aafa-6c3329edccba"></video>
|
||||
</div>
|
||||
</div> */}
|
||||
|
||||
<News zoneId={factoryZoneId} />
|
||||
<Feature />
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
align-items: center;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
background-color: #000316;
|
||||
|
||||
// background-image: url("../images/factory/banner1.png");
|
||||
// background-size: cover;
|
||||
|
|
@ -18,6 +19,11 @@
|
|||
// background-repeat: no-repeat;
|
||||
// background-position: top center;
|
||||
// /* 可选:将图片居中显示 */
|
||||
|
||||
.video-wpapper {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&-video {
|
||||
width: 100%;
|
||||
display: none;
|
||||
|
|
@ -35,6 +41,12 @@
|
|||
align-items: center;
|
||||
height: 40vh;
|
||||
|
||||
.h-wrapper {
|
||||
--delay-factor: 0.45;
|
||||
transition: transform 0.1s ease-out;
|
||||
transform: translateY(calc(var(--scroll-delay) * 1px)) !important;
|
||||
}
|
||||
|
||||
h1 {
|
||||
line-height: normal;
|
||||
font-family: "DouyinSansBold";
|
||||
|
|
@ -49,6 +61,13 @@
|
|||
letter-spacing: 10px;
|
||||
}
|
||||
|
||||
.p-wrapper {
|
||||
--delay-factor: 0.5;
|
||||
transition: transform 0.1s ease-out;
|
||||
transform: translateY(calc(var(--scroll-delay) * 1px)) !important;
|
||||
}
|
||||
|
||||
|
||||
p {
|
||||
opacity: 0;
|
||||
color: #ffffffd3;
|
||||
|
|
@ -61,7 +80,36 @@
|
|||
animation-delay: 2s;
|
||||
}
|
||||
|
||||
.b-wrapper {
|
||||
--delay-factor: 0.5;
|
||||
transition: transform 0.2s ease-out !important;
|
||||
transform: translateY(calc(var(--scroll-delay) * 1px)) !important;
|
||||
display: flex;
|
||||
|
||||
a:nth-child(2) {
|
||||
div {
|
||||
background: url('../images/factory/header-button.png');
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100% 100%;
|
||||
margin-right: 0;
|
||||
&::after {
|
||||
opacity: 0;
|
||||
}
|
||||
&::before {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
margin-top: 50px;
|
||||
|
||||
}
|
||||
|
||||
&-button {
|
||||
margin-right: 50px;
|
||||
opacity: 0;
|
||||
width: 230px;
|
||||
height: 64px;
|
||||
|
|
@ -75,11 +123,11 @@
|
|||
text-align: center;
|
||||
overflow: hidden;
|
||||
line-height: 58px;
|
||||
margin-top: 50px;
|
||||
animation: fadeInUp 2s ease-in-out forwards;
|
||||
animation-delay: 2s;
|
||||
|
||||
&::before {
|
||||
z-index: 1;
|
||||
content: '';
|
||||
opacity: 0.9;
|
||||
width: 138px;
|
||||
|
|
@ -93,6 +141,7 @@
|
|||
}
|
||||
|
||||
&::after {
|
||||
z-index: 1;
|
||||
content: '';
|
||||
width: 138px;
|
||||
position: absolute;
|
||||
|
|
@ -113,12 +162,16 @@
|
|||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding-bottom: 20px;
|
||||
animation: mouseShow 1s forwards;
|
||||
animation: mouseShow 1s forwards;
|
||||
animation-delay: 4s;
|
||||
--delay-factor: 0.05;
|
||||
transition: transform 0.3s ease-out;
|
||||
transform: translateY(calc(var(--scroll-delay) * 1px)) !important;
|
||||
|
||||
div {
|
||||
position: relative;
|
||||
&::after {
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
width: 3px;
|
||||
height: 8px;
|
||||
|
|
@ -126,7 +179,7 @@
|
|||
position: absolute;
|
||||
top: 8px;
|
||||
left: 50%;
|
||||
transform: translate(-50%,0);
|
||||
transform: translate(-50%, 0);
|
||||
animation: mouse 1.5s ease-out infinite;
|
||||
}
|
||||
}
|
||||
|
|
@ -145,6 +198,18 @@
|
|||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.banner-2 {
|
||||
height: 100vh;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
top: 100vh;
|
||||
}
|
||||
|
||||
.hide {
|
||||
opacity: 0 !important;
|
||||
}
|
||||
|
||||
.banner-data {
|
||||
|
|
@ -297,6 +362,7 @@
|
|||
50% {
|
||||
top: 8px
|
||||
}
|
||||
|
||||
to {
|
||||
top: 16px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,12 +76,12 @@ function Index({searchValue, searchType},ref) {
|
|||
{
|
||||
projectList.map((item, index) => {
|
||||
const project = item.projectProperties || {}
|
||||
project.praisesCount = 3
|
||||
project.forkedCount = 3
|
||||
let imageUrl = project.authorImageUrl.split(':4000/') && project.authorImageUrl.split(':4000/')[1]
|
||||
const authorImageUrl = imageUrl ? `${window.location.origin}/${ imageUrl }` : project.authorImageUrl
|
||||
return <ActionItem className="content-item-wrapper" key={index} padding="1px" borderRadius="23px">
|
||||
<div className="content-project-item">
|
||||
<div className="item-top">
|
||||
<img src={project.authorImageUrl} alt="" className="item-img"></img>
|
||||
<img src={authorImageUrl} alt="" className="item-img"></img>
|
||||
<h5 className="item-title">{project.name}</h5>
|
||||
</div>
|
||||
<div className="item-desc">
|
||||
|
|
@ -98,13 +98,8 @@ function Index({searchValue, searchType},ref) {
|
|||
</div>
|
||||
|
||||
<div className="item-data">
|
||||
|
||||
{
|
||||
project.praisesCount > 0 && <span><i className="iconfont icon-liulanliang mr5 font-12" />{project.praisesCount}</span>
|
||||
}
|
||||
{
|
||||
project.forkedCount > 0 && <span><i className="iconfont icon-shoucang mr5 font-12" />{project.forkedCount}</span>
|
||||
}
|
||||
<span><i className="iconfont icon-guanzhu mr5 font-12" />{project.forkedCount}</span>
|
||||
<span><i className="iconfont icon-dianzan mr5 font-12" />{project.praisesCount}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="content-project-btn">
|
||||
|
|
@ -155,7 +150,7 @@ function Index({searchValue, searchType},ref) {
|
|||
{
|
||||
versionList.length > 0 && <div className="search-version">
|
||||
<div className="search-header">
|
||||
<h3>工厂版本</h3>
|
||||
<h3>软件工厂基座</h3>
|
||||
<div><span>#</span>版本 <span>{versionTotal}个</span></div>
|
||||
</div>
|
||||
<div className="search-content">
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ function Index(props) {
|
|||
<span>工具类型</span>
|
||||
</div>
|
||||
</div>
|
||||
<button className="shiny-button" onClick={() => { downloadFunc(`${httpUrl}/file/open/download/${item.fileIds}`) }}>立即下载</button>
|
||||
<button className="shiny-button" onClick={() => { downloadFunc(`${httpUrl}/file/open/download/${detail.fileIds}`) }}>立即下载</button>
|
||||
<h6 className="more-title">更多热门工具</h6>
|
||||
<div className="more-tools">
|
||||
{
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ const DevelopmentTools = Loadable({
|
|||
const zoneId = 199
|
||||
|
||||
const typeMap = [
|
||||
{ name: '工厂版本', key: 'factoryVersion', icon: 'icon-gongchangbanben' },
|
||||
{ name: '软件工厂基座', key: 'factoryVersion', icon: 'icon-gongchangbanben' },
|
||||
{ name: '软件项目', key:'softwareProject', icon: 'icon-ruanjianxiangmu' },
|
||||
{ name: '开发工具', key: 'developmentTools', icon: 'icon-kaifagongju' },
|
||||
]
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import KFIcon from '../../../components/KFIcon';
|
|||
|
||||
import bg1 from '../../../images/factory/resource/project-top-1.webp';
|
||||
import bg2 from '../../../images/factory/resource/project-top-2.webp';
|
||||
import bg3 from '../../../images/factory/resource/project-top-3.png';
|
||||
import bg3 from '../../../images/factory/resource/project-top-3.webp';
|
||||
|
||||
import bg4 from '../../../images/factory/resource/project-content-1.webp';
|
||||
import bg5 from '../../../images/factory/resource/project-content-2.webp';
|
||||
|
|
@ -74,12 +74,14 @@ function Index(props) {
|
|||
{
|
||||
topList.map((item, index) => {
|
||||
const project = item.projectProperties
|
||||
let imageUrl = project.authorImageUrl.split(':4000/') && project.authorImageUrl.split(':4000/')[1]
|
||||
const authorImageUrl = imageUrl ? `${window.location.origin}/${imageUrl}` : project.authorImageUrl
|
||||
// project.praisesCount = Math.floor(Math.random(0, 1) * 50)
|
||||
// project.forkedCount = Math.floor(Math.random(0, 1) * 50)
|
||||
return (
|
||||
<Link to={`/${project.fullName}`}><div className="project-top-item" key={index}>
|
||||
<img src={bgList[index]} alt="" className="item-bg" />
|
||||
<img src={project.authorImageUrl} alt="" className="item-img"></img>
|
||||
<img src={authorImageUrl} alt="" className="item-img"></img>
|
||||
<div className="item-info">
|
||||
<h5 className="item-title">{project.name.split('/')[1]}</h5>
|
||||
<p className="item-desc">{project.description}</p>
|
||||
|
|
@ -92,8 +94,8 @@ function Index(props) {
|
|||
)
|
||||
})
|
||||
} */}
|
||||
<img src={ user1 } alt="" />
|
||||
<span>捐赠者</span> <div></div>{ project.name.split('/')[0] }
|
||||
<img src={user1} alt="" />
|
||||
<span>捐赠者</span> <div></div>{project.name.split('/')[0]}
|
||||
</div>
|
||||
<div className="item-bottom-data">
|
||||
<span><i className="iconfont icon-guanzhu mr5 font-12" />{project.forkedCount}</span>
|
||||
|
|
@ -117,12 +119,14 @@ function Index(props) {
|
|||
{
|
||||
contentList.map((item, index) => {
|
||||
const project = item.projectProperties
|
||||
let imageUrl = project.authorImageUrl.split(':4000/') && project.authorImageUrl.split(':4000/')[1]
|
||||
const authorImageUrl = imageUrl ? `${window.location.origin}/${imageUrl}` : project.authorImageUrl
|
||||
// project.praisesCount = Math.floor(Math.random(0, 1) * 50)
|
||||
// project.forkedCount = Math.floor(Math.random(0, 1) * 50)
|
||||
return <Link to={`/${project.fullName}`} className="content-top-item"><div key={index}>
|
||||
<img className="item-bg" src={contentBgList[index]} alt="" />
|
||||
<div className="item-top">
|
||||
<img src={project.authorImageUrl} alt="" className="item-img"></img>
|
||||
<img src={authorImageUrl} alt="" className="item-img"></img>
|
||||
<h5 className="item-title">{project.name.split('/')[1]}</h5>
|
||||
</div>
|
||||
<div className="item-tags">
|
||||
|
|
@ -133,8 +137,8 @@ function Index(props) {
|
|||
)
|
||||
})
|
||||
} */}
|
||||
<img src={ user2 } alt="" />
|
||||
<span>捐赠者</span> <div></div>{ project.name.split('/')[0] }
|
||||
<img src={user2} alt="" />
|
||||
<span>捐赠者</span> <div></div>{project.name.split('/')[0]}
|
||||
</div>
|
||||
<div className="item-desc">
|
||||
{project.description}
|
||||
|
|
@ -161,6 +165,8 @@ function Index(props) {
|
|||
{
|
||||
fullList.map((item, index) => {
|
||||
const project = item.projectProperties
|
||||
let imageUrl = project.authorImageUrl.split(':4000/') && project.authorImageUrl.split(':4000/')[1]
|
||||
const authorImageUrl = imageUrl ? `${window.location.origin}/${imageUrl}` : project.authorImageUrl
|
||||
// project.praisesCount = Math.floor(Math.random(0, 1) * 50)
|
||||
// project.forkedCount = Math.floor(Math.random(0, 1) * 50)
|
||||
return <ActionItem className="content-bottom-wrapper" key={index} backgroundColor="rgba(19, 53, 91, 0.11)" padding="1px" borderRadius="16px">
|
||||
|
|
@ -173,19 +179,21 @@ function Index(props) {
|
|||
<img src={project.authorImageUrl} alt="" className="item-img"></img>
|
||||
<h5 className="item-title">{project.name.split('/')[1]}</h5>
|
||||
</div>
|
||||
<div className="item-desc">
|
||||
{!project.description ? '暂无简介' : project.description}
|
||||
</div>
|
||||
<div className="item-tags">
|
||||
{
|
||||
<div className="item-bottom-tags">
|
||||
{/* {
|
||||
project.topics && project.topics.split(',').splice(0, 3).map((topic, index) => {
|
||||
return (
|
||||
<span key={index} className="item-tag">{topic}</span>
|
||||
)
|
||||
})
|
||||
}
|
||||
} */}
|
||||
<img src={user1} alt="" />
|
||||
<span>捐赠者</span> <div></div>{project.name.split('/')[0]}
|
||||
</div>
|
||||
|
||||
<div className="item-desc">
|
||||
{!project.description ? '暂无简介' : project.description}
|
||||
</div>
|
||||
<div className="item-data">
|
||||
<span><i className="iconfont icon-guanzhu mr5 font-12" />{project.forkedCount}</span>
|
||||
<span><i className="iconfont icon-dianzan mr5 font-12" />{project.praisesCount}</span>
|
||||
|
|
|
|||
|
|
@ -317,10 +317,10 @@
|
|||
|
||||
.item-bg {
|
||||
position: absolute;
|
||||
width: 420px;
|
||||
height: 219px;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 416px;
|
||||
height: 215px;
|
||||
left: 2px;
|
||||
top: 2px;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
|
|
@ -447,27 +447,59 @@
|
|||
|
||||
.item-desc {
|
||||
-webkit-line-clamp: 2;
|
||||
margin-top: 26px;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.item-tags {
|
||||
margin-top: auto;
|
||||
.item-bottom-tags {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
margin-top: 20px;
|
||||
width: fit-content;
|
||||
background: rgba(179, 190, 252, 0.18);
|
||||
border: 1px solid;
|
||||
border-color: #ffffff;
|
||||
border-radius: 4px;
|
||||
height: 32px;
|
||||
padding: 5px 9px;
|
||||
line-height: 22px;
|
||||
font-family: alibabareg;
|
||||
color: #091221;
|
||||
font-size: 13px;
|
||||
|
||||
.item-tag {
|
||||
display: inline-block;
|
||||
padding: 0 8px;
|
||||
margin-right: 8px;
|
||||
height: 32px;
|
||||
background: rgba(22, 76, 228, 0.05);
|
||||
border-radius: 4px;
|
||||
line-height: 32px;
|
||||
font-family: alibabareg;
|
||||
color: #091221;
|
||||
font-size: 14px;
|
||||
img {
|
||||
width: 15px;
|
||||
margin-right: 5px;
|
||||
height: 15px;
|
||||
}
|
||||
|
||||
div {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
background: #ffffff;
|
||||
border-radius: 50%;
|
||||
margin: 0 6px;
|
||||
}
|
||||
}
|
||||
|
||||
// .item-tags {
|
||||
// margin-top: auto;
|
||||
// display: flex;
|
||||
// justify-content: flex-start;
|
||||
|
||||
// .item-tag {
|
||||
// display: inline-block;
|
||||
// padding: 0 8px;
|
||||
// margin-right: 8px;
|
||||
// height: 32px;
|
||||
// background: rgba(22, 76, 228, 0.05);
|
||||
// border-radius: 4px;
|
||||
// line-height: 32px;
|
||||
// font-family: alibabareg;
|
||||
// color: #091221;
|
||||
// font-size: 14px;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
.content-bottom-btn {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,76 @@
|
|||
/**
|
||||
* 延迟滚动
|
||||
* 延迟滚动元素class:delayed-element
|
||||
* 延迟滚动元素css属性(用于设定延迟多少):--scroll-delay
|
||||
*/
|
||||
class DelayedScroll {
|
||||
constructor() {
|
||||
this.initialized = false;
|
||||
this.maxScroll = window.innerHeight; // 最多监听2屏的高度
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
if (this.initialized) return;
|
||||
|
||||
let ticking = false;
|
||||
|
||||
const handleScroll = () => {
|
||||
if (!ticking) {
|
||||
requestAnimationFrame(() => {
|
||||
const scrollY = window.scrollY;
|
||||
|
||||
// 只在首屏范围内应用效果
|
||||
if (scrollY <= this.maxScroll) {
|
||||
this.updateElements(scrollY);
|
||||
} else {
|
||||
// 超出范围时停止监听
|
||||
window.removeEventListener('scroll', scrollHandler);
|
||||
this.cleanup();
|
||||
}
|
||||
|
||||
ticking = false;
|
||||
});
|
||||
ticking = true;
|
||||
}
|
||||
};
|
||||
|
||||
// 使用节流的滚动监听
|
||||
const scrollHandler = () => {
|
||||
if (window.scrollY <= this.maxScroll) {
|
||||
handleScroll();
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('scroll', scrollHandler, { passive: true });
|
||||
|
||||
// 初始执行一次
|
||||
this.updateElements(window.scrollY);
|
||||
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
updateElements(scrollY) {
|
||||
document.querySelectorAll('.delayed-element').forEach(element => {
|
||||
const delayFactor = this.getDelayFactor(element);
|
||||
const delay = scrollY * delayFactor;
|
||||
element.style.setProperty('--scroll-delay', delay);
|
||||
});
|
||||
}
|
||||
|
||||
getDelayFactor(element) {
|
||||
return parseFloat(
|
||||
getComputedStyle(element).getPropertyValue('--delay-factor') || 0.2
|
||||
);
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
// 可选:移除transform,恢复原状
|
||||
document.querySelectorAll('.delayed-element').forEach(element => {
|
||||
element.style.removeProperty('--scroll-delay');
|
||||
element.style.removeProperty('transform');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default DelayedScroll
|
||||
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 8.1 KiB |
|
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 290 KiB |
|
After Width: | Height: | Size: 8.4 KiB |
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 4.7 KiB |