mirror of https://gitee.com/cf-fz/WebCAD.git
!463 模块设计器
parent
f26b2028d3
commit
75495b4503
@ -0,0 +1,31 @@
|
||||
import { app } from "../../ApplicationServices/Application";
|
||||
import { Board } from "../../DatabaseServices/Entity/Board";
|
||||
import { InitTemplate } from "../../DatabaseServices/Template/TempateUtils";
|
||||
import { Command } from "../../Editor/CommandMachine";
|
||||
import { PromptStatus } from "../../Editor/PromptResult";
|
||||
import { ModalPosition } from "../../UI/Components/Modal/ModalsManage";
|
||||
import { TemplateEditor } from "../../UI/Components/Template/TemplateEditor";
|
||||
import { TempalteEditorStore } from "../../UI/Store/TemplateEditorStore";
|
||||
export class ShowTemplateDesign implements Command
|
||||
{
|
||||
async exec()
|
||||
{
|
||||
let enRes = await app.Editor.GetSelection({
|
||||
Msg: "请选择构建模块的板件",
|
||||
Filter: { filterTypes: [Board] }
|
||||
});
|
||||
if (enRes.Status !== PromptStatus.OK)
|
||||
return;
|
||||
|
||||
let brs = enRes.SelectSet.SelectEntityList as Board[];
|
||||
|
||||
let template = await InitTemplate(brs);
|
||||
if (!template) return;
|
||||
|
||||
let store = TempalteEditorStore.GetInstance() as TempalteEditorStore;
|
||||
template.Name = "模板";
|
||||
store.Template = template;
|
||||
store.InitParams();
|
||||
app.Editor.ModalManage.RenderModeless(TemplateEditor, ModalPosition.Center, { store });
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
export enum DragPointType
|
||||
{
|
||||
Grip = 0,
|
||||
Stretch = 1
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
import { Factory } from "../../CADFactory";
|
||||
import { Board } from "../../Entity/Board";
|
||||
import { TemplateAction } from "./TemplateAction";
|
||||
import { CADFiler } from "../../CADFiler";
|
||||
import { ObjectId } from "../../ObjectId";
|
||||
|
||||
export enum ThicknessDirection
|
||||
{
|
||||
Center = 0,
|
||||
Back = 1,
|
||||
Front = 2,
|
||||
}
|
||||
|
||||
export interface ThicknessActionData
|
||||
{
|
||||
//方向
|
||||
Direction: ThicknessDirection;
|
||||
/**
|
||||
* 附加的动作
|
||||
*/
|
||||
Actions: TemplateAction[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 模版动作
|
||||
*/
|
||||
@Factory
|
||||
export class TempateThicknessAction extends TemplateAction
|
||||
{
|
||||
//正 true 反 false
|
||||
EntityDirectionMap: Map<ObjectId, ThicknessActionData> = new Map();
|
||||
protected _Update(paramDiff: number, newValue: number)
|
||||
{
|
||||
for (let [id, d] of this.EntityDirectionMap)
|
||||
{
|
||||
if (id.IsErase) continue;
|
||||
|
||||
let br = id.Object as Board;
|
||||
|
||||
br.Thickness += paramDiff;
|
||||
if (d.Direction === ThicknessDirection.Back)
|
||||
br.Position = br.Position.sub(br.Normal.multiplyScalar(paramDiff));
|
||||
else if (d.Direction === ThicknessDirection.Center)
|
||||
br.Position = br.Position.sub(br.Normal.multiplyScalar(paramDiff * 0.5));
|
||||
|
||||
for (let a of d.Actions)
|
||||
{
|
||||
a.parent = this.parent;
|
||||
a.Update(paramDiff, newValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//#region -------------------------File-------------------------
|
||||
//对象应该实现dataIn和DataOut的方法,为了对象的序列化和反序列化
|
||||
//对象从文件中读取数据,初始化自身
|
||||
ReadFile(file: CADFiler)
|
||||
{
|
||||
let ver = file.Read();
|
||||
super.ReadFile(file);
|
||||
|
||||
let count = file.Read() as number;
|
||||
this.EntityDirectionMap.clear();
|
||||
for (let i = 0; i < count; i++)
|
||||
{
|
||||
let id = file.ReadObjectId();
|
||||
let direction = file.Read() as ThicknessDirection;
|
||||
let actionsCount = file.Read() as number;
|
||||
let actions: TemplateAction[] = [];
|
||||
for (let j = 0; j < actionsCount; j++)
|
||||
{
|
||||
actions.push(file.ReadObject() as TemplateAction);
|
||||
}
|
||||
this.EntityDirectionMap.set(id, { Direction: direction, Actions: actions });
|
||||
}
|
||||
}
|
||||
//对象将自身数据写入到文件.
|
||||
WriteFile(file: CADFiler)
|
||||
{
|
||||
file.Write(1);
|
||||
super.WriteFile(file);
|
||||
|
||||
file.Write(this.EntityDirectionMap.size);
|
||||
for (let [id, d] of this.EntityDirectionMap)
|
||||
{
|
||||
file.WriteObjectId(id);
|
||||
file.Write(d.Direction);
|
||||
file.Write(d.Actions.length);
|
||||
for (let a of d.Actions)
|
||||
file.WriteObject(a);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
import { EditorService } from "./Editor";
|
||||
import { GetStringPrompt } from "./PromptOptions";
|
||||
import { PromptResult, PromptStatus } from "./PromptResult";
|
||||
import { InitKeyWord } from "./InitKeyword";
|
||||
import { end, begin } from "xaop";
|
||||
import { app } from "../ApplicationServices/Application";
|
||||
import { KeyBoard } from "../Common/KeyEnum";
|
||||
import { InputState } from "../Common/InputState";
|
||||
|
||||
export class GetStringService implements EditorService
|
||||
{
|
||||
IsReady: boolean = true;
|
||||
private _prompt: GetStringPrompt;
|
||||
protected removeCalls: Function[] = []; //结束回调
|
||||
private promisResolve: (value?: any) => void;
|
||||
async Doit(e: MouseEvent): Promise<boolean>
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
Start(prompt?: GetStringPrompt): Promise<PromptResult>
|
||||
{
|
||||
prompt = prompt ? prompt : {};
|
||||
this._prompt = prompt;
|
||||
|
||||
this.InitState();
|
||||
this.InitPrompt(prompt);
|
||||
this.removeCalls.push(InitKeyWord(prompt));
|
||||
this.InitHandleInput(prompt);
|
||||
this.InitHandleKeyDown();
|
||||
return new Promise<PromptResult>((resolve, reject) =>
|
||||
{
|
||||
this.promisResolve = resolve;
|
||||
});
|
||||
}
|
||||
|
||||
Cancel()
|
||||
{
|
||||
let v = new PromptResult();
|
||||
v.Status = PromptStatus.Cancel;
|
||||
this.ReturnResult(v);
|
||||
}
|
||||
|
||||
protected InitState()
|
||||
{
|
||||
app.Editor.InputState |= InputState.GetString;
|
||||
}
|
||||
|
||||
private RestState()
|
||||
{
|
||||
this.IsReady = false;
|
||||
app.Editor.InputState &= ~InputState.GetString;
|
||||
this.removeCalls.forEach(f => f());
|
||||
this.removeCalls.length = 0;
|
||||
}
|
||||
|
||||
protected ReturnResult(retValue: PromptResult)
|
||||
{
|
||||
if (!this.promisResolve) return;
|
||||
this.RestState();
|
||||
this.promisResolve(retValue);
|
||||
this.promisResolve = undefined;
|
||||
}
|
||||
|
||||
protected InitPrompt(prompt: GetStringPrompt)
|
||||
{
|
||||
prompt.Msg = prompt.Msg || "请输入一个点:";
|
||||
if (prompt.Msg)
|
||||
{
|
||||
app.Editor.CommandStore.commandPrompt = prompt.Msg;
|
||||
this.removeCalls.push(() =>
|
||||
{
|
||||
app.Editor.CommandStore.commandPrompt = "";
|
||||
});
|
||||
}
|
||||
}
|
||||
protected InitHandleInput(prompt: GetStringPrompt)
|
||||
{
|
||||
this.removeCalls.push(begin(app.Editor, app.Editor.InputEvent, (inputData: string) =>
|
||||
{
|
||||
let res = new PromptResult();
|
||||
res.Status = PromptStatus.OK;
|
||||
res.StringResult = inputData;
|
||||
|
||||
this.ReturnResult(res);
|
||||
}));
|
||||
}
|
||||
|
||||
private InitHandleKeyDown()
|
||||
{
|
||||
this.removeCalls.push(end(app.Editor.KeyCtrl, app.Editor.KeyCtrl.OnKeyDown, (e: KeyboardEvent) =>
|
||||
{
|
||||
if (e.keyCode === KeyBoard.Escape)
|
||||
{
|
||||
this.Cancel();
|
||||
return true;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
import * as React from 'react';
|
||||
import { Popover, Position, Button, Intent, Classes, Card, IconName, MaybeElement } from '@blueprintjs/core';
|
||||
|
||||
export interface IPopOverButtonProps
|
||||
{
|
||||
position: Position;
|
||||
disabled?: boolean;
|
||||
confirmCallback: () => void;
|
||||
message: string;
|
||||
targetTitle: string;
|
||||
icon?: IconName | MaybeElement;
|
||||
}
|
||||
|
||||
export class PopoverButton extends React.Component<IPopOverButtonProps> {
|
||||
public render()
|
||||
{
|
||||
return (
|
||||
<Popover
|
||||
position={this.props.position}
|
||||
disabled={this.props.disabled}
|
||||
content={
|
||||
<Card>
|
||||
<p>{this.props.message}</p>
|
||||
<div>
|
||||
<Button style={{ marginRight: 10 }}
|
||||
className={Classes.POPOVER_DISMISS}
|
||||
text="取消" />
|
||||
<Button
|
||||
className={Classes.POPOVER_DISMISS}
|
||||
intent={Intent.PRIMARY}
|
||||
onClick={this.props.confirmCallback}
|
||||
text="确定" />
|
||||
</div>
|
||||
</Card>
|
||||
}
|
||||
target={
|
||||
<Button
|
||||
icon={this.props.icon}
|
||||
style={{
|
||||
marginRight: 10
|
||||
}}
|
||||
disabled={this.props.disabled}
|
||||
text={this.props.targetTitle}
|
||||
intent={Intent.DANGER}
|
||||
/>}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
import * as React from 'react';
|
||||
import { H5, Label, Classes, Button, Intent } from '@blueprintjs/core';
|
||||
import { observer } from 'mobx-react';
|
||||
|
||||
export interface ISimpleDialogProps
|
||||
{
|
||||
title: string;
|
||||
childern: JSX.Element[];
|
||||
onclose: Function;
|
||||
isOpen: boolean;
|
||||
}
|
||||
|
||||
@observer
|
||||
export class SimpleDialog extends React.Component<ISimpleDialogProps> {
|
||||
public render()
|
||||
{
|
||||
let { title, childern } = this.props;
|
||||
return (
|
||||
<>
|
||||
{
|
||||
this.props.isOpen && <div className="simple-dialog">
|
||||
<H5>{title}
|
||||
<Button
|
||||
aria-label="Close"
|
||||
minimal
|
||||
icon="cross"
|
||||
className={Classes.DIALOG_CLOSE_BUTTON}
|
||||
/>
|
||||
</H5>
|
||||
<div className="simple-dialog-body">
|
||||
{childern}
|
||||
</div>
|
||||
<div className="simple-dialog-footer">
|
||||
<Button
|
||||
text="确定"
|
||||
intent={Intent.SUCCESS}
|
||||
/>
|
||||
<Button
|
||||
text="取消"
|
||||
intent={Intent.DANGER}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
import { Icon, ITreeNode, TreeEventHandler } from '@blueprintjs/core';
|
||||
import { observer } from 'mobx-react';
|
||||
import * as React from 'react';
|
||||
|
||||
export interface IActionTreeProps<T = {}>
|
||||
{
|
||||
nodes: ITreeNode[];
|
||||
className?: string;
|
||||
onNodeDoubleClick?: TreeEventHandler<T>;
|
||||
onNodeClick?: TreeEventHandler<T>;
|
||||
onNodeCollapse?: (node: ITreeNode) => void;
|
||||
}
|
||||
|
||||
@observer
|
||||
export default class ActionTree extends React.Component<IActionTreeProps> {
|
||||
public render()
|
||||
{
|
||||
return (
|
||||
<div className={"bp3-tree bp3-elevation-0 " + this.props.className}>
|
||||
<ul className="bp3-tree-node-list bp3-tree-root">
|
||||
{
|
||||
this.props.nodes.map((node, i1) =>
|
||||
{
|
||||
return (
|
||||
<li
|
||||
className={`bp3-tree-node bp3-tree-node-expanded ${node.isSelected ? "bp3-tree-node-selected" : ""}`}
|
||||
onDoubleClick={(e) => this.handleDbclick(node, [i1], e)}
|
||||
onClick={e => this.handleclick(node, [i1], e)}
|
||||
>
|
||||
<div className="bp3-tree-node-content">
|
||||
<Icon
|
||||
icon="chevron-right"
|
||||
className={`bp3-icon bp3-icon-chevron-right bp3-tree-node-caret bp3-tree-node-caret-${node.isExpanded ? "open" : "close"}`}
|
||||
onClick={() => this.handleNodeCollapse(node)}
|
||||
/>
|
||||
<span className="bp3-tree-node-label">{node.label}</span>
|
||||
</div>
|
||||
<ul
|
||||
className="bp3-tree-node-list"
|
||||
style={{ display: node.isExpanded ? "block" : "none" }}>
|
||||
{
|
||||
node.childNodes && node.childNodes.map((n, i2) =>
|
||||
{
|
||||
return (
|
||||
<li
|
||||
className={`bp3-tree-node ${n.isSelected ? "bp3-tree-node-selected" : ""}`}
|
||||
onDoubleClick={(e) => this.handleDbclick(n, [i1, i2], e)}
|
||||
onClick={e => this.handleclick(n, [i1, i2], e)}
|
||||
>
|
||||
<div className="bp3-tree-node-content">
|
||||
<span className="bp3-tree-node-caret-none bp3-icon-standard"></span>
|
||||
<span className="bp3-tree-node-label">{n.label}</span>
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
</li>
|
||||
)
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
private handleDbclick = (nodeData: ITreeNode, _nodePath: number[], e: React.MouseEvent<HTMLElement>) =>
|
||||
{
|
||||
if (this.props.onNodeDoubleClick)
|
||||
{
|
||||
this.props.onNodeDoubleClick(nodeData, _nodePath, e);
|
||||
e.stopPropagation();
|
||||
}
|
||||
}
|
||||
private handleclick = (nodeData: ITreeNode, _nodePath: number[], e: React.MouseEvent<HTMLElement>) =>
|
||||
{
|
||||
if (this.props.onNodeClick)
|
||||
{
|
||||
this.props.onNodeClick(nodeData, _nodePath, e);
|
||||
e.stopPropagation();
|
||||
}
|
||||
}
|
||||
private handleNodeCollapse = (nodeData: ITreeNode) =>
|
||||
{
|
||||
if (this.props.onNodeCollapse)
|
||||
{
|
||||
this.props.onNodeCollapse(nodeData);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -0,0 +1,353 @@
|
||||
import { Button, Checkbox, Classes, Dialog, HTMLSelect, Intent, ITreeNode, Label } from '@blueprintjs/core';
|
||||
import { IObservableValue, observable } from 'mobx';
|
||||
import { observer } from 'mobx-react';
|
||||
import * as React from 'react';
|
||||
import { app } from '../../../ApplicationServices/Application';
|
||||
import { KeyBoard } from '../../../Common/KeyEnum';
|
||||
import { TemplateAction } from '../../../DatabaseServices/Template/Action/TemplateAction';
|
||||
import { TemplateParam } from '../../../DatabaseServices/Template/Param/TemplateParam';
|
||||
import { CommandWrap } from '../../../Editor/CommandMachine';
|
||||
import { TempalteEditorStore } from '../../Store/TemplateEditorStore';
|
||||
import { IActionParam } from './TemplateActionList';
|
||||
import { INeedUpdateParams } from './TemplateComponent';
|
||||
import { AppToaster } from '../Toaster';
|
||||
import { AddStretchAction, AddFilletAction } from '../../../DatabaseServices/Template/TempateUtils';
|
||||
|
||||
export enum EEditorActionType
|
||||
{
|
||||
Add = 0,
|
||||
Editor = 1,
|
||||
AddFillet = 2,
|
||||
AddParams = 3,
|
||||
}
|
||||
|
||||
export interface ITempalteActionDialogProps
|
||||
{
|
||||
store: TempalteEditorStore;
|
||||
type: EEditorActionType;
|
||||
open: IObservableValue<boolean>;
|
||||
currentAction?: TemplateAction;
|
||||
currentNodePath?: number[];
|
||||
currentActionInfo?: IActionParam;
|
||||
}
|
||||
|
||||
@observer
|
||||
export class TempalteActionDialog extends React.Component<ITempalteActionDialogProps> {
|
||||
@observable pararmConfig: INeedUpdateParams = {
|
||||
name: "",
|
||||
value: "",
|
||||
expr: "",
|
||||
description: "",
|
||||
isLock: false
|
||||
};
|
||||
private renderDialogBody = () =>
|
||||
{
|
||||
if (this.props.type === EEditorActionType.AddParams)
|
||||
return this.renderParamsDialog();
|
||||
else
|
||||
return this.renderActionDialog();
|
||||
}
|
||||
private renderActionDialog = () =>
|
||||
{
|
||||
const { store, currentActionInfo } = this.props;
|
||||
|
||||
const options = store.params.map(par =>
|
||||
{
|
||||
return {
|
||||
label: [par.name, par.value, par.description].join(","),
|
||||
value: par.name
|
||||
}
|
||||
});
|
||||
|
||||
const isEditor = this.props.type === EEditorActionType.Editor;
|
||||
return (
|
||||
<>
|
||||
{
|
||||
!isEditor &&
|
||||
<Label className={Classes.INLINE}>
|
||||
<span>变量名称:</span>
|
||||
<HTMLSelect
|
||||
options={options}
|
||||
value={currentActionInfo.paramName}
|
||||
onChange={(e) =>
|
||||
{
|
||||
currentActionInfo.paramName = e.target.value;
|
||||
currentActionInfo.expr = currentActionInfo.paramName;
|
||||
}}
|
||||
/>
|
||||
</Label>
|
||||
}
|
||||
<Label className={Classes.INLINE}>
|
||||
<span>动作名称:</span>
|
||||
<input
|
||||
autoFocus
|
||||
className={Classes.INPUT}
|
||||
value={currentActionInfo.actionName}
|
||||
onChange={e => currentActionInfo.actionName = e.target.value}
|
||||
/>
|
||||
</Label>
|
||||
<Label className={Classes.INLINE}>
|
||||
<span>
|
||||
表达式:
|
||||
</span>
|
||||
<input className={Classes.INPUT}
|
||||
value={currentActionInfo.expr}
|
||||
onChange={e => currentActionInfo.expr = e.target.value}
|
||||
/>
|
||||
</Label>
|
||||
</>
|
||||
)
|
||||
}
|
||||
private renderParamsDialog = () =>
|
||||
{
|
||||
return (
|
||||
<>
|
||||
<Label className={Classes.INLINE}>
|
||||
<span>参数名:</span>
|
||||
<input
|
||||
autoFocus
|
||||
className={Classes.INPUT}
|
||||
value={this.pararmConfig.name}
|
||||
onChange={e => this.pararmConfig.name = e.target.value.toUpperCase()}
|
||||
/>
|
||||
</Label>
|
||||
<Label className={Classes.INLINE}>
|
||||
<span>变量值:</span>
|
||||
<input
|
||||
className={Classes.INPUT}
|
||||
value={this.pararmConfig.value}
|
||||
onChange={e => this.pararmConfig.value = e.target.value}
|
||||
/>
|
||||
</Label>
|
||||
<Label className={Classes.INLINE}>
|
||||
<span>备注:</span>
|
||||
<input
|
||||
className={Classes.INPUT}
|
||||
value={this.pararmConfig.description}
|
||||
onChange={e => this.pararmConfig.description = e.target.value}
|
||||
/>
|
||||
</Label>
|
||||
<Checkbox
|
||||
label="设置成锁定参数"
|
||||
checked={this.pararmConfig.isLock}
|
||||
onChange={() => this.pararmConfig.isLock = !this.pararmConfig.isLock}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
componentWillUnmount()
|
||||
{
|
||||
document.getElementById("modal").focus();
|
||||
}
|
||||
public render()
|
||||
{
|
||||
let title = "";
|
||||
switch (this.props.type)
|
||||
{
|
||||
case EEditorActionType.Add:
|
||||
title = "增加动作";
|
||||
break;
|
||||
case EEditorActionType.Editor:
|
||||
title = "编辑动作";
|
||||
break;
|
||||
case EEditorActionType.AddFillet:
|
||||
title = "增加倒角动作";
|
||||
break;
|
||||
case EEditorActionType.AddParams:
|
||||
title = "增加变量";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
canEscapeKeyClose={true}
|
||||
canOutsideClickClose={false}
|
||||
className="action-dialog"
|
||||
usePortal={false}
|
||||
isOpen={true}
|
||||
title={title}
|
||||
onClose={this.handleCloseDialog}
|
||||
onOpening={e =>
|
||||
{
|
||||
e.onkeydown = e =>
|
||||
{
|
||||
if (e.keyCode === KeyBoard.Escape)
|
||||
this.handleCloseDialog();
|
||||
e.stopPropagation()
|
||||
};
|
||||
}}
|
||||
>
|
||||
<div className={Classes.DIALOG_BODY}>
|
||||
{
|
||||
this.renderDialogBody()
|
||||
}
|
||||
</div>
|
||||
<div className={Classes.DIALOG_FOOTER}>
|
||||
<Button
|
||||
text="确定"
|
||||
intent={Intent.SUCCESS}
|
||||
onClick={this.handleClick}
|
||||
/>
|
||||
<Button
|
||||
text="取消"
|
||||
intent={Intent.DANGER}
|
||||
onClick={this.handleCloseDialog}
|
||||
/>
|
||||
</div>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
handleClick = async () =>
|
||||
{
|
||||
switch (this.props.type)
|
||||
{
|
||||
case EEditorActionType.Editor:
|
||||
this.handleModifyAction();
|
||||
break;
|
||||
case EEditorActionType.Add:
|
||||
this.handleCloseDialog();
|
||||
app.Editor.ModalManage.ToggleShow();
|
||||
app.Editor.MaskManage.Clear();
|
||||
await CommandWrap(async () =>
|
||||
{
|
||||
await this.addAction(true);
|
||||
}, "添加动作");
|
||||
app.Editor.ModalManage.ToggleShow();
|
||||
app.Editor.MaskManage.ShowMask();
|
||||
break;
|
||||
case EEditorActionType.AddFillet:
|
||||
this.handleCloseDialog();
|
||||
app.Editor.ModalManage.ToggleShow();
|
||||
app.Editor.MaskManage.Clear();
|
||||
await CommandWrap(async () =>
|
||||
{
|
||||
await this.addAction(false);
|
||||
}, "添加圆角动作");
|
||||
app.Editor.ModalManage.ToggleShow();
|
||||
app.Editor.MaskManage.ShowMask();
|
||||
break;
|
||||
case EEditorActionType.AddParams:
|
||||
if (!this.pararmConfig.name)
|
||||
{
|
||||
AppToaster.show({
|
||||
message: "请输入变量名",
|
||||
timeout: 1500,
|
||||
intent: Intent.DANGER
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (this.props.store.usedParamNames.has(this.pararmConfig.name))
|
||||
{
|
||||
AppToaster.show({
|
||||
message: "变量名重复",
|
||||
timeout: 1500,
|
||||
intent: Intent.DANGER
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.handleCloseDialog();
|
||||
CommandWrap(() =>
|
||||
{
|
||||
this.addTemplateParam();
|
||||
}, "增加变量")
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
handleModifyAction = () =>
|
||||
{
|
||||
let { currentNodePath, currentAction, currentActionInfo } = this.props;
|
||||
currentAction.Name = currentActionInfo.actionName;
|
||||
currentAction.Expr = currentActionInfo.expr;
|
||||
this.handleCloseDialog();
|
||||
let nodes = this.props.store.actionNodes;
|
||||
nodes[currentNodePath[0]].childNodes[currentNodePath[1]].label = this.props.currentActionInfo.actionName + " 表达式:" + this.props.currentActionInfo.expr;
|
||||
}
|
||||
private addAction = async (isStretchAction: boolean) =>
|
||||
{
|
||||
let act: TemplateAction;
|
||||
if (isStretchAction)
|
||||
{
|
||||
let keyRes = await app.Editor.GetKeyWords({
|
||||
Msg: "是否使用比例盒子?",
|
||||
KeyWordList: [
|
||||
{ key: "1", msg: "是" },
|
||||
{ key: "2", msg: "否" },
|
||||
],
|
||||
Default: "1"
|
||||
});
|
||||
act = await AddStretchAction(this.props.store.Template, keyRes.StringResult === "1");
|
||||
}
|
||||
else
|
||||
act = await AddFilletAction(this.props.store.Template);
|
||||
|
||||
if (act)
|
||||
{
|
||||
let info = this.props.currentActionInfo;
|
||||
act.Name = info.actionName;
|
||||
act.Expr = info.expr;
|
||||
this.handleAddNode(info.paramName, act);
|
||||
this.addActionToTemplate(info.paramName, act);
|
||||
this.props.store.actionIndex++;
|
||||
}
|
||||
}
|
||||
private addActionToTemplate = (parName: string, act: TemplateAction) =>
|
||||
{
|
||||
const template = this.props.store.Template;
|
||||
let par = template.GetParam(parName);
|
||||
par.actions.push(act);
|
||||
}
|
||||
private addTemplateParam = () =>
|
||||
{
|
||||
const template = this.props.store.Template;
|
||||
let param = new TemplateParam();
|
||||
this.pararmConfig.name = this.pararmConfig.name;
|
||||
param.name = this.pararmConfig.name;
|
||||
param.value = this.pararmConfig.value;
|
||||
param.description = this.pararmConfig.description;
|
||||
template.Params.push(param);
|
||||
this.props.store.params.push(this.pararmConfig);
|
||||
this.props.store.usedParamNames.add(param.name);
|
||||
}
|
||||
private handleAddNode = (parName: string, act: TemplateAction) =>
|
||||
{
|
||||
let nodes = this.props.store.actionNodes;
|
||||
let node = nodes.find(node => (node.label as string).includes("<" + parName + ">"));
|
||||
|
||||
if (nodes.length === 0 || !node)
|
||||
{
|
||||
nodes.push({
|
||||
id: 0,
|
||||
label: `变量<${parName}>`,
|
||||
hasCaret: true,
|
||||
isExpanded: true,
|
||||
nodeData: { name: parName },
|
||||
childNodes: [
|
||||
{
|
||||
id: 0,
|
||||
label: act.Name + " 表达式:" + act.Expr,
|
||||
hasCaret: false,
|
||||
isExpanded: true,
|
||||
nodeData: { act },
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
else
|
||||
node.childNodes.push({
|
||||
id: node.childNodes.length,
|
||||
label: act.Name + " 表达式:" + act.Expr,
|
||||
hasCaret: false,
|
||||
isExpanded: true,
|
||||
nodeData: { act },
|
||||
})
|
||||
|
||||
}
|
||||
handleCloseDialog = () =>
|
||||
{
|
||||
this.props.open.set(false);
|
||||
}
|
||||
}
|
@ -0,0 +1,287 @@
|
||||
import { Button, H5, ITreeNode } from '@blueprintjs/core';
|
||||
import { observable } from 'mobx';
|
||||
import { observer } from 'mobx-react';
|
||||
import * as React from 'react';
|
||||
import { app } from '../../../ApplicationServices/Application';
|
||||
import { TemplateAction } from '../../../DatabaseServices/Template/Action/TemplateAction';
|
||||
import { InitTempateBoardThicknessActions, InitTempateSizeActions, UpdateTempateBoardThicknessAction } from '../../../DatabaseServices/Template/TempateUtils';
|
||||
import { CommandWrap } from '../../../Editor/CommandMachine';
|
||||
import { TempalteEditorStore, UNRENDERPARS } from '../../Store/TemplateEditorStore';
|
||||
import ActionTree from './ActionTree';
|
||||
import { EEditorActionType, TempalteActionDialog } from './TemplateActionDiglog';
|
||||
|
||||
export interface ITempalteActionListProps
|
||||
{
|
||||
store: TempalteEditorStore;
|
||||
}
|
||||
|
||||
export interface IActionParam
|
||||
{
|
||||
paramName?: string;
|
||||
actionName: string;
|
||||
expr: string;
|
||||
}
|
||||
|
||||
@observer
|
||||
export default class TempalteActionList extends React.Component<ITempalteActionListProps, {}> {
|
||||
private _tree: HTMLDivElement;
|
||||
private index: number;
|
||||
private _currentAction: TemplateAction;
|
||||
private startEditorAction = observable.box(false);
|
||||
@observable actionInfo: IActionParam = { actionName: "", expr: "" };
|
||||
private _currentNodePath: number[];
|
||||
private _editorActType = EEditorActionType.Editor;
|
||||
constructor(props)
|
||||
{
|
||||
super(props);
|
||||
}
|
||||
componentWillMount()
|
||||
{
|
||||
this.parseUpdateNodes();
|
||||
|
||||
}
|
||||
componentDidMount()
|
||||
{
|
||||
this._tree.addEventListener('click', this.handleCancelSelect);
|
||||
}
|
||||
componentWillUnmount()
|
||||
{
|
||||
this._tree.removeEventListener('click', this.handleCancelSelect);
|
||||
}
|
||||
public render()
|
||||
{
|
||||
return (
|
||||
<div
|
||||
className="flexCol"
|
||||
ref={el => this._tree = el}>
|
||||
<H5>动作列表</H5>
|
||||
<ActionTree
|
||||
className="action-list"
|
||||
nodes={this.props.store.actionNodes}
|
||||
onNodeDoubleClick={this.handleDblick}
|
||||
onNodeClick={this.handleNodeClick}
|
||||
onNodeCollapse={(node) => this.handleNodeCollapse(node)}
|
||||
/>
|
||||
<div>
|
||||
<Button
|
||||
text="增加动作"
|
||||
onClick={() => this.handleAddAction(EEditorActionType.Add)}
|
||||
/>
|
||||
<Button
|
||||
text="增加圆角动作"
|
||||
onClick={() => this.handleAddAction(EEditorActionType.AddFillet)}
|
||||
/>
|
||||
<Button
|
||||
text="删除动作"
|
||||
onClick={this.deleteAction}
|
||||
/>
|
||||
<Button
|
||||
text="初始化模板大小动作"
|
||||
onClick={this.InitAction}
|
||||
/>
|
||||
<Button
|
||||
text="初始化板厚动作"
|
||||
onClick={this.clickBrThicknessButton}
|
||||
/>
|
||||
<Button
|
||||
text="添加板厚动作"
|
||||
onClick={this.addBrThicknessButton}
|
||||
/>
|
||||
<Button
|
||||
text="模板大小动作,板厚动作"
|
||||
onClick={this.autoSetTemplate}
|
||||
|
||||
/>
|
||||
</div>
|
||||
{
|
||||
this.startEditorAction.get() &&
|
||||
<TempalteActionDialog
|
||||
store={this.props.store}
|
||||
type={this._editorActType}
|
||||
open={this.startEditorAction}
|
||||
currentAction={this._currentAction}
|
||||
currentActionInfo={this.actionInfo}
|
||||
currentNodePath={this._currentNodePath}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
private InitAction = () =>
|
||||
{
|
||||
InitTempateSizeActions(this.props.store.Template);
|
||||
this.parseUpdateNodes();
|
||||
}
|
||||
private handleCancelSelect = (el: MouseEvent) =>
|
||||
{
|
||||
if ((el.target as HTMLElement).classList.contains('bp3-tree'))
|
||||
{
|
||||
let nodes = this.props.store.actionNodes;
|
||||
this.forEachNode(nodes, n => (n.isSelected = false));
|
||||
this._currentNodePath = undefined;
|
||||
|
||||
}
|
||||
}
|
||||
private handleDblick = (nodeData: ITreeNode, _nodePath: number[], e: React.MouseEvent<HTMLElement> | MouseEvent) =>
|
||||
{
|
||||
if (!nodeData.hasCaret)
|
||||
{
|
||||
let act = nodeData.nodeData["act"] as TemplateAction;
|
||||
this._currentAction = act;
|
||||
this._currentNodePath = _nodePath;
|
||||
this._editorActType = EEditorActionType.Editor;
|
||||
this.actionInfo.actionName = act.Name;
|
||||
this.actionInfo.expr = act.Expr.toString();
|
||||
this.startEditorAction.set(true);
|
||||
}
|
||||
}
|
||||
//展开折叠目录
|
||||
private handleNodeCollapse = (nodeData: ITreeNode) =>
|
||||
{
|
||||
nodeData.isExpanded = !nodeData.isExpanded;
|
||||
};
|
||||
//点击目录
|
||||
private handleNodeClick = async (nodeData: ITreeNode, _nodePath: number[], e: React.MouseEvent<HTMLElement> | MouseEvent) =>
|
||||
{
|
||||
this._currentNodePath = _nodePath;
|
||||
this.SelectNode(nodeData, e);
|
||||
};
|
||||
//选中目录高亮
|
||||
private SelectNode = (nodeData: ITreeNode, e: React.MouseEvent<HTMLElement> | MouseEvent, isSelected?: boolean) =>
|
||||
{
|
||||
const originallySelected = nodeData.isSelected;
|
||||
let nodes = this.props.store.actionNodes;
|
||||
if (!e.shiftKey)
|
||||
{
|
||||
this.forEachNode(nodes, n => (n.isSelected = false));
|
||||
}
|
||||
if (isSelected)
|
||||
nodeData.isSelected = isSelected;
|
||||
else
|
||||
nodeData.isSelected = originallySelected == null ? true : !originallySelected;
|
||||
}
|
||||
//去掉其他节点被选择状态
|
||||
private forEachNode(nodes: ITreeNode[], callback: (node: ITreeNode) => void)
|
||||
{
|
||||
if (nodes == null)
|
||||
return;
|
||||
|
||||
for (const node of nodes)
|
||||
{
|
||||
callback(node);
|
||||
this.forEachNode(node.childNodes, callback);
|
||||
}
|
||||
}
|
||||
private parseUpdateNodes = () =>
|
||||
{
|
||||
const template = this.props.store.Template;
|
||||
let params = template.Params;
|
||||
let newNodes: ITreeNode[] = [];
|
||||
this.index = 0;
|
||||
for (let par of params)
|
||||
{
|
||||
if (UNRENDERPARS.includes(par.name) || par.actions.length === 0) continue;
|
||||
let node: ITreeNode = {
|
||||
id: this.index++,
|
||||
label: `变量<${par.name}>`,
|
||||
hasCaret: true,
|
||||
isExpanded: true,
|
||||
isSelected: false,
|
||||
nodeData: { name: par.name },
|
||||
childNodes: par.actions.map((act, i) =>
|
||||
{
|
||||
return {
|
||||
id: i,
|
||||
label: act.Name + " 表达式:" + (act.Expr || par.name),
|
||||
hasCaret: false,
|
||||
isSelected: false,
|
||||
nodeData: { act },
|
||||
}
|
||||
})
|
||||
}
|
||||
newNodes.push(node);
|
||||
}
|
||||
observable(this.props.store.actionNodes).replace(newNodes);
|
||||
}
|
||||
private handleAddAction = (type: EEditorActionType) =>
|
||||
{
|
||||
this._editorActType = type;
|
||||
const { params, currentParamIndex } = this.props.store;
|
||||
let par = currentParamIndex ? params[currentParamIndex] : params[0];
|
||||
this.actionInfo.paramName = par.name;
|
||||
this.actionInfo.actionName = "动作" + this.props.store.actionIndex;
|
||||
this.actionInfo.expr = par.name.toString();
|
||||
this.startEditorAction.set(true);
|
||||
}
|
||||
private deleteAction = () =>
|
||||
{
|
||||
const template = this.props.store.Template;
|
||||
let nodes = this.props.store.actionNodes;
|
||||
if (nodes.length === 0) return;
|
||||
if (this._currentNodePath === undefined)
|
||||
{
|
||||
let node = nodes.shift();
|
||||
let parName = node.nodeData["name"];
|
||||
template.GetParam(parName).actions.length = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
let targetNodes = nodes;
|
||||
let lastIndex = this._currentNodePath[0];
|
||||
let node = nodes[lastIndex];
|
||||
if (this._currentNodePath.length === 2)
|
||||
{
|
||||
lastIndex = this._currentNodePath[1];
|
||||
targetNodes = node.childNodes;
|
||||
}
|
||||
targetNodes.splice(lastIndex, 1);
|
||||
let parName = node.nodeData["name"];
|
||||
if (this._currentNodePath.length === 2)
|
||||
{
|
||||
template.GetParam(parName).actions.splice(lastIndex, 1);
|
||||
if (targetNodes.length === 0)
|
||||
nodes.splice(this._currentNodePath[0], 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
template.GetParam(parName).actions.length = 0;
|
||||
}
|
||||
|
||||
this._currentNodePath = undefined;
|
||||
}
|
||||
}
|
||||
private clickBrThicknessButton = async () =>
|
||||
{
|
||||
let isAuto = confirm("是否自动设置板厚的变化方向");
|
||||
app.Editor.ModalManage.ToggleShow();
|
||||
app.Editor.MaskManage.Clear();
|
||||
await CommandWrap(async () =>
|
||||
{
|
||||
await InitTempateBoardThicknessActions(this.props.store.Template, isAuto);
|
||||
this.parseUpdateNodes();
|
||||
}, "初始化板厚");
|
||||
|
||||
app.Editor.ModalManage.ToggleShow();
|
||||
app.Editor.MaskManage.ShowMask();
|
||||
}
|
||||
private addBrThicknessButton = async () =>
|
||||
{
|
||||
app.Editor.ModalManage.ToggleShow();
|
||||
app.Editor.MaskManage.Clear();
|
||||
await CommandWrap(async () =>
|
||||
{
|
||||
await UpdateTempateBoardThicknessAction(this.props.store.Template);
|
||||
this.parseUpdateNodes();
|
||||
}, "添加板厚动作");
|
||||
|
||||
app.Editor.ModalManage.ToggleShow();
|
||||
app.Editor.MaskManage.ShowMask();
|
||||
}
|
||||
private autoSetTemplate = async () =>
|
||||
{
|
||||
let template = this.props.store.Template;
|
||||
InitTempateSizeActions(template);
|
||||
InitTempateBoardThicknessActions(template);
|
||||
this.parseUpdateNodes();
|
||||
}
|
||||
}
|
@ -1,73 +1,168 @@
|
||||
import { Button, Classes, HTMLSelect, Icon, Label } from '@blueprintjs/core';
|
||||
import { Button, Classes, Icon, Intent, Label } from '@blueprintjs/core';
|
||||
import { observer } from 'mobx-react';
|
||||
import * as React from 'react';
|
||||
import { app } from '../../../ApplicationServices/Application';
|
||||
import { KeyBoard } from '../../../Common/KeyEnum';
|
||||
import { DirectoryId, PostJson, RequestStatus, uploadLogo } from '../../../Common/Request';
|
||||
import { commandMachine } from '../../../Editor/CommandMachine';
|
||||
import { TempEditor } from '../../../Editor/TempEditor';
|
||||
import { TempalteEditorStore } from '../../Store/TemplateEditorStore';
|
||||
import { BoardModalType } from '../Board/BoardModal';
|
||||
import { UserConfig } from '../Board/UserConfig';
|
||||
import TempalteActionList from './TemplateActionList';
|
||||
import TemplateParamList from './TemplateParamList';
|
||||
import { TemplateOut, TemplateParamsOut, deflate, GetCurrentViewPreViewImage } from '../../../Common/SerializeMaterial';
|
||||
import { TemplateUrls } from '../../../Common/HostUrl';
|
||||
import { AppToaster } from '../Toaster';
|
||||
import { TemplateSaveDir } from './TemplateSaveDir';
|
||||
import { Box3 } from 'three';
|
||||
import { Entity } from '../../../DatabaseServices/Entity/Entity';
|
||||
|
||||
export interface ITemplateEditorProps
|
||||
{
|
||||
store: TempalteEditorStore;
|
||||
tid?: string;
|
||||
}
|
||||
|
||||
@observer
|
||||
export class TemplateEditor extends React.Component<ITemplateEditorProps, any> {
|
||||
private _container: HTMLDivElement;
|
||||
componentDidMount()
|
||||
{
|
||||
this._container.focus();
|
||||
this._container.addEventListener('keydown', e =>
|
||||
{
|
||||
if (e.keyCode === KeyBoard.Escape)
|
||||
{
|
||||
this.handleClose();
|
||||
e.stopPropagation();
|
||||
}
|
||||
});
|
||||
this.props.store.currentParamIndex = undefined;
|
||||
}
|
||||
public render()
|
||||
{
|
||||
const template = this.props.store.Template;
|
||||
return (
|
||||
<div
|
||||
className={Classes.DIALOG_CONTAINER}
|
||||
id="commonModal"
|
||||
ref={el => this._container = el}
|
||||
tabIndex={-1}
|
||||
>
|
||||
<div className={Classes.DIALOG}>
|
||||
<div className={Classes.DIALOG + " template-design"}>
|
||||
<div
|
||||
className={Classes.DIALOG_HEADER}
|
||||
data-id="dragArea"
|
||||
>
|
||||
<Icon icon="bold" iconSize={18} />
|
||||
<Icon icon="style" iconSize={18} />
|
||||
<h4 className="bp3-heading">拉伸参数化模板设计</h4>
|
||||
<Button
|
||||
aria-label="Close"
|
||||
minimal
|
||||
icon="cross"
|
||||
className={Classes.DIALOG_CLOSE_BUTTON}
|
||||
onClick={() => app.Editor.ModalManage.Clear()}
|
||||
onClick={this.handleClose}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className={Classes.DIALOG_BODY}
|
||||
className={Classes.DIALOG_BODY + " " + Classes.CARD}
|
||||
>
|
||||
<div className="flex">
|
||||
<div>
|
||||
<Label className={Classes.INLINE}>
|
||||
<span>模板名称</span>
|
||||
<input type="text" className={Classes.INPUT} />
|
||||
</Label>
|
||||
<Label className={Classes.INLINE}>
|
||||
<span>模板类型</span>
|
||||
<HTMLSelect />
|
||||
</Label>
|
||||
<Label className={Classes.INLINE}>
|
||||
<span>加工</span>
|
||||
<input type="text" className={Classes.INPUT} disabled />
|
||||
<input className={Classes.INPUT}
|
||||
defaultValue={template.Name}
|
||||
onChange={e => template.Name = e.target.value}
|
||||
/>
|
||||
</Label>
|
||||
<Button text="选择" />
|
||||
<Button text="保存" />
|
||||
</div>
|
||||
<div>content</div>
|
||||
<div>editor</div>
|
||||
<div className="flex">
|
||||
<TemplateParamList
|
||||
store={this.props.store}
|
||||
/>
|
||||
<TempalteActionList
|
||||
store={this.props.store}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className={Classes.DIALOG_FOOTER} >
|
||||
<UserConfig
|
||||
type={BoardModalType.TempDes}
|
||||
store={this.props.store}
|
||||
/>
|
||||
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
|
||||
<Button
|
||||
className={Classes.INTENT_SUCCESS}
|
||||
text="动态插入"
|
||||
/>
|
||||
<Button
|
||||
className={Classes.INTENT_DANGER}
|
||||
text="空间拾取"
|
||||
onClick={() => app.Editor.ModalManage.Clear()}
|
||||
/>
|
||||
{
|
||||
this.props.tid ?
|
||||
<>
|
||||
<Button
|
||||
text="取消"
|
||||
intent={Intent.DANGER}
|
||||
onClick={this.handleClose} />
|
||||
<Button
|
||||
text="更新"
|
||||
intent={Intent.SUCCESS}
|
||||
onClick={this.handleUpdateTemplate}
|
||||
/>
|
||||
</> :
|
||||
<TemplateSaveDir
|
||||
upload={this.handleUploadTemplate}
|
||||
/>
|
||||
}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
private handleClose = () =>
|
||||
{
|
||||
if (this.props.tid)
|
||||
{
|
||||
TempEditor.End();
|
||||
commandMachine.ExecCommand("TEMPLATE");
|
||||
}
|
||||
else
|
||||
app.Editor.ModalManage.Clear();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* <更新模块> 写入参数信息,上传
|
||||
*/
|
||||
private handleUpdateTemplate = async () =>
|
||||
{
|
||||
const store = this.props.store;
|
||||
await store.WriteParamsToTemplate();
|
||||
let ens = store.Template.Objects.map(o => o.Object as Entity);
|
||||
let box = new Box3();
|
||||
for (let en of ens)
|
||||
box.union(en.BoundingBox);
|
||||
let blob = GetCurrentViewPreViewImage(box);
|
||||
let logo = await uploadLogo(blob);
|
||||
|
||||
let json = TemplateOut(store.Template);
|
||||
let props = TemplateParamsOut(store.Template.Params);
|
||||
let data = await PostJson(TemplateUrls.update, {
|
||||
name: store.Template.Name,
|
||||
module_id: this.props.tid,
|
||||
props: deflate(props),
|
||||
file: deflate(json),
|
||||
logo
|
||||
});
|
||||
if (data.err_code === RequestStatus.Ok)
|
||||
{
|
||||
AppToaster.show({
|
||||
message: "更新模板成功",
|
||||
timeout: 1000
|
||||
});
|
||||
}
|
||||
this.handleClose();
|
||||
}
|
||||
private handleUploadTemplate = async (dirid: string) =>
|
||||
{
|
||||
await this.props.store.UploadTemplate(dirid || DirectoryId.TemplateDir);
|
||||
this.handleClose();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,218 @@
|
||||
import * as React from 'react';
|
||||
import { Button, Classes, H5, Intent, Position } from '@blueprintjs/core';
|
||||
import { TempalteEditorStore } from '../../Store/TemplateEditorStore';
|
||||
import { observable } from 'mobx';
|
||||
import { EEditorActionType, TempalteActionDialog } from './TemplateActionDiglog';
|
||||
import { observer } from 'mobx-react';
|
||||
import { AppToaster } from '../Toaster';
|
||||
import { app } from '../../../ApplicationServices/Application';
|
||||
import { PromptStatus } from '../../../Editor/PromptResult';
|
||||
import { CommandWrap } from '../../../Editor/CommandMachine';
|
||||
import { PopoverButton } from '../Common/PopoverButton';
|
||||
import { arrayRemoveIf } from '../../../Common/ArrayExt';
|
||||
import { SetTempateCoordinate, UpdateTemplateSizeOffBoards, GetTempateSize } from '../../../DatabaseServices/Template/TempateUtils';
|
||||
import { FixedNotZero } from '../../../Common/Utils';
|
||||
import { INeedUpdateParams } from './TemplateComponent';
|
||||
|
||||
export interface ITemplateParamListProps
|
||||
{
|
||||
store: TempalteEditorStore;
|
||||
}
|
||||
|
||||
@observer
|
||||
export default class TemplateParamList extends React.Component<ITemplateParamListProps> {
|
||||
private startAddParam = observable.box(false);
|
||||
public render()
|
||||
{
|
||||
const store = this.props.store;
|
||||
const { params } = store;
|
||||
return (
|
||||
<div className="flexCol">
|
||||
<H5>变量列表</H5>
|
||||
<ul
|
||||
className={Classes.LIST_UNSTYLED + " params-list"}
|
||||
onClick={() => store.currentParamIndex = undefined}
|
||||
>
|
||||
<li>
|
||||
<span>参数名</span>
|
||||
<span>参数值</span>
|
||||
<span>备注</span>
|
||||
<span>表达式</span>
|
||||
<span>参数锁定</span>
|
||||
</li>
|
||||
{
|
||||
params.map((opt, index) =>
|
||||
{
|
||||
return (
|
||||
<li
|
||||
onClick={(e) =>
|
||||
{
|
||||
store.currentParamIndex = index;
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
background: store.currentParamIndex === index && "#ccc"
|
||||
}}
|
||||
>{opt.name}</span>
|
||||
<input type="text"
|
||||
className={Classes.INPUT}
|
||||
value={opt.value}
|
||||
onChange={e => opt.value = e.target.value}
|
||||
onBlur={() => this.updateTempalteParam(opt)}
|
||||
/>
|
||||
<input type="text"
|
||||
className={Classes.INPUT}
|
||||
value={opt.description}
|
||||
onChange={e => opt.description = e.target.value}
|
||||
onBlur={() => this.updateTempalteParam(opt)}
|
||||
/>
|
||||
<input type="text"
|
||||
className={Classes.INPUT}
|
||||
value={opt.expr}
|
||||
onChange={e => opt.expr = e.target.value}
|
||||
onBlur={() => this.updateTempalteParam(opt)}
|
||||
/>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={opt.isLock}
|
||||
onChange={() => opt.isLock = !opt.isLock}
|
||||
/>
|
||||
</label>
|
||||
</li>
|
||||
)
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
<div>
|
||||
<Button
|
||||
text="增加变量"
|
||||
onClick={() => this.startAddParam.set(true)}
|
||||
/>
|
||||
<PopoverButton
|
||||
position={Position.TOP}
|
||||
message="确定删除变量,以及该变量下的所有动作"
|
||||
confirmCallback={this.deleteParam}
|
||||
targetTitle="删除变量"
|
||||
disabled={this.props.store.currentParamIndex === undefined}
|
||||
/>
|
||||
<Button
|
||||
text="设置变量值"
|
||||
onClick={() => this.setParamOrCoordinate(true)}
|
||||
/>
|
||||
<Button
|
||||
text="设置模板坐标系"
|
||||
onClick={() => this.setParamOrCoordinate(false)}
|
||||
/>
|
||||
<Button
|
||||
text="从图纸更新模板大小"
|
||||
onClick={this.updateSize}
|
||||
/>
|
||||
</div>
|
||||
{
|
||||
this.startAddParam.get() &&
|
||||
<TempalteActionDialog
|
||||
store={this.props.store}
|
||||
type={EEditorActionType.AddParams}
|
||||
open={this.startAddParam}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
private deleteParam = () =>
|
||||
{
|
||||
const store = this.props.store;
|
||||
if (store.currentParamIndex === undefined)
|
||||
{
|
||||
AppToaster.show({
|
||||
message: "未选择变量",
|
||||
timeout: 1500,
|
||||
intent: Intent.DANGER
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (store.currentParamIndex <= 2)
|
||||
{
|
||||
AppToaster.show({
|
||||
message: "不能删除默认变量",
|
||||
timeout: 1500,
|
||||
intent: Intent.DANGER
|
||||
});
|
||||
return;
|
||||
}
|
||||
let deletePar = store.params.splice(store.currentParamIndex, 1)[0];
|
||||
store.usedParamNames.delete(deletePar.name);
|
||||
store.Template.DeleteParam(deletePar.name);
|
||||
arrayRemoveIf(store.actionNodes, (node) => node.nodeData["name"] === deletePar.name);
|
||||
store.currentParamIndex = undefined;
|
||||
}
|
||||
private setParamOrCoordinate = async (isSetPar: boolean) =>
|
||||
{
|
||||
const store = this.props.store;
|
||||
if (isSetPar && (store.currentParamIndex === undefined || store.params.length <= store.currentParamIndex))
|
||||
{
|
||||
AppToaster.show({
|
||||
message: "未选择变量",
|
||||
timeout: 1500,
|
||||
});
|
||||
return;
|
||||
}
|
||||
app.Editor.ModalManage.ToggleShow();
|
||||
app.Editor.MaskManage.Clear();
|
||||
|
||||
if (isSetPar)
|
||||
await this.SetParamValue();
|
||||
else
|
||||
await CommandWrap(async () =>
|
||||
{
|
||||
await this.setCoordinate();
|
||||
}, "设置模板坐标系");
|
||||
|
||||
app.Editor.ModalManage.ToggleShow();
|
||||
app.Editor.MaskManage.ShowMask();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置变量值
|
||||
*/
|
||||
private SetParamValue = async () =>
|
||||
{
|
||||
let distRes = await app.Editor.GetDistance({
|
||||
Msg: "点取距离或者输入"
|
||||
});
|
||||
if (distRes.Status === PromptStatus.OK)
|
||||
{
|
||||
let dist = distRes.Distance;
|
||||
const store = this.props.store;
|
||||
store.params[store.currentParamIndex].value = dist.toFixed(2);
|
||||
store.Template.Params[store.currentParamIndex].value = dist.toFixed(2);
|
||||
await store.Template.UpdateTemplateTree();
|
||||
}
|
||||
}
|
||||
private setCoordinate = async () =>
|
||||
{
|
||||
await SetTempateCoordinate(this.props.store.Template);
|
||||
this.updateSize();
|
||||
}
|
||||
private updateSize = () =>
|
||||
{
|
||||
let template = this.props.store.Template;
|
||||
UpdateTemplateSizeOffBoards(template);
|
||||
let size = GetTempateSize(template);
|
||||
let params = this.props.store.params;
|
||||
params[0].value = FixedNotZero(size.x, 2);
|
||||
params[1].value = FixedNotZero(size.y, 2);
|
||||
params[2].value = FixedNotZero(size.z, 2);
|
||||
}
|
||||
private updateTempalteParam = (opt: INeedUpdateParams) =>
|
||||
{
|
||||
let temp = this.props.store.Template;
|
||||
let par = temp.GetParam(opt.name);
|
||||
par.value = opt.value;
|
||||
par.expr = opt.expr;
|
||||
par.description = opt.description;
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
import * as React from 'react';
|
||||
import { Popover, Position, Button, Intent, Classes, Card, IconName, MaybeElement, ITreeNode, Tree } from '@blueprintjs/core';
|
||||
import { PostJson, DirectoryId, RequestStatus } from '../../../Common/Request';
|
||||
import { DirUrl } from '../../../Common/HostUrl';
|
||||
interface ITemplateSaveDirProps
|
||||
{
|
||||
upload: (dirid: React.ReactText) => void;
|
||||
}
|
||||
|
||||
export class TemplateSaveDir extends React.Component<ITemplateSaveDirProps, { nodes: ITreeNode[] }> {
|
||||
constructor(props)
|
||||
{
|
||||
super(props);
|
||||
this.state = {
|
||||
nodes: []
|
||||
}
|
||||
}
|
||||
async componentWillMount()
|
||||
{
|
||||
//初始化目录
|
||||
let data = await PostJson(DirUrl.query, { dir_type: DirectoryId.TemplateDir });
|
||||
if (data.err_code === RequestStatus.Ok)
|
||||
this.setState({ nodes: this.parseNodes(data.dirs) })
|
||||
}
|
||||
public render()
|
||||
{
|
||||
return (
|
||||
<Popover
|
||||
content={
|
||||
<Tree
|
||||
contents={this.state.nodes}
|
||||
onNodeClick={this.handleNodeClick}
|
||||
onNodeCollapse={(node) => this.handleNodeCollapse(node, true)}
|
||||
onNodeExpand={(node) => this.handleNodeCollapse(node, false)}
|
||||
className={Classes.ELEVATION_0}
|
||||
/>
|
||||
}
|
||||
target={
|
||||
<Button
|
||||
text="保存到"
|
||||
intent={Intent.SUCCESS}
|
||||
/>}
|
||||
/>
|
||||
);
|
||||
}
|
||||
//分析目录转换为ui节点数
|
||||
private parseNodes = (dirs: any) =>
|
||||
{
|
||||
let newNodes: ITreeNode[] = [];
|
||||
for (let dir of dirs)
|
||||
{
|
||||
let node: ITreeNode = {
|
||||
id: dir.dir_id,
|
||||
label: dir.dir_name,
|
||||
icon: "folder-close",
|
||||
hasCaret: dir.childs.length > 0,
|
||||
childNodes: this.parseNodes(dir.childs),
|
||||
};
|
||||
newNodes.push(node);
|
||||
}
|
||||
return newNodes;
|
||||
}
|
||||
private handleNodeCollapse = (nodeData: ITreeNode, isCollapse: boolean) =>
|
||||
{
|
||||
nodeData.isExpanded = !isCollapse;
|
||||
this.setState(this.state);
|
||||
};
|
||||
private handleNodeClick = async (nodeData: ITreeNode, _nodePath: number[], e: React.MouseEvent<HTMLElement> | MouseEvent) =>
|
||||
{
|
||||
await this.props.upload(nodeData.id);
|
||||
};
|
||||
}
|
@ -1,19 +1,134 @@
|
||||
import { ITreeNode } from "@blueprintjs/core";
|
||||
import { observable, toJS } from "mobx";
|
||||
import { Box3 } from "three";
|
||||
import { TemplateUrls } from "../../Common/HostUrl";
|
||||
import { PostJson, RequestStatus, uploadLogo } from "../../Common/Request";
|
||||
import { deflate, GetCurrentViewPreViewImage, TemplateOut, TemplateParamsOut } from "../../Common/SerializeMaterial";
|
||||
import { Singleton } from "../../Common/Singleton";
|
||||
import { Entity } from "../../DatabaseServices/Entity/Entity";
|
||||
import { TemplateParam } from "../../DatabaseServices/Template/Param/TemplateParam";
|
||||
import { TemplateRecord, TempateDefaultParamCount } from "../../DatabaseServices/Template/TemplateRecord";
|
||||
import { IConfigOption } from "../Components/Board/UserConfig";
|
||||
import { INeedUpdateParams } from "../Components/Template/TemplateComponent";
|
||||
import { AppToaster } from "../Components/Toaster";
|
||||
import { IConfigStore } from "./BoardStore";
|
||||
import { FixedNotZero } from "../../Common/Utils";
|
||||
|
||||
export const UNRENDERPARS = ["RX", "RY", "RZ", "PX", "PY", "PZ"];
|
||||
|
||||
export class TempalteEditorStore extends Singleton implements IConfigStore
|
||||
{
|
||||
cabNameIndex = 0;
|
||||
Template: TemplateRecord;
|
||||
|
||||
@observable params: INeedUpdateParams[] = [];
|
||||
@observable currentParamIndex: number;
|
||||
usedParamNames: Set<string> = new Set();
|
||||
@observable actionNodes: ITreeNode[] = [];
|
||||
actionIndex = 1;
|
||||
InitParams()
|
||||
{
|
||||
this.usedParamNames.clear();
|
||||
this.params.length = 0;
|
||||
this.actionNodes.length = 0;
|
||||
for (let par of this.Template.Params)
|
||||
{
|
||||
this.usedParamNames.add(par.name);
|
||||
if (!UNRENDERPARS.includes(par.name))
|
||||
this.params.push({
|
||||
name: par.name,
|
||||
value: FixedNotZero(par.value, 2),
|
||||
description: par.description,
|
||||
expr: par.expr,
|
||||
isLock: false
|
||||
});
|
||||
}
|
||||
}
|
||||
UpdateOption(cof: IConfigOption)
|
||||
{
|
||||
let opts = cof.option as INeedUpdateParams[];
|
||||
this.usedParamNames.clear();
|
||||
for (let cof of opts)
|
||||
this.usedParamNames.add(cof.name);
|
||||
|
||||
observable(this.params).replace(opts);
|
||||
|
||||
//删除模块中的非基本参数
|
||||
this.Template.Params.length = TempateDefaultParamCount;
|
||||
|
||||
for (let par of this.params)
|
||||
{
|
||||
let tempPar = this.Template.GetParam(par.name);
|
||||
if (!tempPar)
|
||||
{
|
||||
tempPar = new TemplateParam();
|
||||
this.Template.Params.push(tempPar);
|
||||
}
|
||||
tempPar.value = par.value;
|
||||
tempPar.expr = par.expr;
|
||||
tempPar.description = par.description;
|
||||
}
|
||||
this.currentParamIndex = undefined;
|
||||
}
|
||||
|
||||
SaveConfig()
|
||||
{
|
||||
//新的配置
|
||||
let newConfig: IConfigOption = {};
|
||||
newConfig.option = toJS(this.params);
|
||||
return newConfig;
|
||||
}
|
||||
|
||||
async WriteParamsToTemplate()
|
||||
{
|
||||
for (let par of this.params)
|
||||
{
|
||||
let tempPar = this.Template.GetParam(par.name);
|
||||
if (!tempPar)
|
||||
{
|
||||
tempPar = new TemplateParam();
|
||||
this.Template.Params.push(tempPar);
|
||||
}
|
||||
tempPar.value = par.value;
|
||||
tempPar.description = par.description;
|
||||
tempPar.expr = par.expr;
|
||||
}
|
||||
await this.Template.UpdateTemplateTree();
|
||||
}
|
||||
|
||||
/**不传入template,则使用store里面的template */
|
||||
async UploadTemplate(dirId: string, template?: TemplateRecord)
|
||||
{
|
||||
if (!template)
|
||||
{
|
||||
template = this.Template;
|
||||
await this.WriteParamsToTemplate();
|
||||
}
|
||||
let ens = template.Objects.map(o => o.Object as Entity);
|
||||
let box = new Box3();
|
||||
for (let en of ens)
|
||||
box.union(en.BoundingBox);
|
||||
let blob = GetCurrentViewPreViewImage(box);
|
||||
let logo = await uploadLogo(blob);
|
||||
|
||||
let json = TemplateOut(template);
|
||||
let props = TemplateParamsOut(template.Params);
|
||||
let data = await PostJson(TemplateUrls.create, {
|
||||
name: template.Name,
|
||||
dir_id: dirId,
|
||||
logo,
|
||||
props: deflate(props),
|
||||
file: deflate(json),
|
||||
zip_type: "gzip",
|
||||
});
|
||||
if (data.err_code === RequestStatus.Ok)
|
||||
{
|
||||
AppToaster.show({
|
||||
message: "模板上传成功",
|
||||
timeout: 1000
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in new issue