diff --git a/src/factory/components/Resource/index.jsx b/src/factory/components/Resource/index.jsx index b21f064f..91496e9d 100644 --- a/src/factory/components/Resource/index.jsx +++ b/src/factory/components/Resource/index.jsx @@ -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) => {
{item.summary}
+ }}>查看 }) } diff --git a/src/factory/resource/factoryVersion/detail/index.jsx b/src/factory/resource/factoryVersion/detail/index.jsx index 25f5e1f7..42fc1b15 100644 --- a/src/factory/resource/factoryVersion/detail/index.jsx +++ b/src/factory/resource/factoryVersion/detail/index.jsx @@ -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 !== '


' ? 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 (
@@ -157,7 +234,7 @@ function Index(props) {
{detail.downloadCount} - 下载量 + 下载量{admin && }
{totalFileSize || (detail.fileList && detail.fileList[0] ? detail.fileList[0].fileSizeInfo : 0)} @@ -232,8 +309,38 @@ function Index(props) {
} -
-
+ + + {/* 下载用户 Modal */} + + + + {/* 分页控件 */} +
+ +
+ + ) } export default TPMIndexHOC(Index) \ No newline at end of file diff --git a/src/factory/resource/factoryVersion/detail/index.scss b/src/factory/resource/factoryVersion/detail/index.scss index fcc6d5fc..b529e7ce 100644 --- a/src/factory/resource/factoryVersion/detail/index.scss +++ b/src/factory/resource/factoryVersion/detail/index.scss @@ -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; + } +} \ No newline at end of file diff --git a/src/factory/resource/factoryVersion/index.jsx b/src/factory/resource/factoryVersion/index.jsx index 4e41c111..11bf1883 100644 --- a/src/factory/resource/factoryVersion/index.jsx +++ b/src/factory/resource/factoryVersion/index.jsx @@ -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; }; - // 将数据按分类分组,并处理子item的name去掉分类前缀 + // 根据 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] = []; } - // 处理子item的name,去掉分类名前缀 + // 处理子 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 (
@@ -111,6 +193,8 @@ function Index(props) { versionList.map((categoryItem, categoryIndex) => { // 判断是否是源码项 const isSourceCode = categoryItem.id === 'code'; + const hasHistory = hasHistoryVersion(categoryItem.category); + return
@@ -124,6 +208,17 @@ function Index(props) { ? categoryItem.items.find(item => item.summary)?.summary : `请依次下载「主服务包」「仓库与账户服务包」「流水线服务包」三个安装包,并参照「部署文档与手册」完成 ${categoryItem.category} 环境的完整部署。`)}
+ {hasHistory && ( +
+ +
+ )}
@@ -154,6 +249,9 @@ function Index(props) { }) }
+ + {/* 历史版本弹窗结束 */} +

用户手册

@@ -181,6 +279,58 @@ function Index(props) { }
+ + {/* 历史版本弹窗 */} + + {(() => { + // 获取当前打开的分类的历史版本数据 + if (!currentHistoryCategory) return null; + + const historyData = getHistoryVersionByCategory(currentHistoryCategory); + if (!historyData || !historyData.items || historyData.items.length === 0) { + return
暂无历史版本
; + } + + return ( +
+
+ {historyData.items.map((item, itemIndex) => { + const displayName = extractDisplayName(item.name, currentHistoryCategory); + return
+ + + +
+ + { displayName } + +
{item.summary}
+
+ { item.fileList ? item.fileList.length : 0 }个文件 + {moment(item.createTime).format('YYYY-MM-DD HH:mm')} +
+
+ 查看 + + +
+ })} +
+
+ ); + })()} +
) } diff --git a/src/factory/resource/factoryVersion/index.scss b/src/factory/resource/factoryVersion/index.scss index 4f52bd2c..d7f9f32e 100644 --- a/src/factory/resource/factoryVersion/index.scss +++ b/src/factory/resource/factoryVersion/index.scss @@ -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; + } } \ No newline at end of file diff --git a/src/forge/projectHome/explore/index.jsx b/src/forge/projectHome/explore/index.jsx index 51381f96..df4576bd 100644 --- a/src/forge/projectHome/explore/index.jsx +++ b/src/forge/projectHome/explore/index.jsx @@ -109,6 +109,12 @@ function ProjectHomePage(props) { } function getProject() { + // 当分类数据尚未加载时,不执行请求 + // 等待 getCate() 返回后再请求正确分类的数据,避免竞态条件 + if (cateList === undefined) { + return; + } + const url = `/projects.json`; let params = { pinned: "d", diff --git a/src/images/factory/resource/modal1.png b/src/images/factory/resource/modal1.png new file mode 100644 index 00000000..cb8543b9 Binary files /dev/null and b/src/images/factory/resource/modal1.png differ