From fc7f4853a0510781626d9e684bb3276f23b7ee69 Mon Sep 17 00:00:00 2001 From: ZLY Date: Mon, 13 Oct 2025 15:48:25 +0800 Subject: [PATCH] =?UTF-8?q?feat(ide):=20=E5=AE=9E=E7=8E=B0=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E5=B8=82=E5=9C=BA=E6=B7=BB=E5=8A=A0=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E5=B9=B6=E4=BC=98=E5=8C=96=E6=95=B0=E6=8D=AE=E5=A4=84=E7=90=86?= =?UTF-8?q?=20-=20=E5=9C=A8=20=5Fapp.tsx=20=E4=B8=AD=E4=B8=BA=E4=B8=8D?= =?UTF-8?q?=E5=90=8C=E7=BB=84=E4=BB=B6=E7=B1=BB=E5=9E=8B=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=20fontCompType=20=E6=A0=87=E8=AF=86=20-=20=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E7=99=BB=E5=BD=95=E8=A1=A8=E5=8D=95=E7=9A=84=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E5=90=8D=E5=AF=86=E7=A0=81=E5=88=9D=E5=A7=8B?= =?UTF-8?q?=E5=8C=96=E5=80=BC-=20=E5=A2=9E=E5=8A=A0=E5=B7=A5=E7=A8=8B?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=E6=95=B0=E6=8D=AE=E8=8E=B7=E5=8F=96=E7=9A=84?= =?UTF-8?q?=E5=BC=82=E6=AD=A5=E7=AD=89=E5=BE=85=E5=A4=84=E7=90=86=20-?= =?UTF-8?q?=E9=87=8D=E6=9E=84=20market.tsx=20=E7=BB=84=E4=BB=B6=E4=BB=A5?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=B7=BB=E5=8A=A0=E5=88=B0=E5=B7=A5=E7=A8=8B?= =?UTF-8?q?=E7=9A=84=E5=8A=9F=E8=83=BD-=20=E6=96=B0=E5=A2=9E=20addProjectC?= =?UTF-8?q?omp=20=E5=92=8C=20addProjectBaseComp=20API=20=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=20-=20=E4=BC=98=E5=8C=96=E5=8F=B3=E4=BE=A7=E8=BE=B9=E6=A0=8F?= =?UTF-8?q?=E5=AE=BD=E5=BA=A6=E4=BB=8E=20350px=20=E8=B0=83=E6=95=B4?= =?UTF-8?q?=E4=B8=BA=20550px-=20=E5=AE=8C=E5=96=84=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=E6=98=AF=E5=90=A6=E5=B7=B2=E6=B7=BB=E5=8A=A0=E7=9A=84=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E6=98=BE=E7=A4=BA=E5=92=8C=E4=BA=A4=E4=BA=92=E9=80=BB?= =?UTF-8?q?=E8=BE=91-=20=E5=A2=9E=E5=8A=A0=E4=BB=8E=E7=BC=93=E5=AD=98?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=E7=BB=84=E4=BB=B6=E5=88=97=E8=A1=A8=E5=8F=8A?= =?UTF-8?q?=E7=9B=91=E5=90=AC=20Redux=20=E7=8A=B6=E6=80=81=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E7=9A=84=E8=83=BD=E5=8A=9B=20-=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E4=BE=A7=E8=BE=B9=E6=A0=8F=E6=A0=91=E8=8A=82=E7=82=B9=E9=80=89?= =?UTF-8?q?=E6=8B=A9=E5=88=A4=E6=96=AD=E9=80=BB=E8=BE=91=EF=BC=8C=E9=81=BF?= =?UTF-8?q?=E5=85=8D=E8=AF=AF=E5=88=A4=E6=97=A0=E5=AD=90=E8=8A=82=E7=82=B9?= =?UTF-8?q?=E6=83=85=E5=86=B5=20-=20=E6=B7=BB=E5=8A=A0=E5=88=B7=E6=96=B0?= =?UTF-8?q?=E6=8C=89=E9=92=AE=E7=94=A8=E4=BA=8E=E9=87=8D=E6=96=B0=E5=8A=A0?= =?UTF-8?q?=E8=BD=BD=E7=BB=84=E4=BB=B6=E5=BA=93=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/scene.ts | 10 ++ src/pages/_app.tsx | 17 ++ src/pages/ideContainer/index.tsx | 7 +- src/pages/ideContainer/market.tsx | 153 +++++++++++++++--- src/pages/ideContainer/rightSideBar.tsx | 14 +- src/pages/login/form.tsx | 1 - .../appCompComponent/sideBar.tsx | 4 +- 7 files changed, 175 insertions(+), 31 deletions(-) diff --git a/src/api/scene.ts b/src/api/scene.ts index 808bb65..2d01b15 100644 --- a/src/api/scene.ts +++ b/src/api/scene.ts @@ -61,4 +61,14 @@ export function getSceneAndId() { // 场景管理-获取工程下的所有组件 export function getProjectComp(projectId: string) { return axios.get(`${urlPrefix}/scenes/projectComp/${projectId}`); +} + +// 增加工程复合组件 +export function addProjectComp(data: any) { + return axios.post(`${urlPrefix}/scenes/addProjectSubComp`, data); +} + +// 增加工程基础组件 +export function addProjectBaseComp(data: any) { + return axios.post(`${urlPrefix}/scenes/addProjectComp`, data); } \ No newline at end of file diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 3cfb34f..3ccfc46 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -99,11 +99,28 @@ export default function MyApp({ const res: any = await promise; if (res?.code === 200) { if (key === 'pubFlow' || key === 'myFlow') { + res?.data.list.forEach(item => { + item['fontCompType'] = 'complex'; + }); // 更新本地存储数据 obj[key] = res?.data.list || null; } + // 给协同组件添加一个后续处理数据时使用的类型 + else if (key === 'teamLibs') { + res.data.forEach(item => { + item.children.forEach(v => { + v['fontCompType'] = 'team'; + }); + }); + obj[key] = res?.data || null; + } else { // 更新本地存储数据 + res.data.forEach(item => { + item.children.forEach(v => { + v['fontCompType'] = 'normal'; + }); + }); obj[key] = res?.data || null; } } diff --git a/src/pages/ideContainer/index.tsx b/src/pages/ideContainer/index.tsx index 603e889..df8a70b 100644 --- a/src/pages/ideContainer/index.tsx +++ b/src/pages/ideContainer/index.tsx @@ -63,7 +63,9 @@ function IDEContainer() { const getProjectCompData = async () => { const res: any = await getProjectComp(urlParams.id); - if (res.code === 200) dispatch(updateProjectComponentData({ [urlParams.id]: res.data })); + if (res.code === 200) { + await dispatch(updateProjectComponentData({ [urlParams.id]: res.data })); + } }; useEffect(() => { @@ -270,7 +272,8 @@ function IDEContainer() { - {['appList', 'appFlow'].includes(selected.parentKey) && } + {['appList', 'appFlow'].includes(selected.parentKey) && + getProjectCompData()}>} ); diff --git a/src/pages/ideContainer/market.tsx b/src/pages/ideContainer/market.tsx index 41a0f20..bcd2770 100644 --- a/src/pages/ideContainer/market.tsx +++ b/src/pages/ideContainer/market.tsx @@ -1,14 +1,24 @@ import React, { useEffect, useState, useCallback } from 'react'; -import { Card, Grid, Input, Tag, Typography, Divider, Collapse, Button } from '@arco-design/web-react'; +import { Card, Grid, Input, Tag, Typography, Divider, Collapse, Button, Message } from '@arco-design/web-react'; import { IconSearch, IconSync } from '@arco-design/web-react/icon'; import styles from './style/market.module.less'; import { useSelector, useDispatch } from 'react-redux'; +import { addProjectComp, addProjectBaseComp } from '@/api/scene'; +import dayjs from 'dayjs'; const { Row, Col } = Grid; const { Title, Text } = Typography; +type componentItemType = { + label: string; + children?: any[]; +}; + +interface MarketProps { + updateProjectComp: () => void; +} -const Market: React.FC = () => { +const Market: React.FC = ({ updateProjectComp }) => { const [compList, setCompList] = useState([]); const [firstLevelCategory, setFirstLevelCategory] = useState('全部'); // 第一层分类:全部,基础,复合 const [secondLevelCategory, setSecondLevelCategory] = useState('全部'); // 第二层分类:全部,我的,公开,协同 @@ -93,7 +103,7 @@ const Market: React.FC = () => { case '全部': return counts.allCount; case '我的': - return counts.myCount; + return counts.myCount + counts.myFlowCount; case '公开': return counts.publicCount + counts.pubFlowCount; case '协同': @@ -206,7 +216,7 @@ const Market: React.FC = () => { (secondLevelCategory === '全部' || secondLevelCategory === '公开') }; - // 添加基础组件 + // 收集符合条件的基础组件 if (conditions.includeMyLibs && compList.myLibs) { filteredComponents = [...filteredComponents, ...compList.myLibs]; } @@ -217,22 +227,49 @@ const Market: React.FC = () => { filteredComponents = [...filteredComponents, ...compList.teamLibs]; } - // 处理复合组件 - const flowComponents = { label: '复合组件', children: [] }; + // 收集符合条件的复合组件 + const flowComponents = []; if (conditions.includeMyFlow && compList.myFlow) { - flowComponents.children.push(...compList.myFlow); + flowComponents.push(...compList.myFlow.map(item => ({ ...item, isFlow: true }))); } if (conditions.includePubFlow && compList.pubFlow) { - flowComponents.children.push(...compList.pubFlow); + flowComponents.push(...compList.pubFlow.map(item => ({ ...item, isFlow: true }))); } - // 如果有复合组件,添加到筛选结果中 - if (flowComponents.children.length > 0) { - filteredComponents = [...filteredComponents, flowComponents]; + // 合并相同标签的组件 + const mergedComponents = {}; + + // 处理基础组件(按label分组) + filteredComponents.forEach(category => { + if (category && category.children && category.children.length > 0) { + const label = category.label; + if (!mergedComponents[label]) { + mergedComponents[label] = { + label: label, + children: [] + }; + } + mergedComponents[label].children.push(...category.children); + } + }); + + // 处理复合组件 + if (flowComponents.length > 0) { + const label = '复合组件'; + if (!mergedComponents[label]) { + mergedComponents[label] = { + label: label, + children: [] + }; + } + mergedComponents[label].children.push(...flowComponents); } + // 转换为数组格式 + const resultComponents = Object.values(mergedComponents); + // 如果没有组件,显示提示信息 - if (filteredComponents.length === 0) { + if (resultComponents.length === 0) { return (
暂无组件数据 @@ -240,10 +277,32 @@ const Market: React.FC = () => { ); } + // 将组件添加到工程中 + const addToProject = async (component) => { + const params = { + sceneId: info.id, + compIds: [] + }; + params['compIds'].push(component.id || component.comp.id); + params['type'] = component.fontCompType; + if (component.fontCompType === 'complex') { + const res: any = await addProjectComp(params); + if (res.code === 200) Message.success('添加成功'); + else Message.error('添加失败'); + } + else { + const res: any = await addProjectBaseComp(params); + if (res.code === 200) Message.success('添加成功'); + else Message.error('添加失败'); + } + // 通知父组件更新 + updateProjectComp(); + }; + // 渲染组件 return ( - {filteredComponents.map((category, index) => { + {resultComponents.map((category: componentItemType, index) => { // 确保category有children属性 if (!category || !category.children || category.children.length === 0) { return null; @@ -261,17 +320,26 @@ const Market: React.FC = () => { } > {category.children.map((component, compIndex) => ( - <> -
+
+
- {component.label} + {component.label || component.flowName}
{/*两种状态 未添加的是primary 已添加的是secondary*/} - + { + component.isAdd ? ( + + ) : ( + + ) + } +
- +
))} ); @@ -280,12 +348,51 @@ const Market: React.FC = () => { ); }, [compList, firstLevelCategory, secondLevelCategory]); - useEffect(() => { + // 给账号下的组件列表(本地存储中的组件列表)增加一个是否添加至工程的初始状态 + const addInitState = (componentData) => { + // 当前工程下已添加的组件ID列表 + const projectComponent = projectComponentData[info.id]; + const compListByProject = projectComponent.compIds.concat(projectComponent.flowIds); + const objectKeys = Object.keys(componentData); + /* + * 账号下的组件列表分两种结构: + * 1. myLibs,pubLibs,teamLibs 这三个是带有children数组的结构 + * 2. pubFlow,myFlow 这两个是直接一个数组 + * */ + objectKeys.forEach(key => { + if (key === 'pubFlow' || key === 'myFlow') { + componentData[key].forEach(item => { + item.isAdd = compListByProject.includes(item.id); + }); + } + else if (key === 'myLibs' || key === 'pubLibs' || key === 'teamLibs') { + componentData[key].length && componentData[key].forEach(item => { + item.children.forEach(v => { + v.isAdd = compListByProject.includes(v.comp.id); + }); + }); + } + }); + + setCompList(componentData); + }; + + // 从缓存中获取组件列表的信息 + const getCompList = () => { const userInfo = JSON.parse(sessionStorage.getItem('userInfo') || '{}'); const componentData = JSON.parse(sessionStorage.getItem(`compLibs${userInfo.userId}`)); - setCompList(componentData); + addInitState(componentData); + }; + + useEffect(() => { + getCompList(); }, []); + // 监听 Redux 中 projectComponentData 的变化,更新组件列表状态 + useEffect(() => { + getCompList(); + }, [projectComponentData]); + return (
{/* 头部搜索区域 */} @@ -300,7 +407,11 @@ const Market: React.FC = () => { style={{ marginRight: 16 }} />
- +
diff --git a/src/pages/ideContainer/rightSideBar.tsx b/src/pages/ideContainer/rightSideBar.tsx index 588484a..9478952 100644 --- a/src/pages/ideContainer/rightSideBar.tsx +++ b/src/pages/ideContainer/rightSideBar.tsx @@ -7,7 +7,11 @@ import Market from './market'; const TabPane = Tabs.TabPane; -const RightSideBar: React.FC = () => { +interface RightSideBarProps { + updateProjectComp: () => void; +} + +const RightSideBar: React.FC = ({ updateProjectComp }) => { const [activeTab, setActiveTab] = useState('1'); const [isExpanded, setIsExpanded] = useState(false); const resizeBoxRef1 = useRef(null); // 引用第一个 ResizeBox 容器 @@ -25,7 +29,7 @@ const RightSideBar: React.FC = () => { // 当 isExpanded 或 activeTab 状态改变时,直接更新对应元素的样式 useEffect(() => { - const width = isExpanded ? 350 : 0; + const width = isExpanded ? 550 : 0; if (activeTab === '1' && resizeBoxRef1.current) { resizeBoxRef1.current.style.width = `${width}px`; @@ -69,7 +73,7 @@ const RightSideBar: React.FC = () => { className={styles['right-resize-box']} directions={['left']} style={{ - width: isExpanded ? 350 : 0, + width: isExpanded ? 550 : 0, maxWidth: '100%', minWidth: 0 }} @@ -92,13 +96,13 @@ const RightSideBar: React.FC = () => { className={styles['right-resize-box']} directions={['left']} style={{ - width: isExpanded ? 350 : 0, + width: isExpanded ? 550 : 0, maxWidth: '100%', minWidth: 0 }} onMoving={handleResize} > - + } diff --git a/src/pages/login/form.tsx b/src/pages/login/form.tsx index 26d4d42..9f0a9d8 100644 --- a/src/pages/login/form.tsx +++ b/src/pages/login/form.tsx @@ -84,7 +84,6 @@ export default function LoginForm() { className={styles['login-form']} layout="vertical" ref={formRef} - initialValues={{ username: 'admin', password: 'admin' }} > { selectedKeys={[]} // 移除选中样式 style={{ background: 'transparent' }} // 移除背景色 onSelect={(value, info) => { - if (info.node.props.dataRef.children) return; + if (info.node.props.dataRef.hasOwnProperty('children')) return; onSelect(info.node?.props?.dataRef || null); }} > - {renderTreeNode(compList)} + {renderTreeNode({ projectCompDto: compList.projectCompDto, projectFlowDto: compList.projectFlowDto })}