工厂版本

This commit is contained in:
Eeeros 2026-03-26 09:09:41 +08:00
parent 898763b99a
commit e3e0c67d2e
14 changed files with 390 additions and 234 deletions

View File

@ -18,7 +18,7 @@ function Index(props) {
const id = props.match.params.id const id = props.match.params.id
const [detail, setDetail] = useState({}) const [detail, setDetail] = useState({})
const [content, setContent] = useState(); const [content, setContent] = useState();
const [otherList, setOtherList] = useState([]); const [totalFileSize, setTotalFileSize] = useState(0);
useEffect(() => { useEffect(() => {
document.title = '工具详情-软件工厂专区'; document.title = '工具详情-软件工厂专区';
@ -31,11 +31,46 @@ function Index(props) {
} }
}, [id]); }, [id]);
useEffect(() => { //
if (detail.id) { const calculateTotalFileSize = (fileList) => {
getOtherList() if (!fileList || fileList.length === 0) return 0;
let totalBytes = 0;
fileList.forEach(file => {
const sizeInfo = file.fileSizeInfo || '';
// "2.31 kB", "181.72 kB"
const match = sizeInfo.match(/([\d.]+)\s*(kB|MB|GB|B)/i);
if (match) {
const value = parseFloat(match[1]);
const unit = match[2].toUpperCase();
switch (unit) {
case 'B':
totalBytes += value;
break;
case 'KB':
totalBytes += value * 1024;
break;
case 'MB':
totalBytes += value * 1024 * 1024;
break;
case 'GB':
totalBytes += value * 1024 * 1024 * 1024;
break;
default:
totalBytes += value;
} }
}, [detail]) }
});
//
if (totalBytes >= 1024 * 1024 * 1024) {
return (totalBytes / (1024 * 1024 * 1024)).toFixed(2) + ' GB';
} else if (totalBytes >= 1024 * 1024) {
return (totalBytes / (1024 * 1024)).toFixed(2) + ' MB';
} else if (totalBytes >= 1024) {
return (totalBytes / 1024).toFixed(2) + ' kB';
} else {
return totalBytes + ' B';
}
};
const getDetail = () => { const getDetail = () => {
getSourceDetail(id).then(response => { getSourceDetail(id).then(response => {
@ -49,32 +84,16 @@ function Index(props) {
const base64content = Base64.decode(extendData.content) const base64content = Base64.decode(extendData.content)
setContent(base64content !== '<p><br></p>' ? base64content : '') setContent(base64content !== '<p><br></p>' ? base64content : '')
} }
setDetail({ ...response.data.data, ...extendData }); const detailData = { ...response.data.data, ...extendData };
setDetail(detailData);
//
if (detailData.fileList && detailData.fileList.length > 0) {
setTotalFileSize(calculateTotalFileSize(detailData.fileList));
}
} }
}).catch(error => { }) }).catch(error => { })
} }
const getOtherList = () => {
getSourceList({ id: factoryZoneId, keywords: versionId, name: '文档' }).then(res => {
const detailName = detail.name.toLowerCase();
let filteredList = [];
if (detailName.includes('docker')) {
filteredList = res.data.rows.filter(e =>
e.id !== detail.id && e.name.toLowerCase().includes('docker')
);
} else if (detailName.includes('k8s')) {
filteredList = res.data.rows.filter(e =>
e.id !== detail.id && e.name.toLowerCase().includes('k8s')
);
} else {
filteredList = res.data.rows.filter(e => e.id !== detail.id && e.name.toLowerCase().includes('x86'));
}
setOtherList(filteredList);
});
};
return ( return (
<div className="news-detail"> <div className="news-detail">
@ -97,33 +116,45 @@ function Index(props) {
<span>下载量</span> <span>下载量</span>
</div> </div>
<div> <div>
<span>{detail.fileList && detail.fileList[0] ? detail.fileList[0].fileSizeInfo : 0}</span> <span>{totalFileSize || (detail.fileList && detail.fileList[0] ? detail.fileList[0].fileSizeInfo : 0)}</span>
<span>文件大小</span> <span>文件大小</span>
</div> </div>
<div>
<span>{detail.fileList ? detail.fileList.length : 0}</span>
<span>文件数量</span>
</div>
<div> <div>
<span>{detail.domainName}</span> <span>{detail.domainName}</span>
<span>工具类型</span> <span>工具类型</span>
</div> </div>
</div> </div>
<button onClick={() => {
if (detail.fileIds) { {/* 文件列表展示 */}
downloadFunc(`${httpUrl}/file/open/download/${detail.fileIds}`) {detail.fileList && detail.fileList.length > 0 && (
} <div className="file-list">
}} disabled={!detail.fileIds} className={`shiny-button ${!detail.fileIds && 'disabled'}`} >{detail.fileIds ? '立即下载' : '稍后下载'}</button> <h6>文件列表</h6>
{ <div className="file-list-content">
detail.name && !detail.name.includes("文档") && <h6 className="more-title">相关文档</h6> {detail.fileList.map((file, index) => (
} <div key={file.fileId || index} className="file-item">
<div className="more-tools"> <div className="file-info">
{ <span className="file-name">{file.fileOriginName}</span>
otherList.length > 0 && otherList.map((e, index) => { <span className="file-size">{file.fileSizeInfo}</span>
return <Link to={`/sf/resources/releases/${e.id}`} key={index}><span >
<img src={headIcon} alt="" />
{e.name}
</span>
</Link>
})
}
</div> </div>
<button
className="file-download-btn"
onClick={() => {
if (file.downloadUrl) {
downloadFunc(file.downloadUrl)
}
}}
>
下载
</button>
</div>
))}
</div>
</div>
)}
<div className="header-img"> <div className="header-img">
<div className="header-img-wrapper"> <div className="header-img-wrapper">

View File

@ -1,8 +1,8 @@
.news-detail { .news-detail {
min-height: calc(100vh - 58px); min-height: calc(100vh - 58px);
margin-bottom: 158px;
.news-detail-header { .news-detail-header {
height: 752px;
width: 100%; width: 100%;
background-image: url("../../../../images/factory/resource/tools-detail-head.webp"); background-image: url("../../../../images/factory/resource/tools-detail-head.webp");
background-repeat: no-repeat; background-repeat: no-repeat;
@ -129,6 +129,89 @@
background-color: #091221; background-color: #091221;
} }
.file-list {
margin-top: 30px;
max-width: 650px;
h6 {
line-height: 22px;
font-family: alibabaBold;
font-weight: 700;
color: #091221;
font-size: 16px;
margin-bottom: 15px;
}
&-content {
background: #f7fbff;
border-radius: 8px;
padding: 15px;
border: 1px solid rgba(22, 76, 228, 0.1);
margin-top: 15px;
.file-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 15px;
background: #ffffff;
border-radius: 6px;
margin-bottom: 10px;
border: 1px solid rgba(22, 76, 228, 0.08);
&:last-child {
margin-bottom: 0;
}
.file-info {
display: flex;
flex-direction: column;
flex: 1;
min-width: 0;
.file-name {
line-height: 20px;
font-family: alibaba;
color: #091221;
font-size: 14px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.file-size {
line-height: 18px;
font-family: alibabareg;
color: #5d6a80;
font-size: 12px;
margin-top: 4px;
}
}
.file-download-btn {
margin-left: 15px;
width: 70px;
height: 28px;
line-height: 28px;
cursor: pointer;
border-radius: 4px;
font-family: alibaba;
font-weight: 500;
color: #164ce4;
font-size: 12px;
text-align: center;
background-color: #e9f2ff;
border: 1px solid rgba(22, 76, 228, 0.2);
flex-shrink: 0;
&:hover {
background: #d6e6ff;
}
}
}
}
}
h6 { h6 {
margin-top: 34px; margin-top: 34px;
line-height: 22px; line-height: 22px;

View File

@ -2,12 +2,21 @@ import React, { useEffect, useState, useRef } from 'react';
import './index.scss'; import './index.scss';
import { getSourceList, httpUrl } from '../../api' import { getSourceList, httpUrl } from '../../api'
import { factoryZoneId, versionId, manualId } from '../../../constants/factory' import { factoryZoneId, versionId, manualId } from '../../../constants/factory'
import dot from '../../../images/factory/resource/version-dot.webp'
import versionNew from '../../../images/factory/resource/version-new.webp'
import '../../common.scss' import '../../common.scss'
import { downloadFunc } from '../../../forge/Utils/utils' import { downloadFunc } from '../../../forge/Utils/utils'
import manual1 from '../../../images/factory/lessons/manual-1.webp' import manual1 from '../../../images/factory/lessons/manual-1.webp'
import manual2 from '../../../images/factory/lessons/manual-2.webp' import manual2 from '../../../images/factory/lessons/manual-2.webp'
import version1 from '../../../images/factory/resource/version-icon-1.webp'
import version2 from '../../../images/factory/resource/version-icon-2.webp'
import version3 from '../../../images/factory/resource/version-icon-3.webp'
import version4 from '../../../images/factory/resource/version-icon-4.webp'
import version5 from '../../../images/factory/resource/version-icon-5.webp'
import version6 from '../../../images/factory/resource/version-icon-6.webp'
import version7 from '../../../images/factory/resource/version-icon-7.webp'
import versionActive from '../../../images/factory/resource/version-active.webp'
import { Tooltip } from 'antd'; import { Tooltip } from 'antd';
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
@ -27,11 +36,47 @@ function Index(props) {
getManualList() getManualList()
}, []); }, []);
// name"ARM""ARM-"
const extractCategory = (name) => {
if (!name) return '';
const parts = name.split('-');
if (parts.length > 1) {
return parts[0] + '';
}
return name;
};
// itemname
const groupByCategory = (list) => {
const grouped = {};
list.forEach(item => {
const category = extractCategory(item.name);
if (!grouped[category]) {
grouped[category] = [];
}
// itemname
let itemName = item.name;
if (category && itemName.startsWith(category + '-')) {
itemName = itemName.substring(category.length + 1);
}
grouped[category].push({ ...item, name: itemName });
});
return grouped;
};
const getFullList = () => { const getFullList = () => {
getSourceList({ id: factoryZoneId, keywords: versionId }).then(res => { getSourceList({ id: factoryZoneId, keywords: versionId }).then(res => {
if (res.data.rows) { if (res.data.rows) {
setVersionList([...res.data.rows, sourceCode]); //
const groupedData = groupByCategory(res.data.rows);
//
const categoryList = Object.keys(groupedData).map(category => ({
category,
items: groupedData[category]
}));
setVersionList(categoryList)
// setVersionList([...categoryList, { ...sourceCode, category: '' }]);
} }
}); });
}; };
@ -51,43 +96,61 @@ function Index(props) {
}); });
} }
const iconList = [version1, version2, version3]
const iconList2 = [version4, version5, version6, version7]
return ( return (
<div className="version"> <div className="versions">
<div className="version-top"> <div className="versions-top">
<h5>生产线安装包</h5> <h5>生产线安装包</h5>
<p>标准化生产线快速迭代版本和稳定支持版本</p> <p>标准化生产线快速迭代版本和稳定支持版本</p>
</div> </div>
<div className="versions-content">
{
versionList.map((categoryItem, categoryIndex) => {
//
const isSourceCode = categoryItem.id === 'code';
return <div key={categoryItem.category || categoryItem.id} className="version-item">
<div className="version-header">
<div className="version-header-top">
<img src={ iconList[categoryIndex % iconList.length] } alt="" />
<h4>软件工厂 · {categoryItem.category}</h4>
</div>
<div className="version-header-desc">
{isSourceCode
? categoryItem.summary
: (categoryItem.items && categoryItem.items.some(item => item.summary)
? categoryItem.items.find(item => item.summary)?.summary
: `请依次下载「主服务包」「仓库与账户服务包」「流水线服务包」三个安装包,并参照「部署文档与手册」完成 ${categoryItem.category} 环境的完整部署。`)}
</div>
</div>
<div className="version-content"> <div className="version-content">
<div className="version-content-box">
{ {
versionList.map((item, index) => { (categoryItem.items || [categoryItem]).map((item, itemIndex) => {
return <div key={item.id} className="version-item"> return <Link
<div className="version-item-left"> key={item.id || itemIndex}
<img src={dot} alt="" /> to={`/sf/resources/releases/${item.id}`}
<div></div> className="version-content-item"
style={{ textDecoration: 'none' }}
>
<img src={ iconList2[itemIndex % iconList2.length] } alt="" className="version-item-icon" />
<div className="version-item-info">
<Tooltip title={ `${item.name}` }>
<span>{ item.name }</span>
</Tooltip>
<span>{ item.fileList ? item.fileList.length : 0 }个文件</span>
</div> </div>
<div className="version-item-right"> <div className="version-content-item-button">查看
<div className="version-item-wrapper"> <img src={ versionActive } alt="" />
<div className="version-item-title">
<Tooltip title={ `${item.name}` }><h4>{item.name}</h4></Tooltip>
{index === 0 ? <img className="new-tag" src={versionNew} /> : item.id === 'code'? <span className="code-tag">源码</span> : <span className="version-tag">安装包</span>}
</div> </div>
<div className="version-item-content">
<Tooltip><p>{item.summary}</p></Tooltip>
<div className="item-data">
<span><i className="iconfont-factory icon-shijian mr5 font-12" />{item.createTime}</span>
</div>
</div>
</div>
{
item.id === 'code' ? <button className={index === 0 ? "shiny-button" : "version-btn"}>暂未开放</button>
: <Link to={`/sf/resources/releases/${item.id}`}>
<button className={index === 0 ? "shiny-button" : "version-btn"}>查看详情</button>
</Link> </Link>
})
} }
</div> </div>
</div> </div>
</div>
}) })
} }
</div> </div>

View File

@ -1,11 +1,11 @@
.version { .versions {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
margin-top: -50px; margin-top: -50px;
background-color: #f1f6fd; background-color: #f1f6fd;
.version-top { .versions-top {
width: 1600px; width: 1600px;
height: 120px; height: 120px;
padding: 21px 52px; padding: 21px 52px;
@ -30,7 +30,7 @@
} }
} }
.version-content { .versions-content {
margin-top: 60px; margin-top: 60px;
width: 1600px; width: 1600px;
display: flex; display: flex;
@ -38,177 +38,156 @@
flex-wrap: wrap; flex-wrap: wrap;
.version-item { .version-item {
display: flex;
margin-bottom: 60px; margin-bottom: 60px;
margin-right: 39px; margin-right: 30px;
height: 642px;
width: 513px;
background: #ffffff4f;
border: 1px solid;
border-color: #ffffff52;
border-radius: 12px;
&:nth-child(3n) { &:nth-child(3n) {
margin-right: 0; margin-right: 0;
} }
.version-header {
&:last-child { height: 220px;
.item-data { background-image: url('../../../images/factory/resource/version-item-header.webp');
opacity: 0; background-repeat: no-repeat;
} background-size: 100% 100%;
} padding: 27px 34px;
.version-header-top {
&-left {
display: flex; display: flex;
flex-direction: column;
align-items: center; align-items: center;
margin-right: 39px;
img { img {
width: 42px; width: 46px;
height: 42px; margin-right: 15px;
} }
h4 {
div { line-height: 30px;
margin-top: -21px; font-family: alibabaBold;
height: 260px; color: #091221;
border-left: 1px dashed rgba(22, 76, 228, 0.31); font-size: 22px;
} }
} }
.version-header-desc {
&-right { margin-top: 22px;
width: 425px; width: 443px;
display: flex; height: 101px;
flex-direction: column; background: linear-gradient(268.73deg,rgba(255, 255, 255, 0.4) 0.25%,rgba(255, 255, 255, 0.84) 54.68%,rgba(255, 255, 255, 0) 99.8%);
border: 1px solid;
.version-item-wrapper { border-color: rgba(255, 255, 255, 0.18);
&:hover { border-radius: 4px 4px 0px 0px;
.version-item-content { padding: 16px 22px;
background: linear-gradient(91.8deg,#f2f0ff 0.4%,rgba(230, 237, 255, 0.78) 99.5%); line-height:25px;
font-family: alibabareg;
color:#404b5d;
font-size:14px;
position: relative;
&::before {
position: absolute;
content: "";
left: 0;
top: 50%;
transform: translate(0, -50%);
width: 3px;
height: 50px;
background: #3a2ce6;
border-radius: 4px;
} }
} }
} }
.version-content {
.version-item-title { height: 422PX;
background-image: url('../../../images/factory/resource/version-content.webp');
background-repeat: no-repeat;
background-size: 100% 100%;
padding: 26px 35px;
.version-content-box {
width: 443px;
height: 367px;
background: #ffffff;
border-radius: 12px;
padding: 5px 0;
overflow-y: auto;
// 隐藏滚动条
&::-webkit-scrollbar {
display: none;
}
-ms-overflow-style: none;
scrollbar-width: none;
.version-content-item {
width: 443px;
height: 88px;
border-radius: 6px;
position: relative;
display: flex; display: flex;
align-items: center; align-items: center;
padding: 20px 22px;
h4 { img {
line-height: 33px; width: 20px;
font-family: alibabaBold; height: 20px;
font-weight: 700; margin: 11px;
}
.version-item-info {
margin-left: 18px;
display: flex;
flex-direction: column;
justify-content: space-between;
span:nth-child(1) {
font-family: alibaba;
color: #091221; color: #091221;
font-size: 24px; font-size: 16px;
max-width: 280px;
white-space: nowrap; white-space: nowrap;
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden; overflow: hidden;
} }
span:nth-child(2) {
.new-tag {
width: 37.49px;
margin-left: 10px;
}
.version-tag, .code-tag {
margin-left: 10px;
width: 41.08px;
height: 21.5px;
line-height: 20px; line-height: 20px;
font-family: DouyinSansBold; font-family: alibabaReg;
color: #ffffff; color: #666b86;
font-size: 12px;
background-repeat: no-repeat;
background-size: 100% 100%;
flex-shrink: 0;
text-align: center;
}
.version-tag {
width: 46px;
background-image: url('../../../images/factory/resource/version-version.webp');
}
.code-tag {
background-image: url('../../../images/factory/resource/version-code.webp');
}
}
.version-item-content {
margin-top: 17px;
background: linear-gradient(92.56deg, #f8fbff 0.8%, rgba(255, 255, 255, 0) 99%);
border: 1px solid;
border-color: #ffffff;
border-radius: 17px;
padding: 22px;
p {
width: 100%;
height: 92px;
line-height: 34px;
font-family: alibabareg;
color: #091221;
font-size: 15px;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
}
.item-data {
margin-top: 10px;
span {
line-height: 18px;
font-family: alibabareg;
color: #5d6a80;
font-size: 13px;
position: relative;
}
span:not(:last-child) {
margin-right: 15px;
padding-right: 15px;
&::after {
position: absolute;
right: 0;
top: 50%;
transform: translate(0, -50%);
content: "";
width: 1px;
height: 9px;
background: rgba(9, 18, 33, 0.22);
}
}
}
}
button {
cursor: pointer;
margin-top: 20px;
width: 104px;
height: 36px;
background-color: rgba(255, 255, 255, 0.42);
border: 1px solid;
border-color: #091221;
border-radius: 9px;
line-height: 20px;
font-family: alibaba;
font-weight: 500;
color: #091221;
font-size: 14px; font-size: 14px;
} }
}
.shiny-button { &:not(:last-child) {
cursor: pointer; position: relative;
background-color: #091221; &::after {
color: #ffffff; position: absolute;
border: 2px solid transparent; content: "";
width: 396px;
height: 0px;
border: 1px dashed;
border-color: #164de41a;
bottom: 0;
} }
} }
.version-content-item-button {
&:not(:first-child) { margin-left: auto;
button:hover { visibility: hidden;
border-color: #164ce4; font-family: alibabareg;
color: #164ce4; color: #164ce4;
font-size: 14px;
text-align: right;
display: flex;
align-items: center;
img {
width: 13.62px;
height: auto;
margin-left: 5px;
}
}
&:hover {
background: rgba(22, 76, 228, 0.04);
.version-content-item-button {
visibility: visible;
} }
} }
}
}
}
} }
} }
.manual { .manual {

Binary file not shown.

After

Width:  |  Height:  |  Size: 482 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 822 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB