diff --git a/src/Add-on/testEntity/TestTemplateDelete.ts b/src/Add-on/testEntity/TestTemplateDelete.ts new file mode 100644 index 000000000..12dbd842a --- /dev/null +++ b/src/Add-on/testEntity/TestTemplateDelete.ts @@ -0,0 +1,47 @@ +import { copyTextToClipboard } from "../../Common/Utils"; +import { ObjectId } from "../../DatabaseServices/ObjectId"; +import { DeleteTempate } from "../../DatabaseServices/Template/TempateUtils"; +import { TemplateRecord } from "../../DatabaseServices/Template/TemplateRecord"; +import { SelectTempate } from "../../DatabaseServices/Template/TemplateTest"; +import { Command, commandMachine } from "../../Editor/CommandMachine"; +import { HotCMD } from "../../Hot/HotCommand"; + +@HotCMD +export class Command_DeleteTemplate implements Command +{ + async exec() + { + let temp = await SelectTempate(); + if (!temp) return; + DeleteTempate(temp); + } +} + +@HotCMD +export class Command_Template2Tree implements Command +{ + async exec() + { + let temp = await SelectTempate(); + if (!temp) return; + + temp = temp.Root; + console.log(JSON.stringify(TemplateToTree(temp.Id), null, 2)); + } +} + + +interface Node +{ + id: number; + children: Node[]; +} + +function TemplateToTree(temp: ObjectId): Node +{ + let record = temp.Object as TemplateRecord; + let node = { id: temp.Index, children: record.Children.filter(id => !id.IsErase).map(TemplateToTree) }; + return node; +} + +commandMachine.RegisterCommand("t2tree", new Command_Template2Tree()) diff --git a/src/DatabaseServices/Template/TempateUtils.ts b/src/DatabaseServices/Template/TempateUtils.ts index 468439c2d..68a942bb9 100644 --- a/src/DatabaseServices/Template/TempateUtils.ts +++ b/src/DatabaseServices/Template/TempateUtils.ts @@ -1,6 +1,7 @@ import { Intent } from "@blueprintjs/core"; import { Box3, Color, Matrix4, Scene, Vector3 } from "three"; import { app } from "../../ApplicationServices/Application"; +import { arrayRemoveOnce } from "../../Common/ArrayExt"; import { DisposeThreeObj } from "../../Common/Dispose"; import { TemplateUrls } from "../../Common/HostUrl"; import { PostJson, RequestStatus, uploadLogo } from "../../Common/Request"; @@ -20,6 +21,7 @@ import { AppToaster } from "../../UI/Components/Toaster"; import { CADFiler } from "../CADFiler"; import { Database } from "../Database"; import { Board } from "../Entity/Board"; +import { Entity } from "../Entity/Entity"; import { Polyline } from "../Entity/Polyline"; import { ObjectId } from "../ObjectId"; import { TempateThicknessAction, ThicknessActionData, ThicknessDirection } from "./Action/TempateThicknessAction"; @@ -931,3 +933,23 @@ export async function UploadUpdateTemplate(template: TemplateRecord, templateId: } return false; } + +/** + * 删除模块节点,同时会删除其子节点 + */ +export function DeleteTempate(template: TemplateRecord) +{ + template.Traverse(t => + { + for (let id of t.Objects) + { + if (!id.IsErase) + { + let e = id.Object as Entity; + e.Erase(); + } + } + + t.Erase(); + }); +} diff --git a/src/DatabaseServices/Template/TemplateRecord.ts b/src/DatabaseServices/Template/TemplateRecord.ts index 5d338f488..94fc16740 100644 --- a/src/DatabaseServices/Template/TemplateRecord.ts +++ b/src/DatabaseServices/Template/TemplateRecord.ts @@ -1,4 +1,5 @@ import { Box3, Math, Matrix4, Vector3 } from "three"; +import { arrayRemoveOnce } from "../../Common/ArrayExt"; import { AutoRecord, ISPROXYKEY } from "../AutoRecord"; import { Factory } from "../CADFactory"; import { CADFiler } from "../CADFiler"; @@ -116,6 +117,10 @@ export class TemplateRecord extends SymbolTableRecord { if (key === ISPROXYKEY) return true; + else if (key === "splice") + this.WriteAllObjectRecord(); + else if (key === "pop") + this.WriteAllObjectRecord(); return Reflect.get(target, key, receiver); } }); diff --git a/src/Editor/CommandRegister.ts b/src/Editor/CommandRegister.ts index 3065a9a92..2b87c5851 100644 --- a/src/Editor/CommandRegister.ts +++ b/src/Editor/CommandRegister.ts @@ -129,6 +129,7 @@ import { CommandServer } from "../DatabaseServices/CommandServer"; import { AutoTempateSizeAction } from "../DatabaseServices/Template/TemplateTest"; import { ICommand } from "../UI/Components/CommandPanel/CommandList"; import { commandMachine } from "./CommandMachine"; +import { Command_DeleteTemplate } from "../Add-on/testEntity/TestTemplateDelete"; export function registerCommand() { @@ -348,8 +349,8 @@ export function registerCommand() commandMachine.RegisterCommand("template", new ShowTemplate()); commandMachine.RegisterCommand("templatedesign", new ShowTemplateDesign()); - commandMachine.RegisterCommand("TemplateSearch", new Command_TemplateSearch()); + commandMachine.RegisterCommand("templateDelete", new Command_DeleteTemplate()); RegistCustomCommand(); } diff --git a/src/UI/Components/RightPanel/RightPanel.tsx b/src/UI/Components/RightPanel/RightPanel.tsx index 71daea16f..bc98578f4 100644 --- a/src/UI/Components/RightPanel/RightPanel.tsx +++ b/src/UI/Components/RightPanel/RightPanel.tsx @@ -83,7 +83,7 @@ export class RightPanel extends React.Component<{ store?: RightPanelStore }> } /> } /> } /> - } /> + } /> } /> diff --git a/src/UI/Components/RightPanel/TemplateParam.less b/src/UI/Components/RightPanel/TemplateParam.less index 71395f8d2..24c07b17a 100644 --- a/src/UI/Components/RightPanel/TemplateParam.less +++ b/src/UI/Components/RightPanel/TemplateParam.less @@ -5,6 +5,9 @@ .bp3-tree{ height: 100%; overflow: auto; + .bp3-tree-node-content{ + height: 20px; + } } } .template-detail { diff --git a/src/UI/Components/RightPanel/TemplateParamPanel.tsx b/src/UI/Components/RightPanel/TemplateParamPanel.tsx index 602c63d24..8c81766a2 100644 --- a/src/UI/Components/RightPanel/TemplateParamPanel.tsx +++ b/src/UI/Components/RightPanel/TemplateParamPanel.tsx @@ -1,80 +1,120 @@ -import React from "react"; -import { observer, inject } from "mobx-react"; -import { TemplateRecord } from "../../../DatabaseServices/Template/TemplateRecord"; -import { TemplateParam } from "../../../DatabaseServices/Template/Param/TemplateParam"; -import { INeedUpdateParams } from "../Template/TemplateComponent"; +import { Button, Classes, ContextMenu, H5, Intent, ITreeNode, Menu, MenuItem, Tree } from "@blueprintjs/core"; import { observable } from "mobx"; -import { H5, Tree, ITreeNode, Classes, ContextMenu, Menu, MenuItem, Intent, Button } from "@blueprintjs/core"; -import { app } from "../../../ApplicationServices/Application"; -import { Entity } from "../../../DatabaseServices/Entity/Entity"; +import { observer } from "mobx-react"; +import React from "react"; import { Object3D } from "three"; -import { Board } from "../../../DatabaseServices/Entity/Board"; +import { end } from "xaop"; +import { app } from "../../../ApplicationServices/Application"; +import { arrayRemoveOnce } from "../../../Common/ArrayExt"; import { EBoardKeyList } from "../../../Common/BoardKeyList"; -import { FixedNotZero } from "../../../Common/Utils"; import { safeEval } from "../../../Common/eval"; -import { CommandWrap } from "../../../Editor/CommandMachine"; +import { FixedNotZero } from "../../../Common/Utils"; +import { CommandHistoryRecord } from "../../../DatabaseServices/CommandHistoryRecord"; +import { Board } from "../../../DatabaseServices/Entity/Board"; +import { Entity } from "../../../DatabaseServices/Entity/Entity"; import { PositioningClampSpace } from "../../../DatabaseServices/Template/Positioning/PositioningClampSpace"; +import { DeleteTempate } from "../../../DatabaseServices/Template/TempateUtils"; +import { TemplateRecord } from "../../../DatabaseServices/Template/TemplateRecord"; import { TemplateSplitType } from "../../../DatabaseServices/Template/TemplateType"; -import { end } from "xaop"; -import { CommandHistoryRecord } from "../../../DatabaseServices/CommandHistoryRecord"; +import { CommandWrap } from "../../../Editor/CommandMachine"; +import { INeedUpdateParams } from "../Template/TemplateComponent"; interface TemplateParamState { - nodes: ITreeNode[];//目录节点 + nodes: ITreeNode[];//节点 +} + +interface TemplateParamProps +{ + selectTemplateId: number; } -@inject('template') + @observer -export class TemplateParamPanel extends React.Component<{ template: TemplateRecord }, TemplateParamState> +export class TemplateParamPanel extends React.Component { - //被展示的模板 - @observable private currentTemplate: TemplateRecord = this.props.template; + //展示模块树的根节点 + private displayTemplateRootId: number; + //当前选中的节点模块id,可能被删除. + private currentTemplateId: number; + //TemplateDetail组件的Props 用于展示模板数据 - @observable private params: TemplateParam[] = []; @observable private currentProps: INeedUpdateParams[] = []; - //记录需要在模板树UI中亮显的节点 - private needSelectedNode: ITreeNode; - //UI交互相关变量 - @observable private currentTreePathNum: number[] = []; //移除注入 private removeFuncs: Function[] = []; constructor(props) { super(props); this.state = { nodes: [], }; - if (this.currentTemplate) - this.InitData(this.currentTemplate); + this.currentTemplateId = this.props.selectTemplateId; + this.InitData = this.InitData.bind(this); + } + + componentDidMount() + { + const commands = ["删除模板节点", "应用参数修改", "分离空间"]; + + let tem = this.GetCurrentTemplate(); + if (tem) + this.displayTemplateRootId = tem.Root.Id.Index; + + this.InitData(); this.removeFuncs.push( end(app.Database.hm, app.Database.hm.RedoEvent, (cmdName: string, historyRec: CommandHistoryRecord) => { - if (historyRec.HistoryList.has(this.props.template.Id)) - this.InitData(this.props.template); + if (commands.includes(cmdName)) + this.InitData(); }), end(app.Database.hm, app.Database.hm.UndoEvent, (cmdName: string, historyRec: CommandHistoryRecord) => { - if (historyRec.HistoryList.has(this.props.template.Id)) - this.InitData(this.props.template); + if (commands.includes(cmdName)) + this.InitData(); }) ); } + componentWillUnmount() { for (let f of this.removeFuncs) f(); this.removeFuncs.length = 0; } - UNSAFE_componentWillReceiveProps(nextProps) + + componentDidUpdate(prevProps: TemplateParamProps) { - if (nextProps.template) + if (prevProps.selectTemplateId !== this.props.selectTemplateId) { - this.InitData(nextProps.template); - this.currentTemplate = nextProps.template; + this.currentTemplateId = this.props.selectTemplateId; + + let tem = this.GetCurrentTemplate(); + if (tem) + this.displayTemplateRootId = tem.Root.Id.Index; + + this.InitData(); } } + + //当前选中的模块记录 + GetCurrentTemplate(): TemplateRecord | undefined + { + let curId = app.Database.GetObjectId(this.currentTemplateId); + if (curId === undefined || curId.IsErase) + curId = app.Database.GetObjectId(this.displayTemplateRootId); + if (!curId || curId.IsErase) + return; + return curId.Object as TemplateRecord; + } + //初始化所有展示数据 - InitData = (template: TemplateRecord, updateTree: boolean = true) => + InitData() { - this.params = template.Params; - observable(this.currentProps).replace(this.params.map((p) => + let temp = this.GetCurrentTemplate(); + if (!temp) + { + this.setState({ nodes: [] }); + return; + } + + observable(this.currentProps).replace(temp.Params.map((p) => { return { name: p.name, @@ -83,57 +123,56 @@ export class TemplateParamPanel extends React.Component<{ template: TemplateReco expr: p.expr, } })); - if (updateTree) - this.setState({ nodes: this.parseNodes(template, [template.Root]) }); - if (this.needSelectedNode) - this.SelectNode(this.needSelectedNode); - } - //转换为节点树 - private parseNodes = (curTempSelected: TemplateRecord, templates: TemplateRecord[]) => - { - let newNodes: ITreeNode[] = []; - for (let template of templates) + let nodes = [this.Template2TreeNode(temp.Root)]; + let allnodes: ITreeNode[] = [...nodes]; + for (let i = 0; i < allnodes.length; i++) { - let node: ITreeNode = { - id: template.Id.Index, - label: template.Name === "" ? "[ 空名称 ]" : template.Name, - hasCaret: template.Children.length > 0, - childNodes: this.parseNodes(curTempSelected, template.Children.map((t) => { return t.Object as TemplateRecord })), - isExpanded: true, - nodeData: template, - }; - //记录被点击的模板的ITreeNode 以便亮显对应的目录树节点 - newNodes.push(node); - if (curTempSelected.Id.Index === template.Id.Index) - this.needSelectedNode = node; + let n = allnodes[i]; + n.isSelected = n.nodeData === temp; + allnodes.push(...n.childNodes); } - return newNodes; + this.setState({ nodes }); } - //展开折叠目录 + + Template2TreeNode = (template: TemplateRecord): ITreeNode => + { + let children = template.Children.filter(id => !id.IsErase).map(id => id.Object as TemplateRecord); + let node: ITreeNode = { + id: template.Id.Index, + label: template.Name === "" ? "[ 空名称 ]" : template.Name, + hasCaret: children.length > 0, + childNodes: children.map(this.Template2TreeNode), + isExpanded: true, + nodeData: template, + }; + return node; + } + + //展开折叠节点 private HandleNodeCollapse = (nodeData: ITreeNode, isCollapse: boolean) => { nodeData.isExpanded = !isCollapse; this.setState(this.state); } //展示右键菜单 - private ShowContextMenu = (nodeData: ITreeNode, _nodePath: number[], e: React.MouseEvent | MouseEvent) => + private ShowContextMenu = (node: ITreeNode, _nodePath: number[], e: React.MouseEvent | MouseEvent) => { - if (nodeData) - { - this.SelectNode(nodeData, true, true); - this.getCurrentDir(nodeData.id, _nodePath); - } + //亮显节点 + let curTemp = node.nodeData as TemplateRecord; + this.currentTemplateId = curTemp.Id.Index; + this.InitData(); + ContextMenu.show( { <> { this.OutlineSpaceChild(nodeData) }} + onClick={() => { this.OutlineSpaceChild(node) }} /> { this.DeleteTreeNode(nodeData) }} + onClick={() => { this.DeleteTreeNode(node) }} /> { this.SeparateSpace(nodeData) }} + onClick={() => { this.SeparateSpace(node) }} /> { this.BatchModifyCurrentLayerCabinetName(nodeData) }} + onClick={() => { this.BatchModifyCurrentLayerCabinetName(node) }} /> { this.BatchModifyCurrentChildrenCabinetName(nodeData) }} + onClick={() => { this.BatchModifyCurrentChildrenCabinetName(node) }} /> } @@ -160,18 +199,12 @@ export class TemplateParamPanel extends React.Component<{ template: TemplateReco e.stopPropagation(); e.preventDefault(); }; + //批量修改当前层的柜体名为节点名称 - private BatchModifyCurrentLayerCabinetName = (nodeData: ITreeNode) => + private BatchModifyCurrentLayerCabinetName = (node: ITreeNode) => { - let curTemp = nodeData.nodeData as TemplateRecord; - let parent = curTemp.Parent ? curTemp.Parent.Object as TemplateRecord : undefined; - if (!parent) - return; - parent.Children.forEach((c) => - { - let tr = c.Object as TemplateRecord; - this.ModifyCabinetName(tr, nodeData.label as string); - }) + let tem = node.nodeData as TemplateRecord; + this.ModifyCabinetName(tem, tem.Name as string); } //修改模板柜体名称 ModifyCabinetName = (tr: TemplateRecord, cabinetName: string) => @@ -186,130 +219,48 @@ export class TemplateParamPanel extends React.Component<{ template: TemplateReco } } //批量修改当前层及下的柜体名为节点名称 - private BatchModifyCurrentChildrenCabinetName = (nodeData: ITreeNode) => + private BatchModifyCurrentChildrenCabinetName = (node: ITreeNode) => { - let curTemp = nodeData.nodeData as TemplateRecord; + let curTemp = node.nodeData as TemplateRecord; let trs = this.GetAllChildFromThisTemplate(curTemp); for (let tr of trs) { - this.ModifyCabinetName(tr, nodeData.label as string); + this.ModifyCabinetName(tr, curTemp.Name as string); } } - //获取当前目录路径 - private getCurrentDir = (dirId: React.ReactText, nodePath: number[]) => - { - let childNodes: ITreeNode[]; - for (let i of nodePath) - { - let node: ITreeNode; - if (!childNodes) - node = this.state.nodes[i]; - else - node = childNodes[i]; - childNodes = node.childNodes; - } - this.currentTreePathNum = nodePath; - } - //点击目录 - private HandleNodeClick = (nodeData: ITreeNode, _nodePath: number[], e: React.MouseEvent | MouseEvent) => - { - this.currentTemplate = nodeData.nodeData as TemplateRecord; - this.InitData(this.currentTemplate, false); - this.SelectNode(nodeData, true, true); - }; - //选中目录高亮 - private SelectNode = (nodeData: ITreeNode, isSelected?: boolean, handleClick?: boolean) => - { - const originallySelected = nodeData.isSelected; - this.forEachNode(this.state.nodes, n => (n.isSelected = false)); - if (isSelected) - nodeData.isSelected = isSelected; - else - nodeData.isSelected = originallySelected == null ? true : !originallySelected; - if (handleClick) - this.setState(this.state) - } - //去掉其他节点被选择状态 - private forEachNode(nodes: ITreeNode[], callback: (node: ITreeNode) => void) + + //点击节点事件 + private HandleNodeClick = (node: ITreeNode, _nodePath: number[], e: React.MouseEvent | MouseEvent) => { - if (!nodes) - return; - for (const node of nodes) - { - callback(node); - this.forEachNode(node.childNodes, callback); - } + let tem = node.nodeData as TemplateRecord; + this.currentTemplateId = tem.Id.Index; + this.InitData(); } + //获取所有模板子节点 private GetAllChildFromThisTemplate = (tr: TemplateRecord) => { - //从tr开始搜索它的所有子节点并加入数组 tr排最后 let trs: TemplateRecord[] = []; - tr.Traverse((node) => { trs.unshift(node) }); + tr.Traverse((node) => { trs.push(node) }); return trs; } - // 删除节点 + + //删除节点 private DeleteTreeNode = (nodeData: ITreeNode) => { - //删除模板 - let curId = nodeData.id; let tr = nodeData.nodeData as TemplateRecord; - let parent = tr.Parent ? tr.Parent.Object as TemplateRecord : undefined; - if (parent) - parent.Children = parent.Children.filter((c) => { c.Index !== curId }); - //从最深处开始删除 - let treeNodes = this.GetAllChildFromThisTemplate(tr); CommandWrap(() => { - for (let tn of treeNodes) - { - app.Database.TemplateTable.Remove(tn); - for (let t of tn.Objects) - { - t.Object.Erase(); - } - } + DeleteTempate(tr); + this.InitData(); }, "删除模板节点"); - if (parent) - this.currentTemplate = this.props.template; - else - this.currentTemplate = undefined; app.Editor.UpdateScreen(); - this.DeleteUITreeNode(); } - private DeleteUITreeNode = () => - { - //删除UI节点 - let nodes = this.state.nodes; - let lastIndex = this.currentTreePathNum.pop(); - let childNodes: ITreeNode[]; - let lastNode: ITreeNode; - for (let i of this.currentTreePathNum) - { - if (!childNodes) - { - lastNode = nodes[i]; - childNodes = nodes[i].childNodes; - } - else - { - lastNode = childNodes[i]; - childNodes = childNodes[i].childNodes; - } - } - if (!childNodes) - childNodes = nodes; - - childNodes.splice(lastIndex, 1); - if (lastNode) - lastNode.hasCaret = childNodes.length > 0; - this.setState({ nodes: [...nodes] }) - } //亮显空间子层 OutlineSpaceChild = (node: ITreeNode) => { - this.ClearSelect(); + app.Editor.SelectCtrl.Cancel(); let tr = node.nodeData as TemplateRecord; let treeNodes = this.GetAllChildFromThisTemplate(tr); let outlineObj3ds: Object3D[] = []; @@ -324,13 +275,7 @@ export class TemplateParamPanel extends React.Component<{ template: TemplateReco app.Viewer.OutlinePass.selectedObjects = outlineObj3ds; app.Editor.UpdateScreen(); } - //清除当前选中 - ClearSelect = () => - { - app.Editor.SelectCtrl.SelectSet.Clear(); - app.Viewer.GripScene.Clear(); - app.Editor.SelectCtrl.UpdateView(); - } + //切割空间 待完善 DivideSpace = () => { @@ -340,22 +285,24 @@ export class TemplateParamPanel extends React.Component<{ template: TemplateReco SeparateSpace = (nodeData: ITreeNode) => { //斩断其与父节点的关联 - let curId = nodeData.id; let tr = nodeData.nodeData as TemplateRecord; let parent = tr.Parent ? tr.Parent.Object as TemplateRecord : undefined; if (!parent) return; else { - tr.Parent = undefined; - parent.Children = parent.Children.filter((c) => c.Object.Id.Index !== curId); + CommandWrap(() => + { + tr.Parent = undefined; + arrayRemoveOnce(parent.Children, tr.Id); + }, "分离空间"); } - this.DeleteUITreeNode(); } render() { + let tem = this.GetCurrentTemplate(); return ( - this.currentTemplate ? + tem ?
{/* 模板树 */}
:
-
请选择模板
+
请选择模板
) } @@ -409,8 +356,8 @@ export class TemplateParamDetail extends React.Component<{ updateParams: INeedUp } }); await currentShowTemplate.UpdateTemplateTree(); - }, "应用参数修改") - callback(currentShowTemplate, false); + }, "应用参数修改"); + callback(); } GetAssociateBrNums = (tr: TemplateRecord) => { @@ -472,7 +419,7 @@ export class TemplateParamDetail extends React.Component<{ updateParams: INeedUp { if (POSITION_PAR.includes(par.name)) return ( -