工厂版本

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 [detail, setDetail] = useState({})
const [content, setContent] = useState();
const [otherList, setOtherList] = useState([]);
const [totalFileSize, setTotalFileSize] = useState(0);
useEffect(() => {
document.title = '工具详情-软件工厂专区';
@ -31,11 +31,46 @@ function Index(props) {
}
}, [id]);
useEffect(() => {
if (detail.id) {
getOtherList()
//
const calculateTotalFileSize = (fileList) => {
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;
}
}
});
//
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';
}
}, [detail])
};
const getDetail = () => {
getSourceDetail(id).then(response => {
@ -49,32 +84,16 @@ function Index(props) {
const base64content = Base64.decode(extendData.content)
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 => { })
}
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 (
<div className="news-detail">
@ -97,33 +116,45 @@ function Index(props) {
<span>下载量</span>
</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>
</div>
<div>
<span>{detail.fileList ? detail.fileList.length : 0}</span>
<span>文件数量</span>
</div>
<div>
<span>{detail.domainName}</span>
<span>工具类型</span>
</div>
</div>
<button onClick={() => {
if (detail.fileIds) {
downloadFunc(`${httpUrl}/file/open/download/${detail.fileIds}`)
}
}} disabled={!detail.fileIds} className={`shiny-button ${!detail.fileIds && 'disabled'}`} >{detail.fileIds ? '立即下载' : '稍后下载'}</button>
{
detail.name && !detail.name.includes("文档") && <h6 className="more-title">相关文档</h6>
}
<div className="more-tools">
{
otherList.length > 0 && otherList.map((e, index) => {
return <Link to={`/sf/resources/releases/${e.id}`} key={index}><span >
<img src={headIcon} alt="" />
{e.name}
</span>
</Link>
})
}
</div>
{/* 文件列表展示 */}
{detail.fileList && detail.fileList.length > 0 && (
<div className="file-list">
<h6>文件列表</h6>
<div className="file-list-content">
{detail.fileList.map((file, index) => (
<div key={file.fileId || index} className="file-item">
<div className="file-info">
<span className="file-name">{file.fileOriginName}</span>
<span className="file-size">{file.fileSizeInfo}</span>
</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-wrapper">

View File

@ -1,8 +1,8 @@
.news-detail {
min-height: calc(100vh - 58px);
margin-bottom: 158px;
.news-detail-header {
height: 752px;
width: 100%;
background-image: url("../../../../images/factory/resource/tools-detail-head.webp");
background-repeat: no-repeat;
@ -129,6 +129,89 @@
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 {
margin-top: 34px;
line-height: 22px;

View File

@ -2,12 +2,21 @@ import React, { useEffect, useState, useRef } from 'react';
import './index.scss';
import { getSourceList, httpUrl } from '../../api'
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 { downloadFunc } from '../../../forge/Utils/utils'
import manual1 from '../../../images/factory/lessons/manual-1.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 { Link } from "react-router-dom";
@ -27,11 +36,47 @@ function Index(props) {
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 = () => {
getSourceList({ id: factoryZoneId, keywords: versionId }).then(res => {
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,41 +96,59 @@ function Index(props) {
});
}
const iconList = [version1, version2, version3]
const iconList2 = [version4, version5, version6, version7]
return (
<div className="version">
<div className="version-top">
<div className="versions">
<div className="versions-top">
<h5>生产线安装包</h5>
<p>标准化生产线快速迭代版本和稳定支持版本</p>
</div>
<div className="version-content">
<div className="versions-content">
{
versionList.map((item, index) => {
return <div key={item.id} className="version-item">
<div className="version-item-left">
<img src={dot} alt="" />
<div></div>
</div>
<div className="version-item-right">
<div className="version-item-wrapper">
<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 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>
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-box">
{
(categoryItem.items || [categoryItem]).map((item, itemIndex) => {
return <Link
key={item.id || itemIndex}
to={`/sf/resources/releases/${item.id}`}
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 className="version-content-item-button">查看
<img src={ versionActive } alt="" />
</div>
</Link>
})
}
</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>
}
</div>
</div>
})

View File

@ -1,11 +1,11 @@
.version {
.versions {
display: flex;
flex-direction: column;
align-items: center;
margin-top: -50px;
background-color: #f1f6fd;
.version-top {
.versions-top {
width: 1600px;
height: 120px;
padding: 21px 52px;
@ -30,7 +30,7 @@
}
}
.version-content {
.versions-content {
margin-top: 60px;
width: 1600px;
display: flex;
@ -38,177 +38,156 @@
flex-wrap: wrap;
.version-item {
display: flex;
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) {
margin-right: 0;
}
&:last-child {
.item-data {
opacity: 0;
}
}
&-left {
display: flex;
flex-direction: column;
align-items: center;
margin-right: 39px;
img {
width: 42px;
height: 42px;
}
div {
margin-top: -21px;
height: 260px;
border-left: 1px dashed rgba(22, 76, 228, 0.31);
}
}
&-right {
width: 425px;
display: flex;
flex-direction: column;
.version-item-wrapper {
&:hover {
.version-item-content {
background: linear-gradient(91.8deg,#f2f0ff 0.4%,rgba(230, 237, 255, 0.78) 99.5%);
}
}
}
.version-item-title {
.version-header {
height: 220px;
background-image: url('../../../images/factory/resource/version-item-header.webp');
background-repeat: no-repeat;
background-size: 100% 100%;
padding: 27px 34px;
.version-header-top {
display: flex;
align-items: center;
h4 {
line-height: 33px;
font-family: alibabaBold;
font-weight: 700;
color: #091221;
font-size: 24px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
.new-tag {
width: 37.49px;
margin-left: 10px;
}
.version-tag, .code-tag {
margin-left: 10px;
width: 41.08px;
height: 21.5px;
line-height: 20px;
font-family: DouyinSansBold;
color: #ffffff;
font-size: 12px;
background-repeat: no-repeat;
background-size: 100% 100%;
flex-shrink: 0;
text-align: center;
}
.version-tag {
img {
width: 46px;
background-image: url('../../../images/factory/resource/version-version.webp');
margin-right: 15px;
}
.code-tag {
background-image: url('../../../images/factory/resource/version-code.webp');
h4 {
line-height: 30px;
font-family: alibabaBold;
color: #091221;
font-size: 22px;
}
}
.version-item-content {
margin-top: 17px;
background: linear-gradient(92.56deg, #f8fbff 0.8%, rgba(255, 255, 255, 0) 99%);
.version-header-desc {
margin-top: 22px;
width: 443px;
height: 101px;
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;
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;
border-color: rgba(255, 255, 255, 0.18);
border-radius: 4px 4px 0px 0px;
padding: 16px 22px;
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;
}
.item-data {
margin-top: 10px;
span {
line-height: 18px;
font-family: alibabareg;
color: #5d6a80;
font-size: 13px;
position: relative;
}
}
.version-content {
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;
align-items: center;
padding: 20px 22px;
img {
width: 20px;
height: 20px;
margin: 11px;
}
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);
.version-item-info {
margin-left: 18px;
display: flex;
flex-direction: column;
justify-content: space-between;
span:nth-child(1) {
font-family: alibaba;
color: #091221;
font-size: 16px;
max-width: 280px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
span:nth-child(2) {
line-height: 20px;
font-family: alibabaReg;
color: #666b86;
font-size: 14px;
}
}
&:not(:last-child) {
position: relative;
&::after {
position: absolute;
content: "";
width: 396px;
height: 0px;
border: 1px dashed;
border-color: #164de41a;
bottom: 0;
}
}
.version-content-item-button {
margin-left: auto;
visibility: hidden;
font-family: alibabareg;
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;
}
}
}
}
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;
}
.shiny-button {
cursor: pointer;
background-color: #091221;
color: #ffffff;
border: 2px solid transparent;
}
}
&:not(:first-child) {
button:hover {
border-color: #164ce4;
color: #164ce4;
}
}
}
}
.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