diff --git a/src/api/interface/index.ts b/src/api/interface/index.ts index c9f2a8a..72ab345 100644 --- a/src/api/interface/index.ts +++ b/src/api/interface/index.ts @@ -267,7 +267,6 @@ export interface ReviewGroup { export interface ComponentMarketParams { componentClassify: string; - componentClassifyLabel: string; keyword: string; current?: string | number; size?: string | number; diff --git a/src/pages/componentMarket/compCard.tsx b/src/pages/componentMarket/compCard.tsx index 14012f2..f2460c6 100644 --- a/src/pages/componentMarket/compCard.tsx +++ b/src/pages/componentMarket/compCard.tsx @@ -1,73 +1,173 @@ import React, { useEffect, useState } from 'react'; import styles from './style/compCard.module.less'; -import { compData } from '@/pages/componentMarket/test/data'; -import { Card, Grid, Rate, Typography } from '@arco-design/web-react'; +import { Card, Grid, Rate, Typography, Pagination, Spin, Empty, Image } from '@arco-design/web-react'; import { useRouter } from 'next/router'; +import { getComponentMarket } from '@/api/componentMarket'; const { Row, Col } = Grid; interface CompCardProps { + selectedTab: string; componentClassify: string; + searchClassify: string; + searchKeyword: string; + selectedComp: (component: ComponentItem) => void; } -const CompCard: React.FC = ({ componentClassify }) => { - const [componentClassifyData, setComponentClassifyData] = useState([]); +interface ComponentItem { + id: string; + name: string; + identifier?: string; + componentClassify?: string; + deployType?: string; + star?: number; + description?: string; + + [key: string]: any; +} + +const CompCard: React.FC = ({ + selectedTab, + componentClassify, + searchClassify, + searchKeyword, + selectedComp + }) => { + const [componentList, setComponentList] = useState([]); + const [loading, setLoading] = useState(false); + const [current, setCurrent] = useState(1); + const [total, setTotal] = useState(0); + const pageSize = 12; // 每页显示12条数据 const router = useRouter(); - useEffect(() => { - if (componentClassify === '全部') setComponentClassifyData(compData); - else { - const filterData = compData.filter(v => v.componentClassify === componentClassify); - setComponentClassifyData(filterData || []); + // 获取组件市场数据 + const fetchComponentData = async (page = 1) => { + try { + setLoading(true); + + // 根据搜索条件或当前Tab确定分类 + let classifyLabel = '全部'; + + // 如果搜索框有选择分类,使用搜索框的分类 + if (searchClassify && searchClassify !== '全部') { + classifyLabel = searchClassify; + } + // 否则使用当前Tab的分类 + else if (componentClassify && componentClassify !== '全部') { + classifyLabel = componentClassify; + } + + const params = { + componentClassify: classifyLabel, + keyword: searchKeyword || '', + current: page, + size: pageSize + }; + + const res: any = await getComponentMarket(params); + + if (res?.code === 200 && res?.data) { + setComponentList(res.data.list || []); + setTotal(res.data.totalCount || 0); + } + else { + setComponentList([]); + setTotal(0); + } + } catch (error) { + console.error('获取组件市场数据失败:', error); + setComponentList([]); + setTotal(0); + } finally { + setLoading(false); } - }, [componentClassify]); + }; + // 监听分类、搜索条件变化 + useEffect(() => { + setCurrent(1); + fetchComponentData(1); + }, [searchKeyword, selectedTab]); - const handelDetails = () => { - router.push('/componentMarket/compDetails'); + // 处理分页变化 + const handlePageChange = (page: number) => { + setCurrent(page); + fetchComponentData(page); + }; + + // 跳转到组件详情 + const handleDetails = (component: ComponentItem) => { + selectedComp(component); }; return (
- - {componentClassifyData.map((v, i) => { - return ( - - handelDetails()}> - {/*左侧图片*/} -
图片
- {/*右侧数据*/} -
-
{v.name}
-
{v.identifier}
-
-
组件评分:
- - - {v.star < 0 ? 5 : v.star}分 - + + {componentList.length > 0 ? ( + <> + + {componentList.map((item) => ( + + handleDetails(item)} + hoverable + > + {/*左侧图片*/} +
+ 暂无图片 +
+ {/*右侧数据*/} +
+
{item.name || '未命名组件'}
+
{item.identifier || '-'}
+
+
组件评分:
+
+ + + {item.star || 5}分 + +
+
+
+
+
+
{item.componentClassify || '-'}
+
{item.deployType || '-'}
-
- -
-
{v.componentClassify}
-
{v.deployType}
+ + ))} + + + {/* 分页组件 */} + {total > pageSize && ( +
+
- - ); - })} - + )} + + ) : ( + + )} +
); }; diff --git a/src/pages/componentMarket/compDetails.tsx b/src/pages/componentMarket/compDetails.tsx index 283f05a..8bffb58 100644 --- a/src/pages/componentMarket/compDetails.tsx +++ b/src/pages/componentMarket/compDetails.tsx @@ -1,10 +1,340 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; +import styles from './style/compDetails.module.less'; +import { Space, Divider, Button, Typography, Card, Image, Rate, Grid } from '@arco-design/web-react'; +import dayjs from 'dayjs'; +import { getComponentMarket } from '@/api/componentMarket'; + +const { Row, Col } = Grid; + +const CompDetails = ({ compInfo }) => { + const [componentList, setComponentList] = useState([]); + const [currentCompInfo, setCurrentCompInfo] = useState(compInfo); + + // 获取组件市场数据 + const fetchComponentData = async () => { + try { + const params = { + componentClassify: compInfo.componentClassify, + keyword: '', + current: 1, + size: 4 + }; + + const res: any = await getComponentMarket(params); + + if (res?.code === 200 && res?.data) { + setComponentList(res.data.list || []); + setComponentList([ + { + 'baseConfigJson': [], + 'codeLanguage': 'Python', + 'collaboratorId': 0, + 'componentBaseId': '1993166956502499330', + 'componentClassify': '设备数采与控制交互组件', + 'componentStatus': 'DEPLOYED', + 'componentVersion': 1, + 'createBy': '1992851580222222338', + 'createTime': 1764228733000, + 'definitionJson': '{"apis":[{"apiType":"EVENT","eventApi":{"topic":"add"},"fieldIns":["a"],"fieldOuts":["v"],"id":"add","restApi":{}}],"dataIns":[{"dataType":"INTEGER","desc":"","id":"a","type":"DATA"}],"dataOuts":[{"dataType":"INTEGER","desc":"","id":"v","type":"DATA"}]}', + 'deployType': 'Python', + 'desc': '', + 'dockerImageId': '', + 'dockerImageName': '', + 'extraSystemId': '', + 'filesName': '', + 'id': '1993945941293449218', + 'identifier': 'admin_add_num_maket1', + 'isDeleted': 0, + 'localProjectPath': '/000000/admin_add_num_maket1/master', + 'logoUrl': '', + 'name': '两数之和2', + 'operatesJson': [], + 'permission': null, + 'projectId': 'add_num_maket1', + 'publicStatus': 1, + 'publishTime': null, + 'reviewOpinion': '', + 'size': '106.30 KB', + 'star': 0, + 'status': 1, + 'tags': [], + 'tenantId': '000000', + 'updateBy': '1992851580222222338', + 'updateTime': 1764232122000, + 'version': 'master' + }, + { + 'baseConfigJson': [], + 'codeLanguage': 'Python', + 'collaboratorId': 0, + 'componentBaseId': '1993166956502499330', + 'componentClassify': '设备数采与控制交互组件', + 'componentStatus': 'DEPLOYED', + 'componentVersion': 1, + 'createBy': '1992851580222222338', + 'createTime': 1764228733000, + 'definitionJson': '{"apis":[{"apiType":"EVENT","eventApi":{"topic":"add"},"fieldIns":["a"],"fieldOuts":["v"],"id":"add","restApi":{}}],"dataIns":[{"dataType":"INTEGER","desc":"","id":"a","type":"DATA"}],"dataOuts":[{"dataType":"INTEGER","desc":"","id":"v","type":"DATA"}]}', + 'deployType': 'Python', + 'desc': '', + 'dockerImageId': '', + 'dockerImageName': '', + 'extraSystemId': '', + 'filesName': '', + 'id': '1993945941293449218', + 'identifier': 'admin_add_num_maket1', + 'isDeleted': 0, + 'localProjectPath': '/000000/admin_add_num_maket1/master', + 'logoUrl': '', + 'name': '两数之和3', + 'operatesJson': [], + 'permission': null, + 'projectId': 'add_num_maket1', + 'publicStatus': 1, + 'publishTime': null, + 'reviewOpinion': '', + 'size': '106.30 KB', + 'star': 0, + 'status': 1, + 'tags': [], + 'tenantId': '000000', + 'updateBy': '1992851580222222338', + 'updateTime': 1764232122000, + 'version': 'master' + }, + { + 'baseConfigJson': [], + 'codeLanguage': 'Python', + 'collaboratorId': 0, + 'componentBaseId': '1993166956502499330', + 'componentClassify': '设备数采与控制交互组件', + 'componentStatus': 'DEPLOYED', + 'componentVersion': 1, + 'createBy': '1992851580222222338', + 'createTime': 1764228733000, + 'definitionJson': '{"apis":[{"apiType":"EVENT","eventApi":{"topic":"add"},"fieldIns":["a"],"fieldOuts":["v"],"id":"add","restApi":{}}],"dataIns":[{"dataType":"INTEGER","desc":"","id":"a","type":"DATA"}],"dataOuts":[{"dataType":"INTEGER","desc":"","id":"v","type":"DATA"}]}', + 'deployType': 'Python', + 'desc': '', + 'dockerImageId': '', + 'dockerImageName': '', + 'extraSystemId': '', + 'filesName': '', + 'id': '1993945941293449218', + 'identifier': 'admin_add_num_maket1', + 'isDeleted': 0, + 'localProjectPath': '/000000/admin_add_num_maket1/master', + 'logoUrl': '', + 'name': '两数之和4', + 'operatesJson': [], + 'permission': null, + 'projectId': 'add_num_maket1', + 'publicStatus': 1, + 'publishTime': null, + 'reviewOpinion': '', + 'size': '106.30 KB', + 'star': 0, + 'status': 1, + 'tags': [], + 'tenantId': '000000', + 'updateBy': '1992851580222222338', + 'updateTime': 1764232122000, + 'version': 'master' + }, + { + 'baseConfigJson': [], + 'codeLanguage': 'Python', + 'collaboratorId': 0, + 'componentBaseId': '1993166956502499330', + 'componentClassify': '设备数采与控制交互组件', + 'componentStatus': 'DEPLOYED', + 'componentVersion': 1, + 'createBy': '1992851580222222338', + 'createTime': 1764228733000, + 'definitionJson': '{"apis":[{"apiType":"EVENT","eventApi":{"topic":"add"},"fieldIns":["a"],"fieldOuts":["v"],"id":"add","restApi":{}}],"dataIns":[{"dataType":"INTEGER","desc":"","id":"a","type":"DATA"}],"dataOuts":[{"dataType":"INTEGER","desc":"","id":"v","type":"DATA"}]}', + 'deployType': 'Python', + 'desc': '', + 'dockerImageId': '', + 'dockerImageName': '', + 'extraSystemId': '', + 'filesName': '', + 'id': '1993945941293449218', + 'identifier': 'admin_add_num_maket1', + 'isDeleted': 0, + 'localProjectPath': '/000000/admin_add_num_maket1/master', + 'logoUrl': '', + 'name': '两数之和5', + 'operatesJson': [], + 'permission': null, + 'projectId': 'add_num_maket1', + 'publicStatus': 1, + 'publishTime': null, + 'reviewOpinion': '', + 'size': '106.30 KB', + 'star': 0, + 'status': 1, + 'tags': [], + 'tenantId': '000000', + 'updateBy': '1992851580222222338', + 'updateTime': 1764232122000, + 'version': 'master' + } + ]); + } + else { + setComponentList([]); + } + } catch (error) { + console.error('获取组件市场数据失败:', error); + setComponentList([]); + } finally { + } + }; + useEffect(() => { + fetchComponentData(); + }, []); + + + // 渲染组件外壳 + const renderCompHousing = () => { + return ( +
+
+
{currentCompInfo.name}
+
+
+ {currentCompInfo.def ? ( + <> +
+
+ {currentCompInfo.def?.apis?.map((item: any, index) =>
{item.id}
)} +
+
+
{currentCompInfo.def?.apiOut?.id}
+
+
+ +
+
+ {currentCompInfo.def?.dataIns?.map((item: any, index) =>
{item.id}
)} +
+
+ {currentCompInfo.def?.dataOuts?.map((item: any, index) =>
{item.id}
)} +
+
+ + ) : ( + <> +
+
+
contour
+
+
+
done
+
+
+ +
+
+ {currentCompInfo.flowHousVO?.dataIns?.map((item: any, index) =>
{item.id}
)} +
+
+ {currentCompInfo.flowHousVO?.dataOuts?.map((item: any, index) =>
{item.id}
)} +
+
+ + )} +
+
+ ); + }; -const CompDetails = () => { return ( -
- 组件详情 -
+ <> +
+
+

组件预览

+ {renderCompHousing()} +
+
+
+ +
{currentCompInfo.name}
+
更新时间: {dayjs(currentCompInfo.updateTime).format('YYYY-MM-DD HH:mm:ss')}
+
组件大小: {currentCompInfo.size}
+
组件版本: V_{currentCompInfo.status}
+
组件评分: {currentCompInfo.star}分
+
+
+
+ + +
{currentCompInfo.identifier}
+
分类:{currentCompInfo.componentClassify}
+
语言:{currentCompInfo.codeLanguage}
+
+
+ +
+ {currentCompInfo.description ? currentCompInfo.description : ' - '} +
+ +
这里是一坨md语法
+ +
+ + +
+ +
+
+
+ 相关推荐 + + + {componentList.map((item) => ( + + setCurrentCompInfo(item)} + hoverable + > + {/*左侧图片*/} +
+ 暂无图片 +
+ {/*右侧数据*/} +
+
{item.name || '未命名组件'}
+
{item.identifier || '-'}
+
+
组件评分:
+
+ + + {item.star || 5}分 + +
+
+
+
+
+
{item.componentClassify || '-'}
+
{item.deployType || '-'}
+
+ + ))} +
+
+ ); }; diff --git a/src/pages/componentMarket/index.tsx b/src/pages/componentMarket/index.tsx index 6163a4a..89066df 100644 --- a/src/pages/componentMarket/index.tsx +++ b/src/pages/componentMarket/index.tsx @@ -2,68 +2,128 @@ import React, { useEffect, useState } from 'react'; import styles from './style/index.module.less'; import CustomCard from '@/components/CustomCard/index'; import CompCard from './compCard'; -import { menu } from './test/data'; -import { Tabs, Input, Select } from '@arco-design/web-react'; -import { getComponentMarket } from '@/api/componentMarket'; +import CompDetails from './compDetails'; +import { Tabs, Input, Select, Button } from '@arco-design/web-react'; +import { IconUndo } from '@arco-design/web-react/icon'; import { getComponentClassify } from '@/api/componentClassify'; const TabPane = Tabs.TabPane; const InputSearch = Input.Search; -function ComponentMarket() { +// 分类数据接口 +interface ClassifyItem { + id: string; + classifyName: string; + classifyType: string; + remark?: string; + sort?: number; +} - const getMarketData = async () => { - const params = { - componentClassify: '', - componentClassifyLabel: '全部', - keyword: '', - current: 1, - size: 15 - }; - const res: any = await getComponentMarket(params); - console.log('res:', res); - }; +function ComponentMarket() { + const [menuList, setMenuList] = useState([]); + const [activeTab, setActiveTab] = useState('全部'); + const [searchClassify, setSearchClassify] = useState('全部'); + const [searchKeyword, setSearchKeyword] = useState(''); + const [selectedComp, setSelectedComp] = useState(null); + // 获取分类菜单列表 const getMenuList = async () => { - const res: any = await getComponentClassify('component'); - console.log('menu:', res); + try { + const res: any = await getComponentClassify('component'); + if (res?.code === 200 && res?.data) { + // 添加"全部"选项到菜单列表 + const allOption = { + id: '0', + classifyName: '全部', + classifyType: 'component', + sort: 0 + }; + setMenuList([allOption, ...res.data]); + } + } catch (error) { + console.error('获取分类菜单失败:', error); + } }; - useEffect(() => { - getMarketData(); getMenuList(); }, []); + // 处理Tab切换 + const handleTabChange = (key: string) => { + setActiveTab(key); + }; + + // 处理搜索 + const handleSearch = (value: string) => { + setSearchKeyword(value); + }; + + // 处理分类选择 + const handleClassifyChange = (value: string) => { + setSearchClassify(value); + }; + return ( <>
{/*公用搜索*/}
+ {selectedComp && ( + )}
- + {menuList.map((item) => ( + + {item.classifyName} + + ))} - +
- {/*单类型数据渲染*/} - - {menu.map((item) => { - return ( - - + {selectedComp ? + + : + + {/*单类型数据渲染*/} + {menuList.map((item) => ( + + {item.classifyName === activeTab && ( + + )} - ); - })} - + ))} + + }
diff --git a/src/pages/componentMarket/style/compCard.module.less b/src/pages/componentMarket/style/compCard.module.less index f6f9008..58da1c9 100644 --- a/src/pages/componentMarket/style/compCard.module.less +++ b/src/pages/componentMarket/style/compCard.module.less @@ -11,17 +11,20 @@ .img-box { width: 80px; height: 110px; - background-color: #cccccc; margin-right: 10px; } .info-box { + width: calc(100% - 100px); .info-author { margin-bottom: 40px; + white-space: nowrap; /* 禁止换行 */ + overflow: hidden; /* 超出部分隐藏 */ + text-overflow: ellipsis; /* 溢出部分用省略号表示 */ } - .info-score { + .info-rate { display: flex; align-items: center; } diff --git a/src/pages/componentMarket/style/compDetails.module.less b/src/pages/componentMarket/style/compDetails.module.less new file mode 100644 index 0000000..cb1c466 --- /dev/null +++ b/src/pages/componentMarket/style/compDetails.module.less @@ -0,0 +1,132 @@ +.comp-container { + box-sizing: border-box; + display: flex; + width: 100%; + height: 100%; + background-color: #fff; + padding: 32px 27px 27px 29px; + + .comp-preview { + box-sizing: border-box; + width: 345px; + height: 100%; + margin-right: 68px; + padding: 20px 25px; + border-radius: 8px; + box-shadow: 2px 2px 20px 0 rgba(0, 0, 0, .25); + + .comp-housing { + width: 95%; + height: 250px; + margin: 0 auto; + border: 1px solid #CCCCCC; + border-top-left-radius: 6px; + border-top-right-radius: 6px; + overflow: hidden; + + .comp-housing-header { + background-color: #ffa100; + color: #ffffff; + padding: 5px 10px; + } + + .comp-housing-content { + .comp-housing-content-api, + .comp-housing-content-data { + display: flex; + padding: 5px; + + .comp-housing-content-api-in, + .comp-housing-content-api-out, + .comp-housing-content-data-in, + .comp-housing-content-data-out { + flex: 1; + } + + .comp-housing-content-api-out, + .comp-housing-content-data-out { + text-align: right; + } + } + } + + } + } + + .comp-info { + flex: 1; + + .header { + .title { + font-size: 22px; + font-weight: 700; + } + + .update-time { + color: #888888; + } + } + + .extra { + .extra-font { + font-size: 18px; + font-weight: 700; + } + } + + .description { + max-height: 15%; + overflow-y: auto; + } + + .params { + box-sizing: border-box; + padding: 10px 0 25px 20px; + background-color: #fbfbfb; + } + } +} + +.recommend { + padding: 20px; + + + :global(.arco-col) { + padding: 10px; + } + + :global(.arco-card-body) { + display: flex; + } + + .img-box { + width: 80px; + height: 110px; + margin-right: 10px; + } + + .info-box { + width: calc(100% - 100px); + + .info-author { + margin-bottom: 40px; + white-space: nowrap; /* 禁止换行 */ + overflow: hidden; /* 超出部分隐藏 */ + text-overflow: ellipsis; /* 溢出部分用省略号表示 */ + } + + .info-rate { + display: flex; + align-items: center; + } + } + + .comp-card-footer { + display: flex; + align-items: center; + justify-content: space-between; + padding: 10px 20px; + background: linear-gradient(90deg, #64b5f6, #2175f3); + color: #ffffff; + } +} \ No newline at end of file