1.新增流程定义查询

2.调整流程引擎js逻辑
This commit is contained in:
wanggeng 2021-12-08 11:56:39 +08:00
parent dd04255ea4
commit 6d4647f557
17 changed files with 1321 additions and 869 deletions

View File

@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSONObject;
import ink.wgink.common.base.DefaultBaseController;
import ink.wgink.exceptions.ParamsException;
import ink.wgink.interfaces.consts.ISystemConstant;
import ink.wgink.module.activiti.pojo.vos.ActivitiModelVO;
import ink.wgink.module.activiti.service.activiti.IActivitiModelService;
import ink.wgink.pojo.result.ErrorResult;
import ink.wgink.pojo.result.SuccessResult;
@ -42,15 +43,14 @@ public class ActivitiModelController extends DefaultBaseController {
})
@ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)})
@PutMapping("save/{modelId}")
public SuccessResult saveModel(@PathVariable String modelId, String name, String description, String json_xml, String svg_xml) throws Exception {
name = name.trim().replace("\\s", "");
if (StringUtils.isBlank(name)) {
public SuccessResult saveModel(@PathVariable String modelId, @RequestBody ActivitiModelVO activitiModelVO) throws Exception {
if (StringUtils.isBlank(activitiModelVO.getName())) {
throw new ParamsException("名称不能为空");
}
if (RegexUtil.isChinese(name)) {
if (RegexUtil.isChinese(activitiModelVO.getName())) {
throw new ParamsException("名称不能有中文");
}
activitiModelService.saveModel(modelId, name, description, json_xml, svg_xml);
activitiModelService.saveModel(modelId, activitiModelVO.getName(), activitiModelVO.getDescription(), activitiModelVO.getJsonXml(), activitiModelVO.getSvgXml());
return new SuccessResult();
}

View File

@ -0,0 +1,44 @@
package ink.wgink.module.activiti.controller.api;
import ink.wgink.common.base.DefaultBaseController;
import ink.wgink.interfaces.consts.ISystemConstant;
import ink.wgink.module.activiti.pojo.dtos.ActivitiProcdefDTO;
import ink.wgink.module.activiti.service.activiti.IActivitiProcdefService;
import ink.wgink.pojo.ListPage;
import ink.wgink.pojo.result.ErrorResult;
import ink.wgink.pojo.result.SuccessResultList;
import io.swagger.annotations.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* @ClassName: ActivitiProcdefController
* @Description: 流程定义
* @Author: wanggeng
* @Date: 2021/12/7 10:14 PM
* @Version: 1.0
*/
@Api(tags = ISystemConstant.API_TAGS_SYSTEM_PREFIX + "流程定义接口")
@RestController
@RequestMapping(ISystemConstant.API_PREFIX + "/activiti/procdef")
public class ActivitiProcdefController extends DefaultBaseController {
@Autowired
private IActivitiProcdefService activitiProcdefService;
@ApiOperation(value = "部署分页列表", notes = "部署分页列表接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "page", value = "当前页码", paramType = "query", dataType = "Integer", defaultValue = "1"),
@ApiImplicitParam(name = "rows", value = "显示数量", paramType = "query", dataType = "Integer", defaultValue = "20"),
})
@ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)})
@GetMapping("listpage")
public SuccessResultList<List<ActivitiProcdefDTO>> listPage(ListPage page) {
return activitiProcdefService.listPage(page.getPage(), page.getRows());
}
}

View File

@ -0,0 +1,28 @@
package ink.wgink.module.activiti.controller.route;
import ink.wgink.interfaces.consts.ISystemConstant;
import io.swagger.annotations.Api;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
/**
* @ClassName: ActivitiProcdefRouteController
* @Description: 流程定义
* @Author: wanggeng
* @Date: 2021/12/7 10:26 PM
* @Version: 1.0
*/
@Api(tags = ISystemConstant.API_TAGS_SYSTEM_PREFIX + "流程定义路由")
@Controller
@RequestMapping(ISystemConstant.ROUTE_PREFIX + "/activiti/procdef")
public class ActivitiProcdefRouteController {
@GetMapping("list")
public ModelAndView list() {
ModelAndView mv = new ModelAndView("activiti/procdef/list");
return mv;
}
}

View File

@ -0,0 +1,114 @@
package ink.wgink.module.activiti.pojo.dtos;
import java.util.HashMap;
import java.util.Map;
/**
* @ClassName: ActivitiProcdefDTO
* @Description: 流程定义
* @Author: wanggeng
* @Date: 2021/12/7 10:47 PM
* @Version: 1.0
*/
public class ActivitiProcdefDTO {
private String id;
private String name;
private String description;
private String key;
private int version;
private String category;
private String deploymentId;
private String resourceName;
private String diagramResourceName;
private Map<String, Object> variables;
private boolean hasStartFormKey;
public String getId() {
return id == null ? "" : id.trim();
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name == null ? "" : name.trim();
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description == null ? "" : description.trim();
}
public void setDescription(String description) {
this.description = description;
}
public String getKey() {
return key == null ? "" : key.trim();
}
public void setKey(String key) {
this.key = key;
}
public int getVersion() {
return version;
}
public void setVersion(int version) {
this.version = version;
}
public String getCategory() {
return category == null ? "" : category.trim();
}
public void setCategory(String category) {
this.category = category;
}
public String getDeploymentId() {
return deploymentId == null ? "" : deploymentId.trim();
}
public void setDeploymentId(String deploymentId) {
this.deploymentId = deploymentId;
}
public String getResourceName() {
return resourceName == null ? "" : resourceName.trim();
}
public void setResourceName(String resourceName) {
this.resourceName = resourceName;
}
public String getDiagramResourceName() {
return diagramResourceName == null ? "" : diagramResourceName.trim();
}
public void setDiagramResourceName(String diagramResourceName) {
this.diagramResourceName = diagramResourceName;
}
public Map<String, Object> getVariables() {
return variables == null ? new HashMap<>() : variables;
}
public void setVariables(Map<String, Object> variables) {
this.variables = variables;
}
public boolean isHasStartFormKey() {
return hasStartFormKey;
}
public void setHasStartFormKey(boolean hasStartFormKey) {
this.hasStartFormKey = hasStartFormKey;
}
}

View File

@ -0,0 +1,49 @@
package ink.wgink.module.activiti.pojo.vos;
/**
* @ClassName: ActivitiModelVO
* @Description: 模型
* @Author: wanggeng
* @Date: 2021/12/7 11:36 PM
* @Version: 1.0
*/
public class ActivitiModelVO {
private String name;
private String description;
private String jsonXml;
private String svgXml;
public String getName() {
return name == null ? "" : name.trim();
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description == null ? "" : description.trim();
}
public void setDescription(String description) {
this.description = description;
}
public String getJsonXml() {
return jsonXml == null ? "" : jsonXml.trim();
}
public void setJsonXml(String jsonXml) {
this.jsonXml = jsonXml;
}
public String getSvgXml() {
return svgXml == null ? "" : svgXml.trim();
}
public void setSvgXml(String svgXml) {
this.svgXml = svgXml;
}
}

View File

@ -1,7 +1,6 @@
package ink.wgink.module.activiti.pojo.vos;
import ink.wgink.annotation.CheckEmptyAnnotation;
import ink.wgink.annotation.CheckNumberAnnotation;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
@ -26,9 +25,6 @@ public class ActivitiVO {
private String modelKey;
@ApiModelProperty(name = "modelDescription", value = "模型描述")
private String modelDescription;
@ApiModelProperty(name = "modelVersion", value = "模型版本")
@CheckNumberAnnotation(name = "模型版本")
private Integer modelVersion;
public String getModelName() {
return modelName == null ? "" : modelName.trim().replaceAll("\\s", "");
@ -53,12 +49,4 @@ public class ActivitiVO {
public void setModelDescription(String modelDescription) {
this.modelDescription = modelDescription;
}
public Integer getModelVersion() {
return modelVersion;
}
public void setModelVersion(Integer modelVersion) {
this.modelVersion = modelVersion;
}
}

View File

@ -0,0 +1,19 @@
package ink.wgink.module.activiti.service.activiti;
import ink.wgink.module.activiti.pojo.dtos.ActivitiProcdefDTO;
import ink.wgink.pojo.result.SuccessResultList;
import java.util.List;
/**
* @ClassName: IActivitiDeploymentService
* @Description: 流程定义
* @Author: wanggeng
* @Date: 2021/12/7 10:11 PM
* @Version: 1.0
*/
public interface IActivitiProcdefService {
SuccessResultList<List<ActivitiProcdefDTO>> listPage(int page, int rows);
}

View File

@ -67,5 +67,4 @@ public interface IActivitiService {
*/
SuccessResultList<List<Model>> listPage(int page, int rows);
}

View File

@ -1,9 +1,9 @@
package ink.wgink.module.activiti.service.activiti.impl;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import ink.wgink.common.base.DefaultBaseService;
import ink.wgink.exceptions.SearchException;
import ink.wgink.exceptions.UpdateException;
import ink.wgink.exceptions.base.SystemException;
import ink.wgink.interfaces.consts.ISystemConstant;
import ink.wgink.module.activiti.service.activiti.IActivitiModelService;
@ -29,7 +29,10 @@ import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
@ -58,15 +61,13 @@ public class ActivitiModelServiceImpl extends DefaultBaseService implements IAct
if (model == null) {
throw new SearchException("模型不存在");
}
if (!StringUtils.isBlank(model.getDeploymentId())) {
throw new UpdateException("已经部署的流程无法编辑");
}
JSONObject modelObject = JSONObject.parseObject(model.getMetaInfo());
JSONObject modelObject = JSONObject.parseObject(model.getMetaInfo());
modelObject.put(MODEL_NAME, name);
modelObject.put(MODEL_DESCRIPTION, description);
model.setMetaInfo(modelObject.toString());
model.setName(name);
model.setVersion(model.getVersion() + 1);
repositoryService.saveModel(model);
repositoryService.addModelEditorSource(model.getId(), json_xml.getBytes(ISystemConstant.CHARSET_UTF8));
@ -115,7 +116,15 @@ public class ActivitiModelServiceImpl extends DefaultBaseService implements IAct
} catch (IOException e) {
throw e;
}
return JSONObject.parseObject(content);
JSONObject jsonObject = JSONObject.parseObject(content);
// 设置默认且唯一的信息
JSONArray propertyPackagesJsonArray = jsonObject.getJSONArray("propertyPackages");
JSONObject processIdPackageJsonObject = propertyPackagesJsonArray.getJSONObject(0);
JSONArray processIdPackagePropertiesJsonArray = processIdPackageJsonObject.getJSONArray("properties");
JSONObject processIdJsonObject = processIdPackagePropertiesJsonArray.getJSONObject(0);
processIdJsonObject.put("value", "P" + System.currentTimeMillis());
return jsonObject;
}
@Override
@ -282,4 +291,5 @@ public class ActivitiModelServiceImpl extends DefaultBaseService implements IAct
return new ArrayList<>(flowSet);
}
}

View File

@ -0,0 +1,44 @@
package ink.wgink.module.activiti.service.activiti.impl;
import ink.wgink.common.base.DefaultBaseService;
import ink.wgink.module.activiti.pojo.dtos.ActivitiProcdefDTO;
import ink.wgink.module.activiti.service.activiti.IActivitiProcdefService;
import ink.wgink.pojo.result.SuccessResultList;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* @ClassName: ActivitiProcdefServiceImpl
* @Description: 流程定义
* @Author: wanggeng
* @Date: 2021/12/7 10:11 PM
* @Version: 1.0
*/
@Service
public class ActivitiProcdefServiceImpl extends DefaultBaseService implements IActivitiProcdefService {
@Autowired
private RepositoryService repositoryService;
@Override
public SuccessResultList<List<ActivitiProcdefDTO>> listPage(int page, int rows) {
ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
List<ProcessDefinition> processDefinitions = processDefinitionQuery.listPage(page - 1, rows);
List<ActivitiProcdefDTO> activitiProcdefDTOs = new ArrayList<>();
processDefinitions.forEach(processDefinition -> {
ActivitiProcdefDTO activitiProcdefDTO = new ActivitiProcdefDTO();
BeanUtils.copyProperties(processDefinition, activitiProcdefDTO);
activitiProcdefDTOs.add(activitiProcdefDTO);
});
return new SuccessResultList<>(activitiProcdefDTOs, page, processDefinitionQuery.count());
}
}

View File

@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import ink.wgink.common.base.DefaultBaseService;
import ink.wgink.exceptions.RemoveException;
import ink.wgink.exceptions.SearchException;
import ink.wgink.interfaces.consts.ISystemConstant;
import ink.wgink.module.activiti.pojo.vos.ActivitiVO;
@ -18,7 +19,7 @@ import org.activiti.engine.RuntimeService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ModelQuery;
import org.activiti.engine.runtime.ProcessInstance;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@ -48,7 +49,7 @@ public class ActivitiServiceImpl extends DefaultBaseService implements IActiviti
@Override
public String create(ActivitiVO activitiVO) throws UnsupportedEncodingException {
long count = repositoryService.createModelQuery().modelName(activitiVO.getModelName()).modelKey(activitiVO.getModelKey()).modelVersion(activitiVO.getModelVersion()).count();
long count = repositoryService.createModelQuery().modelName(activitiVO.getModelName()).modelKey(activitiVO.getModelKey()).count();
if (count >= 1) {
throw new SearchException("模型已经存在");
}
@ -56,6 +57,7 @@ public class ActivitiServiceImpl extends DefaultBaseService implements IActiviti
model.setName(activitiVO.getModelName());
model.setKey(activitiVO.getModelKey());
model.setMetaInfo(createModelMetaInfo(activitiVO));
model.setVersion(1);
LOG.debug("保存模型");
repositoryService.saveModel(model);
addModelEditorSource(model.getId());
@ -68,13 +70,9 @@ public class ActivitiServiceImpl extends DefaultBaseService implements IActiviti
if (model == null) {
throw new SearchException("模型不存在");
}
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processDefinitionKey(model.getKey()).singleResult();
// 删除模型实例
if (processInstance != null) {
runtimeService.deleteProcessInstance(processInstance.getId(), "");
historyService.deleteHistoricProcessInstance(processInstance.getId());
if (!StringUtils.isBlank(model.getDeploymentId())) {
throw new RemoveException("已部署的模型不能删除");
}
//删除模型模型
repositoryService.deleteModel(modelId);
}
@ -93,7 +91,7 @@ public class ActivitiServiceImpl extends DefaultBaseService implements IActiviti
Deployment deployment = repositoryService.createDeployment()
.name(model.getName())
.key(model.getKey())
.addBpmnModel(model.getKey() + ".bpmn20.xml", bpmnModel)
.addBpmnModel(model.getKey() + "." + model.getVersion() + ".bpmn20.xml", bpmnModel)
.deploy();
model.setDeploymentId(deployment.getId());
repositoryService.saveModel(model);
@ -109,7 +107,7 @@ public class ActivitiServiceImpl extends DefaultBaseService implements IActiviti
* 参数不加true:为普通删除如果当前规则下有正在执行的流程则抛异常
* 参数加true:为级联删除,会删除和当前规则相关的所有信息包括历史
*/
repositoryService.deleteDeployment(modelData.getDeploymentId(), true);
repositoryService.deleteDeployment(modelData.getDeploymentId());
}
@Override
@ -134,7 +132,6 @@ public class ActivitiServiceImpl extends DefaultBaseService implements IActiviti
JSONObject metaInfo = new JSONObject();
metaInfo.put(ModelDataJsonConstants.MODEL_NAME, activitiVO.getModelName());
metaInfo.put(ModelDataJsonConstants.MODEL_DESCRIPTION, activitiVO.getModelDescription());
metaInfo.put(ModelDataJsonConstants.MODEL_REVISION, activitiVO.getModelVersion());
return metaInfo.toString();
}

View File

@ -21,15 +21,64 @@
var KISBPM = KISBPM || {};
KISBPM.TOOLBAR = {
ACTIONS: {
saveModel: function (services) {
var scope = services.$scope;
top.dialog.confirm('确定保存吗?', function (index) {
top.dialog.close(index);
var json = scope.editor.getJSON();
if(!json.properties.name) {
top.dialog.msg('流程的 元素名称 不能为空');
return;
}
if(json.childShapes.length == 0) {
top.dialog.msg('流程图节点不能为空');
return;
}
json = JSON.stringify(json);
var selection = scope.editor.getSelection();
scope.editor.setSelection([]);
// Get the serialized svg image source
var svgClone = scope.editor.getCanvas().getSVGRepresentation(true);
scope.editor.setSelection(selection);
if (scope.editor.getCanvas().properties["oryx-showstripableelements"] === false) {
var stripOutArray = jQuery(svgClone).find(".stripable-element");
for (var i = stripOutArray.length - 1; i >= 0; i--) {
stripOutArray[i].remove();
}
}
// Remove all forced stripable elements
var stripOutArray = jQuery(svgClone).find(".stripable-element-force");
for (var i = stripOutArray.length - 1; i >= 0; i--) {
stripOutArray[i].remove();
}
// Parse dom to string
var svgDOM = DataManager.serialize(svgClone);
var modal = services.$modal({
backdrop: true,
keyboard: true,
template: 'editor-app/popups/save-model.html?version=' + Date.now(),
scope: services.$scope
var modelMetaData = scope.editor.getModelMetaData();
var params = {
jsonXml: json,
svgXml: svgDOM,
name: modelMetaData.name,
description: modelMetaData.description
};
var loadLayerIndex;
top.restAjax.put(KISBPM.URL.putModel(modelMetaData.modelId), params, null, function (code, data) {
parent.layer.close(parent.layer.getFrameIndex(window.name));
top.dialog.msg('保存成功');
}, function (code, data) {
top.dialog.msg(data.msg);
}, function () {
loadLayerIndex = top.dialog.msg(top.dataMessage.committing, {icon: 16, time: 0, shade: 0.3});
}, function () {
top.dialog.close(loadLayerIndex);
});
})
// var modal = services.$modal({
// backdrop: true,
// keyboard: true,
// template: 'editor-app/popups/save-model.html?version=' + Date.now(),
// scope: services.$scope
// });
},
undo: function (services) {
@ -43,8 +92,7 @@ KISBPM.TOOLBAR = {
// Force refresh of selection, might be that the undo command
// impacts properties in the selected item
if (services.$rootScope && services.$rootScope.forceSelectionRefresh)
{
if (services.$rootScope && services.$rootScope.forceSelectionRefresh) {
services.$rootScope.forceSelectionRefresh = true;
}
@ -65,14 +113,12 @@ KISBPM.TOOLBAR = {
}
var toggleUndo = false;
if (services.$scope.undoStack.length == 0)
{
if (services.$scope.undoStack.length == 0) {
toggleUndo = true;
}
var toggleRedo = false;
if (services.$scope.redoStack.length > 0)
{
if (services.$scope.redoStack.length > 0) {
toggleRedo = true;
}
@ -83,8 +129,7 @@ KISBPM.TOOLBAR = {
services.$scope.safeApply(function () {
item.enabled = false;
});
}
else if (toggleRedo && item.action === 'KISBPM.TOOLBAR.ACTIONS.redo') {
} else if (toggleRedo && item.action === 'KISBPM.TOOLBAR.ACTIONS.redo') {
services.$scope.safeApply(function () {
item.enabled = true;
});
@ -104,8 +149,7 @@ KISBPM.TOOLBAR = {
// Force refresh of selection, might be that the redo command
// impacts properties in the selected item
if (services.$rootScope && services.$rootScope.forceSelectionRefresh)
{
if (services.$rootScope && services.$rootScope.forceSelectionRefresh) {
services.$rootScope.forceSelectionRefresh = true;
}
@ -142,8 +186,7 @@ KISBPM.TOOLBAR = {
services.$scope.safeApply(function () {
item.enabled = true;
});
}
else if (toggleRedo && item.action === 'KISBPM.TOOLBAR.ACTIONS.redo') {
} else if (toggleRedo && item.action === 'KISBPM.TOOLBAR.ACTIONS.redo') {
services.$scope.safeApply(function () {
item.enabled = false;
});
@ -190,13 +233,10 @@ KISBPM.TOOLBAR = {
var enableAdd = !dockerPlugin.enabledAdd();
dockerPlugin.setEnableAdd(enableAdd);
if (enableAdd)
{
if (enableAdd) {
dockerPlugin.setEnableRemove(false);
document.body.style.cursor = 'pointer';
}
else
{
} else {
document.body.style.cursor = 'default';
}
},
@ -207,13 +247,10 @@ KISBPM.TOOLBAR = {
var enableRemove = !dockerPlugin.enabledRemove();
dockerPlugin.setEnableRemove(enableRemove);
if (enableRemove)
{
if (enableRemove) {
dockerPlugin.setEnableAdd(false);
document.body.style.cursor = 'pointer';
}
else
{
} else {
document.body.style.cursor = 'default';
}
},
@ -303,8 +340,10 @@ var SaveModelCtrl = [ '$rootScope', '$scope', '$http', '$route', '$location',
description = modelMetaData.description;
}
var saveDialog = { 'name' : modelMetaData.name,
'description' : description};
var saveDialog = {
'name': modelMetaData.name,
'description': description
};
$scope.saveDialog = saveDialog;
@ -378,11 +417,14 @@ var SaveModelCtrl = [ '$rootScope', '$scope', '$http', '$route', '$location',
};
// Update
$http({ method: 'PUT',
$http({
method: 'PUT',
data: params,
ignoreErrors: true,
headers: {'Accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'},
headers: {
'Accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
},
transformRequest: function (obj) {
var str = [];
for (var p in obj) {
@ -390,7 +432,8 @@ var SaveModelCtrl = [ '$rootScope', '$scope', '$http', '$route', '$location',
}
return str.join("&");
},
url: KISBPM.URL.putModel(modelMetaData.modelId)})
url: KISBPM.URL.putModel(modelMetaData.modelId)
})
.success(function (data, status, headers, config) {
$scope.editor.handleEvents({

View File

@ -63,7 +63,6 @@ angular.module('activitiModeler')
StencilSet items
*/
$http({method: 'GET', url: KISBPM.URL.getStencilSet()}).success(function (data, status, headers, config) {
var quickMenuDefinition = ['UserTask', 'EndNoneEvent', 'ExclusiveGateway',
'CatchTimerEvent', 'ThrowNoneEvent', 'TextAnnotation',
'SequenceFlow', 'Association'];
@ -71,16 +70,14 @@ angular.module('activitiModeler')
var quickMenuItems = [];
var morphRoles = [];
for (var i = 0; i < data.rules.morphingRules.length; i++)
{
for (var i = 0; i < data.rules.morphingRules.length; i++) {
var role = data.rules.morphingRules[i].role;
var roleItem = {'role': role, 'morphOptions': []};
morphRoles.push(roleItem);
}
// Check all received items
for (var stencilIndex = 0; stencilIndex < data.stencils.length; stencilIndex++)
{
for (var stencilIndex = 0; stencilIndex < data.stencils.length; stencilIndex++) {
// Check if the root group is the 'diagram' group. If so, this item should not be shown.
var currentGroupName = data.stencils[stencilIndex].groups[0];
if (currentGroupName === 'Diagram' || currentGroupName === 'Form') {
@ -121,7 +118,8 @@ angular.module('activitiModeler')
}
// Construct the stencil item
var stencilItem = {'id': data.stencils[stencilIndex].id,
var stencilItem = {
'id': data.stencils[stencilIndex].id,
'name': data.stencils[stencilIndex].title,
'description': data.stencils[stencilIndex].description,
'icon': data.stencils[stencilIndex].icon,
@ -131,7 +129,8 @@ angular.module('activitiModeler')
'customIcon': false,
'canConnect': false,
'canConnectTo': false,
'canConnectAssociation': false};
'canConnectAssociation': false
};
if (data.stencils[stencilIndex].customIconId && data.stencils[stencilIndex].customIconId > 0) {
stencilItem.customIcon = true;
@ -182,10 +181,8 @@ angular.module('activitiModeler')
}
}
for (var i = 0; i < stencilItemGroups.length; i++)
{
if (stencilItemGroups[i].paletteItems && stencilItemGroups[i].paletteItems.length == 0)
{
for (var i = 0; i < stencilItemGroups.length; i++) {
if (stencilItemGroups[i].paletteItems && stencilItemGroups[i].paletteItems.length == 0) {
stencilItemGroups[i].visible = false;
}
}
@ -193,8 +190,7 @@ angular.module('activitiModeler')
$scope.stencilItemGroups = stencilItemGroups;
var containmentRules = [];
for (var i = 0; i < data.rules.containmentRules.length; i++)
{
for (var i = 0; i < data.rules.containmentRules.length; i++) {
var rule = data.rules.containmentRules[i];
containmentRules.push(rule);
}
@ -202,8 +198,7 @@ angular.module('activitiModeler')
// remove quick menu items which are not available anymore due to custom pallette
var availableQuickMenuItems = [];
for (var i = 0; i < quickMenuItems.length; i++)
{
for (var i = 0; i < quickMenuItems.length; i++) {
if (quickMenuItems[i]) {
availableQuickMenuItems[availableQuickMenuItems.length] = quickMenuItems[i];
}
@ -211,9 +206,7 @@ angular.module('activitiModeler')
$scope.quickMenuItems = availableQuickMenuItems;
$scope.morphRoles = morphRoles;
}).
error(function (data, status, headers, config) {
}).error(function (data, status, headers, config) {
console.log('Something went wrong when fetching stencil items:' + JSON.stringify(data));
});
@ -232,14 +225,12 @@ angular.module('activitiModeler')
var selectedShape = shapes.first();
var stencil = selectedShape.getStencil();
if ($rootScope.selectedElementBeforeScrolling && stencil.id().indexOf('BPMNDiagram') !== -1)
{
if ($rootScope.selectedElementBeforeScrolling && stencil.id().indexOf('BPMNDiagram') !== -1) {
// ignore canvas event because of empty selection when scrolling stops
return;
}
if ($rootScope.selectedElementBeforeScrolling && $rootScope.selectedElementBeforeScrolling.getId() === selectedShape.getId())
{
if ($rootScope.selectedElementBeforeScrolling && $rootScope.selectedElementBeforeScrolling.getId() === selectedShape.getId()) {
$rootScope.selectedElementBeforeScrolling = null;
return;
}
@ -292,8 +283,7 @@ angular.module('activitiModeler')
selectedShape.properties[key] = true;
}
if (KISBPM.CONFIG.showRemovedProperties == false && property.isHidden())
{
if (KISBPM.CONFIG.showRemovedProperties == false && property.isHidden()) {
continue;
}
@ -324,8 +314,7 @@ angular.module('activitiModeler')
if (propertyConfig.templateUrl !== undefined && propertyConfig.templateUrl !== null) {
currentProperty.templateUrl = propertyConfig.templateUrl + '?version=' + $rootScope.staticIncludeVersion;
currentProperty.hasReadWriteMode = false;
}
else {
} else {
currentProperty.hasReadWriteMode = true;
}
@ -404,12 +393,9 @@ angular.module('activitiModeler')
var stencilItem = $scope.getStencilItemById(selectedShape.getStencil().idWithoutNs());
var morphShapes = [];
if (stencilItem && stencilItem.morphRole)
{
for (var i = 0; i < $scope.morphRoles.length; i++)
{
if ($scope.morphRoles[i].role === stencilItem.morphRole)
{
if (stencilItem && stencilItem.morphRole) {
for (var i = 0; i < $scope.morphRoles.length; i++) {
if ($scope.morphRoles[i].role === stencilItem.morphRole) {
morphShapes = $scope.morphRoles[i].morphOptions;
}
}
@ -489,22 +475,18 @@ angular.module('activitiModeler')
$scope.safeApply(function () {
var shapes = $rootScope.editor.getSelection();
if (shapes && shapes.length == 1)
{
if (shapes && shapes.length == 1) {
$rootScope.currentSelectedShape = shapes.first();
var stencilItem = $scope.getStencilItemById($rootScope.currentSelectedShape.getStencil().idWithoutNs());
var morphShapes = [];
for (var i = 0; i < $scope.morphRoles.length; i++)
{
if ($scope.morphRoles[i].role === stencilItem.morphRole)
{
for (var i = 0; i < $scope.morphRoles.length; i++) {
if ($scope.morphRoles[i].role === stencilItem.morphRole) {
morphShapes = $scope.morphRoles[i].morphOptions.slice();
}
}
// Method to open shape select dialog (used later on)
var showSelectShapeDialog = function()
{
var showSelectShapeDialog = function () {
$rootScope.morphShapes = morphShapes;
$modal({
backdrop: false,
@ -526,20 +508,16 @@ angular.module('activitiModeler')
$scope.safeApply(function () {
var shapes = $rootScope.editor.getSelection();
if (shapes && shapes.length == 1)
{
if (shapes && shapes.length == 1) {
$rootScope.currentSelectedShape = shapes.first();
var containedStencil = undefined;
var stencilSets = $scope.editor.getStencilSets().values();
for (var i = 0; i < stencilSets.length; i++)
{
for (var i = 0; i < stencilSets.length; i++) {
var stencilSet = stencilSets[i];
var nodes = stencilSet.nodes();
for (var j = 0; j < nodes.length; j++)
{
if (nodes[j].idWithoutNs() === newItemId)
{
for (var j = 0; j < nodes.length; j++) {
if (nodes[j].idWithoutNs() === newItemId) {
containedStencil = nodes[j];
break;
}
@ -548,14 +526,19 @@ angular.module('activitiModeler')
if (!containedStencil) return;
var option = {type: $scope.currentSelectedShape.getStencil().namespace() + newItemId, namespace: $scope.currentSelectedShape.getStencil().namespace()};
var option = {
type: $scope.currentSelectedShape.getStencil().namespace() + newItemId,
namespace: $scope.currentSelectedShape.getStencil().namespace()
};
option['connectedShape'] = $rootScope.currentSelectedShape;
option['parent'] = $rootScope.currentSelectedShape.parent;
option['containedStencil'] = containedStencil;
var args = {sourceShape: $rootScope.currentSelectedShape, targetStencil: containedStencil};
var targetStencil = $scope.editor.getRules().connectMorph(args);
if (!targetStencil){ return; }// Check if there can be a target shape
if (!targetStencil) {
return;
}// Check if there can be a target shape
option['connectingType'] = targetStencil.id();
var command = new KISBPM.CreateCommand(option, undefined, undefined, $scope.editor);
@ -768,26 +751,20 @@ angular.module('activitiModeler')
var containedStencil = undefined;
var stencilSets = $scope.editor.getStencilSets().values();
for (var i = 0; i < stencilSets.length; i++)
{
for (var i = 0; i < stencilSets.length; i++) {
var stencilSet = stencilSets[i];
var nodes = stencilSet.nodes();
for (var j = 0; j < nodes.length; j++)
{
if (nodes[j].idWithoutNs() === ui.draggable[0].id)
{
for (var j = 0; j < nodes.length; j++) {
if (nodes[j].idWithoutNs() === ui.draggable[0].id) {
containedStencil = nodes[j];
break;
}
}
if (!containedStencil)
{
if (!containedStencil) {
var edges = stencilSet.edges();
for (var j = 0; j < edges.length; j++)
{
if (edges[j].idWithoutNs() === ui.draggable[0].id)
{
for (var j = 0; j < edges.length; j++) {
if (edges[j].idWithoutNs() === ui.draggable[0].id) {
containedStencil = edges[j];
break;
}
@ -797,11 +774,9 @@ angular.module('activitiModeler')
if (!containedStencil) return;
if ($scope.quickMenu)
{
if ($scope.quickMenu) {
var shapes = $scope.editor.getSelection();
if (shapes && shapes.length == 1)
{
if (shapes && shapes.length == 1) {
var currentSelectedShape = shapes.first();
var option = {};
@ -830,11 +805,12 @@ angular.module('activitiModeler')
option.position = pos;
if (containedStencil.idWithoutNs() !== 'SequenceFlow' && containedStencil.idWithoutNs() !== 'Association' &&
containedStencil.idWithoutNs() !== 'MessageFlow' && containedStencil.idWithoutNs() !== 'DataAssociation')
{
containedStencil.idWithoutNs() !== 'MessageFlow' && containedStencil.idWithoutNs() !== 'DataAssociation') {
var args = {sourceShape: currentSelectedShape, targetStencil: containedStencil};
var targetStencil = $scope.editor.getRules().connectMorph(args);
if (!targetStencil){ return; }// Check if there can be a target shape
if (!targetStencil) {
return;
}// Check if there can be a target shape
option.connectingType = targetStencil.id();
}
@ -842,9 +818,7 @@ angular.module('activitiModeler')
$scope.editor.executeCommands([command]);
}
}
else
{
} else {
var canAttach = false;
if (containedStencil.idWithoutNs() === 'BoundaryErrorEvent' || containedStencil.idWithoutNs() === 'BoundaryTimerEvent' ||
containedStencil.idWithoutNs() === 'BoundarySignalEvent' || containedStencil.idWithoutNs() === 'BoundaryMessageEvent' ||
@ -993,8 +967,7 @@ angular.module('activitiModeler')
$scope.editor.getCanvas().setHightlightStateBasedOnX(coord.x);
}
if (aShapes.length == 1 && aShapes[0] instanceof ORYX.Core.Canvas)
{
if (aShapes.length == 1 && aShapes[0] instanceof ORYX.Core.Canvas) {
var parentCandidate = aShapes[0];
$scope.dragCanContain = true;
@ -1010,9 +983,7 @@ angular.module('activitiModeler')
highlightId: "shapeRepo.added"
});
return false;
}
else
{
} else {
var item = $scope.getStencilItemById(event.target.id);
var parentCandidate = aShapes.reverse().find(function (candidate) {
@ -1041,17 +1012,13 @@ angular.module('activitiModeler')
if (item.roles.indexOf("IntermediateEventOnActivityBoundary") > -1) {
_canContain = true;
}
}
else if (parentCandidate.getStencil().idWithoutNs() === 'Pool')
{
if (item.id === 'Lane')
{
} else if (parentCandidate.getStencil().idWithoutNs() === 'Pool') {
if (item.id === 'Lane') {
_canContain = true;
}
}
if (_canContain)
{
if (_canContain) {
$scope.editor.handleEvents({
type: ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW,
highlightId: "shapeRepo.attached",
@ -1064,9 +1031,7 @@ angular.module('activitiModeler')
type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
highlightId: "shapeRepo.added"
});
}
else
{
} else {
for (var i = 0; i < $scope.containmentRules.length; i++) {
var rule = $scope.containmentRules[i];
if (rule.role === parentItem.id) {
@ -1180,26 +1145,20 @@ angular.module('activitiModeler')
var stencil = undefined;
var stencilSets = $scope.editor.getStencilSets().values();
for (var i = 0; i < stencilSets.length; i++)
{
for (var i = 0; i < stencilSets.length; i++) {
var stencilSet = stencilSets[i];
var nodes = stencilSet.nodes();
for (var j = 0; j < nodes.length; j++)
{
if (nodes[j].idWithoutNs() === event.target.id)
{
for (var j = 0; j < nodes.length; j++) {
if (nodes[j].idWithoutNs() === event.target.id) {
stencil = nodes[j];
break;
}
}
if (!stencil)
{
if (!stencil) {
var edges = stencilSet.edges();
for (var j = 0; j < edges.length; j++)
{
if (edges[j].idWithoutNs() === event.target.id)
{
for (var j = 0; j < edges.length; j++) {
if (edges[j].idWithoutNs() === event.target.id) {
stencil = edges[j];
break;
}
@ -1210,10 +1169,12 @@ angular.module('activitiModeler')
var candidate = aShapes.last();
var isValid = false;
if (stencil.type() === "node")
{
if (stencil.type() === "node") {
//check containment rules
var canContain = $scope.editor.getRules().canContain({containingShape:candidate, containedStencil:stencil});
var canContain = $scope.editor.getRules().canContain({
containingShape: candidate,
containedStencil: stencil
});
var parentCandidate = aShapes.reverse().find(function (candidate) {
return (candidate instanceof ORYX.Core.Canvas
@ -1236,29 +1197,22 @@ angular.module('activitiModeler')
} else { //Edge
var shapes = $scope.editor.getSelection();
if (shapes && shapes.length == 1)
{
if (shapes && shapes.length == 1) {
var currentSelectedShape = shapes.first();
var curCan = candidate;
var canConnect = false;
var targetStencil = $scope.getStencilItemById(curCan.getStencil().idWithoutNs());
if (targetStencil)
{
if (targetStencil) {
var associationConnect = false;
if (stencil.idWithoutNs() === 'Association' && (curCan.getStencil().idWithoutNs() === 'TextAnnotation' || curCan.getStencil().idWithoutNs() === 'BoundaryCompensationEvent'))
{
if (stencil.idWithoutNs() === 'Association' && (curCan.getStencil().idWithoutNs() === 'TextAnnotation' || curCan.getStencil().idWithoutNs() === 'BoundaryCompensationEvent')) {
associationConnect = true;
}
else if (stencil.idWithoutNs() === 'DataAssociation' && curCan.getStencil().idWithoutNs() === 'DataStore')
{
} else if (stencil.idWithoutNs() === 'DataAssociation' && curCan.getStencil().idWithoutNs() === 'DataStore') {
associationConnect = true;
}
if (targetStencil.canConnectTo || associationConnect)
{
while (!canConnect && curCan && !(curCan instanceof ORYX.Core.Canvas))
{
if (targetStencil.canConnectTo || associationConnect) {
while (!canConnect && curCan && !(curCan instanceof ORYX.Core.Canvas)) {
candidate = curCan;
//check connection rules
canConnect = $scope.editor.getRules().canConnect({
@ -1337,8 +1291,7 @@ KISBPM.CreateCommand = ORYX.Core.Command.extend({
this.shape.dockers.first().setDockedShape(this.connectedShape);
this.shape.dockers.first().setReferencePoint(this.sourceRefPos);
}
}
else {
} else {
this.shape = this.facade.createShape(this.option);
this.edge = (!(this.shape instanceof ORYX.Core.Edge)) ? this.shape.getIncomingShapes().first() : undefined;
}
@ -1350,19 +1303,15 @@ KISBPM.CreateCommand = ORYX.Core.Command.extend({
if (!(this.currentReference instanceof ORYX.Core.Canvas)) {
this.shape.dockers.last().setDockedShape(this.currentReference);
if (this.currentReference.getStencil().idWithoutNs() === 'TextAnnotation')
{
if (this.currentReference.getStencil().idWithoutNs() === 'TextAnnotation') {
var midpoint = {};
midpoint.x = 0;
midpoint.y = this.currentReference.bounds.height() / 2;
this.shape.dockers.last().setReferencePoint(midpoint);
}
else
{
} else {
this.shape.dockers.last().setReferencePoint(this.currentReference.bounds.midPoint());
}
}
else {
} else {
this.shape.dockers.last().bounds.centerMoveTo(this.position);
}
this.sourceRefPos = this.shape.dockers.first().referencePoint;

View File

@ -9,8 +9,8 @@
{
"id": "process_id",
"type": "String",
"title": "流程名称",
"value": "process",
"title": "流程ID",
"value": "",
"description": "流程的特殊唯一的名称标识",
"popular": true
}
@ -35,7 +35,7 @@
{
"id": "name",
"type": "String",
"title": "名称",
"title": "元素名称",
"value": "",
"description": "元素名称",
"popular": true,

View File

@ -122,15 +122,12 @@
if(!row.deploymentId) {
return '-';
}
return '<button class="layui-btn layui-btn-sm" lay-event="flowChatEvent">查看</button>';
return '<button class="layui-btn layui-btn-xs" lay-event="flowChatEvent">查看</button>';
}
},
{field: 'option', width: 80, title: '操作', align:'center', fixed: 'right',
{field: 'option2', width: 80, title: '操作', align:'center', fixed: 'right',
templet: function(row) {
if(!row.deploymentId) {
return '<button class="layui-btn layui-btn-normal layui-btn-sm" lay-event="publishEvent">部署</button>';
}
return '<button class="layui-btn layui-btn-danger layui-btn-sm" lay-event="revokePublishEvent">撤销</button>';
return '<button class="layui-btn layui-btn-normal layui-btn-xs" lay-event="publishEvent">部署</button>';
}
},
]
@ -212,10 +209,6 @@
} else if(checkDatas.length > 1) {
top.dialog.msg(top.dataMessage.table.selectOneEdit);
} else {
if(checkDatas[0].deploymentId) {
top.dialog.msg('已经部署的流程不能编辑');
return;
}
top.dialog.open({
url: top.restAjax.path('route/activiti/update?modelId={modelId}', [checkDatas[0].id]),
title: false,
@ -255,21 +248,6 @@
top.dialog.close(loadLayerIndex);
});
});
} else if(layEvent === 'revokePublishEvent') {
top.dialog.confirm('确定 撤销 部署流程?', function(index) {
top.dialog.close(index);
var loadLayerIndex;
top.restAjax.put(top.restAjax.path('api/activiti/revoke-publish/{modelId}', [data.id]), {}, null, function(code, data) {
top.dialog.msg('操作成功');
reloadTable();
}, function(code, data) {
top.dialog.msg(data.msg);
}, function() {
loadLayerIndex = top.dialog.msg(top.dataMessage.committing, {icon: 16, time: 0, shade: 0.3});
}, function() {
top.dialog.close(loadLayerIndex);
});
});
} else if(layEvent === 'flowChatEvent') {
if(!data.deploymentId) {
top.dialog.msg('流程未部署,无法查看流程图');

View File

@ -0,0 +1,197 @@
<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<base th:href="${#request.getContextPath() + '/'} ">
<meta charset="utf-8">
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=0">
<link rel="stylesheet" href="assets/fonts/font-awesome/css/font-awesome.css"/>
<link rel="stylesheet" href="assets/layuiadmin/layui/css/layui.css" media="all">
<link rel="stylesheet" href="assets/layuiadmin/style/admin.css" media="all">
</head>
<body>
<div class="layui-fluid layui-anim layui-anim-fadein">
<div class="layui-row">
<div class="layui-col-md12">
<div class="layui-card">
<div class="layui-card-body">
<table class="layui-hide" id="dataTable" lay-filter="dataTable"></table>
</div>
</div>
</div>
</div>
</div>
<script src="assets/layuiadmin/layui/layui.js"></script>
<script src="assets/js/vendor/viewer/viewer.min.js"></script>
<script>
layui.config({
base: 'assets/layuiadmin/'
}).extend({
index: 'lib/index'
}).use(['index', 'table', 'laydate', 'common'], function() {
var $ = layui.$;
var $win = $(window);
var table = layui.table;
var admin = layui.admin;
var laydate = layui.laydate;
var common = layui.common;
var resizeTimeout = null;
var tableUrl = 'api/activiti/procdef/listpage';
// 初始化表格
function initTable() {
table.render({
elem: '#dataTable',
id: 'dataTable',
url: top.restAjax.path(tableUrl, []),
width: admin.screen() > 1 ? '100%' : '',
height: $win.height() - 50,
limit: 20,
limits: [20, 40, 60, 80, 100, 200],
request: {
pageName: 'page',
limitName: 'rows'
},
cols: [
[
{field:'rowNum', width:80, title: '序号', fixed: 'left', align:'center', templet: '<span>{{d.LAY_INDEX}}</span>'},
{field:'id', width:150, title: '主键', align:'center',
templet: function(row) {
var rowData = row[this.field];
if(typeof(rowData) === 'undefined' || rowData == null || rowData == '') {
return '-';
}
return rowData;
}
},
{field:'key', width:150, title: '流程定义ID', align:'center',
templet: function(row) {
var rowData = row[this.field];
if(typeof(rowData) === 'undefined' || rowData == null || rowData == '') {
return '-';
}
return rowData;
}
},
{field:'version', width:80, title: '版本', align:'center',
templet: function(row) {
var rowData = row[this.field];
if(typeof(rowData) === 'undefined' || rowData == null || rowData == '') {
return '-';
}
return rowData;
}
},
{field:'name', width:150, title: '名称', align:'center',
templet: function(row) {
var rowData = row[this.field];
if(typeof(rowData) === 'undefined' || rowData == null || rowData == '') {
return '-';
}
return rowData;
}
},
{field:'description', width:150, title: '描述', align:'center',
templet: function(row) {
var rowData = row[this.field];
if(typeof(rowData) === 'undefined' || rowData == null || rowData == '') {
return '-';
}
return rowData;
}
},
{field:'deploymentId', width:150, title: '部署ID', align:'center',
templet: function(row) {
var rowData = row[this.field];
if(typeof(rowData) === 'undefined' || rowData == null || rowData == '') {
return '-';
}
return rowData;
}
},
{field:'category', width:150, title: '目录名称', align:'center',
templet: function(row) {
var rowData = row[this.field];
if(typeof(rowData) === 'undefined' || rowData == null || rowData == '') {
return '-';
}
return rowData;
}
},
{field:'resourceName', width:200, title: '流程资源名称', align:'center',
templet: function(row) {
var rowData = row[this.field];
if(typeof(rowData) === 'undefined' || rowData == null || rowData == '') {
return '-';
}
return rowData;
}
},
{field: 'diagramResourceName', width: 80, title: '流程图', align:'center', fixed: 'right',
templet: function(row) {
if(!row.deploymentId) {
return '-';
}
return '<button class="layui-btn layui-btn-xs" lay-event="flowChatEvent">查看</button>';
}
}
]
],
page: true,
parseData: function(data) {
return {
'code': 0,
'msg': '',
'count': data.total,
'data': data.rows
};
}
});
}
// 重载表格
function reloadTable(currentPage) {
table.reload('dataTable', {
url: top.restAjax.path(tableUrl, []),
where: {},
page: {
curr: currentPage
},
height: $win.height() - 50,
});
}
initTable();
// 事件 - 页面变化
$win.on('resize', function() {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(function() {
reloadTable();
}, 500);
});
// 事件 - 搜索
$(document).on('click', '#search', function() {
reloadTable(1);
});
table.on('tool(dataTable)', function(obj) {
var data = obj.data;
var layEvent = obj.event;
if(layEvent === 'flowChatEvent') {
if(!data.deploymentId) {
top.dialog.msg('流程未部署,无法查看流程图');
return;
}
top.dialog.open({
url: top.restAjax.path('route/activiti/get-process-image/{deploymentId}', [data.deploymentId]),
title: '流程图',
width: '800px',
height: '400px',
onClose: function() {}
});
}
});
});
</script>
</body>
</html>

View File

@ -33,13 +33,6 @@
<input type="text" id="modelDescription" name="modelDescription" class="layui-input" value="" placeholder="请输入模型描述" lay-verify="required">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">模型版本</label>
<div class="layui-input-block">
<input type="number" id="modelVersion" name="modelVersion" class="layui-input" value="" placeholder="请输入模型版本" lay-verify="required">
</div>
</div>
<div class="layui-form-item layui-layout-admin">
<div class="layui-input-block">
<div class="layui-footer" style="left: 0;">