Compare commits
26 Commits
e12c3c0302
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b3fe9b548 | ||
|
|
db30244cd1 | ||
|
|
c9bc62fb8c | ||
|
|
9d54395c29 | ||
|
|
35fd1c8937 | ||
|
|
579d0f2bd8 | ||
|
|
7f8abf2ff2 | ||
|
|
f1bcd3812d | ||
|
|
d3fda27bb0 | ||
|
|
538a43f597 | ||
|
|
fcab8585c5 | ||
|
|
0ffc42ab69 | ||
|
|
46bead08a0 | ||
|
|
e07b11ef95 | ||
|
|
a78781782e | ||
|
|
0b55384442 | ||
|
|
43837901f3 | ||
|
|
8898cd2e6f | ||
|
|
f34274ea35 | ||
|
|
5ffdb5e508 | ||
|
|
a5a3c93135 | ||
|
|
af697e9304 | ||
|
|
e7abfca9f7 | ||
|
|
1058d666a0 | ||
|
|
58d36a3d6d | ||
|
|
b544391b5c |
@@ -1,8 +1,14 @@
|
||||
package com.solution.web.controller.algo;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
@@ -37,6 +43,51 @@ public class AlgorithmController extends BaseController {
|
||||
@Autowired
|
||||
private IAlgorithmService algorithmService;
|
||||
|
||||
/**
|
||||
* 导出规则列表
|
||||
*/
|
||||
@ApiOperation("运行python程序")
|
||||
@PostMapping("/run")
|
||||
public Object run(@RequestBody Algorithm algorithm) {
|
||||
List<Algorithm> list = algorithmService.selectAlgorithmList(algorithm);
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
for (Algorithm al : list) {
|
||||
String codePath = al.getCodePath();
|
||||
|
||||
try {
|
||||
String jsonInput = objectMapper.writeValueAsString(al);
|
||||
String pythonExe = "E:\\Apps\\anaconda3\\python.exe";
|
||||
ProcessBuilder pb = new ProcessBuilder(pythonExe, codePath);
|
||||
Process process = pb.start();
|
||||
|
||||
try (OutputStream os = process.getOutputStream()) {
|
||||
os.write(jsonInput.getBytes(StandardCharsets.UTF_8));
|
||||
os.flush();
|
||||
}
|
||||
|
||||
String result;
|
||||
// 修改你 Java 代码中的这一段
|
||||
try (BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) {
|
||||
result = reader.lines().collect(Collectors.joining("\n"));
|
||||
}
|
||||
|
||||
int exitCode = process.waitFor();
|
||||
if (exitCode != 0) {
|
||||
return "Python执行失败,错误码:" + exitCode;
|
||||
}
|
||||
|
||||
return objectMapper.readTree(result);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return "执行异常: " + e.getMessage();
|
||||
}
|
||||
}
|
||||
return "未找到算法";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 查询规则列表
|
||||
*/
|
||||
@@ -56,7 +107,7 @@ public class AlgorithmController extends BaseController {
|
||||
@PreAuthorize("@ss.hasPermi('algo:algorithm:export')")
|
||||
@Log(title = "规则", businessType = BusinessType.EXPORT)
|
||||
@PostMapping("/export")
|
||||
public void export(HttpServletResponse response, Algorithm algorithm) {
|
||||
public void export(HttpServletResponse response, @RequestBody Algorithm algorithm) {
|
||||
List<Algorithm> list = algorithmService.selectAlgorithmList(algorithm);
|
||||
ExcelUtil<Algorithm> util = new ExcelUtil<Algorithm>(Algorithm.class);
|
||||
util.exportExcel(response, list, "规则数据");
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.solution.web.controller.behaviour;
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.solution.web.core.BehaviortreeProcessor;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
@@ -38,6 +39,9 @@ public class BehaviortreeController extends BaseController
|
||||
@Autowired
|
||||
private IBehaviortreeService behaviortreeService;
|
||||
|
||||
@Autowired
|
||||
private BehaviortreeProcessor behaviortreeProcessor;
|
||||
|
||||
/**
|
||||
* 查询行为树主列表
|
||||
*/
|
||||
@@ -84,7 +88,7 @@ public class BehaviortreeController extends BaseController
|
||||
@PostMapping
|
||||
public AjaxResult add(@RequestBody Behaviortree behaviortree)
|
||||
{
|
||||
return toAjax(behaviortreeService.insertBehaviortree(behaviortree));
|
||||
return toAjax(behaviortreeProcessor.create(behaviortree));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -96,7 +100,7 @@ public class BehaviortreeController extends BaseController
|
||||
@PutMapping
|
||||
public AjaxResult edit(@RequestBody Behaviortree behaviortree)
|
||||
{
|
||||
return toAjax(behaviortreeService.updateBehaviortree(behaviortree));
|
||||
return toAjax(behaviortreeProcessor.update(behaviortree));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -95,26 +95,26 @@ public class NodetemplateController extends BaseController {
|
||||
if (CollectionUtils.isEmpty(list)) {
|
||||
return R.ok(null);
|
||||
}
|
||||
// Map<String, List<NodetemplateDTO>> groupedByTemplateType = list.stream()
|
||||
// .map(template -> {
|
||||
// NodetemplateDTO dto = new NodetemplateDTO();
|
||||
// dto.setId(template.getId());
|
||||
// dto.setName(template.getName());
|
||||
// dto.setDescription(template.getDescription());
|
||||
// dto.setEnglishName(template.getEnglishName());
|
||||
// dto.setLogicHandler(template.getLogicHandler());
|
||||
// dto.setTempleteType(template.getTempleteType());
|
||||
// return dto;
|
||||
// })
|
||||
// .collect(Collectors.groupingBy(NodetemplateDTO::getTempleteType));
|
||||
// List<NodetemplateVO> vos = new ArrayList<>();
|
||||
// groupedByTemplateType.forEach((key, value) -> {
|
||||
// // 处理逻辑
|
||||
// NodetemplateVO vo = new NodetemplateVO();
|
||||
// vo.setTempleteType(key);
|
||||
// vo.setDtoList(value);
|
||||
// vos.add(vo);
|
||||
// });
|
||||
Map<String, List<NodetemplateDTO>> groupedByTemplateType = list.stream()
|
||||
.map(template -> {
|
||||
NodetemplateDTO dto = new NodetemplateDTO();
|
||||
dto.setId(template.getId());
|
||||
dto.setName(template.getName());
|
||||
dto.setDescription(template.getDescription());
|
||||
dto.setEnglishName(template.getEnglishName());
|
||||
dto.setLogicHandler(template.getLogicHandler());
|
||||
dto.setTemplateType(template.getTemplateType());
|
||||
return dto;
|
||||
})
|
||||
.collect(Collectors.groupingBy(NodetemplateDTO::getTemplateType));
|
||||
List<NodetemplateVO> vos = new ArrayList<>();
|
||||
groupedByTemplateType.forEach((key, value) -> {
|
||||
// 处理逻辑
|
||||
NodetemplateVO vo = new NodetemplateVO();
|
||||
vo.setTemplateType(key);
|
||||
vo.setDtoList(value);
|
||||
vos.add(vo);
|
||||
});
|
||||
return R.ok(list);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ public class NodetemplateDTO {
|
||||
@ApiModelProperty("afsim 中转换的节点名")
|
||||
private String englishName;
|
||||
|
||||
private String templeteType;
|
||||
private String templateType;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
@@ -65,11 +65,11 @@ public class NodetemplateDTO {
|
||||
this.englishName = englishName;
|
||||
}
|
||||
|
||||
public String getTempleteType() {
|
||||
return templeteType;
|
||||
public String getTemplateType() {
|
||||
return templateType;
|
||||
}
|
||||
|
||||
public void setTempleteType(String templeteType) {
|
||||
this.templeteType = templeteType;
|
||||
public void setTemplateType(String templateType) {
|
||||
this.templateType = templateType;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,17 +9,17 @@ import java.util.List;
|
||||
public class NodetemplateVO {
|
||||
/** 模版类型,节点模版或者条件判断,例如“node”,precondition“ */
|
||||
@ApiModelProperty("模版类型,节点模版或者条件判断,例如“node”,precondition“")
|
||||
private String templeteType;
|
||||
private String templateType;
|
||||
|
||||
@ApiModelProperty("节点模板数据")
|
||||
private List<NodetemplateDTO> dtoList;
|
||||
|
||||
public String getTempleteType() {
|
||||
return templeteType;
|
||||
public String getTemplateType() {
|
||||
return templateType;
|
||||
}
|
||||
|
||||
public void setTempleteType(String templeteType) {
|
||||
this.templeteType = templeteType;
|
||||
public void setTemplateType(String templateType) {
|
||||
this.templateType = templateType;
|
||||
}
|
||||
|
||||
public List<NodetemplateDTO> getDtoList() {
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
package com.solution.web.core;
|
||||
/*
|
||||
* This file is part of the kernelstudio package.
|
||||
*
|
||||
* (c) 2014-2026 zlin <admin@kernelstudio.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
* that was distributed with this source code.
|
||||
*/
|
||||
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.solution.system.domain.*;
|
||||
import com.solution.system.service.IBehaviortreeService;
|
||||
import com.solution.system.service.INodeconnectionService;
|
||||
import com.solution.system.service.INodeparameterService;
|
||||
import com.solution.system.service.ITreenodeinstanceService;
|
||||
import com.solution.web.core.graph.Graph;
|
||||
import com.solution.web.core.graph.GraphEdge;
|
||||
import com.solution.web.core.graph.GraphNode;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Component
|
||||
public class BehaviortreeProcessor {
|
||||
|
||||
@Autowired
|
||||
private IBehaviortreeService behaviortreeService;
|
||||
|
||||
@Autowired
|
||||
private ITreenodeinstanceService treenodeinstanceService;
|
||||
|
||||
@Autowired
|
||||
private INodeparameterService nodeparameterService;
|
||||
|
||||
@Autowired
|
||||
private INodeconnectionService nodeconnectionService;
|
||||
|
||||
private ObjectMapper objectMapper = createObjectMapper();
|
||||
|
||||
public static ObjectMapper createObjectMapper() {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
return objectMapper;
|
||||
}
|
||||
|
||||
public int create(Behaviortree behaviortree) {
|
||||
int result = behaviortreeService.insertBehaviortree(behaviortree);
|
||||
processGraph(behaviortree);
|
||||
return result;
|
||||
}
|
||||
|
||||
public int update(Behaviortree behaviortree) {
|
||||
int result = behaviortreeService.updateBehaviortree(behaviortree);
|
||||
|
||||
// 删除节点实例
|
||||
treenodeinstanceService.deleteByTreeId(behaviortree.getId());
|
||||
|
||||
// 删除参数
|
||||
nodeparameterService.deleteByTreeId(behaviortree.getId());
|
||||
|
||||
// 删除连线
|
||||
nodeconnectionService.deleteByTreeId(behaviortree.getId());
|
||||
|
||||
processGraph(behaviortree);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void processGraph(Behaviortree behaviortree) {
|
||||
|
||||
Graph graph = null;
|
||||
try {
|
||||
graph = objectMapper.readValue(behaviortree.getXmlContent(), Graph.class);
|
||||
} catch (Exception e) {
|
||||
// skip
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if (null == graph) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 插入节点 treenodeinstance
|
||||
Map<String, Treenodeinstance> instanceKeyMap = new HashMap<>();
|
||||
Map<String, Long> nodeKeyIndexMap = new HashMap<>();
|
||||
if (graph.hasNodes()) {
|
||||
Long index = 0L;
|
||||
for (GraphNode node : graph.getNodes()) {
|
||||
Treenodeinstance instance = createNodeInstance(behaviortree, node);
|
||||
treenodeinstanceService.insertTreenodeinstance(instance);
|
||||
instanceKeyMap.put(node.getKey(), instance);
|
||||
|
||||
if (node.hasParameters()) {
|
||||
// 插入parameter nodeparameter
|
||||
for (Templateparameterdef parameter : node.getParameters()) {
|
||||
Nodeparameter nodeparameter = createNodeParameter(behaviortree, parameter, instance);
|
||||
nodeparameterService.insertNodeparameter(nodeparameter);
|
||||
}
|
||||
}
|
||||
nodeKeyIndexMap.put(node.getKey(), index);
|
||||
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
// 插入连线 nodeconnection
|
||||
if (graph.hasEdges()) {
|
||||
for (GraphEdge edge : graph.getEdges()) {
|
||||
Nodeconnection connection = createConnection(behaviortree, edge, instanceKeyMap, nodeKeyIndexMap);
|
||||
nodeconnectionService.insertNodeconnection(connection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Nodeconnection createConnection(Behaviortree behaviortree, GraphEdge edge,
|
||||
Map<String, Treenodeinstance> instanceKeyMap,
|
||||
Map<String, Long> nodeKeyIndexMap) {
|
||||
Nodeconnection connection = new Nodeconnection();
|
||||
connection.setTreeId(behaviortree.getId());
|
||||
if (null != instanceKeyMap.get(edge.getSource().getCell())) {
|
||||
Treenodeinstance parent = instanceKeyMap.get(edge.getSource().getCell());
|
||||
connection.setParentNodeId(parent.getId());
|
||||
}
|
||||
if (null != instanceKeyMap.get(edge.getTarget().getCell())) {
|
||||
Treenodeinstance children = instanceKeyMap.get(edge.getTarget().getCell());
|
||||
connection.setChildNodeId(children.getId());
|
||||
}
|
||||
if (null != nodeKeyIndexMap.get(edge.getSource().getCell())) {
|
||||
connection.setOrderIndex(nodeKeyIndexMap.get(edge.getSource().getCell()));
|
||||
}
|
||||
return connection;
|
||||
}
|
||||
|
||||
private Nodeparameter createNodeParameter(Behaviortree behaviortree, Templateparameterdef parameter,
|
||||
Treenodeinstance instance) {
|
||||
Nodeparameter nodeparameter = new Nodeparameter();
|
||||
nodeparameter.setTreeId(behaviortree.getId());
|
||||
nodeparameter.setNodeInstanceId(instance.getId());
|
||||
nodeparameter.setParamDefId(parameter.getId());
|
||||
nodeparameter.setValue(parameter.getDefaultValue());
|
||||
return nodeparameter;
|
||||
}
|
||||
|
||||
private Treenodeinstance createNodeInstance(Behaviortree behaviortree, GraphNode node) {
|
||||
Treenodeinstance instance = new Treenodeinstance();
|
||||
instance.setTreeId(behaviortree.getId());
|
||||
instance.setTemplateId((long) node.getTemplate());
|
||||
instance.setInstanceName(node.getName());
|
||||
instance.setIsRoot((long) (node.isRoot() ? 1 : 0));
|
||||
if ("precondition".equalsIgnoreCase(node.getTemplateType())) {
|
||||
instance.setPreconditionTempleteId((long) node.getTemplate());
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.solution.web.core.graph;
|
||||
/*
|
||||
* This file is part of the kernelstudio package.
|
||||
*
|
||||
* (c) 2014-2026 zlin <admin@kernelstudio.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
* that was distributed with this source code.
|
||||
*/
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
public class Graph implements Serializable {
|
||||
|
||||
private List<GraphNode> nodes;
|
||||
|
||||
private List<GraphEdge> edges;
|
||||
|
||||
public boolean hasNodes() {
|
||||
return nodes != null && !nodes.isEmpty();
|
||||
}
|
||||
|
||||
public boolean hasEdges() {
|
||||
return edges != null && !edges.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Graph{" +
|
||||
"nodes=" + nodes +
|
||||
", edges=" + edges +
|
||||
'}';
|
||||
}
|
||||
|
||||
public List<GraphNode> getNodes() {
|
||||
return nodes;
|
||||
}
|
||||
|
||||
public void setNodes(List<GraphNode> nodes) {
|
||||
this.nodes = nodes;
|
||||
}
|
||||
|
||||
public List<GraphEdge> getEdges() {
|
||||
return edges;
|
||||
}
|
||||
|
||||
public void setEdges(List<GraphEdge> edges) {
|
||||
this.edges = edges;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.solution.web.core.graph;
|
||||
/*
|
||||
* This file is part of the kernelstudio package.
|
||||
*
|
||||
* (c) 2014-2026 zlin <admin@kernelstudio.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
* that was distributed with this source code.
|
||||
*/
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class GraphEdge implements Serializable {
|
||||
|
||||
private String key;
|
||||
|
||||
private Line source;
|
||||
|
||||
private Line target;
|
||||
|
||||
public boolean hasSource() {
|
||||
return source != null && StringUtils.hasText(source.cell);
|
||||
}
|
||||
|
||||
public boolean hasTarget() {
|
||||
return target != null && StringUtils.hasText(target.cell);
|
||||
}
|
||||
|
||||
public static class Line implements Serializable {
|
||||
|
||||
private String cell;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Line{" +
|
||||
"cell='" + cell + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
public String getCell() {
|
||||
return cell;
|
||||
}
|
||||
|
||||
public void setCell(String cell) {
|
||||
this.cell = cell;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GraphEdge{" +
|
||||
"key='" + key + '\'' +
|
||||
", source='" + source + '\'' +
|
||||
", target='" + target + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public Line getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
public void setSource(Line source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public Line getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
public void setTarget(Line target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
package com.solution.web.core.graph;
|
||||
/*
|
||||
* This file is part of the kernelstudio package.
|
||||
*
|
||||
* (c) 2014-2026 zlin <admin@kernelstudio.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
* that was distributed with this source code.
|
||||
*/
|
||||
|
||||
import com.solution.system.domain.Templateparameterdef;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
public class GraphNode implements Serializable {
|
||||
|
||||
private int id;
|
||||
|
||||
private int template;
|
||||
|
||||
private String templateType;
|
||||
|
||||
private String type;
|
||||
|
||||
private String key;
|
||||
|
||||
private String name;
|
||||
|
||||
private String description;
|
||||
|
||||
private String category;
|
||||
|
||||
private List<Templateparameterdef> parameters;
|
||||
|
||||
private List<GraphVariable> variables;
|
||||
|
||||
public boolean hasParameters() {
|
||||
return parameters != null && !parameters.isEmpty();
|
||||
}
|
||||
|
||||
public boolean hasVariables() {
|
||||
return variables != null && !variables.isEmpty();
|
||||
}
|
||||
|
||||
public boolean isRoot() {
|
||||
return "root".equalsIgnoreCase(this.category);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GraphNode{" +
|
||||
"id=" + id +
|
||||
", key='" + key + '\'' +
|
||||
", name='" + name + '\'' +
|
||||
", description='" + description + '\'' +
|
||||
", parameters=" + parameters +
|
||||
", variables=" + variables +
|
||||
'}';
|
||||
}
|
||||
|
||||
public String getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
public void setCategory(String category) {
|
||||
this.category = category;
|
||||
}
|
||||
|
||||
public String getTemplateType() {
|
||||
return templateType;
|
||||
}
|
||||
|
||||
public void setTemplateType(String templateType) {
|
||||
this.templateType = templateType;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public int getTemplate() {
|
||||
return template;
|
||||
}
|
||||
|
||||
public void setTemplate(int template) {
|
||||
this.template = template;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public List<Templateparameterdef> getParameters() {
|
||||
return parameters;
|
||||
}
|
||||
|
||||
public void setParameters(List<Templateparameterdef> parameters) {
|
||||
this.parameters = parameters;
|
||||
}
|
||||
|
||||
public List<GraphVariable> getVariables() {
|
||||
return variables;
|
||||
}
|
||||
|
||||
public void setVariables(List<GraphVariable> variables) {
|
||||
this.variables = variables;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.solution.web.core.graph;
|
||||
/*
|
||||
* This file is part of the kernelstudio package.
|
||||
*
|
||||
* (c) 2014-2026 zlin <admin@kernelstudio.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
* that was distributed with this source code.
|
||||
*/
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class GraphVariable implements Serializable {
|
||||
|
||||
private String key;
|
||||
|
||||
private String value;
|
||||
|
||||
private String defaults;
|
||||
|
||||
private String unit;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GraphVariable{" +
|
||||
"key='" + key + '\'' +
|
||||
", value='" + value + '\'' +
|
||||
", defaults='" + defaults + '\'' +
|
||||
", unit='" + unit + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getDefaults() {
|
||||
return defaults;
|
||||
}
|
||||
|
||||
public void setDefaults(String defaults) {
|
||||
this.defaults = defaults;
|
||||
}
|
||||
|
||||
public String getUnit() {
|
||||
return unit;
|
||||
}
|
||||
|
||||
public void setUnit(String unit) {
|
||||
this.unit = unit;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package com.solution.algo;
|
||||
/*
|
||||
* This file is part of the kernelstudio package.
|
||||
*
|
||||
* (c) 2014-2026 zlin <admin@kernelstudio.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
* that was distributed with this source code.
|
||||
*/
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.type.CollectionType;
|
||||
import com.solution.algo.domain.AlgorithmConfig;
|
||||
import org.apache.ibatis.type.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.sql.CallableStatement;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
@MappedTypes(List.class)
|
||||
@MappedJdbcTypes({
|
||||
JdbcType.VARCHAR,
|
||||
JdbcType.BLOB
|
||||
})
|
||||
public class AlgorithmConfigTypeHandler extends BaseTypeHandler<List<AlgorithmConfig>>
|
||||
implements TypeHandler<List<AlgorithmConfig>> {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
private final CollectionType collectionType = objectMapper.getTypeFactory()
|
||||
.constructCollectionType(List.class, AlgorithmConfig.class);
|
||||
|
||||
@Override
|
||||
public void setNonNullParameter(PreparedStatement ps, int i, List<AlgorithmConfig> parameter, JdbcType jdbcType)
|
||||
throws SQLException {
|
||||
ps.setString(i, serialize(parameter));
|
||||
}
|
||||
|
||||
public String serialize(List<AlgorithmConfig> indicatorConfig) {
|
||||
if (null != indicatorConfig) {
|
||||
try {
|
||||
return objectMapper.writeValueAsString(indicatorConfig);
|
||||
} catch (Exception e) {
|
||||
logger.error("Can not serialize", e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<AlgorithmConfig> deserialize(String config) throws SQLException {
|
||||
if (StringUtils.hasText(config)) {
|
||||
try {
|
||||
return objectMapper.readValue(config, collectionType);
|
||||
} catch (Exception e) {
|
||||
logger.error("Can not deserialize", e);
|
||||
}
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AlgorithmConfig> getNullableResult(ResultSet rs, String columnName) throws SQLException {
|
||||
String jsonValue = rs.getString(columnName);
|
||||
return deserialize(jsonValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AlgorithmConfig> getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
|
||||
String jsonValue = rs.getString(columnIndex);
|
||||
return deserialize(jsonValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AlgorithmConfig> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
|
||||
String jsonValue = cs.getString(columnIndex);
|
||||
return deserialize(jsonValue);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -34,6 +34,13 @@ public class Algorithm
|
||||
@Excel(name = "算法描述")
|
||||
private String description;
|
||||
|
||||
/** 算法配置 */
|
||||
@Excel(name = "算法配置")
|
||||
private String algoConfig;
|
||||
|
||||
/** 算法配置 */
|
||||
private List<AlgorithmConfig> algoConfigList;
|
||||
|
||||
/** 算法参数定义信息 */
|
||||
private List<AlgorithmParam> algorithmParamList;
|
||||
|
||||
@@ -87,6 +94,16 @@ public class Algorithm
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setAlgoConfig(String algoConfig)
|
||||
{
|
||||
this.algoConfig = algoConfig;
|
||||
}
|
||||
|
||||
public String getAlgoConfig()
|
||||
{
|
||||
return algoConfig;
|
||||
}
|
||||
|
||||
public List<AlgorithmParam> getAlgorithmParamList()
|
||||
{
|
||||
return algorithmParamList;
|
||||
@@ -97,6 +114,14 @@ public class Algorithm
|
||||
this.algorithmParamList = algorithmParamList;
|
||||
}
|
||||
|
||||
public List<AlgorithmConfig> getAlgoConfigList() {
|
||||
return algoConfigList;
|
||||
}
|
||||
|
||||
public void setAlgoConfigList(List<AlgorithmConfig> algoConfigList) {
|
||||
this.algoConfigList = algoConfigList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
|
||||
@@ -105,6 +130,7 @@ public class Algorithm
|
||||
.append("type", getType())
|
||||
.append("codePath", getCodePath())
|
||||
.append("description", getDescription())
|
||||
.append("algoConfig", getAlgoConfig())
|
||||
.append("algorithmParamList", getAlgorithmParamList())
|
||||
.toString();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.solution.algo.domain;
|
||||
/*
|
||||
* This file is part of the kernelstudio package.
|
||||
*
|
||||
* (c) 2014-2026 zlin <admin@kernelstudio.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
* that was distributed with this source code.
|
||||
*/
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class AlgorithmConfig implements Serializable {
|
||||
|
||||
private String name;
|
||||
|
||||
private String operation;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getOperation() {
|
||||
return operation;
|
||||
}
|
||||
|
||||
public void setOperation(String operation) {
|
||||
this.operation = operation;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,6 +10,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="type" column="type" />
|
||||
<result property="codePath" column="code_path" />
|
||||
<result property="description" column="description" />
|
||||
<result property="algoConfig" column="algo_config" />
|
||||
<result property="algoConfigList" column="algo_config_list" typeHandler="com.solution.algo.AlgorithmConfigTypeHandler" />
|
||||
</resultMap>
|
||||
|
||||
<resultMap id="AlgorithmAlgorithmParamResult" type="Algorithm" extends="AlgorithmResult">
|
||||
@@ -25,7 +27,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectAlgorithmVo">
|
||||
select id, name, type, code_path, description from algorithm
|
||||
select id, name, type, code_path, description, algo_config, algo_config_list from algorithm
|
||||
</sql>
|
||||
|
||||
<select id="selectAlgorithmList" parameterType="Algorithm" resultMap="AlgorithmAlgorithmParamResult">
|
||||
@@ -35,11 +37,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="type != null and type != ''"> and type = #{type}</if>
|
||||
<if test="codePath != null and codePath != ''"> and code_path = #{codePath}</if>
|
||||
<if test="description != null and description != ''"> and description = #{description}</if>
|
||||
<if test="algoConfig != null and algoConfig != ''"> and algo_config = #{algoConfig}</if>
|
||||
</where>
|
||||
</select>
|
||||
|
||||
<select id="selectAlgorithmById" parameterType="Long" resultMap="AlgorithmAlgorithmParamResult">
|
||||
select id, name, type, code_path, description
|
||||
select id, name, type, code_path, description, algo_config
|
||||
from algorithm
|
||||
where id = #{id}
|
||||
</select>
|
||||
@@ -57,12 +60,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="type != null and type != ''">type,</if>
|
||||
<if test="codePath != null">code_path,</if>
|
||||
<if test="description != null">description,</if>
|
||||
<if test="algoConfig != null">algo_config,</if>
|
||||
<if test="algoConfigList != null">algo_config_list,</if>
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="name != null and name != ''">#{name},</if>
|
||||
<if test="type != null and type != ''">#{type},</if>
|
||||
<if test="codePath != null">#{codePath},</if>
|
||||
<if test="description != null">#{description},</if>
|
||||
<if test="algoConfig != null">#{algoConfig},</if>
|
||||
<if test="algoConfigList != null">#{algoConfigList,typeHandler=com.solution.algo.AlgorithmConfigTypeHandler},</if>
|
||||
</trim>
|
||||
</insert>
|
||||
|
||||
@@ -73,6 +80,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="type != null and type != ''">type = #{type},</if>
|
||||
<if test="codePath != null">code_path = #{codePath},</if>
|
||||
<if test="description != null">description = #{description},</if>
|
||||
<if test="algoConfig != null">algo_config = #{algoConfig},</if>
|
||||
<if test="algoConfigList != null">algo_config_list = #{algoConfigList,typeHandler=com.solution.algo.AlgorithmConfigTypeHandler},</if>
|
||||
</trim>
|
||||
where id = #{id}
|
||||
</update>
|
||||
|
||||
@@ -18,6 +18,8 @@ public class Nodeconnection extends BaseEntity
|
||||
/** 连接ID (主键) */
|
||||
private Long id;
|
||||
|
||||
private Long treeId;
|
||||
|
||||
/** 父节点 (外键: TreeInstanceNode.id) */
|
||||
@Excel(name = "父节点 (外键: TreeInstanceNode.id)")
|
||||
private Long parentNodeId;
|
||||
@@ -40,7 +42,15 @@ public class Nodeconnection extends BaseEntity
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setParentNodeId(Long parentNodeId)
|
||||
public Long getTreeId() {
|
||||
return treeId;
|
||||
}
|
||||
|
||||
public void setTreeId(Long treeId) {
|
||||
this.treeId = treeId;
|
||||
}
|
||||
|
||||
public void setParentNodeId(Long parentNodeId)
|
||||
{
|
||||
this.parentNodeId = parentNodeId;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ public class Nodeparameter extends BaseEntity
|
||||
/** 节点参数ID (主键) */
|
||||
private Long id;
|
||||
|
||||
private Long treeId;
|
||||
|
||||
/** 关联到哪个节点实例 (外键: TreeInstanceNode.id) */
|
||||
@Excel(name = "关联到哪个节点实例 (外键: TreeInstanceNode.id)")
|
||||
private Long nodeInstanceId;
|
||||
@@ -40,7 +42,15 @@ public class Nodeparameter extends BaseEntity
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setNodeInstanceId(Long nodeInstanceId)
|
||||
public Long getTreeId() {
|
||||
return treeId;
|
||||
}
|
||||
|
||||
public void setTreeId(Long treeId) {
|
||||
this.treeId = treeId;
|
||||
}
|
||||
|
||||
public void setNodeInstanceId(Long nodeInstanceId)
|
||||
{
|
||||
this.nodeInstanceId = nodeInstanceId;
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ public class Nodetemplate extends BaseEntity
|
||||
|
||||
/** 模版类型,节点模版或者条件判断,例如“node”,precondition“ */
|
||||
@Excel(name = "模版类型,节点模版或者条件判断,例如“node”,precondition“")
|
||||
private String templeteType;
|
||||
private String templateType;
|
||||
|
||||
public Nodetemplate() {
|
||||
}
|
||||
@@ -52,7 +52,7 @@ public class Nodetemplate extends BaseEntity
|
||||
this.logicHandler = template.logicHandler;
|
||||
this.description = template.description;
|
||||
this.englishName = template.englishName;
|
||||
this.templeteType = template.templeteType;
|
||||
this.templateType = template.templateType;
|
||||
}
|
||||
|
||||
public void setId(Long id)
|
||||
@@ -115,14 +115,14 @@ public class Nodetemplate extends BaseEntity
|
||||
return englishName;
|
||||
}
|
||||
|
||||
public void setTempleteType(String templeteType)
|
||||
public void setTemplateType(String templateType)
|
||||
{
|
||||
this.templeteType = templeteType;
|
||||
this.templateType = templateType;
|
||||
}
|
||||
|
||||
public String getTempleteType()
|
||||
public String getTemplateType()
|
||||
{
|
||||
return templeteType;
|
||||
return templateType;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -134,7 +134,7 @@ public class Nodetemplate extends BaseEntity
|
||||
.append("logicHandler", getLogicHandler())
|
||||
.append("description", getDescription())
|
||||
.append("englishName", getEnglishName())
|
||||
.append("templeteType", getTempleteType())
|
||||
.append("templateType", getTemplateType())
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.solution.system.mapper;
|
||||
|
||||
import java.util.List;
|
||||
import com.solution.system.domain.Nodeconnection;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
/**
|
||||
* 节点连接Mapper接口
|
||||
@@ -11,6 +12,8 @@ import com.solution.system.domain.Nodeconnection;
|
||||
*/
|
||||
public interface NodeconnectionMapper
|
||||
{
|
||||
void deleteByTreeId(@Param("treeId") Long treeId);
|
||||
|
||||
/**
|
||||
* 查询节点连接
|
||||
*
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.solution.system.mapper;
|
||||
|
||||
import java.util.List;
|
||||
import com.solution.system.domain.Nodeparameter;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
/**
|
||||
* 节点参数Mapper接口
|
||||
@@ -11,6 +12,8 @@ import com.solution.system.domain.Nodeparameter;
|
||||
*/
|
||||
public interface NodeparameterMapper
|
||||
{
|
||||
void deleteByTreeId(@Param("treeId") Long treeId);
|
||||
|
||||
/**
|
||||
* 查询节点参数
|
||||
*
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.solution.system.mapper;
|
||||
|
||||
import java.util.List;
|
||||
import com.solution.system.domain.Treenodeinstance;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
/**
|
||||
* 行为树实例节点Mapper接口
|
||||
@@ -11,6 +12,8 @@ import com.solution.system.domain.Treenodeinstance;
|
||||
*/
|
||||
public interface TreenodeinstanceMapper
|
||||
{
|
||||
void deleteByTreeId(@Param(value = "treeId") Long treeId);
|
||||
|
||||
/**
|
||||
* 查询行为树实例节点
|
||||
*
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.solution.system.service;
|
||||
|
||||
import java.util.List;
|
||||
import com.solution.system.domain.Nodeconnection;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
/**
|
||||
* 节点连接Service接口
|
||||
@@ -11,6 +12,9 @@ import com.solution.system.domain.Nodeconnection;
|
||||
*/
|
||||
public interface INodeconnectionService
|
||||
{
|
||||
|
||||
void deleteByTreeId(@Param("treeId") Long treeId);
|
||||
|
||||
/**
|
||||
* 查询节点连接
|
||||
*
|
||||
|
||||
@@ -11,6 +11,9 @@ import com.solution.system.domain.Nodeparameter;
|
||||
*/
|
||||
public interface INodeparameterService
|
||||
{
|
||||
|
||||
void deleteByTreeId(Long treeId);
|
||||
|
||||
/**
|
||||
* 查询节点参数
|
||||
*
|
||||
|
||||
@@ -11,6 +11,9 @@ import com.solution.system.domain.Treenodeinstance;
|
||||
*/
|
||||
public interface ITreenodeinstanceService
|
||||
{
|
||||
|
||||
void deleteByTreeId(Long treeId);
|
||||
|
||||
/**
|
||||
* 查询行为树实例节点
|
||||
*
|
||||
|
||||
@@ -19,6 +19,11 @@ public class NodeconnectionServiceImpl implements INodeconnectionService
|
||||
@Autowired
|
||||
private NodeconnectionMapper nodeconnectionMapper;
|
||||
|
||||
@Override
|
||||
public void deleteByTreeId(Long treeId) {
|
||||
nodeconnectionMapper.deleteByTreeId(treeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询节点连接
|
||||
*
|
||||
|
||||
@@ -19,6 +19,11 @@ public class NodeparameterServiceImpl implements INodeparameterService
|
||||
@Autowired
|
||||
private NodeparameterMapper nodeparameterMapper;
|
||||
|
||||
@Override
|
||||
public void deleteByTreeId(Long treeId) {
|
||||
nodeparameterMapper.deleteByTreeId(treeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询节点参数
|
||||
*
|
||||
|
||||
@@ -19,6 +19,11 @@ public class TreenodeinstanceServiceImpl implements ITreenodeinstanceService
|
||||
@Autowired
|
||||
private TreenodeinstanceMapper treenodeinstanceMapper;
|
||||
|
||||
@Override
|
||||
public void deleteByTreeId(Long treeId) {
|
||||
treenodeinstanceMapper.deleteByTreeId(treeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询行为树实例节点
|
||||
*
|
||||
|
||||
@@ -6,18 +6,24 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
|
||||
<resultMap type="Nodeconnection" id="NodeconnectionResult">
|
||||
<result property="id" column="id" />
|
||||
<result property="treeId" column="tree_id" />
|
||||
<result property="parentNodeId" column="parent_node_id" />
|
||||
<result property="childNodeId" column="child_node_id" />
|
||||
<result property="orderIndex" column="order_index" />
|
||||
</resultMap>
|
||||
|
||||
<delete id="deleteByTreeId">
|
||||
delete from nodeconnection where tree_id=#{treeId}
|
||||
</delete>
|
||||
|
||||
<sql id="selectNodeconnectionVo">
|
||||
select id, parent_node_id, child_node_id, order_index from nodeconnection
|
||||
select id, tree_id, parent_node_id, child_node_id, order_index from nodeconnection
|
||||
</sql>
|
||||
|
||||
<select id="selectNodeconnectionList" parameterType="Nodeconnection" resultMap="NodeconnectionResult">
|
||||
<include refid="selectNodeconnectionVo"/>
|
||||
<where>
|
||||
<where>
|
||||
<if test="treeId != null "> and tree_id = #{treeId}</if>
|
||||
<if test="parentNodeId != null "> and parent_node_id = #{parentNodeId}</if>
|
||||
<if test="childNodeId != null "> and child_node_id = #{childNodeId}</if>
|
||||
<if test="orderIndex != null "> and order_index = #{orderIndex}</if>
|
||||
@@ -32,11 +38,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<insert id="insertNodeconnection" parameterType="Nodeconnection" useGeneratedKeys="true" keyProperty="id">
|
||||
insert into nodeconnection
|
||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||
<if test="treeId != null">tree_id,</if>
|
||||
<if test="parentNodeId != null">parent_node_id,</if>
|
||||
<if test="childNodeId != null">child_node_id,</if>
|
||||
<if test="orderIndex != null">order_index,</if>
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="treeId != null">#{treeId},</if>
|
||||
<if test="parentNodeId != null">#{parentNodeId},</if>
|
||||
<if test="childNodeId != null">#{childNodeId},</if>
|
||||
<if test="orderIndex != null">#{orderIndex},</if>
|
||||
@@ -46,6 +54,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<update id="updateNodeconnection" parameterType="Nodeconnection">
|
||||
update nodeconnection
|
||||
<trim prefix="SET" suffixOverrides=",">
|
||||
<if test="treeId != null">tree_id = #{treeId},</if>
|
||||
<if test="parentNodeId != null">parent_node_id = #{parentNodeId},</if>
|
||||
<if test="childNodeId != null">child_node_id = #{childNodeId},</if>
|
||||
<if test="orderIndex != null">order_index = #{orderIndex},</if>
|
||||
|
||||
@@ -6,13 +6,18 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
|
||||
<resultMap type="Nodeparameter" id="NodeparameterResult">
|
||||
<result property="id" column="id" />
|
||||
<result property="treeId" column="tree_id" />
|
||||
<result property="nodeInstanceId" column="node_instance_id" />
|
||||
<result property="paramDefId" column="param_def_id" />
|
||||
<result property="value" column="value" />
|
||||
</resultMap>
|
||||
|
||||
<delete id="deleteByTreeId">
|
||||
delete from nodeparameter where tree_id=#{treeId}
|
||||
</delete>
|
||||
|
||||
<sql id="selectNodeparameterVo">
|
||||
select id, node_instance_id, param_def_id, value from nodeparameter
|
||||
select id, treeId, node_instance_id, param_def_id, value from nodeparameter
|
||||
</sql>
|
||||
|
||||
<select id="selectNodeparameterList" parameterType="Nodeparameter" resultMap="NodeparameterResult">
|
||||
@@ -32,11 +37,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<insert id="insertNodeparameter" parameterType="Nodeparameter" useGeneratedKeys="true" keyProperty="id">
|
||||
insert into nodeparameter
|
||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||
<if test="treeId != null">tree_id,</if>
|
||||
<if test="nodeInstanceId != null">node_instance_id,</if>
|
||||
<if test="paramDefId != null">param_def_id,</if>
|
||||
<if test="value != null">value,</if>
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="treeId != null">#{treeId},</if>
|
||||
<if test="nodeInstanceId != null">#{nodeInstanceId},</if>
|
||||
<if test="paramDefId != null">#{paramDefId},</if>
|
||||
<if test="value != null">#{value},</if>
|
||||
@@ -46,6 +53,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<update id="updateNodeparameter" parameterType="Nodeparameter">
|
||||
update nodeparameter
|
||||
<trim prefix="SET" suffixOverrides=",">
|
||||
<if test="treeId != null">tree_id = #{treeId},</if>
|
||||
<if test="nodeInstanceId != null">node_instance_id = #{nodeInstanceId},</if>
|
||||
<if test="paramDefId != null">param_def_id = #{paramDefId},</if>
|
||||
<if test="value != null">value = #{value},</if>
|
||||
|
||||
@@ -11,7 +11,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="logicHandler" column="logic_handler" />
|
||||
<result property="description" column="description" />
|
||||
<result property="englishName" column="english_name" />
|
||||
<result property="templeteType" column="templete_type" />
|
||||
<result property="templateType" column="templete_type" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectNodetemplateVo">
|
||||
@@ -26,7 +26,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="logicHandler != null and logicHandler != ''"> and logic_handler = #{logicHandler}</if>
|
||||
<if test="description != null and description != ''"> and description = #{description}</if>
|
||||
<if test="englishName != null and englishName != ''"> and english_name like concat('%', #{englishName}, '%')</if>
|
||||
<if test="templeteType != null and templeteType != ''"> and templete_type = #{templeteType}</if>
|
||||
<if test="templateType != null and templateType != ''"> and templete_type = #{templateType}</if>
|
||||
</where>
|
||||
</select>
|
||||
|
||||
@@ -43,7 +43,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="logicHandler != null">logic_handler,</if>
|
||||
<if test="description != null">description,</if>
|
||||
<if test="englishName != null">english_name,</if>
|
||||
<if test="templeteType != null and templeteType != ''">templete_type,</if>
|
||||
<if test="templateType != null and templateType != ''">templete_type,</if>
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="type != null and type != ''">#{type},</if>
|
||||
@@ -51,7 +51,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="logicHandler != null">#{logicHandler},</if>
|
||||
<if test="description != null">#{description},</if>
|
||||
<if test="englishName != null">#{englishName},</if>
|
||||
<if test="templeteType != null and templeteType != ''">#{templeteType},</if>
|
||||
<if test="templateType != null and templateType != ''">#{templateType},</if>
|
||||
</trim>
|
||||
</insert>
|
||||
|
||||
@@ -63,7 +63,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="logicHandler != null">logic_handler = #{logicHandler},</if>
|
||||
<if test="description != null">description = #{description},</if>
|
||||
<if test="englishName != null">english_name = #{englishName},</if>
|
||||
<if test="templeteType != null and templeteType != ''">templete_type = #{templeteType},</if>
|
||||
<if test="templateType != null and templateType != ''">templete_type = #{templateType},</if>
|
||||
</trim>
|
||||
where id = #{id}
|
||||
</update>
|
||||
|
||||
@@ -15,6 +15,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="desciption" column="desciption" />
|
||||
</resultMap>
|
||||
|
||||
<delete id="deleteByTreeId">
|
||||
delete from treenodeinstance where tree_id=#{treeId}
|
||||
</delete>
|
||||
|
||||
<sql id="selectTreenodeinstanceVo">
|
||||
select id, tree_id, template_id, instance_name, is_root, precondition_templete_id, uuid,desciption from treenodeinstance
|
||||
</sql>
|
||||
|
||||
BIN
modeler/src/assets/icons/bg-node-head.png
Normal file
BIN
modeler/src/assets/icons/bg-node-head.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.6 KiB |
@@ -34,4 +34,12 @@ export const routes: RouteRecordRaw[] = [
|
||||
},
|
||||
component: () => import('@/views/decision/designer.vue'),
|
||||
},
|
||||
{
|
||||
name: 'decision-algorithm-management',
|
||||
path: '/app/decision/algorithm/management',
|
||||
meta: {
|
||||
title: '指挥决策规则库管理',
|
||||
},
|
||||
component: () => import('@/views/decision/algorithm/management.vue'),
|
||||
},
|
||||
]
|
||||
@@ -270,20 +270,68 @@
|
||||
border-radius: 0;
|
||||
min-height: calc(100vh - 90px);
|
||||
background: transparent;
|
||||
background: url('@/assets/icons/bg-fk.png') center / 100% 100%;
|
||||
|
||||
.ant-tabs .ant-tabs-ink-bar {
|
||||
position: absolute;
|
||||
background: transparent;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
||||
.ant-steps-horizontal{
|
||||
&.ant-steps.ant-steps-navigation .ant-steps-item.ant-steps-item-active::before {
|
||||
width: 30%;
|
||||
margin-left: 37%;
|
||||
display: none;
|
||||
}
|
||||
.ant-steps-item-active{
|
||||
.ant-steps-item-container{
|
||||
&:after{
|
||||
height: 1px;
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
background: #377ead;
|
||||
display: block;
|
||||
width: 100px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
.ant-card-head {
|
||||
border: 0;
|
||||
color: #11aed7;
|
||||
color: #a2b1ba;
|
||||
}
|
||||
|
||||
& > .ant-card-head {
|
||||
height: 50px;
|
||||
height: 40px;
|
||||
min-height: 40px;
|
||||
font-size: 16px;
|
||||
border-radius: 0;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
& > .ant-card-head {
|
||||
background: url('@/assets/icons/page-card-head.png') center / 100% 100%;
|
||||
background: url('@/assets/icons/bg-fk-title.png') center / 100% 100%;
|
||||
color: #eee;
|
||||
|
||||
.point {
|
||||
background: url('@/assets/icons/bg-fk-point.png') center / 100% 100%;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
display: block;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.text {
|
||||
margin-top: 5px;
|
||||
display: block;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
& > .ant-card-body {
|
||||
@@ -979,6 +1027,8 @@
|
||||
border-right: 1px solid #062850;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
background: #081229;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -986,6 +1036,7 @@
|
||||
width: 300px;
|
||||
float: left;
|
||||
margin-left: -1px;
|
||||
background: #041b36db;
|
||||
|
||||
.observation-items{
|
||||
//height: 160px;
|
||||
@@ -1319,11 +1370,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.ant-btn-default {
|
||||
background: #7dd3f9;
|
||||
border-color: #7dd3f9;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.btn-field {
|
||||
min-width: 120px;
|
||||
@@ -1484,4 +1530,115 @@
|
||||
|
||||
.ant-divider {
|
||||
border-block-start: 1px solid rgb(255 255 255 / 17%);
|
||||
}
|
||||
|
||||
|
||||
|
||||
.ant-pagination {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
width: 100%;
|
||||
|
||||
.ant-pagination-disabled .ant-pagination-item-link,
|
||||
.ant-pagination-disabled:hover .ant-pagination-item-link,
|
||||
.ant-pagination-prev .ant-pagination-item-link,
|
||||
.ant-pagination-next .ant-pagination-item-link,
|
||||
&.ant-pagination-mini .ant-pagination-total-text,
|
||||
&.ant-pagination-mini .ant-pagination-simple-pager {
|
||||
color: rgb(255 255 255 / 95%);
|
||||
}
|
||||
|
||||
&.ant-pagination-simple .ant-pagination-simple-pager input {
|
||||
background-color: transparent;
|
||||
border: 1px solid #0f4a7c;
|
||||
color: #eee;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
.ant-empty-small {
|
||||
margin-block: 8px;
|
||||
color: rgb(207 207 207 / 66%);
|
||||
}
|
||||
.ant-btn >span {
|
||||
display: inline-block;
|
||||
float: inherit;
|
||||
}
|
||||
|
||||
.ant-btn,
|
||||
.ant-input {
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.ant-btn-primary {
|
||||
color: #eee;
|
||||
//border-color: #1b5b7d;
|
||||
//background: linear-gradient(90deg, #1e4c65 0%, #0b3a62 100%);
|
||||
border-color: #29759c;
|
||||
background: linear-gradient(0deg, #0f374c 0, #154c69 100%);
|
||||
&.selected,
|
||||
&:not(:disabled):hover,
|
||||
&:hover,
|
||||
&:active {
|
||||
border-color: #166094;
|
||||
background: linear-gradient(90deg, #3687bc 0%, #074375 100%);
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.ant-btn-default {
|
||||
&.ant-btn-dangerous{
|
||||
background: transparent!important;
|
||||
}
|
||||
|
||||
color: #eee;
|
||||
//border-color: #144087;
|
||||
//background: linear-gradient(90deg, #0f4a7c 0%, #0a365b 100%);
|
||||
border-color: #1c468b;
|
||||
background: linear-gradient(0deg, #14223b 0, #1c468b 100%);
|
||||
&.selected,
|
||||
&:not(:disabled):hover,
|
||||
&:hover,
|
||||
&:active {
|
||||
color: #fff;
|
||||
border-color: #166094;
|
||||
background: linear-gradient(90deg, #3687bc 0%, #074375 100%);
|
||||
}
|
||||
&:disabled,
|
||||
&.disabled{
|
||||
color: #a9a3a3;
|
||||
&:hover{
|
||||
color:#eee;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btn-field {
|
||||
min-width: 120px;
|
||||
background: #5796b2;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
border-radius: 2px;
|
||||
color: #fff;
|
||||
padding: 5px;
|
||||
border: 1px solid #5796b2;
|
||||
|
||||
.anticon {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&.selected {
|
||||
background: #19b0ff;
|
||||
border-color: #19b0ff;
|
||||
}
|
||||
|
||||
&.gold {
|
||||
background: transparent;
|
||||
border-color: #b5b39d;
|
||||
color: #b5b39d;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
36
modeler/src/views/decision/algorithm/api.ts
Normal file
36
modeler/src/views/decision/algorithm/api.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* This file is part of the kernelstudio package.
|
||||
*
|
||||
* (c) 2014-2026 zlin <admin@kernelstudio.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
* that was distributed with this source code.
|
||||
*/
|
||||
|
||||
import { HttpRequestClient } from '@/utils/request';
|
||||
import type { Algorithm, AlgorithmPageableResponse, AlgorithmRequest } from './types';
|
||||
import type { BasicResponse } from '@/types';
|
||||
|
||||
const req = HttpRequestClient.create<BasicResponse>({
|
||||
baseURL: '/api',
|
||||
});
|
||||
|
||||
export const findAlgorithmsByQuery = (query: Partial<AlgorithmRequest> = {}): Promise<AlgorithmPageableResponse> => {
|
||||
return req.get('/algo/algorithm/list', query);
|
||||
};
|
||||
|
||||
export const createAlgorithm = (algorithm: Algorithm): Promise<BasicResponse> => {
|
||||
return req.postJson('/algo/algorithm', algorithm);
|
||||
};
|
||||
|
||||
export const runAlgorithm = (algorithm: Algorithm): Promise<BasicResponse> => {
|
||||
return req.postJson('/algo/algorithm/run', algorithm);
|
||||
};
|
||||
|
||||
export const updateAlgorithm = (algorithm: Algorithm): Promise<BasicResponse> => {
|
||||
return req.putJson('/algo/algorithm', algorithm);
|
||||
};
|
||||
|
||||
export const deleteAlgorithm = (id: number): Promise<BasicResponse> => {
|
||||
return req.delete(`/algo/algorithm/${id}`);
|
||||
};
|
||||
42
modeler/src/views/decision/algorithm/config.ts
Normal file
42
modeler/src/views/decision/algorithm/config.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* This file is part of the kernelstudio package.
|
||||
*
|
||||
* (c) 2014-2026 zlin <admin@kernelstudio.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
* that was distributed with this source code.
|
||||
*/
|
||||
|
||||
export const algorithmTypes = [
|
||||
{
|
||||
type: 'defense',
|
||||
name: '防守',
|
||||
},
|
||||
{
|
||||
type: 'offense',
|
||||
name: '进攻',
|
||||
},
|
||||
{
|
||||
type: 'formation',
|
||||
name: '队形',
|
||||
}
|
||||
]
|
||||
|
||||
export const algorithmConfigOperations = [
|
||||
{
|
||||
type: '+',
|
||||
name: '加',
|
||||
},
|
||||
{
|
||||
type: '-',
|
||||
name: '减',
|
||||
},
|
||||
{
|
||||
type: '*',
|
||||
name: '乘',
|
||||
},
|
||||
{
|
||||
type: '/',
|
||||
name: '除',
|
||||
},
|
||||
]
|
||||
513
modeler/src/views/decision/algorithm/management.vue
Normal file
513
modeler/src/views/decision/algorithm/management.vue
Normal file
@@ -0,0 +1,513 @@
|
||||
<template>
|
||||
<Layout>
|
||||
<template #sidebar>
|
||||
<div class="ks-sidebar-header">
|
||||
<a-flex class="ks-sidebar-title">
|
||||
<span class="icon"></span>
|
||||
<span class="text">指挥决策规则库管理</span>
|
||||
</a-flex>
|
||||
<a-button class="ks-sidebar-add" size="small" @click="handleCreate">
|
||||
<PlusOutlined />
|
||||
新增
|
||||
</a-button>
|
||||
</div>
|
||||
<a-list item-layout="horizontal" :data-source="algorithms" class="ks-algorithm-list">
|
||||
<template #renderItem="{ item }">
|
||||
<a-list-item @click="()=> handleSelect(item)" :class="selectedAlgorithm?.id === item.id ? 'selected' : null">
|
||||
<a-list-item-meta :description="substring(item.description,20)">
|
||||
<template #title>
|
||||
<span class="ks-algorithm-name">{{ substring(item.name, 20) }}</span>
|
||||
<span class="ks-algorithm-type"><a-badge size="small" :count="getAlgorithmTypeName(item.type)"></a-badge></span>
|
||||
</template>
|
||||
</a-list-item-meta>
|
||||
</a-list-item>
|
||||
</template>
|
||||
</a-list>
|
||||
|
||||
<a-pagination
|
||||
v-model:current="query.pageNum"
|
||||
:page-size="query.pageSize"
|
||||
:total="algorithmsTotal"
|
||||
simple size="small" @change="handleChange" />
|
||||
</template>
|
||||
|
||||
<div class="w-full h-full">
|
||||
|
||||
<a-card class="ks-page-card ks-algorithm-card">
|
||||
|
||||
<template #title>
|
||||
<a-space>
|
||||
<span class="point"></span>
|
||||
<span class="text">规则配置</span>
|
||||
</a-space>
|
||||
</template>
|
||||
|
||||
<div class="ks-scrollable" style="height: 80.5vh;overflow-y: auto;padding-right: 10px">
|
||||
|
||||
<a-row :gutter="15">
|
||||
<a-col :span="16">
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:label-col="{span: 6}"
|
||||
:model="selectedAlgorithm"
|
||||
autocomplete="off"
|
||||
layout="horizontal"
|
||||
name="basic"
|
||||
>
|
||||
|
||||
<a-form-item
|
||||
label="规则名称"
|
||||
:rules="[{ required: true, message: '请输入算法名称!', trigger: ['input', 'change'] }]"
|
||||
:name="['name']"
|
||||
>
|
||||
<a-input v-model:value="selectedAlgorithm.name" placeholder="请输入算法名称" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item
|
||||
label="业务类型"
|
||||
:rules="[{ required: true, message: '请输入业务类型!', trigger: ['input', 'change'] }]"
|
||||
:name="['type']"
|
||||
>
|
||||
<a-select placeholder="请选择业务类型" allow-clear v-model:value="selectedAlgorithm.type" @change="(v: any)=> selectedAlgorithm.type = v">
|
||||
<a-select-option v-for="a in algorithmTypes" :value="a.type">{{ a.name }}</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item
|
||||
label="算法文件路径"
|
||||
:rules="[{ required: true, message: '请输入算法文件路径!', trigger: ['input', 'change'] }]"
|
||||
:name="['codePath']"
|
||||
>
|
||||
<a-input v-model:value="selectedAlgorithm.codePath" placeholder="请输入算法文件路径" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item
|
||||
label="算法描述"
|
||||
:rules="[{ required: false, message: '请输入算法描述', trigger: ['input', 'change'] }]"
|
||||
:name="['description']"
|
||||
>
|
||||
<a-textarea v-model:value="selectedAlgorithm.description" placeholder="请输入算法描述" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item
|
||||
label="算法参数定义"
|
||||
:rules="[{ required: true, message: '请输入算法参数定义', trigger: ['input', 'change'] }]"
|
||||
:name="['algorithmParamList']"
|
||||
>
|
||||
<a-form-item-rest>
|
||||
<div class="ks-algorithm-param-list">
|
||||
<div class="ks-algorithm-param-item" v-for="(item,index) in selectedAlgorithm.algorithmParamList">
|
||||
<a-row :gutter="15">
|
||||
<a-col :span="7">
|
||||
<a-input v-model:value="item.paramName" placeholder="请输入参数名" />
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<a-input v-model:value="item.defaultValue" placeholder="请输入默认值" />
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<a-input v-model:value="item.description" placeholder="请输入描述" />
|
||||
</a-col>
|
||||
<a-col :span="3">
|
||||
<a-space class="ks-algorithm-param-actions">
|
||||
<MinusCircleOutlined @click="()=> handleMinus(index)" />
|
||||
<PlusCircleOutlined @click="handleAdd" v-if="index === 0" />
|
||||
</a-space>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</div>
|
||||
</a-form-item-rest>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item
|
||||
label="算法配置"
|
||||
:rules="[{ required: false, message: '请输入算法配置', trigger: ['input', 'change'] }]"
|
||||
:name="['algoConfig']"
|
||||
>
|
||||
<a-form-item-rest>
|
||||
<div class="ks-algorithm-param-list">
|
||||
<div class="ks-algorithm-param-item" v-for="(t,index) in selectedAlgorithm.algoConfigList">
|
||||
<a-row :gutter="15">
|
||||
<a-col :span="7">
|
||||
<a-select placeholder="请选择参数" v-model:value="t.name" @change="(v: any)=> t.name = v">
|
||||
<a-select-option :value="v.paramName" v-for="v in selectedAlgorithm.algorithmParamList">{{v.paramName}}</a-select-option>
|
||||
</a-select>
|
||||
</a-col>
|
||||
<a-col :span="7">
|
||||
<a-select placeholder="请选择操作" v-model:value="t.operation" @change="(v: any)=> t.operation = v">
|
||||
<a-select-option :value="o.type" v-for="o in algorithmConfigOperations">{{o.name}} {{o.type}}</a-select-option>
|
||||
</a-select>
|
||||
</a-col>
|
||||
<a-col :span="3">
|
||||
<a-space class="ks-algorithm-param-actions">
|
||||
<MinusCircleOutlined @click="()=> handleMinusConfig(index)" />
|
||||
<PlusCircleOutlined @click="handleAddConfig" v-if="index === 0" />
|
||||
</a-space>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</div>
|
||||
</a-form-item-rest>
|
||||
|
||||
</a-form-item>
|
||||
|
||||
|
||||
<a-form-item
|
||||
:wrapper-col="{offset: 6}"
|
||||
>
|
||||
<a-space>
|
||||
<a-button @click="handleSave" type="primary">保存规则</a-button>
|
||||
<a-popconfirm
|
||||
v-if="selectedAlgorithm && selectedAlgorithm.id > 0"
|
||||
title="确定运行?"
|
||||
@confirm="handleRun"
|
||||
>
|
||||
<a-button>运行规则</a-button>
|
||||
</a-popconfirm>
|
||||
<a-popconfirm
|
||||
v-if="selectedAlgorithm && selectedAlgorithm.id > 0"
|
||||
title="确定删除?"
|
||||
@confirm="handleDelete"
|
||||
>
|
||||
<a-button danger>删除规则</a-button>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
</a-form-item>
|
||||
|
||||
</a-form>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
</div>
|
||||
|
||||
</a-card>
|
||||
|
||||
</div>
|
||||
</Layout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { type FormInstance, message } from 'ant-design-vue';
|
||||
import { MinusCircleOutlined, PlusCircleOutlined, PlusOutlined } from '@ant-design/icons-vue';
|
||||
import Layout from '../layout.vue';
|
||||
import { createAlgorithm, deleteAlgorithm, findAlgorithmsByQuery, updateAlgorithm, runAlgorithm } from './api';
|
||||
import type { Algorithm, AlgorithmParam, AlgorithmRequest } from './types';
|
||||
import { substring } from '@/utils/strings';
|
||||
import { algorithmTypes,algorithmConfigOperations } from './config';
|
||||
import type { NullableString } from '@/types';
|
||||
|
||||
const query = ref<Partial<AlgorithmRequest>>({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
});
|
||||
|
||||
const defaultAlgorithmParam: AlgorithmParam = {
|
||||
id: 0,
|
||||
algorithmId: 0,
|
||||
// 参数名
|
||||
paramName: null,
|
||||
// 默认值
|
||||
defaultValue: null,
|
||||
// 描述
|
||||
description: null,
|
||||
};
|
||||
const defaultAlgorithm: Algorithm = {
|
||||
id: 0,
|
||||
// 算法名称
|
||||
name: null,
|
||||
// 业务类型
|
||||
type: undefined,
|
||||
// 算法文件路径
|
||||
codePath: null,
|
||||
// 算法描述
|
||||
description: null,
|
||||
// 算法配置
|
||||
algoConfig: null,
|
||||
// 算法配置
|
||||
algoConfigList: [],
|
||||
// 算法参数定义信息
|
||||
algorithmParamList: [
|
||||
{ ...defaultAlgorithmParam },
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
const resolveItem = (item: Algorithm) => {
|
||||
let newItem = JSON.parse(JSON.stringify(item))
|
||||
if (!newItem.algorithmParamList) {
|
||||
newItem.algorithmParamList = [];
|
||||
}
|
||||
if (newItem.algorithmParamList.length === 0) {
|
||||
newItem.algorithmParamList.push({ ...defaultAlgorithmParam });
|
||||
}
|
||||
|
||||
try{
|
||||
newItem.algoConfigList = JSON.parse(newItem.algoConfigList)
|
||||
} catch (e: any){
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
if(!newItem.algoConfigList){
|
||||
newItem.algoConfigList = []
|
||||
}
|
||||
|
||||
if(newItem.algoConfigList.length === 0){
|
||||
newItem.algoConfigList.push({
|
||||
name: undefined,
|
||||
operation: undefined,
|
||||
})
|
||||
}
|
||||
return newItem;
|
||||
};
|
||||
|
||||
const algorithms = ref<Algorithm[]>([]);
|
||||
const algorithmsTotal = ref<number>(0);
|
||||
const selectedAlgorithm = ref<Algorithm>(resolveItem(defaultAlgorithm));
|
||||
const formRef = ref<FormInstance | null>(null);
|
||||
|
||||
const getAlgorithmTypeName = (type: NullableString): NullableString => {
|
||||
let v = algorithmTypes.find(c => c.type === type);
|
||||
return v && v.name ? v.name : type;
|
||||
};
|
||||
|
||||
const load = () => {
|
||||
algorithms.value = [];
|
||||
algorithmsTotal.value = 0;
|
||||
formRef.value?.resetFields();
|
||||
selectedAlgorithm.value = resolveItem(defaultAlgorithm);
|
||||
|
||||
findAlgorithmsByQuery(query.value).then(r => {
|
||||
algorithms.value = r.rows ?? [];
|
||||
algorithmsTotal.value = r.total ?? 0;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
const handleCreate = () => {
|
||||
selectedAlgorithm.value = resolveItem(defaultAlgorithm);
|
||||
};
|
||||
|
||||
const handleSelect = (item: Algorithm) => {
|
||||
selectedAlgorithm.value = resolveItem(item);
|
||||
};
|
||||
|
||||
const handleAdd = () => {
|
||||
selectedAlgorithm.value.algorithmParamList.push({ ...defaultAlgorithmParam });
|
||||
};
|
||||
|
||||
const handleMinus = (index: number) => {
|
||||
const paramList = selectedAlgorithm.value.algorithmParamList;
|
||||
if (index === 0 && selectedAlgorithm.value.algorithmParamList.length === 1) {
|
||||
selectedAlgorithm.value.algorithmParamList = [{ ...defaultAlgorithmParam }];
|
||||
} else if (index >= 0 && index < paramList.length && paramList.length > 1) {
|
||||
paramList.splice(index, 1);
|
||||
selectedAlgorithm.value.algorithmParamList = [...paramList];
|
||||
}
|
||||
};
|
||||
|
||||
const handleAddConfig = () => {
|
||||
selectedAlgorithm.value.algoConfigList.push({ name: null, operation: null });
|
||||
};
|
||||
|
||||
const handleMinusConfig = (index: number) => {
|
||||
const paramList = selectedAlgorithm.value.algoConfigList;
|
||||
if (index === 0 && selectedAlgorithm.value.algoConfigList.length === 1) {
|
||||
selectedAlgorithm.value.algoConfigList = [{ name: null, operation: null }];
|
||||
} else if (index >= 0 && index < paramList.length && paramList.length > 1) {
|
||||
paramList.splice(index, 1);
|
||||
selectedAlgorithm.value.algoConfigList = [...paramList];
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = () => {
|
||||
if (selectedAlgorithm.value && selectedAlgorithm.value.id > 0) {
|
||||
deleteAlgorithm(selectedAlgorithm.value.id).then(r => {
|
||||
if (r.code === 200) {
|
||||
load();
|
||||
message.info(r.msg ?? '删除成功');
|
||||
} else {
|
||||
message.error(r.msg ?? '删除失败');
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
if (formRef.value) {
|
||||
formRef.value.validate().then(() => {
|
||||
let res = null;
|
||||
let savedValue: Algorithm = JSON.parse(JSON.stringify(selectedAlgorithm.value)) as any as Algorithm;
|
||||
let algoConfig = '';
|
||||
if( savedValue.algoConfigList ){
|
||||
savedValue.algoConfigList.forEach(o=> {
|
||||
if(o.name){
|
||||
algoConfig = algoConfig + o.name;
|
||||
}
|
||||
if(o.operation){
|
||||
algoConfig = algoConfig + o.operation;
|
||||
}
|
||||
})
|
||||
}
|
||||
savedValue.algoConfig = algoConfig;
|
||||
|
||||
if (savedValue.id > 0) {
|
||||
res = updateAlgorithm(savedValue);
|
||||
} else {
|
||||
res = createAlgorithm(savedValue);
|
||||
}
|
||||
|
||||
if (res) {
|
||||
res.then(r => {
|
||||
if (r.code === 200) {
|
||||
load();
|
||||
message.info(r.msg ?? '操作成功');
|
||||
} else {
|
||||
message.error(r.msg ?? '操作失败');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleRun = ()=> {
|
||||
if (formRef.value) {
|
||||
formRef.value.validate().then(() => {
|
||||
runAlgorithm(selectedAlgorithm.value).then(r => {
|
||||
if (r.code === 200) {
|
||||
message.info(r.msg ?? '运行成功');
|
||||
} else {
|
||||
message.error(r.msg ?? '运行失败');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const handleChange = (page: number, pageSize: number) => {
|
||||
query.value.pageNum = page;
|
||||
query.value.pageSize = pageSize;
|
||||
load();
|
||||
};
|
||||
|
||||
onMounted(() => load());
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.ks-algorithm-card {
|
||||
.ant-card-head-title {
|
||||
span.text {
|
||||
display: block;
|
||||
line-height: 30px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ks-sidebar-header {
|
||||
line-height: 40px;
|
||||
padding: 5px 15px;
|
||||
background: #081d36;
|
||||
min-height: 40px;
|
||||
background: url(@/assets/icons/card-head.png) left / 180% 100%;
|
||||
padding: 0 10px;
|
||||
|
||||
.ks-sidebar-title {
|
||||
color: #7ae8fc;
|
||||
font-size: 16px;
|
||||
.icon {
|
||||
background: url(@/assets/icons/list.png) center / 100% 100%;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
display: block;
|
||||
margin-top: 7px;
|
||||
}
|
||||
.text{
|
||||
margin-left: 40px;
|
||||
font-size: 16px;
|
||||
color: #eee;
|
||||
}
|
||||
}
|
||||
|
||||
.ks-sidebar-add {
|
||||
position: absolute;
|
||||
right: 7px;
|
||||
top: 8px;
|
||||
font-size: 12px;
|
||||
|
||||
.anticon {
|
||||
display: block;
|
||||
float: left;
|
||||
line-height: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-list {
|
||||
&.ks-algorithm-list {
|
||||
.ant-list-item {
|
||||
cursor: pointer;
|
||||
transition: all 0.5s;
|
||||
border-left: 2px solid transparent;
|
||||
position: relative;
|
||||
|
||||
&.selected,
|
||||
&:hover {
|
||||
background: #0a1b3c;
|
||||
border-left: 2px solid #11377e;
|
||||
}
|
||||
}
|
||||
|
||||
.ks-algorithm-type {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
|
||||
.ant-badge {
|
||||
.ant-badge-count {
|
||||
color: #c3c2c2;
|
||||
background: #333f7d;
|
||||
box-shadow: 0 0 0 1px #325478;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ant-list-item-meta {
|
||||
.ant-list-item-meta-title {
|
||||
color: #7ae8fc;
|
||||
}
|
||||
|
||||
.ant-list-item-meta-description {
|
||||
color: #4d8c98;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ks-algorithm-param-list {
|
||||
padding: 15px;
|
||||
border: 1px solid #475f71;
|
||||
border-radius: 2px;
|
||||
|
||||
.ks-algorithm-param-item {
|
||||
margin-bottom: 15px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
.ks-algorithm-param-actions {
|
||||
.anticon {
|
||||
color: #7ae8fc;
|
||||
font-size: 20px;
|
||||
display: block;
|
||||
line-height: 26px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
54
modeler/src/views/decision/algorithm/types.ts
Normal file
54
modeler/src/views/decision/algorithm/types.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* This file is part of the kernelstudio package.
|
||||
*
|
||||
* (c) 2014-2026 zlin <admin@kernelstudio.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
* that was distributed with this source code.
|
||||
*/
|
||||
|
||||
import type { NullableString, PageableResponse } from '@/types';
|
||||
|
||||
export interface AlgorithmParam {
|
||||
id: number,
|
||||
algorithmId: number,
|
||||
// 参数名
|
||||
paramName: NullableString,
|
||||
// 默认值
|
||||
defaultValue: NullableString,
|
||||
// 描述
|
||||
description: NullableString,
|
||||
}
|
||||
|
||||
export interface AlgorithmConfig {
|
||||
name: NullableString,
|
||||
operation: NullableString,
|
||||
}
|
||||
|
||||
export interface Algorithm {
|
||||
|
||||
id: number,
|
||||
// 算法名称
|
||||
name: NullableString,
|
||||
// 业务类型
|
||||
type: NullableString | undefined,
|
||||
// 算法文件路径
|
||||
codePath: NullableString,
|
||||
// 算法描述
|
||||
description: NullableString,
|
||||
// 算法配置
|
||||
algoConfig: NullableString,
|
||||
// 算法配置
|
||||
algoConfigList: AlgorithmConfig[],
|
||||
// 算法参数定义信息
|
||||
algorithmParamList: AlgorithmParam[]
|
||||
}
|
||||
|
||||
export interface AlgorithmRequest extends Algorithm {
|
||||
pageNum: number,
|
||||
pageSize: number,
|
||||
}
|
||||
|
||||
export interface AlgorithmPageableResponse extends PageableResponse<Algorithm> {
|
||||
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
import { HttpRequestClient } from '@/utils/request';
|
||||
import type { BehaviorTree, BehaviorTreeDetailsResponse, BehaviorTreePageResponse, NodeTemplatesResponse } from './types';
|
||||
import type { BehaviorTree, BehaviorTreeDetailsResponse, BehaviorTreePageResponse, BehaviorTreeRequest, NodeTemplatesResponse } from './types';
|
||||
import type { BasicResponse } from '@/types';
|
||||
|
||||
const req = HttpRequestClient.create<BasicResponse>({
|
||||
@@ -19,7 +19,7 @@ export const findNodeTemplates = (): Promise<NodeTemplatesResponse> => {
|
||||
return req.get('/system/nodetemplate/all');
|
||||
};
|
||||
|
||||
export const findTreesByQuery = (query: Partial<BehaviorTree> = {}): Promise<BehaviorTreePageResponse> => {
|
||||
export const findTreesByQuery = (query: Partial<BehaviorTreeRequest> = {}): Promise<BehaviorTreePageResponse> => {
|
||||
return req.get<BehaviorTreePageResponse>('/system/behaviortree/list', query);
|
||||
};
|
||||
|
||||
@@ -27,6 +27,9 @@ export const findOneTreeById = (id: number): Promise<BehaviorTreeDetailsResponse
|
||||
return req.get(`/system/behaviortree/${id}`);
|
||||
};
|
||||
|
||||
export const deleteOneTreeById = (id: number): Promise<BehaviorTreeDetailsResponse> => {
|
||||
return req.delete(`/system/behaviortree/${id}`);
|
||||
};
|
||||
|
||||
export const updateTree = (rt: Partial<BehaviorTree>): Promise<BasicResponse> => {
|
||||
return req.postJson(`/system/behaviortree`, rt);
|
||||
@@ -35,18 +38,3 @@ export const updateTree = (rt: Partial<BehaviorTree>): Promise<BasicResponse> =>
|
||||
export const createTree = (rt: Partial<BehaviorTree>): Promise<BasicResponse> => {
|
||||
return req.putJson(`/system/behaviortree`, rt);
|
||||
};
|
||||
|
||||
|
||||
// export const findOneTreeById = (id: number): Promise<TreeModelDetailsResponse> => {
|
||||
// return req.postJson(`/tree-details`,{
|
||||
// tree_id: id
|
||||
// });
|
||||
// };
|
||||
//
|
||||
// export const updateTree = (rt: Partial<TreeModelGraph>): Promise<BasicResponse> => {
|
||||
// return req.postJson('/update-tree', rt);
|
||||
// };
|
||||
//
|
||||
// export const createTree = (rt: Partial<TreeModelGraph>): Promise<BasicResponse> => {
|
||||
// return req.postJson('/update-tree', rt);
|
||||
// };
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
*/
|
||||
|
||||
import type { NullableString } from '@/types';
|
||||
import type { NodeSetting } from '@/views/decision/types';
|
||||
|
||||
export interface DraggableElement {
|
||||
id: number | null,
|
||||
@@ -19,12 +18,23 @@ export interface DraggableElement {
|
||||
draggable: boolean,
|
||||
parent?: DraggableElement,
|
||||
children: DraggableElement[]
|
||||
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export type ElementStatus = 'default' | 'success' | 'failed' | 'running' | string | null
|
||||
|
||||
export interface ElementPosition {
|
||||
export interface ElementParameter {
|
||||
id: number,
|
||||
templateId: number,
|
||||
paramKey: NullableString,
|
||||
dataType: NullableString,
|
||||
defaultValue: NullableString,
|
||||
description: NullableString,
|
||||
templateType: NullableString,
|
||||
}
|
||||
|
||||
export interface GraphPosition {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
@@ -37,57 +47,57 @@ export interface ElementVariable {
|
||||
unit: NullableString;
|
||||
}
|
||||
|
||||
export interface BaseElement {
|
||||
key: string;
|
||||
name: string;
|
||||
type: string;
|
||||
width: number;
|
||||
height: number;
|
||||
position: ElementPosition;
|
||||
category: NullableString;
|
||||
element?: DraggableElement;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface TaskNodeRect {
|
||||
export interface GraphTaskRect {
|
||||
width?: number;
|
||||
height?: number;
|
||||
x?: number;
|
||||
y?: number;
|
||||
}
|
||||
|
||||
export interface TaskNodeElement extends BaseElement {
|
||||
template: number;
|
||||
inputs: any;
|
||||
outputs: any;
|
||||
variables: ElementVariable[];
|
||||
parameters: Record<any, any>;
|
||||
|
||||
children?: TaskNodeElement[],
|
||||
export interface BaseElement {
|
||||
id: number;
|
||||
key: NullableString;
|
||||
name: NullableString;
|
||||
description: NullableString;
|
||||
type: NullableString;
|
||||
width: number;
|
||||
height: number;
|
||||
position: GraphPosition;
|
||||
category: NullableString;
|
||||
element?: DraggableElement;
|
||||
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface SettingTaskNodeElement extends TaskNodeElement {
|
||||
settings: NodeSetting[];
|
||||
export interface GraphTaskElement extends BaseElement {
|
||||
template: number;
|
||||
templateType: NullableString,
|
||||
inputs: any;
|
||||
outputs: any;
|
||||
variables: ElementVariable[];
|
||||
parameters: ElementParameter[];
|
||||
children?: GraphTaskElement[],
|
||||
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface ModelElement extends BaseElement {
|
||||
edges: EdgeNodeElement[];
|
||||
edges: GraphEdgeElement[];
|
||||
}
|
||||
|
||||
export interface EdgeNodeElement {
|
||||
export interface GraphEdgeElement {
|
||||
id: number;
|
||||
key: NullableString;
|
||||
source: NullableString;
|
||||
sourceName: NullableString;
|
||||
target: NullableString;
|
||||
targetName: NullableString;
|
||||
attrs: Record<any, any>;
|
||||
router: Record<any, any>;
|
||||
connector: any;
|
||||
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface NodeGraph {
|
||||
edges: EdgeNodeElement[];
|
||||
nodes: TaskNodeElement[];
|
||||
edges: GraphEdgeElement[];
|
||||
nodes: GraphTaskElement[];
|
||||
}
|
||||
@@ -10,7 +10,7 @@
|
||||
import { Edge, Graph, Path, Selection } from '@antv/x6';
|
||||
import type { ModelElement } from './element';
|
||||
import type { Connecting } from '@antv/x6/lib/graph/options';
|
||||
import {createLineOptions} from './line'
|
||||
import { createLineOptions } from './line';
|
||||
|
||||
Graph.registerConnector(
|
||||
'sequenceFlowConnector',
|
||||
@@ -55,7 +55,7 @@ export const createGraphConnectingAttributes = (): Partial<Connecting> => {
|
||||
attrs: lineOptions.attrs,
|
||||
animation: lineOptions.animation,
|
||||
markup: lineOptions.markup,
|
||||
})
|
||||
});
|
||||
return edge;
|
||||
},
|
||||
validateConnection(this: Graph, { sourceCell, targetCell }) {
|
||||
|
||||
@@ -13,6 +13,7 @@ import type { NodeViewPositionEventArgs } from '@antv/x6/es/view/node/type';
|
||||
import { createGraphCanvas } from './graph';
|
||||
import { EventListener } from '@/utils/event';
|
||||
import type { ModelElement } from './element';
|
||||
|
||||
// import {createLineOptions} from './line'
|
||||
|
||||
export interface UseGraphCanvas {
|
||||
@@ -155,6 +156,7 @@ export const useGraphCanvas = (readonly: boolean = false): UseGraphCanvas => {
|
||||
|
||||
if (!existingEdge) {
|
||||
sourceEdges.push({
|
||||
id: sourceData.id,
|
||||
key: edge.id,
|
||||
source: sourceNode.id,
|
||||
sourceName: sourceData.name,
|
||||
@@ -162,7 +164,7 @@ export const useGraphCanvas = (readonly: boolean = false): UseGraphCanvas => {
|
||||
router: {},
|
||||
attrs: {},
|
||||
target: targetNode.id,
|
||||
targetName: targetData.name
|
||||
targetName: targetData.name,
|
||||
});
|
||||
sourceNode.replaceData({ ...sourceData, edges: sourceEdges });
|
||||
}
|
||||
|
||||
@@ -43,5 +43,5 @@ export const createLineOptions = (): any => {
|
||||
},
|
||||
],
|
||||
],
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -9,64 +9,25 @@
|
||||
>
|
||||
<template #title>
|
||||
<a-space>
|
||||
<span class="ks-designer-node-icon"></span>
|
||||
<span class="ks-designer-node-title">{{ element?.name ?? '-' }}</span>
|
||||
</a-space>
|
||||
</template>
|
||||
|
||||
<!-- 节点内容区域 -->
|
||||
<div class="port port-in" data-port="in-0" magnet="passive"></div>
|
||||
<div class="w-full">
|
||||
<div class="ks-designer-node-content">
|
||||
<div
|
||||
v-for="(item, index) in element?.children || []"
|
||||
:key="item.id || index"
|
||||
class="ks-designer-node-row"
|
||||
>
|
||||
<div
|
||||
:data-port="`in-${item.id || index}`"
|
||||
:title="`入桩: ${item.name}`"
|
||||
class="port port-in"
|
||||
magnet="passive"
|
||||
></div>
|
||||
|
||||
<!-- child名称 -->
|
||||
<div class="ks-designer-node-name">
|
||||
{{ item.name }}
|
||||
</div>
|
||||
|
||||
<!-- 右侧出桩:只能作为连线源 -->
|
||||
<div
|
||||
:data-port="`out-${item.id || index}`"
|
||||
:title="`出桩: ${item.name}`"
|
||||
class="port port-out"
|
||||
magnet="active"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<div v-if="!(element?.children && element?.children?.length > 0)" class="ks-designer-node-row">
|
||||
<div class="port port-in" data-port="in-0" magnet="passive"></div>
|
||||
<div class="ks-designer-node-name" v-if="element?.category !== 'component'">
|
||||
{{ element?.name ?? '-' }}
|
||||
</div>
|
||||
<div class="ks-designer-node-name" v-else>
|
||||
<p>隐藏纬度: {{ element?.parameters?.hiddenLatitude ?? '-' }}</p>
|
||||
<p>激活函数: {{ element?.parameters?.activationFunction ?? '-' }}</p>
|
||||
</div>
|
||||
<div class="port port-out" data-port="out-0" magnet="active"></div>
|
||||
</div>
|
||||
</div>
|
||||
<a-tooltip v-if="(element?.description ?? (element?.name ?? '-')).length >= 38">
|
||||
<template #title>
|
||||
{{ element?.description ?? element?.name }}
|
||||
</template>
|
||||
<p>
|
||||
{{ substring(element?.description ?? (element?.name ?? '-'), 40) }}
|
||||
</p>
|
||||
</a-tooltip>
|
||||
<p v-else>
|
||||
{{ substring(element?.description ?? (element?.name ?? '-'), 40) }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- <div class="w-full" v-else>-->
|
||||
<!-- <div class="ks-designer-node-content">-->
|
||||
<!-- <div class="port port-in" data-port="in-0" magnet="passive"></div>-->
|
||||
<!-- <div class="ks-designer-node-name">-->
|
||||
<!-- <p>隐藏纬度: {{ element?.parameters?.hiddenLatitude ?? '-' }}</p>-->
|
||||
<!-- <p>激活函数: {{ element?.parameters?.activationFunction ?? '-' }}</p>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="port port-out" data-port="out-0" magnet="active"></div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<div class="port port-out" data-port="out-0" magnet="active"></div>
|
||||
</a-card>
|
||||
|
||||
<template #overlay>
|
||||
@@ -88,6 +49,7 @@ import { elementProps } from './props';
|
||||
import type { ModelElement } from './element';
|
||||
import { DeleteOutlined, SettingOutlined } from '@ant-design/icons-vue';
|
||||
import type { Graph } from '@antv/x6';
|
||||
import { substring } from '@/utils/strings';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ModelElement',
|
||||
@@ -157,6 +119,7 @@ export default defineComponent({
|
||||
|
||||
return {
|
||||
element,
|
||||
substring,
|
||||
handleMenuClick,
|
||||
handleVisibleChange,
|
||||
};
|
||||
@@ -166,23 +129,37 @@ export default defineComponent({
|
||||
|
||||
<style lang="less">
|
||||
.ks-designer-node {
|
||||
background: linear-gradient(150deg, #093866 1%, #1f69b3 55%);
|
||||
border: 0;
|
||||
background: linear-gradient(150deg, rgba(108, 99, 255) 1%, rgba(108, 99, 255) 100%);
|
||||
border-radius: 8px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
position: relative;
|
||||
background: #1e2533;
|
||||
border: 1px solid #4a7aff;
|
||||
|
||||
border: 2px solid #000000;
|
||||
|
||||
&:hover {
|
||||
border: 2px solid #4a7aff;
|
||||
box-shadow: 0 0 10px rgba(74, 122, 255, 0.3);
|
||||
}
|
||||
|
||||
.ant-card-head {
|
||||
border: 0;
|
||||
height: 38px;
|
||||
min-height: 38px;
|
||||
height: 25px;
|
||||
min-height: 25px;
|
||||
border-radius: 0;
|
||||
color: #ddd;
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
padding: 0 20px;
|
||||
background: linear-gradient(to bottom, #3a4c70, #2d3a56);
|
||||
border-top-left-radius: 8px;
|
||||
border-top-right-radius: 8px;
|
||||
background: linear-gradient(to bottom, rgba(108, 99, 255, 0.15), rgba(108, 99, 255, 0.05));
|
||||
background: url('@/assets/icons/bg-node-head.png') center / 100% 100%;
|
||||
//background: linear-gradient(to bottom, rgb(234 234 234 / 20%), rgb(191 191 191 / 58%));
|
||||
}
|
||||
|
||||
.ks-designer-node-icon {
|
||||
@@ -197,136 +174,163 @@ export default defineComponent({
|
||||
|
||||
.ks-designer-node-title {
|
||||
font-size: 13px;
|
||||
color: #fff;
|
||||
color: #312e2e;
|
||||
}
|
||||
|
||||
.ant-card-body {
|
||||
color: #fff;
|
||||
height: calc(100% - 38px);
|
||||
color: #f5f5f5;
|
||||
height: calc(100% - 25px);
|
||||
border-radius: 0;
|
||||
font-size: 12px;
|
||||
padding: 8px 15px;
|
||||
overflow-y: auto;
|
||||
border-top: 1px solid #195693;
|
||||
padding: 8px 15px !important;
|
||||
border-top: 1px solid rgba(108, 99, 255, 0.5);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
box-shadow: 0 0 10px rgba(74, 122, 255, 0.3);
|
||||
|
||||
white-space: normal; // 恢复默认的换行行为
|
||||
word-wrap: break-word; // 允许长单词换行
|
||||
word-break: break-all; // 允许在任意字符处换行
|
||||
line-height: 1.4; // 增加行高提升可读性
|
||||
box-shadow: 0 0 10px rgba(74, 122, 255, 0.3);
|
||||
}
|
||||
|
||||
&.ks-designer-task-node {
|
||||
background: linear-gradient(150deg, #20421b 1%, #4a6646 55%);
|
||||
|
||||
.ant-card-body {
|
||||
border-top: 1px solid #466741;
|
||||
}
|
||||
|
||||
.ks-designer-node-icon {
|
||||
background: url('@/assets/icons/m-02.png') center / 100% 100%;
|
||||
}
|
||||
}
|
||||
&.ks-designer-input-node {
|
||||
background: linear-gradient(150deg, #083058 1%, #1e5d9b 55%);
|
||||
|
||||
.ant-card-body {
|
||||
border-top: 1px solid #105ca7;
|
||||
}
|
||||
|
||||
.ks-designer-node-icon {
|
||||
background: url('@/assets/icons/icon-model-input.png') center / 100% 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&.ks-designer-action-node {
|
||||
background: linear-gradient(150deg, #343207 1%, #485010 55%);
|
||||
|
||||
.ant-card-body {
|
||||
border-top: 1px solid #59550e;
|
||||
}
|
||||
|
||||
.ks-designer-node-icon {
|
||||
background: url('@/assets/icons/bg-fk-point.png') center / 100% 100%;
|
||||
}
|
||||
}
|
||||
&.ks-designer-component-node {
|
||||
background: linear-gradient(150deg, #06226b 1%, #1a43a7 55%);
|
||||
|
||||
.ant-card-body {
|
||||
border-top: 1px solid #26448c;
|
||||
}
|
||||
}
|
||||
|
||||
&.ks-designer-control-node {
|
||||
background: linear-gradient(150deg, #1d4f32 1%, #326a5d 55%);
|
||||
|
||||
.ant-card-body {
|
||||
border-top: 1px solid #326a5d;
|
||||
}
|
||||
|
||||
.ks-designer-node-icon {
|
||||
background: url('@/assets/icons/bg-model-builder-card-title.png') center / 100% 100%;
|
||||
}
|
||||
}
|
||||
// model/task 节点:浅紫渐变
|
||||
//&.ks-designer-model-node,
|
||||
//&.ks-designer-task-node {
|
||||
// background: linear-gradient(150deg, rgba(92,84,247,0.9) 1%, rgba(115,108,250,0.7) 55%);
|
||||
//
|
||||
// .ant-card-body {
|
||||
// border-top: 1px solid rgba(92,84,247,0.5);
|
||||
// }
|
||||
//
|
||||
// .ks-designer-node-icon {
|
||||
// background: url('@/assets/icons/m-02.png') center / 100% 100%;
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//// input 节点:深紫渐变
|
||||
//&.ks-designer-input-node {
|
||||
// background: linear-gradient(150deg, rgba(82,73,245,0.9) 1%, rgba(105,98,249,0.7) 55%);
|
||||
//
|
||||
// .ant-card-body {
|
||||
// border-top: 1px solid rgba(82,73,245,0.5);
|
||||
// }
|
||||
//
|
||||
// .ks-designer-node-icon {
|
||||
// background: url('@/assets/icons/icon-model-input.png') center / 100% 100%;
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//// action 节点:亮紫渐变(对应之前的action-node)
|
||||
//&.ks-designer-action-node {
|
||||
// background: linear-gradient(150deg, rgba(108,99,255,0.9) 1%, rgba(140,133,255,0.7) 55%);
|
||||
//
|
||||
// .ant-card-body {
|
||||
// border-top: 1px solid rgba(108,99,255,0.5);
|
||||
// }
|
||||
//
|
||||
// .ks-designer-node-icon {
|
||||
// background: url('@/assets/icons/bg-fk-point.png') center / 100% 100%;
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//// precondition/component 节点:蓝紫渐变
|
||||
//&.ks-designer-precondition-node,
|
||||
//&.ks-designer-component-node {
|
||||
// background: linear-gradient(150deg, rgba(72,64,243,0.9) 1%, rgba(95,88,248,0.7) 55%);
|
||||
//
|
||||
// .ant-card-body {
|
||||
// border-top: 1px solid rgba(72,64,243,0.5);
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//// select/control 节点:浅蓝紫渐变
|
||||
//&.ks-designer-select-node,
|
||||
//&.ks-designer-control-node {
|
||||
// background: linear-gradient(150deg, rgba(90,82,246,0.9) 1%, rgba(118,111,251,0.7) 55%);
|
||||
//
|
||||
// .ant-card-body {
|
||||
// border-top: 1px solid rgba(90,82,246,0.5);
|
||||
// }
|
||||
//
|
||||
// .ks-designer-node-icon {
|
||||
// background: url('@/assets/icons/bg-model-builder-card-title.png') center / 100% 100%;
|
||||
// }
|
||||
//}
|
||||
|
||||
// 连接桩容器样式
|
||||
.ks-designer-node-content {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px; // 每个child行之间的间距
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
// 每个child行(包含左右桩+文本)
|
||||
.ks-designer-node-row {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
min-height: 24px; // 固定行高,保证桩对齐
|
||||
min-height: 24px;
|
||||
}
|
||||
|
||||
// 连接桩基础样式
|
||||
.port {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
cursor: crosshair;
|
||||
flex-shrink: 0;
|
||||
box-shadow: 0 0 0 2px rgb(74 114 214 / 80%);
|
||||
z-index: 10; // 确保桩在最上层
|
||||
// X6 标记为可连线的磁体
|
||||
box-shadow: 0 0 0 2px rgb(108, 99, 255, 0.8);
|
||||
z-index: 10;
|
||||
magnet: true;
|
||||
}
|
||||
|
||||
// 左侧入桩样式
|
||||
.port-in {
|
||||
background-color: #093866; // 青色:入桩
|
||||
margin-right: 8px; // 与文本的间距
|
||||
background-color: #6C63FF;
|
||||
margin-right: 8px;
|
||||
//border: 1px solid #093866;
|
||||
// X6 只能作为连线目标(入)
|
||||
magnet: passive;
|
||||
box-shadow: none;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
display: block;
|
||||
background: url('@/assets/icons/point.svg') center / 100% 100%;
|
||||
|
||||
position: absolute;
|
||||
//top: 7px;
|
||||
left: -8px;
|
||||
top: 50%;
|
||||
}
|
||||
|
||||
// 右侧出桩样式
|
||||
.port-out {
|
||||
margin-left: 8px; // 与文本的间距
|
||||
margin-left: 8px;
|
||||
margin-right: 5px;
|
||||
// X6 只能作为连线源(出)
|
||||
magnet: active;
|
||||
box-shadow: none;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
display: block;
|
||||
background: url('@/assets/icons/arrow-right.svg') center / 100% 100%;
|
||||
background: url('@/assets/icons/point.svg') center / 100% 100%;
|
||||
|
||||
position: absolute;
|
||||
//right: 8px;
|
||||
//top: 7px;
|
||||
top: 50%;
|
||||
right: -12px;
|
||||
|
||||
}
|
||||
|
||||
// 节点文本样式
|
||||
.ks-designer-node-name {
|
||||
flex: 1; // 占满中间空间
|
||||
flex: 1;
|
||||
line-height: 24px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
//white-space: nowrap;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -21,7 +21,7 @@ import ModelElement from './node.vue';
|
||||
export const registerNodeElement = () => {
|
||||
console.info('registerNodeElement');
|
||||
register({
|
||||
shape: 'node',
|
||||
shape: 'task',
|
||||
component: ModelElement,
|
||||
width: 120,
|
||||
attrs: {
|
||||
|
||||
@@ -6,20 +6,20 @@
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
* that was distributed with this source code.
|
||||
*/
|
||||
import type { EdgeNodeElement, NodeGraph, TaskNodeElement } from './element';
|
||||
import type { GraphEdgeElement, GraphTaskElement, NodeGraph } from './element';
|
||||
import { Edge, Graph, Node } from '@antv/x6';
|
||||
|
||||
export const defaultHeight: Record<string, number> = {
|
||||
component: 110,
|
||||
};
|
||||
|
||||
export const createModelNode = (element: TaskNodeElement, width: number = 250, height: number = 120): any => {
|
||||
export const createGraphTaskElement = (element: GraphTaskElement, width: number = 250, height: number = 120): any => {
|
||||
let realHeight = defaultHeight[element.category as string];
|
||||
if (!realHeight) {
|
||||
realHeight = 120;
|
||||
}
|
||||
return {
|
||||
shape: 'node',
|
||||
shape: 'task',
|
||||
id: element.key,
|
||||
position: {
|
||||
x: element.position?.x || 0,
|
||||
@@ -38,13 +38,13 @@ export const createModelNode = (element: TaskNodeElement, width: number = 250, h
|
||||
};
|
||||
};
|
||||
|
||||
export const resolveNodeTaskElements = (graph: Graph): TaskNodeElement[] => {
|
||||
const taskElements: TaskNodeElement[] = [];
|
||||
export const resolveGraphTaskElements = (graph: Graph): GraphTaskElement[] => {
|
||||
const taskElements: GraphTaskElement[] = [];
|
||||
if (graph) {
|
||||
const nodes = graph?.getNodes() as Node[];
|
||||
if (nodes) {
|
||||
nodes.forEach(node => {
|
||||
const nodeData = node.getData() as TaskNodeElement;
|
||||
const nodeData = node.getData() as GraphTaskElement;
|
||||
const newElement = {
|
||||
...nodeData,
|
||||
key: node.id,
|
||||
@@ -59,13 +59,13 @@ export const resolveNodeTaskElements = (graph: Graph): TaskNodeElement[] => {
|
||||
return taskElements;
|
||||
};
|
||||
|
||||
export const resolveNodeEdgeElements = (graph: Graph): EdgeNodeElement[] => {
|
||||
const edgeElements: EdgeNodeElement[] = [];
|
||||
export const resolveGraphEdgeElements = (graph: Graph): GraphEdgeElement[] => {
|
||||
const edgeElements: GraphEdgeElement[] = [];
|
||||
if (graph) {
|
||||
const graphEdges = graph?.getEdges() ?? [] as Edge[];
|
||||
if (graphEdges) {
|
||||
graphEdges.forEach(edge => {
|
||||
const nodeData = edge.getData() as TaskNodeElement;
|
||||
const nodeData = edge.getData() as GraphTaskElement;
|
||||
edgeElements.push({
|
||||
id: nodeData?.id ?? 0,
|
||||
key: edge.id,
|
||||
@@ -84,8 +84,8 @@ export const resolveNodeEdgeElements = (graph: Graph): EdgeNodeElement[] => {
|
||||
};
|
||||
|
||||
export const resolveNodeGraph = (graph: Graph): NodeGraph => {
|
||||
const nodes: TaskNodeElement[] = resolveNodeTaskElements(graph);
|
||||
const edges: EdgeNodeElement[] = resolveNodeEdgeElements(graph);
|
||||
const nodes: GraphTaskElement[] = resolveGraphTaskElements(graph);
|
||||
const edges: GraphEdgeElement[] = resolveGraphEdgeElements(graph);
|
||||
return {
|
||||
nodes,
|
||||
edges,
|
||||
@@ -94,7 +94,7 @@ export const resolveNodeGraph = (graph: Graph): NodeGraph => {
|
||||
|
||||
export const hasElements = (graph: Graph): boolean => {
|
||||
if (graph) {
|
||||
const taskElements: TaskNodeElement[] = resolveNodeTaskElements(graph);
|
||||
const taskElements: GraphTaskElement[] = resolveGraphTaskElements(graph);
|
||||
return taskElements.length > 0;
|
||||
}
|
||||
return false;
|
||||
@@ -102,7 +102,7 @@ export const hasElements = (graph: Graph): boolean => {
|
||||
|
||||
export const hasRootElementNode = (graph: Graph): boolean => {
|
||||
if (graph) {
|
||||
const taskElements: TaskNodeElement[] = resolveNodeTaskElements(graph);
|
||||
const taskElements: GraphTaskElement[] = resolveGraphTaskElements(graph);
|
||||
return taskElements.filter(e => e.type === 'root').length === 1;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
<div class="ks-model-builder-body">
|
||||
<div class="ks-model-builder-left">
|
||||
<TressCard
|
||||
ref="treesCardRef"
|
||||
@create-tree="handleCreateTree"
|
||||
@select-tree="handleSelectTree"
|
||||
/>
|
||||
<NodesCard
|
||||
@@ -17,20 +19,24 @@
|
||||
<div class="ks-model-builder-content">
|
||||
<div class="ks-model-builder-actions">
|
||||
<a-space>
|
||||
<a-tooltip v-if="graph && currentBehaviorTree" placement="top">
|
||||
<template #title>
|
||||
保存
|
||||
</template>
|
||||
<a-popconfirm
|
||||
title="确定保存?"
|
||||
@confirm="handleSave"
|
||||
>
|
||||
<a-button class="ks-model-builder-save" size="small">
|
||||
<CheckOutlined />
|
||||
<span>保存</span>
|
||||
</a-button>
|
||||
</a-popconfirm>
|
||||
</a-tooltip>
|
||||
<!-- <a-tooltip v-if="graph && currentBehaviorTree" placement="top">-->
|
||||
<!-- <template #title>-->
|
||||
<!-- 保存-->
|
||||
<!-- </template>-->
|
||||
<!-- <a-popconfirm-->
|
||||
<!-- title="确定保存?"-->
|
||||
<!-- @confirm="handleSave"-->
|
||||
<!-- >-->
|
||||
<!-- <a-button class="ks-model-builder-save" size="small">-->
|
||||
<!-- <CheckOutlined />-->
|
||||
<!-- <span>保存</span>-->
|
||||
<!-- </a-button>-->
|
||||
<!-- </a-popconfirm>-->
|
||||
<!-- </a-tooltip>-->
|
||||
<a-button v-if="graph && currentBehaviorTree" class="ks-model-builder-save" size="small" @click="handleSave">
|
||||
<CheckOutlined />
|
||||
<span>保存</span>
|
||||
</a-button>
|
||||
</a-space>
|
||||
</div>
|
||||
<!-- 画布容器,添加拖放事件 -->
|
||||
@@ -46,10 +52,12 @@
|
||||
</div>
|
||||
<Properties
|
||||
v-if="graph"
|
||||
@update-element="handleUpdateElement"
|
||||
:element="selectedNodeTaskElement"
|
||||
:graph="graph as any"
|
||||
:node="selectedModelNode as any" />
|
||||
:node="selectedModelNode as any"
|
||||
:tree="currentBehaviorTree"
|
||||
:tree-editing="currentTreeEditing"
|
||||
@update-element="handleUpdateElement" />
|
||||
</div>
|
||||
</a-layout>
|
||||
</a-layout>
|
||||
@@ -66,16 +74,16 @@ import { Wrapper } from '@/components/wrapper';
|
||||
import { safePreventDefault, safeStopPropagation } from '@/utils/event';
|
||||
import Header from './header.vue';
|
||||
import Properties from './properties.vue';
|
||||
import type { BehaviorTree, NodeTemplate } from './types';
|
||||
import type { GraphTaskElement, NodeGraph } from './builder/element';
|
||||
import { useGraphCanvas } from './builder/hooks';
|
||||
import { registerNodeElement } from './builder/register';
|
||||
import type { BehaviorTree, NodeTemplate } from './types';
|
||||
import type { NodeGraph, SettingTaskNodeElement, TaskNodeElement } from './builder/element';
|
||||
import { createLineOptions } from './builder/line';
|
||||
import { createTree, findOneTreeById, updateTree } from './api';
|
||||
import { createModelNode, hasElements, hasRootElementNode, resolveNodeGraph } from './builder/utils';
|
||||
import { createTaskNodeElementFromTemplate } from './utils/node';
|
||||
import { createGraphTaskElement, hasElements, hasRootElementNode, resolveNodeGraph } from './builder/utils';
|
||||
import { createGraphTaskElementFromTemplate } from './utils/node';
|
||||
import TressCard from './trees-card.vue';
|
||||
import NodesCard from './nodes-card.vue';
|
||||
import { createLineOptions } from '@/views/decision/builder/line.ts';
|
||||
|
||||
const TeleportContainer = defineComponent(getTeleport());
|
||||
|
||||
@@ -100,11 +108,13 @@ export default defineComponent({
|
||||
const currentZoom = ref<number>(1);
|
||||
const draggedNodeData = ref<NodeTemplate | null>(null);
|
||||
const isDraggingOver = ref(false);
|
||||
const currentTreeEditing = ref<boolean>(false);
|
||||
const currentBehaviorTree = ref<BehaviorTree | null>(null);
|
||||
const currentNodeGraph = ref<NodeGraph | null>(null);
|
||||
const selectedModelNode = ref<Node<NodeProperties> | null>(null);
|
||||
const selectedNodeTaskElement = ref<SettingTaskNodeElement | null>(null);
|
||||
const changed = ref<boolean>(false)
|
||||
const selectedNodeTaskElement = ref<GraphTaskElement | null>(null);
|
||||
const changed = ref<boolean>(false);
|
||||
const treesCardRef = ref<InstanceType<typeof TressCard> | null>(null);
|
||||
|
||||
const {
|
||||
handleGraphEvent,
|
||||
@@ -154,6 +164,7 @@ export default defineComponent({
|
||||
safePreventDefault(e);
|
||||
safeStopPropagation(e);
|
||||
isDraggingOver.value = false;
|
||||
currentTreeEditing.value = false;
|
||||
|
||||
if (!currentBehaviorTree.value) {
|
||||
message.error('请先选择或者创建行为树.');
|
||||
@@ -188,9 +199,9 @@ export default defineComponent({
|
||||
console.log('放置节点:', { ...template, x, y });
|
||||
|
||||
// 创建节点数据
|
||||
const settingTaskElement: SettingTaskNodeElement = createTaskNodeElementFromTemplate(template, { x, y });
|
||||
const settingTaskElement: GraphTaskElement = createGraphTaskElementFromTemplate(template, { x, y });
|
||||
// 创建节点
|
||||
const settingTaskNode = createModelNode(settingTaskElement);
|
||||
const settingTaskNode = createGraphTaskElement(settingTaskElement);
|
||||
console.info('create settingTaskNode: ', settingTaskElement, settingTaskNode);
|
||||
|
||||
// 将节点添加到画布
|
||||
@@ -224,6 +235,7 @@ export default defineComponent({
|
||||
...r.data,
|
||||
graph: nodeGraph,
|
||||
};
|
||||
currentTreeEditing.value = true;
|
||||
createElements();
|
||||
} else {
|
||||
message.error(r.msg ?? '行为树不存在.');
|
||||
@@ -233,31 +245,61 @@ export default defineComponent({
|
||||
|
||||
const createElements = () => {
|
||||
nextTick(() => {
|
||||
graph.value?.clearCells();
|
||||
if (currentBehaviorTree.value?.graph && graph.value) {
|
||||
if (currentBehaviorTree.value?.graph.nodes) {
|
||||
currentBehaviorTree.value?.graph.nodes.forEach(ele => {
|
||||
const node = createModelNode(ele as TaskNodeElement);
|
||||
console.info('create node: ', ele);
|
||||
// 将节点添加到画布
|
||||
graph.value?.addNode(node as Node);
|
||||
});
|
||||
}
|
||||
if (currentBehaviorTree.value?.graph.edges) {
|
||||
// 然后添加所有边,确保包含桩点信息
|
||||
setTimeout(() => {
|
||||
currentBehaviorTree.value?.graph.edges.forEach(edgeData => {
|
||||
graph.value?.addEdge({
|
||||
...edgeData,
|
||||
...createLineOptions(),
|
||||
});
|
||||
});
|
||||
}, 100); // 延迟一会儿,免得连线错位
|
||||
}
|
||||
try {
|
||||
graph.value?.clearCells();
|
||||
} catch (e: any) {
|
||||
console.error('clear cells error, cause:', e);
|
||||
}
|
||||
setTimeout(() => {
|
||||
if (currentBehaviorTree.value?.graph && graph.value) {
|
||||
if (currentBehaviorTree.value?.graph.nodes) {
|
||||
currentBehaviorTree.value?.graph.nodes.forEach(ele => {
|
||||
const node = createGraphTaskElement(ele as GraphTaskElement);
|
||||
console.info('create node: ', ele);
|
||||
// 将节点添加到画布
|
||||
graph.value?.addNode(node as Node);
|
||||
});
|
||||
}
|
||||
if (currentBehaviorTree.value?.graph.edges) {
|
||||
// 然后添加所有边,确保包含桩点信息
|
||||
setTimeout(() => {
|
||||
currentBehaviorTree.value?.graph.edges.forEach(edgeData => {
|
||||
graph.value?.addEdge({
|
||||
...edgeData,
|
||||
...createLineOptions(),
|
||||
});
|
||||
});
|
||||
}, 100); // 延迟一会儿,免得连线错位
|
||||
}
|
||||
}
|
||||
}, 100);
|
||||
});
|
||||
};
|
||||
|
||||
const handleCreateTree = () => {
|
||||
currentBehaviorTree.value = {
|
||||
id: 0,
|
||||
name: '行为树',
|
||||
description: null,
|
||||
englishName: null,
|
||||
xmlContent: null,
|
||||
createdAt: null,
|
||||
graph: {
|
||||
edges: [],
|
||||
nodes: [],
|
||||
},
|
||||
updatedAt: null,
|
||||
};
|
||||
currentNodeGraph.value = {
|
||||
edges: [],
|
||||
nodes: [],
|
||||
};
|
||||
selectedModelNode.value = null;
|
||||
selectedNodeTaskElement.value = null;
|
||||
|
||||
createElements();
|
||||
};
|
||||
|
||||
// 初始化X6画布
|
||||
const initGraph = () => {
|
||||
if (!canvas.value) {
|
||||
@@ -275,12 +317,18 @@ export default defineComponent({
|
||||
currentZoom.value = sx;
|
||||
});
|
||||
|
||||
handleGraphEvent('blank:click', () => {
|
||||
selectedModelNode.value = null;
|
||||
selectedNodeTaskElement.value = null;
|
||||
currentTreeEditing.value = null !== currentBehaviorTree.value;
|
||||
});
|
||||
|
||||
handleGraphEvent('node:click', (args: any) => {
|
||||
const node = args.node as Node<NodeProperties>;
|
||||
const newElement = node.getData() as SettingTaskNodeElement;
|
||||
const newElement = node.getData() as GraphTaskElement;
|
||||
|
||||
selectedModelNode.value = node;
|
||||
selectedNodeTaskElement.value = JSON.parse(JSON.stringify(newElement || {})) as SettingTaskNodeElement;
|
||||
selectedNodeTaskElement.value = JSON.parse(JSON.stringify(newElement || {})) as GraphTaskElement;
|
||||
});
|
||||
|
||||
// 监听节点鼠标事件,显示/隐藏连接点
|
||||
@@ -310,7 +358,7 @@ export default defineComponent({
|
||||
});
|
||||
};
|
||||
|
||||
const handleUpdateElement = (element: SettingTaskNodeElement) => {
|
||||
const handleUpdateElement = (element: GraphTaskElement) => {
|
||||
// 更新选中的节点数据
|
||||
if (selectedModelNode.value) {
|
||||
selectedModelNode.value.replaceData(element);
|
||||
@@ -318,7 +366,7 @@ export default defineComponent({
|
||||
console.info('handleUpdateElement', element);
|
||||
// 更新本地引用
|
||||
selectedNodeTaskElement.value = element;
|
||||
changed.value = true
|
||||
changed.value = true;
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
@@ -333,6 +381,14 @@ export default defineComponent({
|
||||
graph: graphData,
|
||||
xmlContent: JSON.stringify(graphData),
|
||||
};
|
||||
if (!newTree.name) {
|
||||
message.error('行为树名称不能为空.');
|
||||
return;
|
||||
}
|
||||
if (!newTree.englishName) {
|
||||
message.error('行为树英文名称不能为空.');
|
||||
return;
|
||||
}
|
||||
let res = null;
|
||||
if (currentBehaviorTree.value.id > 0) {
|
||||
res = createTree(newTree);
|
||||
@@ -341,6 +397,7 @@ export default defineComponent({
|
||||
}
|
||||
res.then(r => {
|
||||
if (r.code === 200) {
|
||||
treesCardRef.value?.refresh();
|
||||
message.success(r.msg ?? '操作成功.');
|
||||
} else {
|
||||
message.error(r.msg ?? '操作失败.');
|
||||
@@ -368,6 +425,9 @@ export default defineComponent({
|
||||
});
|
||||
|
||||
return {
|
||||
treesCardRef,
|
||||
handleCreateTree,
|
||||
currentTreeEditing,
|
||||
currentBehaviorTree,
|
||||
currentNodeGraph,
|
||||
selectedNodeTaskElement,
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
</template>
|
||||
<div class="w-full h-full">
|
||||
<a-row>
|
||||
<a-col :span="12" v-for="nm in controlTemplates">
|
||||
<a-col v-for="nm in controlTemplates" :span="12">
|
||||
<div
|
||||
:key="nm.id"
|
||||
:data-type="nm.type"
|
||||
@@ -15,7 +15,7 @@
|
||||
@dragend="handleDragEnd"
|
||||
@dragstart="handleDragStart($event, nm)"
|
||||
>
|
||||
<img class="icon" src="@/assets/icons/model-4.svg" :alt="nm.name ?? ''" />
|
||||
<img :alt="nm.name ?? ''" class="icon" src="@/assets/icons/model-4.svg" />
|
||||
<span class="desc">{{ nm.name }}</span>
|
||||
</div>
|
||||
</a-col>
|
||||
@@ -28,7 +28,7 @@
|
||||
</template>
|
||||
<div class="w-full h-full">
|
||||
<a-row>
|
||||
<a-col :span="12" v-for="nm in conditionTemplates">
|
||||
<a-col v-for="nm in conditionTemplates" :span="12">
|
||||
<div
|
||||
:key="nm.id"
|
||||
:data-type="nm.type"
|
||||
@@ -36,7 +36,7 @@
|
||||
@dragend="handleDragEnd"
|
||||
@dragstart="handleDragStart($event, nm)"
|
||||
>
|
||||
<img class="icon" src="@/assets/icons/model-4.svg" :alt="nm.name ?? ''" />
|
||||
<img :alt="nm.name ?? ''" class="icon" src="@/assets/icons/model-4.svg" />
|
||||
<span class="desc">{{ nm.name }}</span>
|
||||
</div>
|
||||
</a-col>
|
||||
@@ -49,7 +49,7 @@
|
||||
</template>
|
||||
<div class="w-full h-full">
|
||||
<a-row>
|
||||
<a-col :span="12" v-for="nm in actionsTemplates">
|
||||
<a-col v-for="nm in actionsTemplates" :span="12">
|
||||
<div
|
||||
:key="nm.id"
|
||||
:data-type="nm.type"
|
||||
@@ -57,7 +57,7 @@
|
||||
@dragend="handleDragEnd"
|
||||
@dragstart="handleDragStart($event, nm)"
|
||||
>
|
||||
<img class="icon" src="@/assets/icons/model-4.svg" :alt="nm.name ?? ''" />
|
||||
<img :alt="nm.name ?? ''" class="icon" src="@/assets/icons/model-4.svg" />
|
||||
<span class="desc">{{ nm.name }}</span>
|
||||
</div>
|
||||
</a-col>
|
||||
@@ -161,6 +161,6 @@ export default defineComponent({
|
||||
handleDragEnd,
|
||||
};
|
||||
},
|
||||
})
|
||||
});
|
||||
|
||||
</script>
|
||||
@@ -1,12 +1,35 @@
|
||||
<template>
|
||||
<div class="ks-model-builder-right">
|
||||
<template v-if="currentElement">
|
||||
<template v-if="currentElement || tree">
|
||||
|
||||
<a-tabs v-model:activeKey="activeTopTabsKey" class="ks-model-builder-tabs settings-tab">
|
||||
<template #leftExtra>
|
||||
<span class="ks-model-builder-title-icon icon-input"></span>
|
||||
</template>
|
||||
<a-tab-pane key="1" tab="节点属性">
|
||||
|
||||
<a-tab-pane v-if="tree" key="1" tab="行为树属性">
|
||||
<a-form
|
||||
autocomplete="off"
|
||||
layout="vertical"
|
||||
name="basic"
|
||||
style="padding-bottom:15px;"
|
||||
>
|
||||
<a-form-item label="行为树名称">
|
||||
<a-input v-model:value="tree.name" placeholder="行为树名称" size="small" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="行为树英文名称">
|
||||
<a-input v-model:value="tree.englishName" placeholder="行为树英文名称" size="small" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="行为树说明">
|
||||
<a-textarea v-model:value="tree.description" placeholder="行为树说明" size="small" />
|
||||
</a-form-item>
|
||||
|
||||
</a-form>
|
||||
</a-tab-pane>
|
||||
|
||||
<a-tab-pane v-if="currentElement" key="2" tab="节点属性">
|
||||
<a-form
|
||||
autocomplete="off"
|
||||
layout="vertical"
|
||||
@@ -14,41 +37,35 @@
|
||||
style="padding-bottom:15px;"
|
||||
>
|
||||
<a-form-item label="节点名称">
|
||||
<a-input size="small" v-model:value="currentElement.name" :placeholder="currentElement.name" />
|
||||
<a-input v-model:value="currentElement.name" :placeholder="currentElement.name" size="small" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="节点介绍">
|
||||
<a-textarea size="small" v-model:value="currentElement.description" :placeholder="currentElement.description" />
|
||||
<a-textarea v-model:value="currentElement.description" :placeholder="currentElement.description" size="small" />
|
||||
</a-form-item>
|
||||
|
||||
<a-divider />
|
||||
|
||||
<a-form-item label="输入">
|
||||
<a-textarea size="small" v-model:value="currentElement.inputs" />
|
||||
<a-textarea v-model:value="currentElement.inputs" size="small" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="输出">
|
||||
<a-textarea size="small" v-model:value="currentElement.outputs" />
|
||||
<a-textarea v-model:value="currentElement.outputs" size="small" />
|
||||
</a-form-item>
|
||||
|
||||
<a-divider v-if="currentElement.settings && currentElement.settings.length > 0"/>
|
||||
<a-divider v-if="currentElement.settings && currentElement.parameters.length > 0" />
|
||||
|
||||
<a-form-item :label="setting.description" v-for="setting in currentElement.settings">
|
||||
<a-input-number size="small" style="width:100%;" v-if="setting.data_type === 'double'" v-model:value="setting.default_value" :placeholder="setting.description" />
|
||||
<a-input v-else size="small" v-model:value="setting.default_value" :placeholder="setting.description" />
|
||||
<a-form-item v-for="setting in currentElement.parameters" :label="setting.description">
|
||||
<a-input-number v-if="setting.dataType === 'double'" v-model:value="setting.defaultValue" :placeholder="setting.description" size="small" style="width:100%;" />
|
||||
<a-input v-else v-model:value="setting.defaultValue" :placeholder="setting.description" size="small" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-tab-pane>
|
||||
|
||||
<!-- <a-tab-pane key="2" tab="外观">-->
|
||||
|
||||
<!-- </a-tab-pane>-->
|
||||
<!-- <a-tab-pane key="3" tab="系统">-->
|
||||
|
||||
<!-- </a-tab-pane>-->
|
||||
</a-tabs>
|
||||
|
||||
<a-tabs v-model:activeKey="activeBottomTabsKey" class="ks-model-builder-tabs parameters-tabs">
|
||||
<a-tabs v-if="currentElement" v-model:activeKey="activeBottomTabsKey" class="ks-model-builder-tabs parameters-tabs">
|
||||
<template #leftExtra>
|
||||
<span class="ks-model-builder-title-icon icon-input"></span>
|
||||
</template>
|
||||
@@ -83,7 +100,7 @@
|
||||
</a-button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-input v-model:value="record[column.dataIndex]" size="small"/>
|
||||
<a-input v-model:value="record[column.dataIndex]" size="small" />
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
@@ -111,9 +128,10 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent, onMounted, type PropType, ref, watch } from 'vue';
|
||||
import { CheckOutlined } from '@ant-design/icons-vue';
|
||||
import type { ElementVariable, SettingTaskNodeElement } from './builder/element';
|
||||
import type { ElementVariable, GraphTaskElement } from './builder/element';
|
||||
import type { BehaviorTree } from './types';
|
||||
import type { Graph, Node, NodeProperties } from '@antv/x6';
|
||||
import {generateKey} from '@/utils/strings'
|
||||
import { generateKey } from '@/utils/strings';
|
||||
|
||||
const actionSpaceColumns = [
|
||||
{ title: '序号', dataIndex: 'index', key: 'index', width: 40 },
|
||||
@@ -126,17 +144,21 @@ const actionSpaceColumns = [
|
||||
export default defineComponent({
|
||||
components: { CheckOutlined },
|
||||
props: {
|
||||
tree: { type: [Object, null] as PropType<BehaviorTree | null | undefined>, required: false },
|
||||
treeEditing: { type: Boolean as PropType<boolean>, required: true, default: false },
|
||||
node: { type: [Object, null] as PropType<Node<NodeProperties> | null | undefined>, required: false },
|
||||
graph: { type: [Object, null] as PropType<Graph | null | undefined>, required: true }
|
||||
graph: { type: [Object, null] as PropType<Graph | null | undefined>, required: true },
|
||||
},
|
||||
emits: ['update-element'],
|
||||
emits: ['update-element', 'update-tree'],
|
||||
setup(props, { emit }) {
|
||||
const activeTopTabsKey = ref<string>('1');
|
||||
const activeBottomTabsKey = ref<string>('1');
|
||||
const activeBottomTabs2Key = ref<string>('1');
|
||||
|
||||
const currentTree = ref<BehaviorTree | null>(props.tree ?? null);
|
||||
const treeEditing = ref<boolean>(props.treeEditing);
|
||||
const currentNode = ref<Node | null>(props.node ?? null);
|
||||
const currentElement = ref<SettingTaskNodeElement | null>(null);
|
||||
const currentElement = ref<GraphTaskElement | null>(null);
|
||||
|
||||
const load = () => {
|
||||
};
|
||||
@@ -145,13 +167,13 @@ export default defineComponent({
|
||||
currentNode.value = n ?? null;
|
||||
if (n) {
|
||||
const data = n.getData();
|
||||
currentElement.value = JSON.parse(JSON.stringify(data || {})) as SettingTaskNodeElement;
|
||||
currentElement.value = JSON.parse(JSON.stringify(data || {})) as GraphTaskElement;
|
||||
} else {
|
||||
currentElement.value = null;
|
||||
}
|
||||
};
|
||||
|
||||
const addVariable = ()=> {
|
||||
const addVariable = () => {
|
||||
if (!currentElement.value) {
|
||||
return;
|
||||
}
|
||||
@@ -165,8 +187,8 @@ export default defineComponent({
|
||||
value: null,
|
||||
defaults: null,
|
||||
unit: null,
|
||||
})
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const removeVariable = (row: ElementVariable) => {
|
||||
if (currentElement.value && currentElement.value.variables) {
|
||||
@@ -178,7 +200,7 @@ export default defineComponent({
|
||||
const updateNode = () => {
|
||||
if (currentNode.value && currentElement.value) {
|
||||
// 深拷贝当前元素数据
|
||||
const newElement = JSON.parse(JSON.stringify(currentElement.value)) as SettingTaskNodeElement;
|
||||
const newElement = JSON.parse(JSON.stringify(currentElement.value)) as GraphTaskElement;
|
||||
// 更新节点数据
|
||||
currentNode.value.replaceData(newElement);
|
||||
// 触发事件通知父组件
|
||||
@@ -192,6 +214,22 @@ export default defineComponent({
|
||||
{ deep: true, immediate: true },
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.tree,
|
||||
(n?: BehaviorTree | null | undefined) => {
|
||||
currentTree.value = n ?? null;
|
||||
},
|
||||
{ deep: true, immediate: true },
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.treeEditing,
|
||||
(n?: boolean | null | undefined) => {
|
||||
treeEditing.value = n === true;
|
||||
},
|
||||
{ deep: true, immediate: true },
|
||||
);
|
||||
|
||||
watch(() => currentElement.value, () => updateNode(), { deep: true });
|
||||
|
||||
onMounted(() => load());
|
||||
@@ -204,6 +242,8 @@ export default defineComponent({
|
||||
currentElement,
|
||||
addVariable,
|
||||
removeVariable,
|
||||
currentTree,
|
||||
treeEditing,
|
||||
// currentElementParameters,
|
||||
};
|
||||
},
|
||||
|
||||
@@ -1,45 +1,97 @@
|
||||
<template>
|
||||
<a-collapse v-model:activeKey="activeKey" :accordion="false">
|
||||
<a-collapse-panel key="1">
|
||||
<a-collapse v-model:activeKey="activeKey" :accordion="false" class="ks-trees-collapse">
|
||||
<a-collapse-panel key="1" style="position: relative">
|
||||
<template #header>
|
||||
<span class="ks-model-builder-title-icon icon-model"></span>我的行为树
|
||||
<a-flex>
|
||||
<span class="ks-model-builder-title-icon icon-model"></span>
|
||||
<span style="color: #82c4e9;font-size: 16px;">我的行为树</span>
|
||||
<!-- <span style="position: absolute; right: 15px;"><PlusOutlined @click="$emit('create-tree')"/></span>-->
|
||||
</a-flex>
|
||||
</template>
|
||||
<a-list size="small" :data-source="behaviorTrees || []" style="min-height: 25vh">
|
||||
<div class="w-full" style="padding: 5px;">
|
||||
<a-flex>
|
||||
<a-input-search v-model:value="behaviorTreeQuery.name" allowClear placeholder="行为树名称" size="small" @search="loadTress" />
|
||||
<a-button size="small" style="margin-left: 10px;">
|
||||
<PlusOutlined style="margin-top: 0px;display: block;" @click="$emit('create-tree')" />
|
||||
</a-button>
|
||||
</a-flex>
|
||||
</div>
|
||||
<a-list :data-source="behaviorTrees || []" size="small" style="min-height: 25vh">
|
||||
<template #renderItem="{ item }">
|
||||
<a-tooltip placement="right">
|
||||
<template #title>
|
||||
<p>名称: {{ item.name }}</p>
|
||||
<p>说明: {{item.description}}</p>
|
||||
</template>
|
||||
<a-list-item @click="()=> handleSelect(item)">
|
||||
{{ substring(item.name, 15) }}
|
||||
</a-list-item>
|
||||
</a-tooltip>
|
||||
<a-list-item>
|
||||
<a-flex>
|
||||
<a-tooltip placement="bottom">
|
||||
<template #title>
|
||||
<p>名称: {{ item.name }}</p>
|
||||
<p>说明: {{ item.description }}</p>
|
||||
</template>
|
||||
<span>{{ substring(item.name, 15) }}</span>
|
||||
</a-tooltip>
|
||||
<a-flex class="ks-tree-actions">
|
||||
<span style="margin-right: 10px" @click="()=> handleSelect(item)"><EditFilled /></span>
|
||||
<a-popconfirm
|
||||
title="确定删除?"
|
||||
@confirm="()=> handleDelete(item)"
|
||||
>
|
||||
<span class="ks-tree-action ks-tree-action-delete"><DeleteOutlined /></span>
|
||||
</a-popconfirm>
|
||||
</a-flex>
|
||||
</a-flex>
|
||||
</a-list-item>
|
||||
</template>
|
||||
</a-list>
|
||||
<a-pagination
|
||||
v-model:current="behaviorTreeQuery.pageNum"
|
||||
:page-size="behaviorTreeQuery.pageSize"
|
||||
:total="totalTress"
|
||||
simple size="small" @change="handleChange" />
|
||||
</a-collapse-panel>
|
||||
</a-collapse>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, onMounted, ref } from 'vue';
|
||||
import { PlusOutlined } from '@ant-design/icons-vue';
|
||||
import type { BehaviorTree } from './types';
|
||||
import { findTreesByQuery } from './api';
|
||||
import {substring} from '@/utils/strings'
|
||||
import { CheckOutlined, DeleteOutlined, EditFilled, PlusOutlined } from '@ant-design/icons-vue';
|
||||
import type { BehaviorTree, BehaviorTreeRequest } from './types';
|
||||
import { deleteOneTreeById, findTreesByQuery } from './api';
|
||||
import { substring } from '@/utils/strings';
|
||||
|
||||
export default defineComponent({
|
||||
emits: ['select-tree'],
|
||||
emits: ['select-tree', 'create-tree'],
|
||||
components: {
|
||||
PlusOutlined
|
||||
CheckOutlined,
|
||||
PlusOutlined,
|
||||
DeleteOutlined,
|
||||
EditFilled,
|
||||
},
|
||||
setup(_props, { emit }) {
|
||||
const behaviorTrees = ref<BehaviorTree[]>([]);
|
||||
const behaviorTreeQuery = ref<Partial<BehaviorTree>>({});
|
||||
const activeKey = ref<number>(1)
|
||||
const behaviorTreeQuery = ref<Partial<BehaviorTreeRequest>>({
|
||||
name: null,
|
||||
pageNum: 1,
|
||||
pageSize: 8,
|
||||
});
|
||||
const activeKey = ref<number>(1);
|
||||
const totalTress = ref<number>(0);
|
||||
|
||||
const loadTress = () => {
|
||||
findTreesByQuery(behaviorTreeQuery.value).then(r => {
|
||||
behaviorTrees.value = r.rows;
|
||||
totalTress.value = r.total ?? 0;
|
||||
});
|
||||
};
|
||||
|
||||
const handleChange = (page: number, pageSize: number) => {
|
||||
behaviorTreeQuery.value.pageNum = page;
|
||||
behaviorTreeQuery.value.pageSize = pageSize;
|
||||
loadTress();
|
||||
};
|
||||
|
||||
const handleDelete = (item: BehaviorTree) => {
|
||||
deleteOneTreeById(item.id).then(r => {
|
||||
if (r.code === 200) {
|
||||
loadTress();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -52,7 +104,7 @@ export default defineComponent({
|
||||
|
||||
const handleSelect = (record: BehaviorTree) => {
|
||||
emit('select-tree', record);
|
||||
}
|
||||
};
|
||||
|
||||
const customRow = (record: BehaviorTree) => {
|
||||
return {
|
||||
@@ -62,11 +114,15 @@ export default defineComponent({
|
||||
};
|
||||
};
|
||||
|
||||
const refresh = () => loadTress();
|
||||
|
||||
onMounted(() => {
|
||||
loadTress();
|
||||
});
|
||||
|
||||
return {
|
||||
refresh,
|
||||
totalTress,
|
||||
substring,
|
||||
activeKey,
|
||||
behaviorTrees,
|
||||
@@ -75,19 +131,57 @@ export default defineComponent({
|
||||
columns,
|
||||
customRow,
|
||||
handleSelect,
|
||||
handleChange,
|
||||
handleDelete,
|
||||
};
|
||||
},
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.create-tree-icon{
|
||||
<style lang="less">
|
||||
.ant-collapse {
|
||||
.ant-list-sm {
|
||||
.ant-list-item {
|
||||
padding: 4px 15px;
|
||||
cursor: pointer;
|
||||
color: rgb(130 196 233);
|
||||
position: relative;
|
||||
|
||||
.ks-tree-actions {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: #0d2d4e;
|
||||
|
||||
.ks-tree-actions {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
&.ks-trees-collapse {
|
||||
|
||||
.ant-collapse-content-box {
|
||||
padding: 0;
|
||||
height: 40vh;
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.create-tree-icon {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ant-list-item {
|
||||
padding: 5px 5px;
|
||||
padding: 3px 5px;
|
||||
cursor: pointer;
|
||||
color: rgb(130 196 233);
|
||||
|
||||
|
||||
@@ -7,6 +7,5 @@
|
||||
* that was distributed with this source code.
|
||||
*/
|
||||
|
||||
export * from './tree'
|
||||
export * from './template'
|
||||
export * from './parameter'
|
||||
export * from './tree';
|
||||
export * from './template';
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
/*
|
||||
* This file is part of the kernelstudio package.
|
||||
*
|
||||
* (c) 2014-2026 zlin <admin@kernelstudio.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
* that was distributed with this source code.
|
||||
*/
|
||||
|
||||
import type { NullableString } from '@/types';
|
||||
|
||||
export interface NodeSetting {
|
||||
id: number;
|
||||
name: NullableString;
|
||||
type: NullableString;
|
||||
default_value: NullableString;
|
||||
data_type: NullableString;
|
||||
required: boolean;
|
||||
description: NullableString;
|
||||
}
|
||||
@@ -8,16 +8,7 @@
|
||||
*/
|
||||
|
||||
import type { ApiDataResponse, NullableString } from '@/types';
|
||||
|
||||
export interface NodeTemplateParameter {
|
||||
id: number,
|
||||
templateId: number,
|
||||
paramKey: NullableString,
|
||||
dataType: NullableString,
|
||||
defaultValue: NullableString,
|
||||
description: NullableString,
|
||||
templateType: NullableString,
|
||||
}
|
||||
import type { ElementParameter } from '../builder/element';
|
||||
|
||||
export interface NodeTemplate {
|
||||
id: number;
|
||||
@@ -25,9 +16,9 @@ export interface NodeTemplate {
|
||||
type: NullableString;
|
||||
logicHandler: NullableString;
|
||||
description: NullableString;
|
||||
templeteType: NullableString;
|
||||
templateType: NullableString;
|
||||
englishName: NullableString;
|
||||
parameters: NodeTemplateParameter[],
|
||||
parameters: ElementParameter[],
|
||||
}
|
||||
|
||||
export interface NodeTemplatesResponse extends ApiDataResponse<NodeTemplate[]> {
|
||||
|
||||
@@ -21,6 +21,11 @@ export interface BehaviorTree {
|
||||
graph: NodeGraph
|
||||
}
|
||||
|
||||
export interface BehaviorTreeRequest extends BehaviorTree {
|
||||
pageNum: number,
|
||||
pageSize: number,
|
||||
}
|
||||
|
||||
|
||||
export interface BehaviorTreeDetailsResponse extends ApiDataResponse<BehaviorTree> {
|
||||
|
||||
|
||||
@@ -8,22 +8,23 @@
|
||||
*/
|
||||
|
||||
import type { NodeTemplate } from '../types';
|
||||
import type { SettingTaskNodeElement, TaskNodeRect } from '../builder/element';
|
||||
import type { GraphTaskElement, GraphTaskRect } from '../builder/element';
|
||||
import { generateKey } from '@/utils/strings';
|
||||
|
||||
export const createTaskNodeElementFromTemplate = (
|
||||
export const createGraphTaskElementFromTemplate = (
|
||||
template: NodeTemplate,
|
||||
rect?: TaskNodeRect,
|
||||
): SettingTaskNodeElement => {
|
||||
let realRect = { width: 200, height: 100, x: 0, y: 0, ...rect || {} }
|
||||
console.info('rect',rect)
|
||||
rect?: GraphTaskRect,
|
||||
): GraphTaskElement => {
|
||||
let realRect = { width: 120, height: 80, x: 0, y: 0, ...rect || {} };
|
||||
console.info('rect', rect);
|
||||
return {
|
||||
id: 0,
|
||||
key: generateKey(template.type),
|
||||
status: null,
|
||||
type: 'task',
|
||||
template: template.id,
|
||||
type: template.type,
|
||||
templateType: template.templateType,
|
||||
name: template.name,
|
||||
category: template.type,
|
||||
description: template.description,
|
||||
position: {
|
||||
x: realRect.x ?? 0,
|
||||
@@ -31,25 +32,9 @@ export const createTaskNodeElementFromTemplate = (
|
||||
},
|
||||
width: realRect.width,
|
||||
height: realRect.height,
|
||||
// settings: JSON.parse(JSON.stringify(template.parameter_defs ?? [])),
|
||||
inputs: null,
|
||||
outputs: null,
|
||||
parameters: {},
|
||||
variables: [
|
||||
{
|
||||
key: generateKey('var_'),
|
||||
name: '范围',
|
||||
value: '1000',
|
||||
defaults: '1000',
|
||||
unit: 'KM'
|
||||
},
|
||||
{
|
||||
key: generateKey('var_'),
|
||||
name: '武器名称',
|
||||
value: '地对空导弹',
|
||||
defaults: '地对空导弹',
|
||||
unit: '个'
|
||||
}
|
||||
],
|
||||
} as SettingTaskNodeElement;
|
||||
parameters: template.parameters ?? [],
|
||||
variables: [],
|
||||
} as GraphTaskElement;
|
||||
};
|
||||
|
||||
22
modeler/types/components.d.ts
vendored
22
modeler/types/components.d.ts
vendored
@@ -13,10 +13,9 @@ export {}
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
AAvatar: typeof import('ant-design-vue/es')['Avatar']
|
||||
ABadge: typeof import('ant-design-vue/es')['Badge']
|
||||
AButton: typeof import('ant-design-vue/es')['Button']
|
||||
ACard: typeof import('ant-design-vue/es')['Card']
|
||||
ACarousel: typeof import('ant-design-vue/es')['Carousel']
|
||||
ACheckbox: typeof import('ant-design-vue/es')['Checkbox']
|
||||
ACol: typeof import('ant-design-vue/es')['Col']
|
||||
ACollapse: typeof import('ant-design-vue/es')['Collapse']
|
||||
ACollapsePanel: typeof import('ant-design-vue/es')['CollapsePanel']
|
||||
@@ -29,7 +28,6 @@ declare module 'vue' {
|
||||
AFormItem: typeof import('ant-design-vue/es')['FormItem']
|
||||
AFormItemRest: typeof import('ant-design-vue/es')['FormItemRest']
|
||||
AInput: typeof import('ant-design-vue/es')['Input']
|
||||
AInputGroup: typeof import('ant-design-vue/es')['InputGroup']
|
||||
AInputNumber: typeof import('ant-design-vue/es')['InputNumber']
|
||||
AInputPassword: typeof import('ant-design-vue/es')['InputPassword']
|
||||
AInputSearch: typeof import('ant-design-vue/es')['InputSearch']
|
||||
@@ -39,18 +37,15 @@ declare module 'vue' {
|
||||
ALayoutSider: typeof import('ant-design-vue/es')['LayoutSider']
|
||||
AList: typeof import('ant-design-vue/es')['List']
|
||||
AListItem: typeof import('ant-design-vue/es')['ListItem']
|
||||
AListItemMeta: typeof import('ant-design-vue/es')['ListItemMeta']
|
||||
AMenu: typeof import('ant-design-vue/es')['Menu']
|
||||
AMenuItem: typeof import('ant-design-vue/es')['MenuItem']
|
||||
AModal: typeof import('ant-design-vue/es')['Modal']
|
||||
APagination: typeof import('ant-design-vue/es')['Pagination']
|
||||
APopconfirm: typeof import('ant-design-vue/es')['Popconfirm']
|
||||
APopover: typeof import('ant-design-vue/es')['Popover']
|
||||
AProgress: typeof import('ant-design-vue/es')['Progress']
|
||||
ARow: typeof import('ant-design-vue/es')['Row']
|
||||
ASelect: typeof import('ant-design-vue/es')['Select']
|
||||
ASelectOption: typeof import('ant-design-vue/es')['SelectOption']
|
||||
ASlider: typeof import('ant-design-vue/es')['Slider']
|
||||
ASpace: typeof import('ant-design-vue/es')['Space']
|
||||
ASteps: typeof import('ant-design-vue/es')['Steps']
|
||||
ASubMenu: typeof import('ant-design-vue/es')['SubMenu']
|
||||
ATable: typeof import('ant-design-vue/es')['Table']
|
||||
ATabPane: typeof import('ant-design-vue/es')['TabPane']
|
||||
@@ -65,10 +60,9 @@ declare module 'vue' {
|
||||
// For TSX support
|
||||
declare global {
|
||||
const AAvatar: typeof import('ant-design-vue/es')['Avatar']
|
||||
const ABadge: typeof import('ant-design-vue/es')['Badge']
|
||||
const AButton: typeof import('ant-design-vue/es')['Button']
|
||||
const ACard: typeof import('ant-design-vue/es')['Card']
|
||||
const ACarousel: typeof import('ant-design-vue/es')['Carousel']
|
||||
const ACheckbox: typeof import('ant-design-vue/es')['Checkbox']
|
||||
const ACol: typeof import('ant-design-vue/es')['Col']
|
||||
const ACollapse: typeof import('ant-design-vue/es')['Collapse']
|
||||
const ACollapsePanel: typeof import('ant-design-vue/es')['CollapsePanel']
|
||||
@@ -81,7 +75,6 @@ declare global {
|
||||
const AFormItem: typeof import('ant-design-vue/es')['FormItem']
|
||||
const AFormItemRest: typeof import('ant-design-vue/es')['FormItemRest']
|
||||
const AInput: typeof import('ant-design-vue/es')['Input']
|
||||
const AInputGroup: typeof import('ant-design-vue/es')['InputGroup']
|
||||
const AInputNumber: typeof import('ant-design-vue/es')['InputNumber']
|
||||
const AInputPassword: typeof import('ant-design-vue/es')['InputPassword']
|
||||
const AInputSearch: typeof import('ant-design-vue/es')['InputSearch']
|
||||
@@ -91,18 +84,15 @@ declare global {
|
||||
const ALayoutSider: typeof import('ant-design-vue/es')['LayoutSider']
|
||||
const AList: typeof import('ant-design-vue/es')['List']
|
||||
const AListItem: typeof import('ant-design-vue/es')['ListItem']
|
||||
const AListItemMeta: typeof import('ant-design-vue/es')['ListItemMeta']
|
||||
const AMenu: typeof import('ant-design-vue/es')['Menu']
|
||||
const AMenuItem: typeof import('ant-design-vue/es')['MenuItem']
|
||||
const AModal: typeof import('ant-design-vue/es')['Modal']
|
||||
const APagination: typeof import('ant-design-vue/es')['Pagination']
|
||||
const APopconfirm: typeof import('ant-design-vue/es')['Popconfirm']
|
||||
const APopover: typeof import('ant-design-vue/es')['Popover']
|
||||
const AProgress: typeof import('ant-design-vue/es')['Progress']
|
||||
const ARow: typeof import('ant-design-vue/es')['Row']
|
||||
const ASelect: typeof import('ant-design-vue/es')['Select']
|
||||
const ASelectOption: typeof import('ant-design-vue/es')['SelectOption']
|
||||
const ASlider: typeof import('ant-design-vue/es')['Slider']
|
||||
const ASpace: typeof import('ant-design-vue/es')['Space']
|
||||
const ASteps: typeof import('ant-design-vue/es')['Steps']
|
||||
const ASubMenu: typeof import('ant-design-vue/es')['SubMenu']
|
||||
const ATable: typeof import('ant-design-vue/es')['Table']
|
||||
const ATabPane: typeof import('ant-design-vue/es')['TabPane']
|
||||
|
||||
Reference in New Issue
Block a user