胖蔡说技术
随便扯扯

Bpmn-js 属性控制

胖蔡阅读(159)

bpmn-js 阅读指南:

我们可以通过bpmn-js来访问对应的BPMN图例的属性信息。对应的流程图中的每个图例元素(如开始、结束、中间/边界事件等都通过businessObject属性存储对基础BPMN元素的引用。业务对象是从BPMN 2.0 XML导入并在导出过程中序列化的实际元素。使用业务对象来读取和写入BPMN特定的属性。

属性读取

我们需要通过关系图的图例的引用来获取对应的属性值:

var elementRegistry = bpmnJS.get('elementRegistry');

var sequenceFlowElement = elementRegistry.get('SequenceFlow_1'),
    sequenceFlow = sequenceFlowElement.businessObject;

sequenceFlow.name; // 'YES'
sequenceFlow.conditionExpression; // ModdleElement { $type: 'bpmn:FormalExpression', ... }

属性设置

在进行属性设置之前,需要了解bpmn-js内置支持哪些元素,哪些属性,具体的元素和属性可参考:bpmn.json文件,其详细内容如下:

{
"name": "BPMN20",
"uri": "http://www.omg.org/spec/BPMN/20100524/MODEL",
"prefix": "bpmn",
"associations": [],
"types": [
{
"name": "Interface",
"superClass": [
"RootElement"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "operations",
"type": "Operation",
"isMany": true
},
{
"name": "implementationRef",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "Operation",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "inMessageRef",
"type": "Message",
"isReference": true
},
{
"name": "outMessageRef",
"type": "Message",
"isReference": true
},
{
"name": "errorRef",
"type": "Error",
"isMany": true,
"isReference": true
},
{
"name": "implementationRef",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "EndPoint",
"superClass": [
"RootElement"
]
},
{
"name": "Auditing",
"superClass": [
"BaseElement"
]
},
{
"name": "GlobalTask",
"superClass": [
"CallableElement"
],
"properties": [
{
"name": "resources",
"type": "ResourceRole",
"isMany": true
}
]
},
{
"name": "Monitoring",
"superClass": [
"BaseElement"
]
},
{
"name": "Performer",
"superClass": [
"ResourceRole"
]
},
{
"name": "Process",
"superClass": [
"FlowElementsContainer",
"CallableElement"
],
"properties": [
{
"name": "processType",
"type": "ProcessType",
"isAttr": true
},
{
"name": "isClosed",
"isAttr": true,
"type": "Boolean"
},
{
"name": "auditing",
"type": "Auditing"
},
{
"name": "monitoring",
"type": "Monitoring"
},
{
"name": "properties",
"type": "Property",
"isMany": true
},
{
"name": "laneSets",
"isMany": true,
"replaces": "FlowElementsContainer#laneSets",
"type": "LaneSet"
},
{
"name": "flowElements",
"isMany": true,
"replaces": "FlowElementsContainer#flowElements",
"type": "FlowElement"
},
{
"name": "artifacts",
"type": "Artifact",
"isMany": true
},
{
"name": "resources",
"type": "ResourceRole",
"isMany": true
},
{
"name": "correlationSubscriptions",
"type": "CorrelationSubscription",
"isMany": true
},
{
"name": "supports",
"type": "Process",
"isMany": true,
"isReference": true
},
{
"name": "definitionalCollaborationRef",
"type": "Collaboration",
"isAttr": true,
"isReference": true
},
{
"name": "isExecutable",
"isAttr": true,
"type": "Boolean"
}
]
},
{
"name": "LaneSet",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "lanes",
"type": "Lane",
"isMany": true
},
{
"name": "name",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "Lane",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "partitionElementRef",
"type": "BaseElement",
"isAttr": true,
"isReference": true
},
{
"name": "partitionElement",
"type": "BaseElement"
},
{
"name": "flowNodeRef",
"type": "FlowNode",
"isMany": true,
"isReference": true
},
{
"name": "childLaneSet",
"type": "LaneSet",
"xml": {
"serialize": "xsi:type"
}
}
]
},
{
"name": "GlobalManualTask",
"superClass": [
"GlobalTask"
]
},
{
"name": "ManualTask",
"superClass": [
"Task"
]
},
{
"name": "UserTask",
"superClass": [
"Task"
],
"properties": [
{
"name": "renderings",
"type": "Rendering",
"isMany": true
},
{
"name": "implementation",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "Rendering",
"superClass": [
"BaseElement"
]
},
{
"name": "HumanPerformer",
"superClass": [
"Performer"
]
},
{
"name": "PotentialOwner",
"superClass": [
"HumanPerformer"
]
},
{
"name": "GlobalUserTask",
"superClass": [
"GlobalTask"
],
"properties": [
{
"name": "implementation",
"isAttr": true,
"type": "String"
},
{
"name": "renderings",
"type": "Rendering",
"isMany": true
}
]
},
{
"name": "Gateway",
"isAbstract": true,
"superClass": [
"FlowNode"
],
"properties": [
{
"name": "gatewayDirection",
"type": "GatewayDirection",
"default": "Unspecified",
"isAttr": true
}
]
},
{
"name": "EventBasedGateway",
"superClass": [
"Gateway"
],
"properties": [
{
"name": "instantiate",
"default": false,
"isAttr": true,
"type": "Boolean"
},
{
"name": "eventGatewayType",
"type": "EventBasedGatewayType",
"isAttr": true,
"default": "Exclusive"
}
]
},
{
"name": "ComplexGateway",
"superClass": [
"Gateway"
],
"properties": [
{
"name": "activationCondition",
"type": "Expression",
"xml": {
"serialize": "xsi:type"
}
},
{
"name": "default",
"type": "SequenceFlow",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "ExclusiveGateway",
"superClass": [
"Gateway"
],
"properties": [
{
"name": "default",
"type": "SequenceFlow",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "InclusiveGateway",
"superClass": [
"Gateway"
],
"properties": [
{
"name": "default",
"type": "SequenceFlow",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "ParallelGateway",
"superClass": [
"Gateway"
]
},
{
"name": "RootElement",
"isAbstract": true,
"superClass": [
"BaseElement"
]
},
{
"name": "Relationship",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "type",
"isAttr": true,
"type": "String"
},
{
"name": "direction",
"type": "RelationshipDirection",
"isAttr": true
},
{
"name": "source",
"isMany": true,
"isReference": true,
"type": "Element"
},
{
"name": "target",
"isMany": true,
"isReference": true,
"type": "Element"
}
]
},
{
"name": "BaseElement",
"isAbstract": true,
"properties": [
{
"name": "id",
"isAttr": true,
"type": "String",
"isId": true
},
{
"name": "documentation",
"type": "Documentation",
"isMany": true
},
{
"name": "extensionDefinitions",
"type": "ExtensionDefinition",
"isMany": true,
"isReference": true
},
{
"name": "extensionElements",
"type": "ExtensionElements"
}
]
},
{
"name": "Extension",
"properties": [
{
"name": "mustUnderstand",
"default": false,
"isAttr": true,
"type": "Boolean"
},
{
"name": "definition",
"type": "ExtensionDefinition",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "ExtensionDefinition",
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "extensionAttributeDefinitions",
"type": "ExtensionAttributeDefinition",
"isMany": true
}
]
},
{
"name": "ExtensionAttributeDefinition",
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "type",
"isAttr": true,
"type": "String"
},
{
"name": "isReference",
"default": false,
"isAttr": true,
"type": "Boolean"
},
{
"name": "extensionDefinition",
"type": "ExtensionDefinition",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "ExtensionElements",
"properties": [
{
"name": "valueRef",
"isAttr": true,
"isReference": true,
"type": "Element"
},
{
"name": "values",
"type": "Element",
"isMany": true
},
{
"name": "extensionAttributeDefinition",
"type": "ExtensionAttributeDefinition",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "Documentation",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "text",
"type": "String",
"isBody": true
},
{
"name": "textFormat",
"default": "text/plain",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "Event",
"isAbstract": true,
"superClass": [
"FlowNode",
"InteractionNode"
],
"properties": [
{
"name": "properties",
"type": "Property",
"isMany": true
}
]
},
{
"name": "IntermediateCatchEvent",
"superClass": [
"CatchEvent"
]
},
{
"name": "IntermediateThrowEvent",
"superClass": [
"ThrowEvent"
]
},
{
"name": "EndEvent",
"superClass": [
"ThrowEvent"
]
},
{
"name": "StartEvent",
"superClass": [
"CatchEvent"
],
"properties": [
{
"name": "isInterrupting",
"default": true,
"isAttr": true,
"type": "Boolean"
}
]
},
{
"name": "ThrowEvent",
"isAbstract": true,
"superClass": [
"Event"
],
"properties": [
{
"name": "dataInputs",
"type": "DataInput",
"isMany": true
},
{
"name": "dataInputAssociations",
"type": "DataInputAssociation",
"isMany": true
},
{
"name": "inputSet",
"type": "InputSet"
},
{
"name": "eventDefinitions",
"type": "EventDefinition",
"isMany": true
},
{
"name": "eventDefinitionRef",
"type": "EventDefinition",
"isMany": true,
"isReference": true
}
]
},
{
"name": "CatchEvent",
"isAbstract": true,
"superClass": [
"Event"
],
"properties": [
{
"name": "parallelMultiple",
"isAttr": true,
"type": "Boolean",
"default": false
},
{
"name": "dataOutputs",
"type": "DataOutput",
"isMany": true
},
{
"name": "dataOutputAssociations",
"type": "DataOutputAssociation",
"isMany": true
},
{
"name": "outputSet",
"type": "OutputSet"
},
{
"name": "eventDefinitions",
"type": "EventDefinition",
"isMany": true
},
{
"name": "eventDefinitionRef",
"type": "EventDefinition",
"isMany": true,
"isReference": true
}
]
},
{
"name": "BoundaryEvent",
"superClass": [
"CatchEvent"
],
"properties": [
{
"name": "cancelActivity",
"default": true,
"isAttr": true,
"type": "Boolean"
},
{
"name": "attachedToRef",
"type": "Activity",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "EventDefinition",
"isAbstract": true,
"superClass": [
"RootElement"
]
},
{
"name": "CancelEventDefinition",
"superClass": [
"EventDefinition"
]
},
{
"name": "ErrorEventDefinition",
"superClass": [
"EventDefinition"
],
"properties": [
{
"name": "errorRef",
"type": "Error",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "TerminateEventDefinition",
"superClass": [
"EventDefinition"
]
},
{
"name": "EscalationEventDefinition",
"superClass": [
"EventDefinition"
],
"properties": [
{
"name": "escalationRef",
"type": "Escalation",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "Escalation",
"properties": [
{
"name": "structureRef",
"type": "ItemDefinition",
"isAttr": true,
"isReference": true
},
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "escalationCode",
"isAttr": true,
"type": "String"
}
],
"superClass": [
"RootElement"
]
},
{
"name": "CompensateEventDefinition",
"superClass": [
"EventDefinition"
],
"properties": [
{
"name": "waitForCompletion",
"isAttr": true,
"type": "Boolean",
"default": true
},
{
"name": "activityRef",
"type": "Activity",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "TimerEventDefinition",
"superClass": [
"EventDefinition"
],
"properties": [
{
"name": "timeDate",
"type": "Expression",
"xml": {
"serialize": "xsi:type"
}
},
{
"name": "timeCycle",
"type": "Expression",
"xml": {
"serialize": "xsi:type"
}
},
{
"name": "timeDuration",
"type": "Expression",
"xml": {
"serialize": "xsi:type"
}
}
]
},
{
"name": "LinkEventDefinition",
"superClass": [
"EventDefinition"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "target",
"type": "LinkEventDefinition",
"isReference": true
},
{
"name": "source",
"type": "LinkEventDefinition",
"isMany": true,
"isReference": true
}
]
},
{
"name": "MessageEventDefinition",
"superClass": [
"EventDefinition"
],
"properties": [
{
"name": "messageRef",
"type": "Message",
"isAttr": true,
"isReference": true
},
{
"name": "operationRef",
"type": "Operation",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "ConditionalEventDefinition",
"superClass": [
"EventDefinition"
],
"properties": [
{
"name": "condition",
"type": "Expression",
"xml": {
"serialize": "xsi:type"
}
}
]
},
{
"name": "SignalEventDefinition",
"superClass": [
"EventDefinition"
],
"properties": [
{
"name": "signalRef",
"type": "Signal",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "Signal",
"superClass": [
"RootElement"
],
"properties": [
{
"name": "structureRef",
"type": "ItemDefinition",
"isAttr": true,
"isReference": true
},
{
"name": "name",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "ImplicitThrowEvent",
"superClass": [
"ThrowEvent"
]
},
{
"name": "DataState",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "ItemAwareElement",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "itemSubjectRef",
"type": "ItemDefinition",
"isAttr": true,
"isReference": true
},
{
"name": "dataState",
"type": "DataState"
}
]
},
{
"name": "DataAssociation",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "sourceRef",
"type": "ItemAwareElement",
"isMany": true,
"isReference": true
},
{
"name": "targetRef",
"type": "ItemAwareElement",
"isReference": true
},
{
"name": "transformation",
"type": "FormalExpression",
"xml": {
"serialize": "property"
}
},
{
"name": "assignment",
"type": "Assignment",
"isMany": true
}
]
},
{
"name": "DataInput",
"superClass": [
"ItemAwareElement"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "isCollection",
"default": false,
"isAttr": true,
"type": "Boolean"
},
{
"name": "inputSetRef",
"type": "InputSet",
"isMany": true,
"isVirtual": true,
"isReference": true
},
{
"name": "inputSetWithOptional",
"type": "InputSet",
"isMany": true,
"isVirtual": true,
"isReference": true
},
{
"name": "inputSetWithWhileExecuting",
"type": "InputSet",
"isMany": true,
"isVirtual": true,
"isReference": true
}
]
},
{
"name": "DataOutput",
"superClass": [
"ItemAwareElement"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "isCollection",
"default": false,
"isAttr": true,
"type": "Boolean"
},
{
"name": "outputSetRef",
"type": "OutputSet",
"isMany": true,
"isVirtual": true,
"isReference": true
},
{
"name": "outputSetWithOptional",
"type": "OutputSet",
"isMany": true,
"isVirtual": true,
"isReference": true
},
{
"name": "outputSetWithWhileExecuting",
"type": "OutputSet",
"isMany": true,
"isVirtual": true,
"isReference": true
}
]
},
{
"name": "InputSet",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "dataInputRefs",
"type": "DataInput",
"isMany": true,
"isReference": true
},
{
"name": "optionalInputRefs",
"type": "DataInput",
"isMany": true,
"isReference": true
},
{
"name": "whileExecutingInputRefs",
"type": "DataInput",
"isMany": true,
"isReference": true
},
{
"name": "outputSetRefs",
"type": "OutputSet",
"isMany": true,
"isReference": true
}
]
},
{
"name": "OutputSet",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "dataOutputRefs",
"type": "DataOutput",
"isMany": true,
"isReference": true
},
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "inputSetRefs",
"type": "InputSet",
"isMany": true,
"isReference": true
},
{
"name": "optionalOutputRefs",
"type": "DataOutput",
"isMany": true,
"isReference": true
},
{
"name": "whileExecutingOutputRefs",
"type": "DataOutput",
"isMany": true,
"isReference": true
}
]
},
{
"name": "Property",
"superClass": [
"ItemAwareElement"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "DataInputAssociation",
"superClass": [
"DataAssociation"
]
},
{
"name": "DataOutputAssociation",
"superClass": [
"DataAssociation"
]
},
{
"name": "InputOutputSpecification",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "dataInputs",
"type": "DataInput",
"isMany": true
},
{
"name": "dataOutputs",
"type": "DataOutput",
"isMany": true
},
{
"name": "inputSets",
"type": "InputSet",
"isMany": true
},
{
"name": "outputSets",
"type": "OutputSet",
"isMany": true
}
]
},
{
"name": "DataObject",
"superClass": [
"FlowElement",
"ItemAwareElement"
],
"properties": [
{
"name": "isCollection",
"default": false,
"isAttr": true,
"type": "Boolean"
}
]
},
{
"name": "InputOutputBinding",
"properties": [
{
"name": "inputDataRef",
"type": "InputSet",
"isAttr": true,
"isReference": true
},
{
"name": "outputDataRef",
"type": "OutputSet",
"isAttr": true,
"isReference": true
},
{
"name": "operationRef",
"type": "Operation",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "Assignment",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "from",
"type": "Expression",
"xml": {
"serialize": "xsi:type"
}
},
{
"name": "to",
"type": "Expression",
"xml": {
"serialize": "xsi:type"
}
}
]
},
{
"name": "DataStore",
"superClass": [
"RootElement",
"ItemAwareElement"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "capacity",
"isAttr": true,
"type": "Integer"
},
{
"name": "isUnlimited",
"default": true,
"isAttr": true,
"type": "Boolean"
}
]
},
{
"name": "DataStoreReference",
"superClass": [
"ItemAwareElement",
"FlowElement"
],
"properties": [
{
"name": "dataStoreRef",
"type": "DataStore",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "DataObjectReference",
"superClass": [
"ItemAwareElement",
"FlowElement"
],
"properties": [
{
"name": "dataObjectRef",
"type": "DataObject",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "ConversationLink",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "sourceRef",
"type": "InteractionNode",
"isAttr": true,
"isReference": true
},
{
"name": "targetRef",
"type": "InteractionNode",
"isAttr": true,
"isReference": true
},
{
"name": "name",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "ConversationAssociation",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "innerConversationNodeRef",
"type": "ConversationNode",
"isAttr": true,
"isReference": true
},
{
"name": "outerConversationNodeRef",
"type": "ConversationNode",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "CallConversation",
"superClass": [
"ConversationNode"
],
"properties": [
{
"name": "calledCollaborationRef",
"type": "Collaboration",
"isAttr": true,
"isReference": true
},
{
"name": "participantAssociations",
"type": "ParticipantAssociation",
"isMany": true
}
]
},
{
"name": "Conversation",
"superClass": [
"ConversationNode"
]
},
{
"name": "SubConversation",
"superClass": [
"ConversationNode"
],
"properties": [
{
"name": "conversationNodes",
"type": "ConversationNode",
"isMany": true
}
]
},
{
"name": "ConversationNode",
"isAbstract": true,
"superClass": [
"InteractionNode",
"BaseElement"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "participantRef",
"type": "Participant",
"isMany": true,
"isReference": true
},
{
"name": "messageFlowRefs",
"type": "MessageFlow",
"isMany": true,
"isReference": true
},
{
"name": "correlationKeys",
"type": "CorrelationKey",
"isMany": true
}
]
},
{
"name": "GlobalConversation",
"superClass": [
"Collaboration"
]
},
{
"name": "PartnerEntity",
"superClass": [
"RootElement"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "participantRef",
"type": "Participant",
"isMany": true,
"isReference": true
}
]
},
{
"name": "PartnerRole",
"superClass": [
"RootElement"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "participantRef",
"type": "Participant",
"isMany": true,
"isReference": true
}
]
},
{
"name": "CorrelationProperty",
"superClass": [
"RootElement"
],
"properties": [
{
"name": "correlationPropertyRetrievalExpression",
"type": "CorrelationPropertyRetrievalExpression",
"isMany": true
},
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "type",
"type": "ItemDefinition",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "Error",
"superClass": [
"RootElement"
],
"properties": [
{
"name": "structureRef",
"type": "ItemDefinition",
"isAttr": true,
"isReference": true
},
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "errorCode",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "CorrelationKey",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "correlationPropertyRef",
"type": "CorrelationProperty",
"isMany": true,
"isReference": true
},
{
"name": "name",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "Expression",
"superClass": [
"BaseElement"
],
"isAbstract": false,
"properties": [
{
"name": "body",
"isBody": true,
"type": "String"
}
]
},
{
"name": "FormalExpression",
"superClass": [
"Expression"
],
"properties": [
{
"name": "language",
"isAttr": true,
"type": "String"
},
{
"name": "evaluatesToTypeRef",
"type": "ItemDefinition",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "Message",
"superClass": [
"RootElement"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "itemRef",
"type": "ItemDefinition",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "ItemDefinition",
"superClass": [
"RootElement"
],
"properties": [
{
"name": "itemKind",
"type": "ItemKind",
"isAttr": true
},
{
"name": "structureRef",
"isAttr": true,
"type": "String"
},
{
"name": "isCollection",
"default": false,
"isAttr": true,
"type": "Boolean"
},
{
"name": "import",
"type": "Import",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "FlowElement",
"isAbstract": true,
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "auditing",
"type": "Auditing"
},
{
"name": "monitoring",
"type": "Monitoring"
},
{
"name": "categoryValueRef",
"type": "CategoryValue",
"isMany": true,
"isReference": true
}
]
},
{
"name": "SequenceFlow",
"superClass": [
"FlowElement"
],
"properties": [
{
"name": "isImmediate",
"isAttr": true,
"type": "Boolean"
},
{
"name": "conditionExpression",
"type": "Expression",
"xml": {
"serialize": "xsi:type"
}
},
{
"name": "sourceRef",
"type": "FlowNode",
"isAttr": true,
"isReference": true
},
{
"name": "targetRef",
"type": "FlowNode",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "FlowElementsContainer",
"isAbstract": true,
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "laneSets",
"type": "LaneSet",
"isMany": true
},
{
"name": "flowElements",
"type": "FlowElement",
"isMany": true
}
]
},
{
"name": "CallableElement",
"isAbstract": true,
"superClass": [
"RootElement"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "ioSpecification",
"type": "InputOutputSpecification",
"xml": {
"serialize": "property"
}
},
{
"name": "supportedInterfaceRef",
"type": "Interface",
"isMany": true,
"isReference": true
},
{
"name": "ioBinding",
"type": "InputOutputBinding",
"isMany": true,
"xml": {
"serialize": "property"
}
}
]
},
{
"name": "FlowNode",
"isAbstract": true,
"superClass": [
"FlowElement"
],
"properties": [
{
"name": "incoming",
"type": "SequenceFlow",
"isMany": true,
"isReference": true
},
{
"name": "outgoing",
"type": "SequenceFlow",
"isMany": true,
"isReference": true
},
{
"name": "lanes",
"type": "Lane",
"isMany": true,
"isVirtual": true,
"isReference": true
}
]
},
{
"name": "CorrelationPropertyRetrievalExpression",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "messagePath",
"type": "FormalExpression"
},
{
"name": "messageRef",
"type": "Message",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "CorrelationPropertyBinding",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "dataPath",
"type": "FormalExpression"
},
{
"name": "correlationPropertyRef",
"type": "CorrelationProperty",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "Resource",
"superClass": [
"RootElement"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "resourceParameters",
"type": "ResourceParameter",
"isMany": true
}
]
},
{
"name": "ResourceParameter",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "isRequired",
"isAttr": true,
"type": "Boolean"
},
{
"name": "type",
"type": "ItemDefinition",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "CorrelationSubscription",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "correlationKeyRef",
"type": "CorrelationKey",
"isAttr": true,
"isReference": true
},
{
"name": "correlationPropertyBinding",
"type": "CorrelationPropertyBinding",
"isMany": true
}
]
},
{
"name": "MessageFlow",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "sourceRef",
"type": "InteractionNode",
"isAttr": true,
"isReference": true
},
{
"name": "targetRef",
"type": "InteractionNode",
"isAttr": true,
"isReference": true
},
{
"name": "messageRef",
"type": "Message",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "MessageFlowAssociation",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "innerMessageFlowRef",
"type": "MessageFlow",
"isAttr": true,
"isReference": true
},
{
"name": "outerMessageFlowRef",
"type": "MessageFlow",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "InteractionNode",
"isAbstract": true,
"properties": [
{
"name": "incomingConversationLinks",
"type": "ConversationLink",
"isMany": true,
"isVirtual": true,
"isReference": true
},
{
"name": "outgoingConversationLinks",
"type": "ConversationLink",
"isMany": true,
"isVirtual": true,
"isReference": true
}
]
},
{
"name": "Participant",
"superClass": [
"InteractionNode",
"BaseElement"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "interfaceRef",
"type": "Interface",
"isMany": true,
"isReference": true
},
{
"name": "participantMultiplicity",
"type": "ParticipantMultiplicity"
},
{
"name": "endPointRefs",
"type": "EndPoint",
"isMany": true,
"isReference": true
},
{
"name": "processRef",
"type": "Process",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "ParticipantAssociation",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "innerParticipantRef",
"type": "Participant",
"isAttr": true,
"isReference": true
},
{
"name": "outerParticipantRef",
"type": "Participant",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "ParticipantMultiplicity",
"properties": [
{
"name": "minimum",
"default": 0,
"isAttr": true,
"type": "Integer"
},
{
"name": "maximum",
"default": 1,
"isAttr": true,
"type": "Integer"
}
],
"superClass": [
"BaseElement"
]
},
{
"name": "Collaboration",
"superClass": [
"RootElement"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "isClosed",
"isAttr": true,
"type": "Boolean"
},
{
"name": "participants",
"type": "Participant",
"isMany": true
},
{
"name": "messageFlows",
"type": "MessageFlow",
"isMany": true
},
{
"name": "artifacts",
"type": "Artifact",
"isMany": true
},
{
"name": "conversations",
"type": "ConversationNode",
"isMany": true
},
{
"name": "conversationAssociations",
"type": "ConversationAssociation"
},
{
"name": "participantAssociations",
"type": "ParticipantAssociation",
"isMany": true
},
{
"name": "messageFlowAssociations",
"type": "MessageFlowAssociation",
"isMany": true
},
{
"name": "correlationKeys",
"type": "CorrelationKey",
"isMany": true
},
{
"name": "choreographyRef",
"type": "Choreography",
"isMany": true,
"isReference": true
},
{
"name": "conversationLinks",
"type": "ConversationLink",
"isMany": true
}
]
},
{
"name": "ChoreographyActivity",
"isAbstract": true,
"superClass": [
"FlowNode"
],
"properties": [
{
"name": "participantRef",
"type": "Participant",
"isMany": true,
"isReference": true
},
{
"name": "initiatingParticipantRef",
"type": "Participant",
"isAttr": true,
"isReference": true
},
{
"name": "correlationKeys",
"type": "CorrelationKey",
"isMany": true
},
{
"name": "loopType",
"type": "ChoreographyLoopType",
"default": "None",
"isAttr": true
}
]
},
{
"name": "CallChoreography",
"superClass": [
"ChoreographyActivity"
],
"properties": [
{
"name": "calledChoreographyRef",
"type": "Choreography",
"isAttr": true,
"isReference": true
},
{
"name": "participantAssociations",
"type": "ParticipantAssociation",
"isMany": true
}
]
},
{
"name": "SubChoreography",
"superClass": [
"ChoreographyActivity",
"FlowElementsContainer"
],
"properties": [
{
"name": "artifacts",
"type": "Artifact",
"isMany": true
}
]
},
{
"name": "ChoreographyTask",
"superClass": [
"ChoreographyActivity"
],
"properties": [
{
"name": "messageFlowRef",
"type": "MessageFlow",
"isMany": true,
"isReference": true
}
]
},
{
"name": "Choreography",
"superClass": [
"Collaboration",
"FlowElementsContainer"
]
},
{
"name": "GlobalChoreographyTask",
"superClass": [
"Choreography"
],
"properties": [
{
"name": "initiatingParticipantRef",
"type": "Participant",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "TextAnnotation",
"superClass": [
"Artifact"
],
"properties": [
{
"name": "text",
"type": "String"
},
{
"name": "textFormat",
"default": "text/plain",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "Group",
"superClass": [
"Artifact"
],
"properties": [
{
"name": "categoryValueRef",
"type": "CategoryValue",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "Association",
"superClass": [
"Artifact"
],
"properties": [
{
"name": "associationDirection",
"type": "AssociationDirection",
"isAttr": true
},
{
"name": "sourceRef",
"type": "BaseElement",
"isAttr": true,
"isReference": true
},
{
"name": "targetRef",
"type": "BaseElement",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "Category",
"superClass": [
"RootElement"
],
"properties": [
{
"name": "categoryValue",
"type": "CategoryValue",
"isMany": true
},
{
"name": "name",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "Artifact",
"isAbstract": true,
"superClass": [
"BaseElement"
]
},
{
"name": "CategoryValue",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "categorizedFlowElements",
"type": "FlowElement",
"isMany": true,
"isVirtual": true,
"isReference": true
},
{
"name": "value",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "Activity",
"isAbstract": true,
"superClass": [
"FlowNode"
],
"properties": [
{
"name": "isForCompensation",
"default": false,
"isAttr": true,
"type": "Boolean"
},
{
"name": "default",
"type": "SequenceFlow",
"isAttr": true,
"isReference": true
},
{
"name": "ioSpecification",
"type": "InputOutputSpecification",
"xml": {
"serialize": "property"
}
},
{
"name": "boundaryEventRefs",
"type": "BoundaryEvent",
"isMany": true,
"isReference": true
},
{
"name": "properties",
"type": "Property",
"isMany": true
},
{
"name": "dataInputAssociations",
"type": "DataInputAssociation",
"isMany": true
},
{
"name": "dataOutputAssociations",
"type": "DataOutputAssociation",
"isMany": true
},
{
"name": "startQuantity",
"default": 1,
"isAttr": true,
"type": "Integer"
},
{
"name": "resources",
"type": "ResourceRole",
"isMany": true
},
{
"name": "completionQuantity",
"default": 1,
"isAttr": true,
"type": "Integer"
},
{
"name": "loopCharacteristics",
"type": "LoopCharacteristics"
}
]
},
{
"name": "ServiceTask",
"superClass": [
"Task"
],
"properties": [
{
"name": "implementation",
"isAttr": true,
"type": "String"
},
{
"name": "operationRef",
"type": "Operation",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "SubProcess",
"superClass": [
"Activity",
"FlowElementsContainer",
"InteractionNode"
],
"properties": [
{
"name": "triggeredByEvent",
"default": false,
"isAttr": true,
"type": "Boolean"
},
{
"name": "artifacts",
"type": "Artifact",
"isMany": true
}
]
},
{
"name": "LoopCharacteristics",
"isAbstract": true,
"superClass": [
"BaseElement"
]
},
{
"name": "MultiInstanceLoopCharacteristics",
"superClass": [
"LoopCharacteristics"
],
"properties": [
{
"name": "isSequential",
"default": false,
"isAttr": true,
"type": "Boolean"
},
{
"name": "behavior",
"type": "MultiInstanceBehavior",
"default": "All",
"isAttr": true
},
{
"name": "loopCardinality",
"type": "Expression",
"xml": {
"serialize": "xsi:type"
}
},
{
"name": "loopDataInputRef",
"type": "ItemAwareElement",
"isReference": true
},
{
"name": "loopDataOutputRef",
"type": "ItemAwareElement",
"isReference": true
},
{
"name": "inputDataItem",
"type": "DataInput",
"xml": {
"serialize": "property"
}
},
{
"name": "outputDataItem",
"type": "DataOutput",
"xml": {
"serialize": "property"
}
},
{
"name": "complexBehaviorDefinition",
"type": "ComplexBehaviorDefinition",
"isMany": true
},
{
"name": "completionCondition",
"type": "Expression",
"xml": {
"serialize": "xsi:type"
}
},
{
"name": "oneBehaviorEventRef",
"type": "EventDefinition",
"isAttr": true,
"isReference": true
},
{
"name": "noneBehaviorEventRef",
"type": "EventDefinition",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "StandardLoopCharacteristics",
"superClass": [
"LoopCharacteristics"
],
"properties": [
{
"name": "testBefore",
"default": false,
"isAttr": true,
"type": "Boolean"
},
{
"name": "loopCondition",
"type": "Expression",
"xml": {
"serialize": "xsi:type"
}
},
{
"name": "loopMaximum",
"type": "Integer",
"isAttr": true
}
]
},
{
"name": "CallActivity",
"superClass": [
"Activity",
"InteractionNode"
],
"properties": [
{
"name": "calledElement",
"type": "String",
"isAttr": true
}
]
},
{
"name": "Task",
"superClass": [
"Activity",
"InteractionNode"
]
},
{
"name": "SendTask",
"superClass": [
"Task"
],
"properties": [
{
"name": "implementation",
"isAttr": true,
"type": "String"
},
{
"name": "operationRef",
"type": "Operation",
"isAttr": true,
"isReference": true
},
{
"name": "messageRef",
"type": "Message",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "ReceiveTask",
"superClass": [
"Task"
],
"properties": [
{
"name": "implementation",
"isAttr": true,
"type": "String"
},
{
"name": "instantiate",
"default": false,
"isAttr": true,
"type": "Boolean"
},
{
"name": "operationRef",
"type": "Operation",
"isAttr": true,
"isReference": true
},
{
"name": "messageRef",
"type": "Message",
"isAttr": true,
"isReference": true
}
]
},
{
"name": "ScriptTask",
"superClass": [
"Task"
],
"properties": [
{
"name": "scriptFormat",
"isAttr": true,
"type": "String"
},
{
"name": "script",
"type": "String"
}
]
},
{
"name": "BusinessRuleTask",
"superClass": [
"Task"
],
"properties": [
{
"name": "implementation",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "AdHocSubProcess",
"superClass": [
"SubProcess"
],
"properties": [
{
"name": "completionCondition",
"type": "Expression",
"xml": {
"serialize": "xsi:type"
}
},
{
"name": "ordering",
"type": "AdHocOrdering",
"isAttr": true
},
{
"name": "cancelRemainingInstances",
"default": true,
"isAttr": true,
"type": "Boolean"
}
]
},
{
"name": "Transaction",
"superClass": [
"SubProcess"
],
"properties": [
{
"name": "protocol",
"isAttr": true,
"type": "String"
},
{
"name": "method",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "GlobalScriptTask",
"superClass": [
"GlobalTask"
],
"properties": [
{
"name": "scriptLanguage",
"isAttr": true,
"type": "String"
},
{
"name": "script",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "GlobalBusinessRuleTask",
"superClass": [
"GlobalTask"
],
"properties": [
{
"name": "implementation",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "ComplexBehaviorDefinition",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "condition",
"type": "FormalExpression"
},
{
"name": "event",
"type": "ImplicitThrowEvent"
}
]
},
{
"name": "ResourceRole",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "resourceRef",
"type": "Resource",
"isReference": true
},
{
"name": "resourceParameterBindings",
"type": "ResourceParameterBinding",
"isMany": true
},
{
"name": "resourceAssignmentExpression",
"type": "ResourceAssignmentExpression"
},
{
"name": "name",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "ResourceParameterBinding",
"properties": [
{
"name": "expression",
"type": "Expression",
"xml": {
"serialize": "xsi:type"
}
},
{
"name": "parameterRef",
"type": "ResourceParameter",
"isAttr": true,
"isReference": true
}
],
"superClass": [
"BaseElement"
]
},
{
"name": "ResourceAssignmentExpression",
"properties": [
{
"name": "expression",
"type": "Expression",
"xml": {
"serialize": "xsi:type"
}
}
],
"superClass": [
"BaseElement"
]
},
{
"name": "Import",
"properties": [
{
"name": "importType",
"isAttr": true,
"type": "String"
},
{
"name": "location",
"isAttr": true,
"type": "String"
},
{
"name": "namespace",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "Definitions",
"superClass": [
"BaseElement"
],
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "targetNamespace",
"isAttr": true,
"type": "String"
},
{
"name": "expressionLanguage",
"default": "http://www.w3.org/1999/XPath",
"isAttr": true,
"type": "String"
},
{
"name": "typeLanguage",
"default": "http://www.w3.org/2001/XMLSchema",
"isAttr": true,
"type": "String"
},
{
"name": "imports",
"type": "Import",
"isMany": true
},
{
"name": "extensions",
"type": "Extension",
"isMany": true
},
{
"name": "rootElements",
"type": "RootElement",
"isMany": true
},
{
"name": "diagrams",
"isMany": true,
"type": "bpmndi:BPMNDiagram"
},
{
"name": "exporter",
"isAttr": true,
"type": "String"
},
{
"name": "relationships",
"type": "Relationship",
"isMany": true
},
{
"name": "exporterVersion",
"isAttr": true,
"type": "String"
}
]
}
],
"enumerations": [
{
"name": "ProcessType",
"literalValues": [
{
"name": "None"
},
{
"name": "Public"
},
{
"name": "Private"
}
]
},
{
"name": "GatewayDirection",
"literalValues": [
{
"name": "Unspecified"
},
{
"name": "Converging"
},
{
"name": "Diverging"
},
{
"name": "Mixed"
}
]
},
{
"name": "EventBasedGatewayType",
"literalValues": [
{
"name": "Parallel"
},
{
"name": "Exclusive"
}
]
},
{
"name": "RelationshipDirection",
"literalValues": [
{
"name": "None"
},
{
"name": "Forward"
},
{
"name": "Backward"
},
{
"name": "Both"
}
]
},
{
"name": "ItemKind",
"literalValues": [
{
"name": "Physical"
},
{
"name": "Information"
}
]
},
{
"name": "ChoreographyLoopType",
"literalValues": [
{
"name": "None"
},
{
"name": "Standard"
},
{
"name": "MultiInstanceSequential"
},
{
"name": "MultiInstanceParallel"
}
]
},
{
"name": "AssociationDirection",
"literalValues": [
{
"name": "None"
},
{
"name": "One"
},
{
"name": "Both"
}
]
},
{
"name": "FormalExpression
"literalValues": [
{
"name": "Parallel"
},
{
"name": "Sequential"
}
]
}
],
"xml": {
"tagAlias": "lowerCase",
"typePrefix": "t"
}
}

其元素类型如下:

元素名父类支持属性
OperationBaseElementname: String
inMessageRef: Message
outMessageRef: Message
errorRef: Message
implementationRef: String
BaseElementRootElementname: String
operations: 一对多Operation类型
implementationRef: String
EndPointRootElement
AuditingBaseElement
GlobalTaskCallableElementresources: 一对多,ResourceRole
MonitoringBaseElement
PerformerResourceRole
ProcessFlowElementsContainer
CallableElement
processType: ProcessType
isClosed: Boolean
auditing: Auditing
monitoring: Monitoring
properties: 一对多,Property
laneSets: 一对多,LaneSet
flowElements: 一对多,FlowElement
artifacts: 一对多,Artifact
resources: 一对多,ResourceRole
correlationSubscriptions:一对多,CorrelationSubscription
supports:一对多,Process
definitionalCollaborationRef:Collaboration
isExecutable:Boolean
LaneSetBaseElementlanes:一对多,Lane
name: String
LaneBaseElementname:String
partitionElementRef: BaseElement
partitionElement: BaseElement
flowNodeRef: 一对多,FlowNode
childLaneSet: LaneSet
GlobalManualTaskGlobalTask
ManualTaskTask
UserTaskTaskrenderings:一对多,Rendering
implementation: String
RenderingBaseElement
HumanPerformerPerformer
PotentialOwnerHumanPerformer
GlobalUserTaskGlobalTaskimplementation:String
renderings:一对多,Rendering
GatewayFlowNodegatewayDirection:GatewayDirection
EventBasedGatewayGatewayinstantiate: Boolean
eventGatewayType: EventBasedGatewayType
ComplexGatewayGatewayactivationCondition: Expression
default:SequenceFlow
ExclusiveGatewayGatewaydefault:SequenceFlow
InclusiveGatewayGatewaydefault:SequenceFlow
ParallelGatewayGateway
RootElementBaseElement
RelationshipBaseElementtype:String
direction:RelationshipDirection
source:Element
target:Element
BaseElementid:String
documentation:一对多,Documentation
extensionDefinitions:一对多,ExtensionDefinition
extensionElements:ExtensionElements
抽象的,不可直接创建
ExtensionmustUnderstand: Boolean
definition: ExtensionDefinition
ExtensionDefinitionname:String
extensionAttributeDefinitions:一对多,ExtensionAttributeDefinition
ExtensionAttributeDefinitionname:String
type:String
isReference:Boolean
extensionDefinition:ExtensionDefinition
ExtensionElementsvalueRef:Element
values:一对多,Element
extensionAttributeDefinition:ExtensionAttributeDefinition
DocumentationBaseElementtext:String
textFormat:String
……

当无需撤回操作支持的时候,可以使用如下方式写入属性:

var moddle = bpmnJS.get('moddle');
var newCondition = moddle.create('bpmn:FormalExpression', {
body: '${ value > 100 }'
});
// write property, no undo support
sequenceFlow.conditionExpression = newCondition;

为了获得undo/redo支持,我们需要通过modeling来获取更新属性:

var modeling = bpmnJS.get('modeling');
modeling.updateProperties(sequenceFlowElement, {
conditionExpression: newCondition
});

无论通过如上哪种方式更新,其最终都会将其生成xml文件进行保存。

测试示例

如下为截取官网的一个测试设置的Demo示例供参考,示例代码如下:

import {
bootstrapModeler,
inject
} from '../TestHelper';
import coreModule from 'bpmn-js/lib/core';
import modelingModule from 'bpmn-js/lib/features/modeling';
describe('bpmn properties', function() {
var testModules = [
coreModule,
modelingModule
];
var diagramXML = require('./diagram.bpmn');
beforeEach(bootstrapModeler(diagramXML, {
modules: testModules
}));
describe('read properties', function() {
it('should read name', inject(function(elementRegistry) {
// given
var sequenceFlowElement = elementRegistry.get('SequenceFlow_1'),
sequenceFlow = sequenceFlowElement.businessObject;
// when
var name = sequenceFlow.name;
// then
expect(name).to.eql('FOO > BAR?');
}));
it('should read conditionExpression', inject(function(elementRegistry) {
// given
var sequenceFlowElement = elementRegistry.get('SequenceFlow_1'),
sequenceFlow = sequenceFlowElement.businessObject;
// when
var condition = sequenceFlow.conditionExpression;
// then
expect(condition.body).to.eql('${foo > bar}');
}));
});
describe('write properties', function() {
it('should write conditionExpression', inject(
function(elementRegistry, moddle, modeling) {
// given
var sequenceFlowElement = elementRegistry.get('SequenceFlow_2'),
sequenceFlow = sequenceFlowElement.businessObject;
var newCondition = moddle.create('bpmn:FormalExpression', {
body: '${ value > 100 }'
});
// assume
expect(sequenceFlow.conditionExpression).not.to.exist;
// when
modeling.updateProperties(sequenceFlowElement, {
conditionExpression: newCondition
});
// then
expect(sequenceFlow.conditionExpression).to.equal(newCondition);
}
));
it('should undo write conditionExpression', inject(
function(elementRegistry, moddle, modeling, commandStack) {
// given
var sequenceFlowElement = elementRegistry.get('SequenceFlow_2'),
sequenceFlow = sequenceFlowElement.businessObject;
var newCondition = moddle.create('bpmn:FormalExpression', {
body: '${ value > 100 }'
});
modeling.updateProperties(sequenceFlowElement, {
conditionExpression: newCondition
});
// when
commandStack.undo();
// then
expect(sequenceFlow.conditionExpression).not.to.exist;
}
));
});
});

示例生成的xml数据如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:vendor="http://vendor" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn">
<bpmn:process id="Process_1" isExecutable="false">
<bpmn:startEvent id="StartEvent_1">
<bpmn:outgoing>SequenceFlow_1</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:task id="Task_1">
<bpmn:incoming>SequenceFlow_1</bpmn:incoming>
<bpmn:outgoing>SequenceFlow_2</bpmn:outgoing>
</bpmn:task>
<bpmn:sequenceFlow id="SequenceFlow_1" sourceRef="StartEvent_1" targetRef="Task_1" name="FOO &gt; BAR?">
<bpmn:conditionExpression xsi:type="bpmn:tFormalExpression"><![CDATA[${foo > bar}]]></bpmn:conditionExpression>
</bpmn:sequenceFlow>
<bpmn:endEvent id="EndEvent_1">
<bpmn:incoming>SequenceFlow_2</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="SequenceFlow_2" sourceRef="Task_1" targetRef="EndEvent_1" />
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
<dc:Bounds x="173" y="102" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Task_1_di" bpmnElement="Task_1">
<dc:Bounds x="319" y="80" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="SequenceFlow_1_di" bpmnElement="SequenceFlow_1">
<di:waypoint xsi:type="dc:Point" x="209" y="120" />
<di:waypoint xsi:type="dc:Point" x="319" y="120" />
<bpmndi:BPMNLabel>
<dc:Bounds x="219" y="110" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="EndEvent_1_di" bpmnElement="EndEvent_1">
<dc:Bounds x="531" y="102" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="504" y="138" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="SequenceFlow_2_di" bpmnElement="SequenceFlow_2">
<di:waypoint xsi:type="dc:Point" x="419" y="120" />
<di:waypoint xsi:type="dc:Point" x="531" y="120" />
<bpmndi:BPMNLabel>
<dc:Bounds x="430" y="110" width="90" height="20" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>

使用bpmn-js 配置颜色

胖蔡阅读(166)

bpmn-js 阅读指南:

本篇文章介绍如何使用bpmn-js给图例配置颜色。该示例展示了如何向BPMN图添加颜色的多种不同方法。

通过层叠设置颜色

这种方式比较简单,直接通过设置图片的CSS层叠样式就可实现。

.highlight-overlay {
background-color: green; /* color elements as green */
opacity: 0.4;
pointer-events: none; /* no pointer events, allows clicking through onto the element */
}

将具有给定类的高亮显示覆盖附着到某些元素:

await viewer.importXML(diagramXML);
var overlays = viewer.get('overlays');
var elementRegistry = viewer.get('elementRegistry');
var shape = elementRegistry.get('UserTask_1');
var $overlayHtml =
$('<div class="highlight-overlay">')
.css({
width: shape.width,
height: shape.height
});
overlays.add('UserTask_1', {
position: {
top: -5,
left: -5
},
html: $overlayHtml
});

通过BPMN 2.0扩展

如果希望颜色成为BPMN 2.0图表的一部分,可以使用BPMN内置的BPMN 2.0颜色扩展。要添加颜色,请选择建模器并使用Modeling#setColor API将笔划和填充指定给BPMN元素:

var bpmnModeler = ...;
var modeling = bpmnModeler.get('modeling');
var elementsToColor = [ element1, element2 ];
// setting colors
modeling.setColor(elementsToColor, {
stroke: '#00ff00',
fill: '#ffff00'
});
// removing previously set colors
modeling.setColor(elementsToColor, null);

这些颜色在BPMN2.0图表中保持不变,并由BPMN查看器显示。

标记+CSS样式

添加一个CSS到页面,代码如下:

.highlight:not(.djs-connection) .djs-visual > :nth-child(1) {
fill: green !important; /* color elements as green */
}

该代码段确保具有高亮显示类的元素获得绿色的SVG填充。导入后,将高亮显示类作为元素标记添加到要显示为绿色的每个元素中:

await viewer.importXML(diagramXML);
var canvas = viewer.get('canvas');
canvas.addMarker('UserTask_1', 'highlight');

自定义渲染器

可以通过bpmn-js-task-priorities来自定义颜色渲染,实现组件颜色的渲染。

$ npm install bpmn-js-task-priorities

使用bpmn-js-task-priorities模块:

import Modeler from 'bpmn-js/lib/Modeler';
import prioritiesModule from 'bpmn-js-task-priorities/lib/priorities';
var modeler = new Modeler({
additionalModules: [
prioritiesModule
]
});

XML中指定任务颜色,并读取序列化的tp:colortp:priority扩展属性:

<definitions ... xmlns:tp="http://tp">
<process>
<task id="Task_1" tp:color="green" />
<task id="Task_2" tp:color="red" tp:priority="100041" />
...
</process>
</definitions>

如上即可指定渲染颜色。

【一周一荐】| bpmn.js一个基于Bpmn 2.0的前端工作流展示和绘制工具

胖蔡阅读(192)

bpmn-js 阅读指南:

bpmn.js是由开源工作流引擎camunda内部组织BPMN.IO组织开发的一款基于BPMN 2.0的工作流展示、编辑的web端工具库。由于工作流引擎activiti、flowable、camunda属于同宗分流,其工作流定义格式大致相同,所以我们可以使用bpmn.js完美融合其中任一工作流引擎。

什么是工作流引擎?

这里引用咖啡兔在《Activiti实战》一书中对于工作流的描述:工作流(Work Flow)引擎被广泛应用于各种信息化系统中,将原本散乱甚至混乱的业务梳理后制定成业务规范流程,进而约束业务的规范化处理和运转。需求人员、开发人员共同协作制定了符合BPMN 2.0规范的流程定义,之后将其部署到工作流引擎中,由它自动驱动业务流程的进行。

完整的工作流周期如上:

  • 定义:收集业务需求并转化为流程定义。
  • 发布:在系统管理(平台)中发布流程定义。
  • 执行:体的流程引擎按照事先定义的流程处理路线以任务驱动的方式执行业务流程。
  • 监控:办理任务的同时收集每个任务(Task)的结果,然后根据结果做出相应处理。
  • 优化:根据整个流程的运行过程结果分析问题的根源,然后在此基础上进一步改进,并再次开始一个新的周期。

BPMN是什么?

BPMNBusiness Process Modeling Notation的简写,中文含义是业务流程管理,是一套达成企业各种业务环节整合的全面管理模式。是由BPMN标准组织发布的,其第一版BPMN 1.0规范于2004年5月发布。经过多年的改进新的规范BPMN 2.0于2011年发布。而bpmn.js则是一款由BPMN.IO组织基于BPMN 2.0开发的一款前端开发工具集。

bpmn.js的官网对其定位也是“View and edit BPMN 2.0 diagrams in the browser.”,如下介绍如何在vue3中使用bpmn.j来绘制和展示流程图。

安装

我使用的是vue3接入bpmn.js,可以通过npm对齐进行包管理,需要注意的bpmn.js目前尚未支持typescript,所以开发过程中可能会爆红,直接忽略即可。

$ npm i --save bpmn-js 

展示流程图

bpmn支持使用读取xml文件通过xml文件反解析生成对应的svg组合的方式生成流程图。如下为一个简单的Web展示流程图的样式。

<script setup lang="ts">
import BpmnViewer from 'bpmn-js'
import { onMounted } from 'vue'
onMounted(() => {
// the diagram you are going to display
const bpmnXML: string = `<?xml version="1.0" encoding="UTF-8"?>
<bpmn2:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="diagram_Process_1706756685602" targetNamespace="http://bpmn.io/schema/bpmn" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd"><bpmn2:process id="Process_1706756685602" name="业务流程_1706756685602" isExecutable="true"><bpmn2:startEvent id="Event_1smj9ot"><bpmn2:outgoing>Flow_0jdcl54</bpmn2:outgoing></bpmn2:startEvent><bpmn2:intermediateThrowEvent id="Event_0jfhfba"><bpmn2:incoming>Flow_0jdcl54</bpmn2:incoming><bpmn2:outgoing>Flow_1ol0ntk</bpmn2:outgoing></bpmn2:intermediateThrowEvent><bpmn2:sequenceFlow id="Flow_0jdcl54" sourceRef="Event_1smj9ot" targetRef="Event_0jfhfba" /><bpmn2:endEvent id="Event_0a5hgl0"><bpmn2:incoming>Flow_1ol0ntk</bpmn2:incoming></bpmn2:endEvent><bpmn2:sequenceFlow id="Flow_1ol0ntk" sourceRef="Event_0jfhfba" targetRef="Event_0a5hgl0" /></bpmn2:process><bpmndi:BPMNDiagram id="BPMNDiagram_1"><bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1706756685602"><bpmndi:BPMNEdge id="Flow_0jdcl54_di" bpmnElement="Flow_0jdcl54"><di:waypoint x="448" y="180" /><di:waypoint x="572" y="180" /></bpmndi:BPMNEdge><bpmndi:BPMNEdge id="Flow_1ol0ntk_di" bpmnElement="Flow_1ol0ntk"><di:waypoint x="608" y="180" /><di:waypoint x="732" y="180" /></bpmndi:BPMNEdge><bpmndi:BPMNShape id="Event_1smj9ot_di" bpmnElement="Event_1smj9ot"><dc:Bounds x="412" y="162" width="36" height="36" /></bpmndi:BPMNShape><bpmndi:BPMNShape id="Event_0jfhfba_di" bpmnElement="Event_0jfhfba"><dc:Bounds x="572" y="162" width="36" height="36" /></bpmndi:BPMNShape><bpmndi:BPMNShape id="Event_0a5hgl0_di" bpmnElement="Event_0a5hgl0"><dc:Bounds x="732" y="162" width="36" height="36" /></bpmndi:BPMNShape></bpmndi:BPMNPlane></bpmndi:BPMNDiagram></bpmn2:definitions>
`
const viewer = new BpmnViewer({
container: '#bpmnCanvas',
})
viewer
.importXML(bpmnXML)
.then((result) => {
const { warnings } = result
console.log('success !', warnings)
viewer.get('canvas').zoom('fit-viewport')
})
.catch((err) => {
const { warnings, message } = err
console.log('something went wrong:', warnings, message)
})
})
</script>
<template>
<div>
测试页面
<div id="bpmnCanvas" class="bpmn-viewer" style="width: 90%; height: 70vh" />
</div>
</template>

展示结果如下:

如上,需要注意的是:

  1. bpmn-js是通过挂载元素实现canvas绘制的,需要在元素加载后进行创建操作。
  2. 导入的xml数据为txt文本数据

流程编辑器

bpmn-js提供 BpmnModeler用于实现流程编辑器的绘制工作。编辑器支持基础流程组件的绘制,同时也可以根据需要自行进行客制化组件的开发。流程编辑器除了主编辑模块还需要右侧的属性板用于配置组件属性等相关信息。

$ npm i --save bpmn-js-properties-panel  camunda-bpmn-moddle

通过将其使用tsx封装,在vue文件中直接调用使用,以便于后期的扩展和自定义组件信息。如下为创建自定义组件 bpmn.tsx

import { defineComponent, markRaw, onMounted } from 'vue'
import type { ExtractPublicPropTypes } from 'vue'
// bpmn相关引入
import 'bpmn-js/dist/assets/diagram-js.css' // 左边工具栏以及编辑节点的样式
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css'
import BpmnModeler from 'bpmn-js/lib/Modeler'
// bpmn-js-properties-panel相关
import 'bpmn-js-properties-panel/dist/assets/bpmn-js-properties-panel.css'
import propertiesPanelModule from 'bpmn-js-properties-panel'
import propertiesProviderModule from 'bpmn-js-properties-panel/lib/provider/camunda'
import camundaModdleDescriptor from 'camunda-bpmn-moddle/resources/camunda'
import './bpmn.css'
const bpmnViewProps = {
data: {
type: String,
},
}
export type BpmnViewProps = ExtractPublicPropTypes<typeof bpmnViewProps>
const BpmnView = defineComponent((props: BpmnViewProps, context) => {
onMounted(() => {
const containerEl = document.getElementById('container')
const bpmnModeler = markRaw(
new BpmnModeler({
container: containerEl,
// 添加控制板
propertiesPanel: {
parent: '#js-properties-panel',
},
// 右侧属性面板
additionalModules: [propertiesPanelModule, propertiesProviderModule, translateModule],
moddleExtensions: {
camunda: camundaModdleDescriptor,
},
}),
)
bpmnModeler.createDiagram(() => {
bpmnModeler.get('canvas').zoom('fit-viewport')
})
})
return () => {
return (
<div class="containerBox" style="position: relative">
<div id="container" style="width: calc(100vw - 750px); height: calc(100vh - 150px)"></div>
<div id="js-properties-panel" class="panel"></div>
</div>
)
}
})
export default BpmnView

组件创建完成后只需在Vue中进行引用即可:

// main.vue
<script setup lang="ts">
import { BpmnView } from '../../components'
</script>
<template>
<div>
<BpmnView />
</div>
</template>
<style scoped></style>

Unexpected aliasing of ‘this’ to local variable.eslint@typescript-eslint/no-this-alias)

胖蔡阅读(122)

代码仓库的提交Git上添加了代码规则校验ESlint,有关eslint的相关介绍可参考:Webpack 项目添加 eslint 实现代码检测功能。在提交代码的时候出现报错信息:

其原因是默认在eslint中存在如下配置:

// .eslintrc.cjs
module.exports = {
"rules": {
"@typescript-eslint/no-this-alias": "error"
}
};

官方说明如下:

为其分配一个变量而不是正确使用箭头lambdas可能是ES6之前的实践或没有很好地管理范围的症状。

Assigning a variable to this instead of properly using arrow lambdas may be a symptom of pre-ES6 practices or not managing scope well.

解决方法

知道了问题的原因所在,就方便对于问题的解决了。我的思路是有两个解决方式,无非就是我们常说的:解决提出的问题又或者是解决提出问题的人。

1、解决问题提出者

这种就简单了,直接从源头上解决,直接在当前项目的eslint配置文件中将这个规则删除,若是继承规则,就在配置文件中覆写规则,替换错误等级,如下:

// .eslintrc.cjs
module.exports = {
"rules": {
"@typescript-eslint/no-this-alias": "off" // 或者改成warn
}
};

2、解决问题本身

个人体长这种方式,这个问题本身就是为了防止出现this指向混乱引入的规则,出现这种报错也就是我们的代码本身的规范存在问题,修改函数为箭头函数,去除this的变量赋值分配,如下:

function ActivitiModdleExtension(eventBus) {
-  let that = this // older
eventBus.on(
'property.clone',
-    functoin(context) {
+    (context) => {
const newElement = context.newElement
const propDescriptor = context.propertyDescriptor
-      that.canCloneProperty(newElement, propDescriptor)
+      this.canCloneProperty(newElement, propDescriptor)
},
)
}

使用vite-plugin-require-transform解决Vue3中require报错的问题

胖蔡阅读(149)

问题

Vue3开发过程中的三方库存在使用require方式进行包的引入,这时候项目运行过程中出现报错问题:

当前使用Vite做为开发构建工具,而Vite默认不支持使用require方式进行模块导入导出。

解决方法

可通过vite-plugin-require-transform插件解决该问题。

安装插件

$ pnpm i vite-plugin-require-transform -D #或
$ npm i vite-plugin-require-transform --save-dev

应用插件

安装好插件后需要在vite中添加使用,代码如下:

// vite.config.ts
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import requireTransform from 'vite-plugin-require-transform';
export default defineConfig({
plugins: [
vue(),
vueJsx(),
requireTransform({ fileRegex: /.ts$|.vue$|.png$|.tsx$|.jpg$/ }),
],
});

【开发一个Vue3组件】 | 如何创建一个本地库项目

胖蔡阅读(151)

开发过程中需要开发一个内部的组件库来实现某些业务功能,遵循Vue3的开发习惯,我这里再当前项目创建packages包专门用来存放本地的组件库文件,本文主要是记录下整个组件库环境的搭建过程。

搭建要求

在组件库整体搭建之前,我需要明确几个组件库需要支持的功能或者说是特性,然后安装需求来一一填补搭建。具体需求如下:

  • Vue3:这是最基础的,项目组件是基于Vue3项目之下。
  • TypeScript:选用typescript作为开发语言是为了开发应用中便于查阅配置熟悉已经调用关系。
  • Vite: vue3配套构建工具。
  • pnpm: 这个是和项目同步了。
  • unocss:原子样式,便于组件样式的快速修正。
  • UI库:UI库这里自行选择,不做赘述。

初始化项目

接下来,让我们一步步按顺序来创建一个基础的组件库,首先我们需要通过pnpm来初始化一下工程,如pnpm为安装的需要提前安装下:

$ npm install -g pnpm  # 若未安装请提前安装,若已安装请忽略
$ pnpm config set registry https://registry.npmmirror.com/ # 修改为淘宝镜像
$ pnpm init # 初始化项目
D:\Projects\CareeLink\packages\flowable>pnpm init
Wrote to D:\Projects\CareeLink\packages\flowable\package.json
{
"name": "flowable",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}

项目初始化成功后,会创建如上内容的一个package.json文件。

依赖安装

项目初始化完成后,我们需要安装必要的依赖来保证项目的完整性。

1、安装基础依赖

$ pnpm install vue@latest typescript sass @vue/tsconfig @tsconfig/node18 -D

2、安装unocss

添加unocss库来实现样式绘制,安装命令如下:

$ pnpm install  unocss -D

3、安装vite及其相关构建插件

$ pnpm install vite@latest @vitejs/plugin-vue-jsx @vitejs/plugin-vue -D

创建配置文件

1、创建tsconfig.json、tsconfig.app.json、tsconfig.node.json

// tsconfig.app.json
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
"exclude": ["src/**/__tests__/*","src/**/examples/*","src/App.vue","src/main.ts"],
"compilerOptions": {
"composite": true,
"baseUrl": ".",
"paths": {
}
}
}
// tsconfig.node.json
{
"extends": "@tsconfig/node18/tsconfig.json",
"include": [
"vite.config.*",
"vitest.config.*",
"cypress.config.*",
"nightwatch.conf.*",
"playwright.config.*"
],
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "Bundler",
"types": ["node"]
}
}
// tsconfig.json
{
"files": [],
"references": [
{
"path": "./tsconfig.node.json"
},
{
"path": "./tsconfig.app.json"
}
]
}

2、创建unocss.config.ts

// unocss.config.ts
import { defineConfig, presetUno, transformerVariantGroup } from 'unocss'
export default defineConfig({
presets: [presetUno()],
transformers: [transformerVariantGroup()],
})

3、创建vite.config.ts


// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
// 引入Unocss
import Unocss from 'unocss/vite'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
vueJsx(),
Unocss()
]
})

4、创建main.ts并导入unocss

import 'unocss'
export * from './components'

5、创建一个测试组件

创建一个components目录,components目录下创建一个index.ts文件,一个button.tsx文件

// index.ts
import Button from './button.tsx'
export { Button }
// button.tsx
import  { defineComponent } from 'vue'
const Button  = defineComponent((props: any, context: any) => {
return (<div>
<Button> 测试组件</Button>
</div>)
})
Button.install = (app: unknown) => {
app.use(Button)
}
export default Button

记录一次基于Vite搭建Vue3项目的过程

胖蔡阅读(149)

Vue2已经于2023年12月31日停止维护了,2024年算是vue3的崭新的一年,我们的项目也基本从vue2逐渐向着Vue3过渡,Vue3相较于vue2有更好的开发体验,和ts的自然融合使得项目的结构、功能拆分变得更加的清晰;组合式声明有种MVC向着MVP、MVVM转变的错觉;而Vite的使用使得我们的编译速度产生质的飞跃。总体来说,Vue3是一个不错的选择,今天这边文章主要就是来记录一下如何创建一个Vue3的项目。

环境配置

Vue3的安装其实对于环境的要求不高,只需要我们的电脑安装了nodejs即可,这边有关npm、yarn、pnpm等安装工具的安装请自选搜索选择。安装前可参考:修改yarn和npm为国内镜像源 ,修改npm为国内源,提高安装速度。如下是各个包管理工具的安装

$ npm install -g yarn  # 全局安装yarn
$ npm install -g cnpm  # 全局安装cnpm
$ npm install -g pnpm  # 全局安装pnpm 

创建Vue项目

Vue项目有两种快捷创建的方式,可以任意选择自己喜欢的方式:

1、使用vue-cli创建

通过全局安装Vue-cli工具链来快捷创建Vue项目,vue-cli是一个基于vuejs的交互式脚手架:

$ npm install -g @vue/cli  # 全局下载vue-cli
$ vue create vue3-demo # 创建vue3-demo项目
Vue CLI v5.0.8
? Please pick a preset: (Use arrow keys)
> Default ([Vue 3] babel, eslint)  # 选择vue3
Default ([Vue 2] babel, eslint) 
Manually select features 
Vue CLI v5.0.8
✨  Creating project in D:\Projects\CareeLink\packages\flowable\vue3.
⚙️  Installing CLI plugins. This might take a while...
yarn install v1.22.19
info No lockfile found.
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
success Saved lockfile.
Done in 82.89s.
🚀  Invoking generators...
📦  Installing additional dependencies...
yarn install v1.22.19
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Saved lockfile.
Done in 18.36s.
⚓  Running completion hooks...
📄  Generating README.md...
🎉  Successfully created project vue3.      
👉  Get started with the following commands:
$ cd vue3
$ yarn serve

这样Vue3项目是创建成功了,但是需要知道的是vue-cli是基于webpack的,不是vite,这种方式不适合我,接下来看下一种方式。

2、使用vue@latest创建

这个也是目前Vue3官网上推荐的的方式,这种方式创建的Vue3项目是基于Vite构建的。

$ npm init vue@latest

使用这种方式是基于create-vue脚手架构建工具创建的项目,我们可以选择我们需要的配置项目来实现项目的创建。

如此就创建了一个基础的Vue3项目了。

Vue3工程中接入tailwindcss

胖蔡阅读(154)

之前的vue项目中需要写入样式,发现不支持原子样式,写起来不是特别方便,布局也麻烦,就把tailwind css接入到这个项目里了,这里记录下整体的接入流程,不得不说tailwind使用起来是真的简单。

安装

接入tailwind这里使用和postcss配合使用:

$ yarn  add tailwind postcss autoprefixer -D

这里接入的tailwind是基于postcss的基础上的,参考:Tailwindcss 配置使用指南。autoprefixer 适用于自动解析css文件并自动添加前缀适配的(前缀如: -webkit

配置

安装完成后,我们可以通过npx来生成对应的配置文件:

$ npx tailwindcss init -p
Created Tailwind CSS config file: tailwind.config.js
Created PostCSS config file: postcss.config.js

这样我们会在当前目录生成tailwind.config.jspostcss.config.js两个配置文件,配置设置如下:

// tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./src/**/*.{js,ts,jsx,tsx,mdx,vue}',
],
theme: {
extend: {},
},
plugins: [],
}
// postcss.config.js
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

配置后我们需要在入口引入tailwindcss的样式文件,可以通过两种方式引入

1、直接导入tailwind

// main.ts
import "tailwindcss/tailwind.css";

2、导入全局css文件,通过css导入tailwind

// global.css
@import "tailwindcss/base";
@import "tailwindcss/components";
@import "tailwindcss/utilities";

使用

配置完成后,我们就可以直接在如上配置的路径下使用tailwind了,使用代码如下:

<div class="flex-row flex">
<div class="flex-1">
<el-form-item>
<el-input placeholder="文章标题" />
</el-form-item>
<el-form-item>
<cl-editor-wang />
</el-form-item>
</div>
<div class="min-w-[300px]">
<div class="info">44444</div>
</div>

实现效果如下:

Next.js 国际化(i18n)教程

胖蔡阅读(200)

Next.js是由Vercel(原 ZEIT)创建的开源框架。它建立在 React 之上,为 React 组件的服务器端渲染(SSR) 提供了一个开箱即用的解决方案。此外,它还支持静态站点生成(SSG),这可以帮助我们立即构建超快速且用户友好的网站。虽然是一个相对年轻的框架,但它具有良好的国际化基础与现有的 i18n 库很好地互补。在接下来的章节中,您将了解如何在 Next.js 应用程序中设置国际化。

新建Next.js项目

首先,让我们使用create-next-app命令行工具创建一个新的 Next.js 项目。

npx create-next-app nextjs-i18n-example

添加 React Intl 依赖

正如我们之前提到的,Next.js 可以很好地与现有的 i18n 库(react-intl、lingui、next-intl等)配合使用。在本教程中,我们将使用react-intl

$ cd nextjs-i18n-example 
$ npm i react-intl

为国际化路由添加配置

翻译和路由是国际化的两个主要支柱。之前添加的react-intl库将处理翻译和格式化。在路由方面,Next.js 有内置支持。这种内置支持提供了两个选项,子路径路由和域路由。在本教程中,我们将使用子路径路由,因为它不太复杂,并且对于普通网站来说更常见。为此,让我们使用i18n配置更新next.config.js文件。

/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
i18n: {
// The locales you want to support in your app
locales: ["en", "fr"],
// The default locale you want to be used when visiting a non-locale prefixed path e.g. `/hello`
defaultLocale: "en",
},
};
module.exports = nextConfig;

国际化路由从Next.js 10开始可用。

创建本地化文件

下一个重要的事情是添加本地化文件。为此,让我们创建一个lang目录。在其中添加两个 JSON 文件:en.jsonfr.json。这些文件将分别保存英语和法语的翻译。下面,您可以看到添加上述文件后的项目结构。

nextjs-i18n-example
|-- lang
|   |-- en.json
|   |-- fr.json
|-- pages
|   |-- api
|   |-- _app.js
|   |-- index.js
|   |-- ...
|-- public
|-- ...
|-- package.json
|-- package-lock.json

然后,在本地化文件中填写我们稍后将使用的消息。

en.json文件:

{
"page.home.head.title": "Next.js i18n example",
"page.home.head.meta.description": "Next.js i18n example - English",
"page.home.title": "Welcome to <b>Next.js i18n tutorial</b>",
"page.home.description": "You are currently viewing the homepage in English 🚀"
}

fr.json文件:

{
"page.home.head.title": "Next.js i18n exemple",
"page.home.head.meta.description": "Next.js i18n exemple - Français",
"page.home.title": "Bienvenue à <b>Next.js i18n didacticiel</b>",
"page.home.description": "Vous consultez actuellement la page d'accueil en Français 🚀"
}

在 Next.js 项目中配置 react-intl

国际化的路由和本地化文件只是任务的第一部分。第二部分是设置react-intl库。下面,您可以看到_app.js文件中发生了哪些更改。

import { useRouter } from "next/router";
import { IntlProvider } from "react-intl";
import en from "../lang/en.json";
import fr from "../lang/fr.json";
import "../styles/globals.css";
const messages = {
en,
fr,
};
function MyApp({ Component, pageProps }) {
const { locale } = useRouter();
return (
<IntlProvider locale={locale} messages={messages[locale]}>
<Component {...pageProps} />
</IntlProvider>
);
}
export default MyApp;

为 i18n 适配页面

我们做了大部分工作。最后一步是将所有这些放在一起。因此,我们将更新 pages 目录下的index.js文件。下面,您可以找到两种访问本地化消息的方法,命令式和声明式。我们已经介绍了这两种使用方式、格式化选项以及类似的另一个帖子。

index.js文件:

import Head from "next/head";
import Link from "next/link";
import { useRouter } from "next/router";
import { FormattedMessage, useIntl } from "react-intl";
import styles from "../styles/Home.module.css";
export default function Home(props) {
const { locales } = useRouter();
const intl = useIntl();
const title = intl.formatMessage({ id: "page.home.head.title" });
const description = intl.formatMessage({ id: "page.home.head.meta.description" });
return (
<div className={styles.container}>
<Head>
<title>{title}</title>
<meta name="description" content={description} />
<link rel="icon" href="/favicon.ico" />
{/* Add hreflang links */}
<link rel="alternate" href="http://example.com" hrefLang="x-default" />
<link rel="alternate" href="http://example.com" hrefLang="en" />
<link rel="alternate" href="http://example.com/fr" hrefLang="fr" />
</Head>
<header>
<div className={styles.languages}>
{[...locales].sort().map((locale) => (
<Link key={locale} href="/" locale={locale}>
{locale}
</Link>
))}
</div>
</header>
<main className={styles.main}>
<h1 className={styles.title}>
<FormattedMessage id="page.home.title" values={{ b: (chunks) => <b>{chunks}</b> }} />
</h1>
<p className={styles.description}>
<FormattedMessage id="page.home.description" />
</p>
</main>
</div>
);
}

恭喜! 🎉

您已在 Next.js 项目中成功设置国际化。

您可以在原始帖子中找到更多详细信息和示例。

本文中使用的所有代码示例都可在GitHub 存储库上找到。

我希望您发现本指南有用。

使用Docker部署nextjs应用

胖蔡阅读(176)

最近使用nextjs网站开发,希望使用docker进行生产环境的部署,减少环境的依赖可重复部署操作。我采用的是Dockerfile编写应用镜像方式+ docker-compose实现容器部署的功能。

编写Dockerfile文件

1、在项目根目录创建Dockerfile文件

构建应用镜像的基础是创建对应的dockerfile文件,常规的我们会选择将dockerfile文件创建在项目的根目录,当然也可以自定义指定位置,只要在之后的构建过程指定路径就可以了

2、dockerfile编写依赖

我这里为了减少容器的大小,所以dockerfile使用了多阶段构建的方式实现,首先是依赖阶段实现依赖库的下载,具体如下:

FROM node:18-alpine AS deps
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories
RUN apk update
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json yarn.lock ./
RUN yarn config set registry https://registry.npm.taobao.org
RUN yarn install --frozen-lockfile

需要注意:

  • nodejs对应版本,查看package.json中的版本要求
  "engines": {
"node": ">=18.17.0"
}
  • 我这里将软件源修改为国内镜像,默认的国外镜像可能导致软件无法更新下载
  • 更新前端库镜像为淘宝镜像

3、构建阶段

由于已经完成依赖的下载,构建阶段只需要进行编译打包即可:

FROM node:18-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN yarn config set registry https://registry.npm.taobao.org
RUN yarn build && yarn install --production 

4、运行阶段

完成构建后,就需要我们配置容器的基础配置,如环境变量、工作目录、监听端口、应用启动命令等:

FROM node:18-alpine AS runner
WORKDIR /app
ENV NEXT_TELEMETRY_DISABLED 1
ENV NODE_ENV production
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
RUN yarn config set registry https://registry.npm.taobao.org
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next ./.next
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json
USER nextjs
EXPOSE 3000
ENV PORT 3000
CMD ["node_modules/.bin/next", "start"]

运行配置

1、编写docker-compse.yml文件

完成dockerfile文件编写后,我们就需要通过编写docker-compose.yml配置文件来启动容器了。我这里是使用nginx来部署,配置如下:

version: "3.1"
services:
db:
image: mysql:8
command:
--default-authentication-plugin=mysql_native_password
--sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION
--group_concat_max_len=102400
restart: unless-stopped
volumes:
- ./data/mysql/:/var/lib/mysql/
environment:
TZ: Asia/Shanghai # 指定时区
MYSQL_ROOT_PASSWORD: "nextjs" # 配置root用户密码
MYSQL_DATABASE: "nextjs" # 业务库名
MYSQL_USER: "nextjs" # 业务库用户名
MYSQL_PASSWORD: "nextjs" # 业务库密码
ports:
- 3306:3306
nextjs:
build: .
ports:
- "3000:3000"
environment:
TZ: Asia/Shanghai # 指定时区
container_name: nextjs
volumes:
- ./:/app/
depends_on:
- db
restart: unless-stopped
nginx:
image: nginx:mainline-alpine
container_name: nginxserver
restart: unless-stopped
ports:
- "80:80"
volumes:
- ./.next:/var/www/html
- ./nginx-conf:/etc/nginx/conf.d
networks:
nodeapp-network:
driver: bridge

2、添加nginx配置

如上可以看出,我一共创建了三个容器,一个数据容器db,一个nextjs容器是我们的应用还有一个nginx容器是web服务,nginx里还需要将我们的配置文件映射到容器内,nginx-conf内容如下:

server {
listen 80;
listen [::]:80;
root /var/www/html;
index index.html index.htm index.nginx-debian.html;
server_name patientplatypus.com www.patientplatypus.com localhost;
# location /back {
#   proxy_set_header X-Real-IP $remote_addr;
#   proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
#   proxy_set_header Host $http_host;
#   proxy_set_header X-NginX-Proxy true;
#   proxy_http_version 1.1;
#   proxy_set_header Upgrade $http_upgrade;
#   proxy_set_header Connection "upgrade";
#   proxy_pass http://nodejs:8000;
# }
location / {
proxy_pass http://nextjs:3000;
}
location ~ /.well-known/acme-challenge {
allow all;
root /var/www/html;
}
}

3、启动容器

只需要我们的宿主机器安装了docker-compse即可(docker compose 使用教程)直接使用如下命令:

$docker-compose up -d