feat(ide): 实现组件市场添加功能并优化数据处理

- 在 _app.tsx 中为不同组件类型添加 fontCompType 标识
- 移除登录表单的默认用户名密码初始化值- 增加工程组件数据获取的异步等待处理
-重构 market.tsx 组件以支持添加到工程的功能- 新增 addProjectComp 和 addProjectBaseComp API 接口
- 优化右侧边栏宽度从 350px 调整为 550px- 完善组件是否已添加的状态显示和交互逻辑- 增加从缓存获取组件列表及监听 Redux 状态更新的能力
- 修复侧边栏树节点选择判断逻辑,避免误判无子节点情况
- 添加刷新按钮用于重新加载组件库数据
master
钟良源 4 months ago
parent cb07ba397b
commit fc7f4853a0

@ -61,4 +61,14 @@ export function getSceneAndId() {
// 场景管理-获取工程下的所有组件 // 场景管理-获取工程下的所有组件
export function getProjectComp(projectId: string) { export function getProjectComp(projectId: string) {
return axios.get(`${urlPrefix}/scenes/projectComp/${projectId}`); 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);
} }

@ -99,11 +99,28 @@ export default function MyApp({
const res: any = await promise; const res: any = await promise;
if (res?.code === 200) { if (res?.code === 200) {
if (key === 'pubFlow' || key === 'myFlow') { if (key === 'pubFlow' || key === 'myFlow') {
res?.data.list.forEach(item => {
item['fontCompType'] = 'complex';
});
// 更新本地存储数据 // 更新本地存储数据
obj[key] = res?.data.list || null; 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 { else {
// 更新本地存储数据 // 更新本地存储数据
res.data.forEach(item => {
item.children.forEach(v => {
v['fontCompType'] = 'normal';
});
});
obj[key] = res?.data || null; obj[key] = res?.data || null;
} }
} }

@ -63,7 +63,9 @@ function IDEContainer() {
const getProjectCompData = async () => { const getProjectCompData = async () => {
const res: any = await getProjectComp(urlParams.id); 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(() => { useEffect(() => {
@ -270,7 +272,8 @@ function IDEContainer() {
</div> </div>
</div> </div>
{['appList', 'appFlow'].includes(selected.parentKey) && <RightSideBar></RightSideBar>} {['appList', 'appFlow'].includes(selected.parentKey) &&
<RightSideBar updateProjectComp={() => getProjectCompData()}></RightSideBar>}
</div> </div>
</> </>
); );

@ -1,14 +1,24 @@
import React, { useEffect, useState, useCallback } from 'react'; 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 { IconSearch, IconSync } from '@arco-design/web-react/icon';
import styles from './style/market.module.less'; import styles from './style/market.module.less';
import { useSelector, useDispatch } from 'react-redux'; import { useSelector, useDispatch } from 'react-redux';
import { addProjectComp, addProjectBaseComp } from '@/api/scene';
import dayjs from 'dayjs';
const { Row, Col } = Grid; const { Row, Col } = Grid;
const { Title, Text } = Typography; const { Title, Text } = Typography;
type componentItemType = {
label: string;
children?: any[];
};
interface MarketProps {
updateProjectComp: () => void;
}
const Market: React.FC = () => { const Market: React.FC<MarketProps> = ({ updateProjectComp }) => {
const [compList, setCompList] = useState<any>([]); const [compList, setCompList] = useState<any>([]);
const [firstLevelCategory, setFirstLevelCategory] = useState('全部'); // 第一层分类:全部,基础,复合 const [firstLevelCategory, setFirstLevelCategory] = useState('全部'); // 第一层分类:全部,基础,复合
const [secondLevelCategory, setSecondLevelCategory] = useState('全部'); // 第二层分类:全部,我的,公开,协同 const [secondLevelCategory, setSecondLevelCategory] = useState('全部'); // 第二层分类:全部,我的,公开,协同
@ -93,7 +103,7 @@ const Market: React.FC = () => {
case '全部': case '全部':
return counts.allCount; return counts.allCount;
case '我的': case '我的':
return counts.myCount; return counts.myCount + counts.myFlowCount;
case '公开': case '公开':
return counts.publicCount + counts.pubFlowCount; return counts.publicCount + counts.pubFlowCount;
case '协同': case '协同':
@ -206,7 +216,7 @@ const Market: React.FC = () => {
(secondLevelCategory === '全部' || secondLevelCategory === '公开') (secondLevelCategory === '全部' || secondLevelCategory === '公开')
}; };
// 添加基础组件 // 收集符合条件的基础组件
if (conditions.includeMyLibs && compList.myLibs) { if (conditions.includeMyLibs && compList.myLibs) {
filteredComponents = [...filteredComponents, ...compList.myLibs]; filteredComponents = [...filteredComponents, ...compList.myLibs];
} }
@ -217,22 +227,49 @@ const Market: React.FC = () => {
filteredComponents = [...filteredComponents, ...compList.teamLibs]; filteredComponents = [...filteredComponents, ...compList.teamLibs];
} }
// 处理复合组件 // 收集符合条件的复合组件
const flowComponents = { label: '复合组件', children: [] }; const flowComponents = [];
if (conditions.includeMyFlow && compList.myFlow) { if (conditions.includeMyFlow && compList.myFlow) {
flowComponents.children.push(...compList.myFlow); flowComponents.push(...compList.myFlow.map(item => ({ ...item, isFlow: true })));
} }
if (conditions.includePubFlow && compList.pubFlow) { if (conditions.includePubFlow && compList.pubFlow) {
flowComponents.children.push(...compList.pubFlow); flowComponents.push(...compList.pubFlow.map(item => ({ ...item, isFlow: true })));
} }
// 如果有复合组件,添加到筛选结果中 // 合并相同标签的组件
if (flowComponents.children.length > 0) { const mergedComponents = {};
filteredComponents = [...filteredComponents, flowComponents];
// 处理基础组件按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 ( return (
<div style={{ textAlign: 'center', padding: '40px 0', color: '#999' }}> <div style={{ textAlign: 'center', padding: '40px 0', color: '#999' }}>
@ -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 ( return (
<Collapse expandIconPosition={'right'} bordered={false}> <Collapse expandIconPosition={'right'} bordered={false}>
{filteredComponents.map((category, index) => { {resultComponents.map((category: componentItemType, index) => {
// 确保category有children属性 // 确保category有children属性
if (!category || !category.children || category.children.length === 0) { if (!category || !category.children || category.children.length === 0) {
return null; return null;
@ -261,17 +320,26 @@ const Market: React.FC = () => {
} }
> >
{category.children.map((component, compIndex) => ( {category.children.map((component, compIndex) => (
<> <div key={compIndex}>
<div className={styles['component-list']} key={compIndex}> <div className={styles['component-list']}>
<div className={styles['component-info']}> <div className={styles['component-info']}>
<img src="/icons/compIcon.png" alt="" /> <img src="/icons/compIcon.png" alt="" />
<span>{component.label}</span> <span>{component.label || component.flowName}</span>
</div> </div>
{/*两种状态 未添加的是primary 已添加的是secondary*/} {/*两种状态 未添加的是primary 已添加的是secondary*/}
<Button type="primary" style={{ borderRadius: 4 }}></Button> {
component.isAdd ? (
<Button type="secondary" style={{ borderRadius: 4 }}
></Button>
) : (
<Button type="primary" style={{ borderRadius: 4 }}
onClick={() => addToProject(component)}></Button>
)
}
</div> </div>
<Divider /> <Divider />
</> </div>
))} ))}
</Collapse.Item> </Collapse.Item>
); );
@ -280,12 +348,51 @@ const Market: React.FC = () => {
); );
}, [compList, firstLevelCategory, secondLevelCategory]); }, [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 userInfo = JSON.parse(sessionStorage.getItem('userInfo') || '{}');
const componentData = JSON.parse(sessionStorage.getItem(`compLibs${userInfo.userId}`)); const componentData = JSON.parse(sessionStorage.getItem(`compLibs${userInfo.userId}`));
setCompList(componentData); addInitState(componentData);
};
useEffect(() => {
getCompList();
}, []); }, []);
// 监听 Redux 中 projectComponentData 的变化,更新组件列表状态
useEffect(() => {
getCompList();
}, [projectComponentData]);
return ( return (
<div className={styles['market-container']}> <div className={styles['market-container']}>
{/* 头部搜索区域 */} {/* 头部搜索区域 */}
@ -300,7 +407,11 @@ const Market: React.FC = () => {
style={{ marginRight: 16 }} style={{ marginRight: 16 }}
/> />
<div className={styles['filter-tags']}> <div className={styles['filter-tags']}>
<Button type="primary" icon={<IconSync />}></Button> <Button
type="primary"
icon={<IconSync />}
onClick={() => getCompList()}
/>
</div> </div>
</div> </div>
</div> </div>

@ -7,7 +7,11 @@ import Market from './market';
const TabPane = Tabs.TabPane; const TabPane = Tabs.TabPane;
const RightSideBar: React.FC = () => { interface RightSideBarProps {
updateProjectComp: () => void;
}
const RightSideBar: React.FC<RightSideBarProps> = ({ updateProjectComp }) => {
const [activeTab, setActiveTab] = useState('1'); const [activeTab, setActiveTab] = useState('1');
const [isExpanded, setIsExpanded] = useState(false); const [isExpanded, setIsExpanded] = useState(false);
const resizeBoxRef1 = useRef<HTMLDivElement>(null); // 引用第一个 ResizeBox 容器 const resizeBoxRef1 = useRef<HTMLDivElement>(null); // 引用第一个 ResizeBox 容器
@ -25,7 +29,7 @@ const RightSideBar: React.FC = () => {
// 当 isExpanded 或 activeTab 状态改变时,直接更新对应元素的样式 // 当 isExpanded 或 activeTab 状态改变时,直接更新对应元素的样式
useEffect(() => { useEffect(() => {
const width = isExpanded ? 350 : 0; const width = isExpanded ? 550 : 0;
if (activeTab === '1' && resizeBoxRef1.current) { if (activeTab === '1' && resizeBoxRef1.current) {
resizeBoxRef1.current.style.width = `${width}px`; resizeBoxRef1.current.style.width = `${width}px`;
@ -69,7 +73,7 @@ const RightSideBar: React.FC = () => {
className={styles['right-resize-box']} className={styles['right-resize-box']}
directions={['left']} directions={['left']}
style={{ style={{
width: isExpanded ? 350 : 0, width: isExpanded ? 550 : 0,
maxWidth: '100%', maxWidth: '100%',
minWidth: 0 minWidth: 0
}} }}
@ -92,13 +96,13 @@ const RightSideBar: React.FC = () => {
className={styles['right-resize-box']} className={styles['right-resize-box']}
directions={['left']} directions={['left']}
style={{ style={{
width: isExpanded ? 350 : 0, width: isExpanded ? 550 : 0,
maxWidth: '100%', maxWidth: '100%',
minWidth: 0 minWidth: 0
}} }}
onMoving={handleResize} onMoving={handleResize}
> >
<Market></Market> <Market updateProjectComp={updateProjectComp}></Market>
</ResizeBox>} </ResizeBox>}
</TabPane> </TabPane>
</Tabs> </Tabs>

@ -84,7 +84,6 @@ export default function LoginForm() {
className={styles['login-form']} className={styles['login-form']}
layout="vertical" layout="vertical"
ref={formRef} ref={formRef}
initialValues={{ username: 'admin', password: 'admin' }}
> >
<Form.Item <Form.Item
field="username" field="username"

@ -100,11 +100,11 @@ const SideBar = ({ compList, onSelect }) => {
selectedKeys={[]} // 移除选中样式 selectedKeys={[]} // 移除选中样式
style={{ background: 'transparent' }} // 移除背景色 style={{ background: 'transparent' }} // 移除背景色
onSelect={(value, info) => { onSelect={(value, info) => {
if (info.node.props.dataRef.children) return; if (info.node.props.dataRef.hasOwnProperty('children')) return;
onSelect(info.node?.props?.dataRef || null); onSelect(info.node?.props?.dataRef || null);
}} }}
> >
{renderTreeNode(compList)} {renderTreeNode({ projectCompDto: compList.projectCompDto, projectFlowDto: compList.projectFlowDto })}
</Tree> </Tree>
</div> </div>
</div> </div>

Loading…
Cancel
Save