import React, { useState, useEffect, useCallback, useMemo } from 'react'; import { useParams } from 'react-router-dom'; import { useKey } from 'react-use'; import { message, Modal } from 'antd'; import { UploadFile } from 'antd/lib/upload/interface'; import { useRecoilState } from 'recoil'; import { ExclamationCircleOutlined } from '@easyv/react-icons'; import { isCtrlKey, isInputTag, ComponentInfo, ComponentDetailInfo, ProjectStateConfig, ModelStateConfig, PointLightStateConfig, DirectLightStateConfig, ComponentTypeEnum, ModelAnimationPlayNumEnum, EasyTwinRenderer, EasyTwinLoader, EventName, DragChangedEvent, DragLightChangedEvent, ComponentConfig, CreateComponentQuery, UpdateComponentQuery, BaseMapId, BaseMap, IVector3, FlowFence, treeList2SimpleList, StateTypeEnum, Scene, AnimationInfo, } from '@easytwin/core'; import { _themeLoading } from '@/pages/module'; import { useProject } from '@/pages/ProjectManage/module'; import { ComponentApi, initStateData, SceneApi } from '@/server'; import { useQuery } from 'react-query'; import { ProjectEditorPageParams, ProjectEditorModeEnum, _projectInfo, _sceneStateList, _sceneStatesSelectKey, _sceneViewpointList, _sceneViewpointSelectKey, _sceneId, _componenterTreeList, _componentSelectList, _componenterSearchQuery, _componenterRemovedList, _currentComponentDetail, _componentStateList, _componentStateSelectKey, _componenterViewpointList, _componenterViewpointSelectKey, _componenterAnimationList, _componenterAnimationSelectKey, _currentScene, _scenePickPositionModeOpen, _sceneObjectCreateModeOpen, _sceneObjectEditModeOpen, useComponenter, useComponenterState, } from './module'; import { ComponenterTree, TreeSearch, RightClickContextMenu, TransformButtonGroup, ComponenterConfigForm, SceneConfigForm, TemplateTool, BatchModelTool, OfficialModelPopup, BatchModelPrams, } from './components'; import Header from './Header'; const CONTAINER_ID = 'scene-editor'; export default function ProjectEditor() { const { projectId } = useParams(); const [, setThemeLoading] = useRecoilState(_themeLoading); const [easyTwinRenderer, setEasyTwinRenderer] = useState(); const [easyTwinLoader, setEasyTwinLoader] = useState(); const [sceenLoaded, setSceenLoaded] = useState(false); const [sceenUpdated, setSceenUpdated] = useState(false); // 官方模型库显隐控制 const [officialModelPopup, setOfficialModelPopup] = useState(false); const [batchModelParams, setBatchModelParams] = useState(null); // 通过单次交互操作所创建的新增元件列表详情 const [addedComponentDetailList, setAddedComponentDetailList] = useState([]); /** * project */ const { getProjectInfo } = useProject(); const { data: sceneInfo } = useQuery('sceneInfo', () => { if (!projectId) { throw Error('缺失项目Id'); } return SceneApi.getScene({ projectId }); }); const [, setProjectInfo] = useRecoilState(_projectInfo); const [sceneId, setSceneId] = useRecoilState(_sceneId); useEffect(() => { if (sceneInfo?.id) { setSceneId(sceneInfo.id); } }, [setSceneId, sceneInfo?.id]); useEffect(() => { if (!easyTwinRenderer || !sceneInfo) return; const viewport = easyTwinRenderer.getViewport(); const { commonConfig: { config: { useGroundImg, groundImgUrl, minLng, maxLng, minLat, maxLat, near, far }, }, } = sceneInfo as Scene; const baseMap = easyTwinRenderer.getAnchorById(BaseMapId) as BaseMap; viewport?.setCameraOption?.({ near, far, }); if (!groundImgUrl) { return easyTwinRenderer.setBaseMap(baseMap, { mapVisible: false, gridVisible: true, }); } let geobox: number[][] | undefined; if (minLng && maxLng && minLat && maxLat) { geobox = [ [maxLng, minLat], [minLng, maxLat], ]; } if (baseMap) { return easyTwinRenderer.setBaseMap(baseMap, { mapVisible: useGroundImg, gridVisible: !useGroundImg, mapImageUrl: groundImgUrl, geobox, }); } }, [easyTwinRenderer, sceneInfo]); /** * project state */ // 当前项目下的场景状态列表 const [projectStateList, setProjectStateList] = useRecoilState(_sceneStateList); // 当前项目下的场景状态选中 key const [projectStatesSelectKey, setProjectStatesSelectKey] = useRecoilState(_sceneStatesSelectKey); useEffect(() => { if (!easyTwinRenderer) return; // 选中的状态信息 const state = projectStateList.find((item) => item.id === projectStatesSelectKey); if (!state) return; const defaultState = initStateData[StateTypeEnum.PROJECT]; const { ambientLightColor, ambientLightIntensity, useHdr, hdrFileUrl, backgroundColor, backgroundOpacity, } = defaultState as ProjectStateConfig; // 设置环境光 easyTwinRenderer.setAmbientLight({ color: ambientLightColor, intensity: ambientLightIntensity, }); // 设置场景背景色 easyTwinRenderer.setSceneBackgroundImage(backgroundColor, backgroundOpacity); // 如果开启 HDR 功能,并且有 HDR 文件,则加载 HDR 文件 if (useHdr && hdrFileUrl) { easyTwinRenderer.loadBackgroundTexture(hdrFileUrl); } // 关闭 HDR else { easyTwinRenderer.removeHDR(); } }, [easyTwinRenderer, projectStateList, projectStatesSelectKey]); /** * project viewpoint */ // 当前项目下的场景镜头列表 const [projectViewpointList, setProjectViewpointList] = useRecoilState(_sceneViewpointList); // 当前项目下的场景镜头选中 key const [projectViewpointSelectKey, setProjectViewpointSelectKey] = useRecoilState(_sceneViewpointSelectKey); const [, setAnimationSelectKey] = useRecoilState(_componenterAnimationSelectKey); useEffect(() => { if (!easyTwinRenderer) return; // 选中的镜头信息 const viewpoint = projectViewpointList.find((item) => item.id === projectViewpointSelectKey); // 设置场景相机镜头 if (viewpoint && viewpoint.config) { easyTwinRenderer.setViewPortAttr(viewpoint.config, { duration: 1000, delay: 0 }); } }, [easyTwinRenderer, projectViewpointList, projectViewpointSelectKey]); /** * init project info */ // 获取项目信息 const handleGetProjectInfo = useCallback(async () => { if (!projectId) return; const projectInfo = await getProjectInfo(projectId); const scenes = projectInfo?.scenes; if (!scenes) return; const [defaultScene] = scenes; if (defaultScene) { const { stateConfig, stateConfig: [defaultState], viewpointConfig, viewpointConfig: [defaultViewpoint], } = defaultScene; setProjectInfo(projectInfo); // 初始化场景状态列表 setProjectStateList(stateConfig); // 默认选中第一个状态 setProjectStatesSelectKey(defaultState?.id); // 初始化场景镜头列表 setProjectViewpointList(viewpointConfig); // 默认选中第一个镜头 setProjectViewpointSelectKey(defaultViewpoint?.id); } }, [ projectId, getProjectInfo, setProjectInfo, setProjectStateList, setProjectStatesSelectKey, setProjectViewpointList, setProjectViewpointSelectKey, ]); // 初始化项目信息 useEffect(() => { handleGetProjectInfo(); return () => { // 退出时清空项目信息 setProjectInfo(undefined); }; }, [handleGetProjectInfo, setProjectInfo]); /** * componenter */ const { getComponenterFilteredTreeList, getComponenterTreeList, getComponenterDetail, createComponent, createComponenterByLocalModelList, createComponentByModelUrl, createComponentByBatchModelGroup, createComponentByBatchModel, // createComponenterByAsset, // createComponenterAssetChild, createComponenterGroup, updateComponenter, updateComponenterInLayer, copyComponenter, removeComponent, removeComponentGroup, } = useComponenter(); const [currentComponentDetail, setCurrentComponenterDetail] = useRecoilState(_currentComponentDetail); // 初始化元件树过滤数据 useEffect(() => { getComponenterFilteredTreeList(); }, [getComponenterFilteredTreeList]); const [componenterTreeList, setComponenterTreeList] = useRecoilState(_componenterTreeList); // 初始化元件树原数据 useEffect(() => { getComponenterTreeList(); return () => { // 退出时清空元件树数据 setComponenterTreeList(undefined); setCurrentComponenterDetail(undefined); }; }, [getComponenterTreeList, setComponenterTreeList, setCurrentComponenterDetail]); // 控制元件树数据更新时主题加载组件开启的状态 useEffect(() => { if (componenterTreeList) { if (!componenterTreeList.length) setThemeLoading(false); else setThemeLoading(!sceenLoaded); } else { setThemeLoading(true); } }, [sceenLoaded, setThemeLoading, componenterTreeList]); // 元件树数据更新时,重新加载场景 const loadScene = useCallback(async () => { if (easyTwinLoader && componenterTreeList) { setSceenUpdated(false); await easyTwinLoader.initScene(treeList2SimpleList(componenterTreeList)); setSceenUpdated(true); setSceenLoaded(true); } }, [componenterTreeList, easyTwinLoader]); // 监听加载场景的方式事件 useEffect(() => { loadScene(); }, [loadScene]); const { updateState } = useComponenterState(); // 元件树上选中的元件节点列表 const [selectComponents, setSelectComponents] = useRecoilState(_componentSelectList); // 获取选中的元件节点列表中第一个节点的详情 useEffect(() => { getComponenterDetail(selectComponents[0]?.id); }, [selectComponents, getComponenterDetail]); // 通过本地模型创建元件 const handleCreateComponenterByLocalModelList = async ({ fileList }: any) => { const list = await createComponenterByLocalModelList( fileList.map((i: UploadFile) => i.originFileObj), ); setAddedComponentDetailList(list); }; // 通过线上模型创建元件 const handleCreateComponentByModelUrl = async (url: string) => { setOfficialModelPopup(false); const detail = await createComponentByModelUrl(url); if (detail) { setAddedComponentDetailList([detail]); setCurrentComponenterDetail(detail); } }; const onCreateBatchModelMode = async (props: BatchModelPrams) => { setOfficialModelPopup(false); setBatchModelParams(props); }; const handleCreateComponentByBatchModel = async (positions: IVector3[]) => { if (batchModelParams) { const detail = await createComponentByBatchModelGroup(batchModelParams); if (detail) { const { id } = detail; const _detail = await createComponentByBatchModel({ ...batchModelParams, positions, parentObjId: id, }); if (_detail) { setAddedComponentDetailList([_detail]); setCurrentComponenterDetail(_detail); } } } }; // 创建灯光元件 const handleCreateComponenterLight = async ( query: Pick, ) => { if (!sceneId) return; const detail = await createComponent({ sceneId, // parentObjId: currentComponentDetail.parentObjId, // FIXME: ...query, }); if (detail) { setAddedComponentDetailList([detail]); } }; // 创建数据要素(资产)类型元件的子类 const handleCreateAssetChildComponenter = async (position: IVector3 | IVector3[]) => { // if (!currentComponentDetail) return; // let config: ComponentConfig = {}; // if (Array.isArray(position)) { // if (currentComponentDetail.type === ComponentTypeEnum.FENCE) { // config = { // positionList: JSON.stringify(position, null, 2), // }; // } // } else if ( // currentComponentDetail.type === ComponentTypeEnum.POINT || // currentComponentDetail.type === ComponentTypeEnum.POIPANNEL // ) { // config = { // positionX: position.x, // positionY: position.y, // positionZ: position.z, // }; // } else if (currentComponentDetail.type === ComponentTypeEnum.POIIFRAME) { // config = { // positionX: position.x, // positionY: position.y, // positionZ: position.z, // url: 'https://easyv.cloud/', // }; // } // await createComponenterAssetChild(currentComponentDetail.id, config); // await getComponenterTreeList(); const config: ComponentConfig = {}; if (position) { console.log(position); // config = { // positionList: JSON.stringify(position, null, 2), // }; // await createComponenterAssetChild(currentComponentDetail.id, config); } }; /** * scene */ // 切换编辑模式 const handleEditModeChange = useCallback(() => { handleGetProjectInfo(); setSelectComponents([]); }, [handleGetProjectInfo, setSelectComponents]); useEffect(() => { handleEditModeChange(); }, [handleEditModeChange]); // 存储当前场景实例 const [, setCurrentScene] = useRecoilState(_currentScene); // 是否开启在场景中点选拾取坐标模式 const [pickPositionModeOpen] = useRecoilState(_scenePickPositionModeOpen); // 是否开启场景中空间对象创建模式 const [sceneObjectCreateModeOpen] = useRecoilState(_sceneObjectCreateModeOpen); // 是否开启场景中空间对象编辑模式 const [sceneObjectEditModeOpen] = useRecoilState(_sceneObjectEditModeOpen); // 当前选中元件发生变化时,针对模型和灯光的操作逻辑 useEffect(() => { if (!easyTwinRenderer || !sceenUpdated) return; // 取消场景中的所有选中状态 easyTwinRenderer.selectAnchor(null); if (!currentComponentDetail) return; const { visible, type, id } = currentComponentDetail; // 如果不是模型或灯光,则退出 if ( !visible || (type !== ComponentTypeEnum.MODEL && type !== ComponentTypeEnum.POINTLIGHT && type !== ComponentTypeEnum.DIRECTLIGHT) ) return; // 获取选中元件对应的空间对象 const anchor = easyTwinRenderer.getAnchorById(id); if (!anchor) return; // 设置选中状态 easyTwinRenderer.selectAnchor(anchor); }, [easyTwinRenderer, currentComponentDetail, sceenUpdated]); // 当前选中元件发生变化时,针对 散点、信息面板、Iframe 面板子要素操作逻辑 useEffect(() => { if (!easyTwinRenderer || !currentComponentDetail) return; const { visible, type, id, parentObjId: pid, commonConfig: { isGroup = false }, } = currentComponentDetail; if ( visible === undefined || isGroup || !pid || (type !== ComponentTypeEnum.POINT && type !== ComponentTypeEnum.POIPANNEL && type !== ComponentTypeEnum.POIIFRAME) ) return; const viewport = easyTwinRenderer.getViewport(); const template = viewport.templateController.getTemplate(pid); const child = template?.getChildrenById(id); child && easyTwinRenderer.selectAnchor(child); }, [easyTwinRenderer, currentComponentDetail]); // 更新围栏元件点位坐标的绑定事件 const updateFenceComponenterPointsEvent = useCallback( ({ anchor }: { anchor: FlowFence }) => { // if (!currentComponentDetail) return; // console.log(anchor); // updateComponenter({ // id: currentComponentDetail.id, // config: { // ...currentComponentDetail.config, // positionList: JSON.stringify(anchor.getPoints(), null, 2), // }, // }); }, [currentComponentDetail, updateComponenter], ); // 围栏空间对象 const [flowFence, setFlowFence] = useState(); // 当前选中元件发生变化时,针对围栏的操作逻辑 useEffect(() => { if (!easyTwinRenderer || !sceneInfo) return; const viewport = easyTwinRenderer.getViewport(); const { commonConfig: { config: { useGroundImg, groundImgUrl, minLng, maxLng, minLat, maxLat, near, far }, }, } = sceneInfo as Scene; const baseMap = easyTwinRenderer.getAnchorById(BaseMapId) as BaseMap; viewport?.setCameraOption?.({ near, far, }); if (!groundImgUrl) { return easyTwinRenderer.setBaseMap(baseMap, { mapVisible: false, gridVisible: true, }); } let geobox: number[][] | undefined; if (minLng && maxLng && minLat && maxLat) { geobox = [ [maxLng, minLat], [minLng, maxLat], ]; } if (baseMap) { return easyTwinRenderer.setBaseMap(baseMap, { mapVisible: useGroundImg, gridVisible: !useGroundImg, mapImageUrl: groundImgUrl, geobox, }); } }, [easyTwinRenderer, sceneInfo]); // 选中围栏空间对象时,进入编辑模式 useEffect(() => { if (!flowFence) return; if (sceneObjectEditModeOpen) { flowFence.startEditor(); flowFence.addEventListener( EventName.Template.FenceUpdateEnd, updateFenceComponenterPointsEvent, ); } else { flowFence.endEditor(); flowFence.removeEventListener( EventName.Template.FenceUpdateEnd, updateFenceComponenterPointsEvent, ); } }, [flowFence, sceneObjectEditModeOpen, updateFenceComponenterPointsEvent]); // 新增元件列表发生变化时,自动加载到场景中 const handleCreateAnimationList = useCallback(async () => { if (!easyTwinLoader || !addedComponentDetailList.length) return; await easyTwinLoader.addComponenterList(addedComponentDetailList, async (info, model) => { if (model.animations.length > 0) { const animationConfig = (await ComponentApi.addAnimations({ rank: 0, objId: info.id, names: model.animations.map((i) => i.name), })) as AnimationInfo[]; setCurrentComponenterDetail((prev) => ({ ...(prev as ComponentDetailInfo), animationConfig, })); setAnimationSelectKey(animationConfig[0].id); } }); // 加载后清空列表 setAddedComponentDetailList([]); // 更新元件树 getComponenterTreeList(); }, [easyTwinLoader, addedComponentDetailList, getComponenterTreeList]); // 监听加载新增元件列表的函数事件 useEffect(() => { handleCreateAnimationList(); }, [handleCreateAnimationList]); // 被删除的元件列表 const [componenterRemovedList, setComponenterRemovedList] = useRecoilState(_componenterRemovedList); // 被删除的元件列表更新时删除在场景中的对应空间对象 useEffect(() => { if (!easyTwinLoader || !componenterRemovedList.length) return; componenterRemovedList.forEach((item) => easyTwinLoader.removeComponterItem(item)); // 清空列表 setComponenterRemovedList([]); }, [easyTwinLoader, componenterRemovedList, setComponenterRemovedList]); // 初始化场景 useEffect(() => { const containerDom = document.getElementById(CONTAINER_ID); if (containerDom !== null && !easyTwinRenderer && !easyTwinLoader) { const renderer = new EasyTwinRenderer({ containerId: CONTAINER_ID, viewHelper: { enable: true, position: 'rightTop', padding: 16, }, }); setEasyTwinRenderer(renderer); setCurrentScene(renderer); const loader = new EasyTwinLoader(renderer); setEasyTwinLoader(loader); } return () => { if (easyTwinRenderer) { easyTwinRenderer.disposeViewport(); setEasyTwinRenderer(undefined); } if (easyTwinLoader) { setEasyTwinLoader(undefined); } }; }, [easyTwinLoader, easyTwinRenderer, setCurrentScene]); // 元件树右键菜单显示状态 const [contextMenuVisible, setContextMenuVisible] = useState(false); // 元件树右键菜单样式 const [contextMenuStyle, setContextMenuStyle] = useState(); // 已复制的元件列表 const [copyComponents, setCopyComponents] = useState([]); // 当前编辑中的元件节点 const [editingComponenter, setEditingComponenter] = useState(); /** * componenter state */ const [componentStateList] = useRecoilState(_componentStateList); const [componentStateSelectKey] = useRecoilState(_componentStateSelectKey); useEffect(() => { if (!easyTwinLoader || !currentComponentDetail) return; const state = componentStateList.find((item) => item.id === componentStateSelectKey); easyTwinLoader.updateComponterItem(currentComponentDetail, state); }, [easyTwinLoader, currentComponentDetail, componentStateList, componentStateSelectKey]); /** * componenter animation */ const [componentAnimationList] = useRecoilState(_componenterAnimationList); const [componenterAnimationSelectKey] = useRecoilState(_componenterAnimationSelectKey); const runungAnimation = useMemo( () => componentAnimationList.find((item) => item.id === componenterAnimationSelectKey), [componentAnimationList, componenterAnimationSelectKey], ); useEffect(() => { if (!easyTwinRenderer || !runungAnimation || !currentComponentDetail) return; const { id, type } = currentComponentDetail; if (type !== ComponentTypeEnum.MODEL) return; const modelAnchor = easyTwinRenderer.getAnchorById(id); if (!modelAnchor) return; const { name, config: { autoPlay, playNum, timeScale }, } = runungAnimation; if (autoPlay) { easyTwinRenderer.playAnimation(modelAnchor, { name, repetitions: playNum === ModelAnimationPlayNumEnum.ONE ? 1 : Infinity, timeScale, }); } else { easyTwinRenderer.stopAnimation(modelAnchor); } }, [runungAnimation, currentComponentDetail, easyTwinRenderer]); /** * componenter viewpoint */ const [componenterViewpointList] = useRecoilState(_componenterViewpointList); const [componenterViewpointSelectKey] = useRecoilState(_componenterViewpointSelectKey); useEffect(() => { const viewpoint = componenterViewpointList.find( (item) => item.id === componenterViewpointSelectKey, ); if (easyTwinRenderer && viewpoint) { easyTwinRenderer.setViewPortAttr(viewpoint.config, { duration: 1000, delay: 0 }); } }, [componenterViewpointList, componenterViewpointSelectKey, easyTwinRenderer]); /** * scene event */ // 场景空间对象点击时的交互逻辑 const handleSceneObjectClick = useCallback( async (event: any) => { if (pickPositionModeOpen || sceneObjectCreateModeOpen || sceneObjectEditModeOpen) return; const bizData = event.anchor?.userData?.bizData; if (bizData) { setSelectComponents([bizData]); } else { setSelectComponents([]); } }, [pickPositionModeOpen, sceneObjectCreateModeOpen, sceneObjectEditModeOpen, setSelectComponents], ); // 场景空间对象被拖拽时的交互逻辑 const handleSceneObjectDrag = useCallback( async (event: DragChangedEvent) => { const { anchor } = event; if (!anchor) return; const { position, rotation, scale } = anchor; const editState = componentStateList.find((item) => item.id === componentStateSelectKey); if (!editState) return; const [component] = selectComponents; if (!component) return; const editStateConfig = { ...editState.config } as ModelStateConfig; // editStateConfig.positionX = position.x; editStateConfig.positionY = position.y; editStateConfig.positionZ = position.z; // editStateConfig.rotationX = (rotation.x * 180) / Math.PI; editStateConfig.rotationY = (rotation.y * 180) / Math.PI; editStateConfig.rotationZ = (rotation.z * 180) / Math.PI; // editStateConfig.scaleX = scale.x; editStateConfig.scaleY = scale.y; editStateConfig.scaleZ = scale.z; await updateState({ // not use ...editViewpoint, because not need prop 'name'. id: editState.id, bizId: component.id, config: editStateConfig, }); }, [selectComponents, componentStateList, componentStateSelectKey, updateState, selectComponents], ); // 场景灯光对象被拖拽时的交互逻辑 const handleSceneLightDrag = useCallback( async (event: DragLightChangedEvent) => { const { light, lightTarget } = event; if (!light) return; const { position } = light; const editState = componentStateList.find((item) => item.id === componentStateSelectKey); if (!editState) return; const [component] = selectComponents; if (!component) return; const editStateConfig = { ...editState.config } as | PointLightStateConfig | DirectLightStateConfig; // editStateConfig.positionX = position.x; editStateConfig.positionY = position.y; editStateConfig.positionZ = position.z; // 如果有灯光目标属性 if (lightTarget) { const { position: targetPosition } = lightTarget; (editStateConfig as DirectLightStateConfig).targetX = targetPosition.x; (editStateConfig as DirectLightStateConfig).targetY = targetPosition.y; (editStateConfig as DirectLightStateConfig).targetZ = targetPosition.z; } await updateState({ // not use ...editViewpoint, because not need prop 'name'. id: editState.id, bizId: component.id, config: editStateConfig, }); }, [componentStateList, componentStateSelectKey, updateState, selectComponents], ); // 初始化场景交互事件绑定 useEffect(() => { if (easyTwinRenderer) { const dispatcher = easyTwinRenderer.getEventDispatcher(); dispatcher.addEventListener(EventName.Scene.Click, handleSceneObjectClick); dispatcher.addEventListener(EventName.BaseAnchor.DragChanged, handleSceneObjectDrag); dispatcher.addEventListener(EventName.BaseAnchor.LightChanged, handleSceneLightDrag); } // 退出时移除场景交互事件绑定 return () => { if (easyTwinRenderer) { const dispatcher = easyTwinRenderer.getEventDispatcher(); dispatcher.removeEventListener(EventName.Scene.Click, handleSceneObjectClick); dispatcher.removeEventListener(EventName.BaseAnchor.DragChanged, handleSceneObjectDrag); dispatcher.removeEventListener(EventName.BaseAnchor.LightChanged, handleSceneLightDrag); } }; }, [easyTwinRenderer, handleSceneLightDrag, handleSceneObjectClick, handleSceneObjectDrag]); /** * */ // 元件节点成组操作 const handleCreateComponenterGroup = () => { createComponenterGroup(); setContextMenuVisible(false); }; // 元件节点解组操作 const handleRemovComponentereGroup = () => { if (!currentComponentDetail) return; removeComponentGroup({ ids: [currentComponentDetail.id], }); setContextMenuVisible(false); }; // 元件节点剪贴操作 const handleCloneComponenter = () => { if (!selectComponents.length) return; sceneId && copyComponenter({ sceneId, parentObjId: currentComponentDetail?.parentObjId || null, ids: selectComponents.map((item) => item.id), }); setContextMenuVisible(false); }; // 元件节点复制操作 const handleCopyComponenter = () => { if (!selectComponents.length) return; setCopyComponents(selectComponents); message.success('已复制到剪贴板'); setContextMenuVisible(false); }; // 元件节点粘贴操作 const handlePasteComponenter = () => { if (!selectComponents.length) return; sceneId && copyComponenter({ sceneId, parentObjId: currentComponentDetail && currentComponentDetail.type === ComponentTypeEnum.BIZGROUP ? currentComponentDetail.id : null, ids: copyComponents.map((item) => item.id), }); setContextMenuVisible(false); }; // 元件节点对应空间对象视角聚焦操作 const handleFlyToComponenter = () => { if ( currentComponentDetail && currentComponentDetail.type === ComponentTypeEnum.MODEL && easyTwinRenderer ) { const anchor = easyTwinRenderer.getAnchorById(currentComponentDetail.id); anchor && easyTwinRenderer.flyToAnchor(anchor); } }; // 元件节点其他信息更新操作 const handleUpdateComponenter = async (query: Partial) => { const info = await updateComponenter(query); console.log(info); if (info) { setCurrentComponenterDetail(info); } }; // 元件节点显隐状态切换操作 const handleChangeVisibleComponenter = async (id: string, visible: boolean) => { const info = await updateComponenterInLayer({ id, visible, }); if (info) { setCurrentComponenterDetail(info); } }; // 元件节点锁定状态切换操作 const handleChangeActiveComponenter = async (id: string, inActive: boolean) => { const info = await updateComponenterInLayer({ id, locked: inActive, }); if (info) { setCurrentComponenterDetail(info); } }; const handleOpenNameEditComponenter = async () => { setEditingComponenter(selectComponents[0]); }; // 元件节点重命名 const handleEditNameComponenter = async (id: string, name: string) => { if (name === currentComponentDetail?.name) return; const info = await updateComponenter({ id, name, }); setEditingComponenter(undefined); info && setCurrentComponenterDetail(info); }; // 展开/收起元件节点操作 const handleExpandComponenter = async (id: string, collapsed: boolean) => { await updateComponenter( { id, commonConfig: { collapsed }, }, false, ); }; /** * 官方模型库 */ const handleShowOfficialModelPopup = () => { setOfficialModelPopup(true); }; // 删除选中元件 const handleRemoveComponenter = async () => { if (!selectComponents.length) return; Modal.confirm({ title: '确认删除', icon: , content: '删除后不可恢复,确认删除选中项?', async onOk() { if (sceneId) { await removeComponent({ ids: selectComponents.map((item) => item.id), }); } else { message.error('未找到sceneId'); } // 关闭元件树右键菜单 setContextMenuVisible(false); }, }); }; /** * 快捷键事件 */ // 元件节点成组快捷键 useKey((e) => isCtrlKey(e) && e.code === 'KeyG', handleCreateComponenterGroup, undefined, [ handleCreateComponenterGroup, ]); // 元件节点解组快捷键 useKey( (e) => isCtrlKey(e) && e.shiftKey && e.code === 'KeyG', handleRemovComponentereGroup, undefined, [handleRemovComponentereGroup], ); // 元件节点剪贴快捷键 useKey( (e) => isCtrlKey(e) && !isInputTag(e.target as HTMLElement) && e.code === 'KeyX', handleCloneComponenter, undefined, [handleCloneComponenter], ); // 元件节点复制快捷键 useKey( (e) => isCtrlKey(e) && !isInputTag(e.target as HTMLElement) && e.code === 'KeyC', handleCopyComponenter, undefined, [handleCopyComponenter], ); // 元件节点粘贴快捷键 useKey( (e) => isCtrlKey(e) && !isInputTag(e.target as HTMLElement) && e.code === 'KeyV', handlePasteComponenter, undefined, [handlePasteComponenter], ); // 元件节点对应空间对象聚焦快捷键 useKey( (e) => isCtrlKey(e) && !isInputTag(e.target as HTMLElement) && e.code === 'KeyF', handleFlyToComponenter, undefined, [handleFlyToComponenter], ); // 元件节点显示/隐藏快捷键 useKey( (e) => isCtrlKey(e) && e.code === 'KeyH', () => { currentComponentDetail && handleChangeVisibleComponenter(currentComponentDetail?.id, !currentComponentDetail.visible); }, undefined, [currentComponentDetail, handleChangeVisibleComponenter], ); // 元件节点锁定/解锁快捷键 useKey( (e) => isCtrlKey(e) && e.code === 'KeyL', () => { currentComponentDetail && handleChangeActiveComponenter( currentComponentDetail?.id, !currentComponentDetail.commonConfig.inActive, ); }, undefined, [currentComponentDetail, handleChangeVisibleComponenter], ); // 元件节点重命名快捷键 useKey((e) => isCtrlKey(e) && e.code === 'KeyR', handleOpenNameEditComponenter, undefined, [ handleOpenNameEditComponenter, ]); // 元件节点删除快捷键 useKey( ({ code, target }) => !isInputTag(target as HTMLElement) && code === 'Backspace', handleRemoveComponenter, undefined, [handleRemoveComponenter], ); return (
{/* header */}
{/* main */}
{/* left */}
setContextMenuVisible(false)} onVisibleClick={handleChangeVisibleComponenter} onInActiveClick={handleChangeActiveComponenter} onRightClick={(style) => { setContextMenuStyle(style); setContextMenuVisible(true); }} onNameEditChange={handleEditNameComponenter} onExpandChange={handleExpandComponenter} onDrop={(id, pid, rank) => { // updateComponenterInLayer({ // id, // sortUpperObjId, // sortLowerObjId, // }); }} />
{contextMenuVisible && ( { currentComponentDetail && handleChangeVisibleComponenter( currentComponentDetail.id, !currentComponentDetail.visible, ); }} onActiveChange={() => { currentComponentDetail && handleChangeActiveComponenter( currentComponentDetail.id, !currentComponentDetail.commonConfig.inActive, ); }} onEdit={handleOpenNameEditComponenter} onRemove={handleRemoveComponenter} onClose={() => setContextMenuVisible(false)} /> )} {/* { currentComponentDetail && removeComponent({ ids: [currentComponentDetail.id], }); }} /> */}
{/* edit */}
{/* scene edit */}
{/* scene */}
{/* Tool Box */} {currentComponentDetail && currentComponentDetail.type === ComponentTypeEnum.MODEL && !currentComponentDetail.commonConfig.inActive && ( easyTwinRenderer?.setTransformMode(mode)} /> )} {/* officialModel */}
{/* config form */}
{currentComponentDetail ? ( ) : ( )}
); }