Merge branch 'factory' of https://gitlink.org.cn/Gitlink/forgeplus-react-v2 into factory
Context testing / run-01 (push) Successful in 2m28s Details
Context testing / run-02 (push) Successful in 2m37s Details
Context testing / dump_contexts_to_log (push) Successful in 51s Details
Context testing / run-03 (push) Successful in 3m30s Details
Context testing / run-04 (push) Successful in 3m31s Details

This commit is contained in:
Eeeros 2026-06-29 15:03:24 +08:00
commit d165b65e08
7 changed files with 461 additions and 22 deletions

View File

@ -229,7 +229,9 @@ const Resource = forwardRef(({ zoneId, history }, ref) => {
const getVersionList = () => {
getSourceList({ id: factoryZoneId, keywords: versionId }).then(res => {
if (!res || !res.data || !res.data.rows) return
setResourceList2(res.data.rows.slice(0, 1));
// "-"
const filteredRows = res.data.rows.filter(item => !item.name || !item.name.startsWith('历史-'));
setResourceList2(filteredRows.slice(0, 1));
});
}
@ -316,7 +318,7 @@ const Resource = forwardRef(({ zoneId, history }, ref) => {
<div className="item-desc" title={item.summary}>{item.summary}</div>
<button onClick={() => {
history.push(`/sf/resources/releases`)
}} disabled={!item.fileIds}>查看</button>
}}>查看</button>
</div>
})
}

View File

@ -14,7 +14,7 @@ import buttonRight from '../../../../images/factory/resource/version-list-right.
import { Link } from 'react-router-dom';
import RenderHtml from '../../../../components/render-html';
import { Breadcrumb, Tooltip } from 'antd';
import { Breadcrumb, Button, Tooltip, Modal, Table, Pagination } from 'antd';
import { getSourceDetail, httpUrl, getSourceList } from '../../../api';
import { downloadFunc } from '../../../../forge/Utils/utils'
import { factoryZoneId, versionId } from '../../../../constants/factory'
@ -30,6 +30,13 @@ function Index(props) {
const [hoveredFileId, setHoveredFileId] = useState(null);
const [currentPage, setCurrentPage] = useState(1);
const itemsPerPage = 6;
// Modal
const [isDownloadUserModalVisible, setIsDownloadUserModalVisible] = useState(false);
const [downloadUserPage, setDownloadUserPage] = useState(1);
const downloadUsersPerPage = 10;
const {current_user} = props;
const {admin} = current_user || {}
const {downloadUserSummary = []} = detail || {}
useEffect(() => {
document.title = '工具详情-软件工厂专区';
@ -95,7 +102,7 @@ function Index(props) {
const base64content = Base64.decode(extendData.content)
setContent(base64content !== '<p><br></p>' ? base64content : '')
}
const detailData = { ...response.data.data, ...extendData };
const detailData = { ...response.data.data, ...extendData, name: response.data.data.name.replace("历史-", "") };
setDetail(detailData);
//
if (detailData.fileList && detailData.fileList.length > 0) {
@ -137,6 +144,76 @@ function Index(props) {
}
}
//
const getCurrentPageDownloadUsers = () => {
if (!downloadUserSummary || downloadUserSummary.length === 0) {
return [];
}
const startIndex = (downloadUserPage - 1) * downloadUsersPerPage;
const endIndex = startIndex + downloadUsersPerPage;
return downloadUserSummary.slice(startIndex, endIndex);
};
//
const downloadUserColumns = [
{
title: '用户昵称',
dataIndex: 'nickName',
key: 'nickName',
align: 'center',
render: (text, record) => text || record.userName || '-'
},
{
title: '手机号',
dataIndex: 'phonenumber',
key: 'phonenumber',
align: 'center',
render: (text) => text || '-'
},
{
title: '邮箱',
dataIndex: 'email',
key: 'email',
align: 'center',
render: (text) => text || '-'
},
{
title: 'IP 地址',
dataIndex: 'ipAddr',
key: 'ipAddr',
align: 'center',
render: (text) => text || '-'
},
{
title: '下载文件',
dataIndex: 'fileOriginName',
align: 'center',
key: 'fileOriginName'
},
{
title: '下载时间',
dataIndex: 'downloadTime',
align: 'center',
key: 'downloadTime'
}
];
// Modal
const handleOpenDownloadUserModal = () => {
setDownloadUserPage(1);
setIsDownloadUserModalVisible(true);
};
// Modal
const handleCloseDownloadUserModal = () => {
setIsDownloadUserModalVisible(false);
};
//
const handlePaginationChange = (page) => {
setDownloadUserPage(page);
};
return (
<div className="release-detail">
@ -157,7 +234,7 @@ function Index(props) {
<div className="tools-info">
<div>
<span>{detail.downloadCount}</span>
<span>下载量</span>
<span>下载量{admin && <Button className='downloadUser' onClick={handleOpenDownloadUserModal}>查看下载用户</Button>}</span>
</div>
<div>
<span>{totalFileSize || (detail.fileList && detail.fileList[0] ? detail.fileList[0].fileSizeInfo : 0)}</span>
@ -232,8 +309,38 @@ function Index(props) {
<RenderHtml className="informations_detail imageLayerParent" value={content} url={props.history.location} />
</div>
}
</div>
</div>
</div>
{/* 下载用户 Modal */}
<Modal
title="下载用户列表"
visible={isDownloadUserModalVisible}
onCancel={handleCloseDownloadUserModal}
footer={null}
width={1000}
className="download-user-modal"
>
<Table
columns={downloadUserColumns}
dataSource={getCurrentPageDownloadUsers()}
pagination={false}
size="middle"
scroll={{ y: 400 }}
/>
{/* 分页控件 */}
<div className="download-user-pagination edu-txt-right mt20">
<Pagination
current={downloadUserPage}
total={downloadUserSummary.length}
pageSize={downloadUsersPerPage}
onChange={handlePaginationChange}
showSizeChanger={false}
hideOnSinglePage
/>
</div>
</Modal>
</div>
)
}
export default TPMIndexHOC(Index)

View File

@ -86,9 +86,21 @@
display: flex;
flex-direction: column;
position: relative;
min-width: 140px;
min-width: 170px;
margin-bottom: 40px;
.downloadUser.ant-btn {
padding: 0 5px;
margin-left: 10px;
span {
color: black;
font-weight: normal;
font-family: 'alibabaReg';
font-size: 15px;
}
}
span:nth-child(1) {
line-height: 32px;
font-family: alibabaBold;
@ -438,3 +450,25 @@
}
}
}
.download-user-modal{
.ant-modal-content{
background-image: url("../../../../images/factory/resource/modal1.png");
background-size: 100% auto;
background-repeat: no-repeat;
border-radius:20px;
padding: 20px 25px;
}
.ant-modal-header{
background: transparent;
border-bottom: none;
padding-top: 10px;
padding-bottom: 0;
}
.ant-table-content{
border-radius: 10px;
overflow: hidden;
}
.ant-table-body{
overflow-y: auto !important;
}
}

View File

@ -17,8 +17,9 @@ 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, Modal } from 'antd';
import { Link } from "react-router-dom";
import moment from 'moment';
const sourceCode = {
name: '软件工厂源码',
@ -30,23 +31,46 @@ function Index(props) {
const [versionList, setVersionList] = useState([]);
const [manualList, setManualList] = useState([]);
const [historyVersionList, setHistoryVersionList] = useState([]);
const [isHistoryModalVisible, setIsHistoryModalVisible] = useState(false);
const [currentHistoryCategory, setCurrentHistoryCategory] = useState(null);
useEffect(() => {
getFullList();
getManualList()
}, []);
// name"ARM""ARM-"
// name "ARM""ARM-"
const extractCategory = (name) => {
if (!name) return '';
const parts = name.split('-');
// "-""-"
let cleanName = name;
if (cleanName.startsWith('历史-')) {
cleanName = cleanName.substring(3);
}
const parts = cleanName.split('-');
if (parts.length > 1) {
return parts[0] + '';
}
return name;
return cleanName;
};
// itemname
// name "-"
const extractDisplayName = (name, category) => {
if (!name) return '';
let displayName = name;
// "-"
if (displayName.startsWith('历史 -')) {
displayName = displayName.substring(3);
}
// "ARM-"
if (category && displayName.startsWith(category + '-')) {
displayName = displayName.substring(category.length + 1);
}
return displayName;
};
// item name
const groupByCategory = (list) => {
const grouped = {};
list.forEach(item => {
@ -54,7 +78,7 @@ function Index(props) {
if (!grouped[category]) {
grouped[category] = [];
}
// itemname
// item name
let itemName = item.name;
if (category && itemName.startsWith(category + '-')) {
itemName = itemName.substring(category.length + 1);
@ -64,19 +88,55 @@ function Index(props) {
return grouped;
};
// "-"
const separateVersions = (list) => {
const currentVersions = [];
const historyVersions = [];
list.forEach(item => {
if (item.name && item.name.startsWith('历史-')) {
// "-"
const newItem = { ...item, name: item.name.substring(3) };
historyVersions.push(newItem);
} else {
currentVersions.push(item);
}
});
return { currentVersions, historyVersions };
};
const getFullList = () => {
getSourceList({ id: factoryZoneId, keywords: versionId }).then(res => {
if (res.data.rows) {
//
const groupedData = groupByCategory(res.data.rows);
//
const categoryList = Object.keys(groupedData).map(category => ({
category,
items: groupedData[category]
}));
setVersionList(categoryList)
// setVersionList([...categoryList, { ...sourceCode, category: '' }]);
//
const categoryList = [];
const historyCategoryList = [];
Object.keys(groupedData).forEach(category => {
const { currentVersions, historyVersions } = separateVersions(groupedData[category]);
if (currentVersions.length > 0) {
categoryList.push({
category,
items: currentVersions
});
}
if (historyVersions.length > 0) {
historyCategoryList.push({
category,
items: historyVersions
});
}
});
setVersionList(categoryList);
setHistoryVersionList(historyCategoryList);
}
});
};
@ -100,6 +160,28 @@ function Index(props) {
const iconList2 = [version4, version5, version6, version7]
//
const hasHistoryVersion = (category) => {
return historyVersionList.some(hv => hv.category === category);
};
//
const getHistoryVersionByCategory = (category) => {
return historyVersionList.find(hv => hv.category === category);
};
//
const showHistoryModal = (category) => {
setCurrentHistoryCategory(category);
setIsHistoryModalVisible(true);
};
//
const closeHistoryModal = () => {
setIsHistoryModalVisible(false);
setCurrentHistoryCategory(null);
};
return (
<div className="versions">
<div className="versions-top">
@ -111,6 +193,8 @@ function Index(props) {
versionList.map((categoryItem, categoryIndex) => {
//
const isSourceCode = categoryItem.id === 'code';
const hasHistory = hasHistoryVersion(categoryItem.category);
return <div key={categoryItem.category || categoryItem.id} className="version-item">
<div className="version-header">
<div className="version-header-top">
@ -124,6 +208,17 @@ function Index(props) {
? categoryItem.items.find(item => item.summary)?.summary
: `请依次下载「主服务包」「仓库与账户服务包」「流水线服务包」三个安装包,并参照「部署文档与手册」完成 ${categoryItem.category} 环境的完整部署。`)}
</div>
{hasHistory && (
<div className="history-version-btn-wrapper">
<button
className="history-version-btn"
onClick={() => showHistoryModal(categoryItem.category)}
>
历史版本
{/* <i className="iconfont icon-changjiantou font-12 ml8"></i> */}
</button>
</div>
)}
</div>
<div className="version-content">
<div className="version-content-box">
@ -154,6 +249,9 @@ function Index(props) {
})
}
</div>
{/* 历史版本弹窗结束 */}
<div className="manual">
<h2>用户手册</h2>
<div className="manual-content">
@ -181,6 +279,58 @@ function Index(props) {
}
</div>
</div>
{/* 历史版本弹窗 */}
<Modal
title="历史版本"
visible={isHistoryModalVisible}
onCancel={closeHistoryModal}
footer={null}
width={1000}
className="history-version-modal"
>
{(() => {
//
if (!currentHistoryCategory) return null;
const historyData = getHistoryVersionByCategory(currentHistoryCategory);
if (!historyData || !historyData.items || historyData.items.length === 0) {
return <div className="no-history-data">暂无历史版本</div>;
}
return (
<div className="history-version-content">
<div className="version-content-box">
{historyData.items.map((item, itemIndex) => {
const displayName = extractDisplayName(item.name, currentHistoryCategory);
return <div
key={item.id || itemIndex}
className="version-content-item"
style={{ textDecoration: 'none' }}
>
<span className='version-item-icon-box'>
<img src={ iconList2[itemIndex % iconList2.length] } alt="" className="version-item-icon" />
</span>
<div className="version-item-info">
<Tooltip title={ `${displayName}` }>
<span>{ displayName }</span>
</Tooltip>
<div style={{color: '#546889'}} className='mt5 mb5'>{item.summary}</div>
<div className='version-item-info-bottom'>
<span className='mr20'>{ item.fileList ? item.fileList.length : 0 }个文件</span>
<span>{moment(item.createTime).format('YYYY-MM-DD HH:mm')}</span>
</div>
</div>
<Link to={`/sf/resources/releases/${item.id}`} className="version-content-item-button">查看
<img src={ versionActive } alt="" />
</Link>
</div>
})}
</div>
</div>
);
})()}
</Modal>
</div>
)
}

View File

@ -59,7 +59,7 @@
background-image: url('../../../images/factory/resource/version-item-header3.webp');
}
.version-header {
height: 220px;
height: 260px;
background-repeat: no-repeat;
background-size: 100% 100%;
padding: 27px 34px;
@ -103,6 +103,24 @@
border-radius: 4px;
}
}
.history-version-btn-wrapper {
margin-top: 15px;
.history-version-btn {
cursor: pointer;
width:106px;
height:32px;
border:1px solid;
border-color:#091221;
border-radius:8px;
line-height: 32px;
font-family: alibaba;
font-weight: 500;
color:#091221;
font-size: 14px;
background-color: transparent;
}
}
}
.version-content {
height: 422PX;
@ -333,4 +351,126 @@
}
}
}
}
// 历史版本弹窗样式
.history-version-modal {
.ant-modal-content{
background-image: url("../../../images/factory/resource/modal1.png");
background-size: 100% auto;
background-repeat: no-repeat;
border-radius:20px;
padding: 20px 25px;
}
.ant-modal-header{
background: transparent;
border-bottom: none;
padding-top: 10px;
padding-bottom: 0;
}
.ant-modal-body {
padding: 20px;
}
.history-version-content {
.version-content-box {
min-height: 500px;
max-height: 700px;
background:#ffffff;
border-radius:8px;
box-shadow:0px 0px 8px rgba(0, 88, 68, 0.05);
padding: 15px 0;
overflow-y: auto;
.version-content-item {
border-radius: 6px;
position: relative;
display: flex;
align-items: center;
padding: 20px 22px;
// .version-item-icon-box{
// background:linear-gradient(46.14deg,#f1f7fe 5.48%,#f8f5ff 94.87%);
// border-radius:9px;
// }
img {
width: 30px;
height: 30px;
margin: 15px;
}
.version-item-info {
margin-left: 8px;
display: flex;
flex-direction: column;
justify-content: space-between;
flex: 1;
span:nth-child(1) {
font-family: alibaba;
color: #091221;
font-size: 16px;
max-width: 280px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
.version-item-info-bottom{
background: #f7f8fc;
border-radius: 6px;
padding: 5px 10px;
margin-top: 5px;
}
.version-item-info-bottom span {
line-height: 20px;
font-family: alibabaReg;
color:#72748e;
font-size: 15px;
}
}
&:not(:last-child) {
position: relative;
&::after {
position: absolute;
content: "";
width: 860px;
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;
flex-shrink: 0;
margin-left: 20px;
img {
width: 13.62px;
height: auto;
margin-left: 5px;
}
}
&:hover {
background:rgba(22, 76, 228, 0.07);
.version-content-item-button {
visibility: visible;
}
}
}
}
}
.no-history-data {
text-align: center;
color: #999;
font-size: 14px;
padding: 40px 0;
}
}

View File

@ -109,6 +109,12 @@ function ProjectHomePage(props) {
}
function getProject() {
//
// getCate()
if (cateList === undefined) {
return;
}
const url = `/projects.json`;
let params = {
pinned: "d",

Binary file not shown.

After

Width:  |  Height:  |  Size: 652 KiB