Migration to Vue 3
Overview
Starting with Workflow Engine 20.0.0 templates are based on Vue 3 which leads to some code changes.
ElementUI is replaced by Element Plus, which also affects component usage.
This upgrade drops Internet Explorer support.
Vue3
Due to Vue 3 reactivity and template compiler changes, some adjustments are required in template definitions, syntax, and the event system.
Data shape
In Vue 3 the full reactive state shape should exist on first render. Each template now has a <template_name>_Data factory alongside <template_name>_Init.
The data factory must return the entire state with default values. In init methods you have me.VueConfig.state — a reactive object holding state.
Before
function customforms_basicsplugin_sendemailform_Init(me) {
  me.VueConfig.methods.UpdateLanguage = function () {
    me.VueConfig.data = Object.assign(me.VueConfig.data, {
      labels: WorkflowDesignerConstants.EditParametersFormlabel,
      customLabels: WorkflowDesignerConstants.EditParametersFormlabel.CustomForms.SendEmail,
      ButtonTextSave: WorkflowDesignerConstants.ButtonTextSave,
      ButtonTextCancel: WorkflowDesignerConstants.ButtonTextCancel,
      parameterFromProcessColor: WorkflowDesignerConstants.ParameterFromProcessColor,
    })
  }
  me.VueConfig.methods.UpdateLanguage();
  me.VueConfig.data = Object.assign(me.VueConfig.data, {
    customTitle: undefined,
    readonly: false,
    parameterFromProcess: {
      MailServerPort: false,
      MailServerSsl: false,
      IsHTML: false
    },
    FormData: {},
    Field: {},
    useAuth: false
  });
}
After
function customforms_basicsplugin_sendemailform_Data(me) {
  return {
    customTitle: undefined,
    readonly: false,
    parameterFromProcess: {
      MailServerPort: false,
      MailServerSsl: false,
      IsHTML: false
    },
    FormData: {},
    Field: {
      Name: {
        Name: '',
        Title: '',
        Comment: '',
        Type: '',
        IsRequired: '',
        DefaultValue: null
      },
      State: { /* Same structure as Name field */},
      MailServer: { /* Same structure as Name field */},
      MailServerPort: { /* Same structure as Name field */},
      MailServerFrom: { /* Same structure as Name field */},
      To: { /* Same structure as Name field */},
      CcList: { /* Same structure as Name field */},
      BccList: { /* Same structure as Name field */},
      ReplyToList: { /* Same structure as Name field */},
      Subject: { /* Same structure as Name field */},
      MailServerSsl: { /* Same structure as Name field */},
      IsHTML: { /* Same structure as Name field */},
      MailServerLogin: { /* Same structure as Name field */},
      MailServerPass: { /* Same structure as Name field */},
      Body: { /* Same structure as Name field */}
    },
    useAuth: false
  }
}
function customforms_basicsplugin_sendemailform_Init(me) {
  const {VueConfig} = me;
  const {state: data, methods} = VueConfig;
    
  methods.UpdateLanguage = function () {
    Object.assign(data, {
      labels: WorkflowDesignerConstants.EditParametersFormlabel,
      customLabels: WorkflowDesignerConstants.EditParametersFormlabel.CustomForms.SendEmail,
      ButtonTextSave: WorkflowDesignerConstants.ButtonTextSave,
      ButtonTextCancel: WorkflowDesignerConstants.ButtonTextCancel,
      parameterFromProcessColor: WorkflowDesignerConstants.ParameterFromProcessColor,
    })
  }
    
  methods.UpdateLanguage();
}
Binding to undefined
Expressions that resolve to undefined on the first render may cause warnings and break reactivity. Use conditional rendering to ensure the value exists before binding.
<el-select
  v-if="!!transition?.condition?.Type"
  v-model="transition.condition.Type"
Accessing possibly undefined properties
Vue 3 no longer silently ignores access errors. Use optional chaining when reading deep properties that may not exist yet.
Before
<el-form-item
  :label="customLabels.LoopName"
  :prop="Field.LoopName.Name"
<!-- ... -->
After
<el-form-item
  :label="customLabels.LoopName"
  :prop="Field?.LoopName?.Name"
<!-- ... -->
Before
<table v-if="FormData.PreExecutionImplementation.length > 0" class="WorkflowDesignerTable">
After
<table v-if="FormData.PreExecutionImplementation?.length > 0" class="WorkflowDesignerTable">
Undefined values in loops
Looping over an undefined value throws. Split v-if and v-for so Vue won't attempt to iterate when data is missing.
For some elements you must move v-for to a wrapping <template>.
Before
<tr v-for="(item, index) in items" :key="index" v-if="showPredefined || item.IsPredefined!=true"
    :class="dragOverIndex == index && dragOverBlock  == item ? 'dragOver' : ''"
    @dragend="dragend($event)" @dragover="dragover(item, index, $event)"
    @dragstart="dragstart(index, $event)">
After
<template v-for="(item, index) in items" :key="index">
  <tr v-if="showPredefined || item.IsPredefined !== true"
      :class="dragOverIndex == index && dragOverBlock  == item ? 'dragOver' : ''"
      @dragend="dragend($event)" @dragover="dragover(item, index, $event)"
      @dragstart="dragstart(index, $event)">
Native events
The .native modifier was removed in Vue 3. Attach events to components via the component's own events or on the root element inside the component.
  <!-- Do not use -->  
  @keyup.native
Template syntax change
Inline callbacks
Inline callback now are deprecated, they create a new function on every render. Move the logic into methods.
Before
<span @click="function(){FormData.Post = parameterFromProcess.Post ? false : null; changeParameterInput('Post')}">@</span>
After
<span @click="toggleStoreResponse">@</span>
methods.togglePost = function () {
  data.FormData.Post = data.parameterFromProcess.Post ? false : null;
  methods.changeParameterInput('Post');
}
Element plus components
Please also check official migration guide and changelist.
Props
All non-string props must be bound as expressions.
Before
<el-input rows="5"></el-input>
After
<el-input :rows="5"></el-input>
Before
<div draggable="true"></div>
After
<div :draggable="true"></div>
The size="mini" value has been replaced with size="small" in components that support size.
Button
The type="text" prop has been replaced by link attribute.
Before
<el-button type="text" :icon="Edit" />
After
<el-button link :icon="Edit" />
Icons
Icons though predefined classes are removed. Consult documentation for complete changelist.
Now we use @element-plus/icons-vue package.
All icons registered with Icon prefix, ie Element Plus Refresh icon become IconRefresh and <icon-reresh /> inside WFE templates.
Before
<div class="WorkflowDesignerToolbarIcon"><i class="el-icon-circle-plus" style="color: #28ae60;"></i></div>
After
<div class="WorkflowDesignerToolbarIcon">
  <icon-circle-plus style="color: #28ae60;"></icon-circle-plus>
</div>
Before
<el-button v-if="!readonly && !itemHasComment" circle icon="el-icon-s-comment" @click="showUserComment()"></el-button>
After
<el-button v-if="!readonly && !itemHasComment" circle icon="IconRefresh" @click="showUserComment()">Label</el-button>
If icon is not centered in some case you may need to wrap icon in <el-icon />.
<el-button v-if="!readonly && !itemHasComment" circle @click="showUserComment()">
  <el-icon>
    <icon-refresh/>
  </el-icon>
</el-button>
<el-icon>
  <div v-html="icons.menu"></div>
</el-icon>
Before
<i class="el-icon-info"></i>
After
<el-icon>
  <icon-info-filled />
</el-icon>
Checkbox
Label now set via value prop.
Before
<el-checkbox v-for="persist in persists" :key="persist" :disabled="readonly" :label="persist">{{ persist }}</el-checkbox>
After
<el-checkbox v-for="persist in persists" :key="persist" :disabled="readonly" :value="persist">{{ persist }}</el-checkbox>
Value binding replace with v-model
Before
<el-checkbox :value="FormData.EnableOtherwise"></el-checkbox>
After
<el-checkbox v-model="FormData.EnableOtherwise"></el-checkbox>
RadioGroup
Label now set via value prop.
Before
<el-radio-group v-model="FormData.Alignment" size="small">
  <el-radio-button label="left">
After
<el-radio-group v-model="FormData.Alignment" size="small">
  <el-radio-button value="left">
Dialog
- property before-closeis removed and should not be needed.
- visibility now controlled via v-model.
- slot is replaced with template
Before
<el-dialog
  :before-close="function() {visible = false}"
  :title="title"
  :visible="visible"
  width="30%"
  append-to-body>
  <span v-html="message"></span>
  <span slot="footer" class="dialog-footer">
<!-- ... -->
After
<el-dialog
  :title="title"
  v-model="visible"
  class="Dialog"
  append-to-body>
  <span v-html="message"></span>
  <template #footer>
    <footer>
<!-- ... -->
Slot and templates
Slots must be replaced with template with id set to slot name.
Before
<span slot="footer" class="dialog-footer">
  <template>
    <!-- ... -->
  </template>
</span>
After
<template #footer>
  <span class="dialog-footer">
    <!-- ... -->
  </span>
</template>
Slot scope
Slot scope now must be defined with v-slot.
Before
<child-component>
  <template slot-scope="scope">
    {{ scope.item }}
  </template>
</child-component>
After
<child-component>
  <template v-slot:default="scope">
    {{ scope.item }}
  </template>
</child-component>
Or this way
<template v-slot:default="{ item }">
  {{ item }}
</template>
Tooltip
In some case tooltip content may need an extra wrapper.
Before
<el-tooltip v-if="!settings.readonly" :content="labels.Label">
  <el-popover placement="bottom" trigger="click" width="310">
    <!-- ... -->
  </el-popover>
</el-tooltip>
After
<el-tooltip v-if="!settings.readonly" :content="labels.Label">
  <div>
    <el-popover placement="bottom" trigger="click" width="310">
      <!-- ... -->
    </el-popover>
  </div>
</el-tooltip>
Example: Weather Activity
Based on the React integration guide.
WeatherActivity.html
<h3>{{ labels.Title }}</h3>
<el-form ref="form"
         :model="FormData"
         class="WorkflowDesignerWindowForm"
         label-position="top"
         label-width="150px">
  <div class="SettingsWithPadding">
    <div class="el-form--inline el-form--label-top">
      <el-form-item :label="labels.Name" :rules="activityNameRules()" class="el-form-item" prop="Name" style="flex-grow: 1;">
        <el-input v-model="FormData.Name" :placeholder="labels.Name" :readonly="readonly" @input="nameOnChange"></el-input>
      </el-form-item>
      <el-form-item :label="labels.State" class="el-form-item" prop="State" style="flex-grow: 1;">
        <el-input v-model="FormData.State" :placeholder="labels.State" :readonly="readonly"></el-input>
      </el-form-item>
    </div>
    <el-form-item size="small">
      <el-checkbox v-model="FormData.IsInitial" :disabled="readonly" :label="labels.IsInitial" name="type"
                   @change="onInitialChange"></el-checkbox>
      <el-checkbox v-model="FormData.IsFinal" :disabled="readonly" :label="labels.IsFinal" name="type"
                   @change="onFinalChange"></el-checkbox>
      <el-checkbox v-model="FormData.IsForSetState" :disabled="readonly" :label="labels.IsForSetState" name="type"></el-checkbox>
      <el-checkbox v-model="FormData.IsAutoSchemeUpdate" :disabled="readonly" :label="labels.IsAutoSchemeUpdate" name="type"></el-checkbox>
      <el-checkbox v-if="expertMode" v-model="disableAllPersist" :disabled="readonly" :indeterminate="isIndeterminate"
                   :label="labels.DisablePersist" @change="handleCheckAllPersistsChange"></el-checkbox>
    </el-form-item>
    <el-checkbox-group v-if="expertMode && (disableAllPersist || isIndeterminate)" v-model="checkedPersists"
                       @change="handleCheckedPersistsChange">
      <el-checkbox v-for="persist in persists" :key="persist" :disabled="readonly" :label="persist" :value="persist">{{ persist }}</el-checkbox>
    </el-checkbox-group>
  </div>
  <div style="margin-bottom: 10px;">
    <h4 class="WorkflowDesignerTitleWithCreate Underline">
      {{labels.Implementation}}
      <a v-if="!readonly" @click="addActionRow(FormData.Implementation)">{{ButtonTextCreate}}</a>
    </h4>
    <table v-if="FormData.Implementation.length > 0" class="WorkflowDesignerTable">
      <tr>
        <th></th>
        <th>{{labels.ImpAction}}</th>
        <th>{{labels.ImpActionParameter}}</th>
      </tr>
      <tr v-for="(imp, index) in FormData.Implementation" :key="index" :class="dragOverIndex == index && dragOverBlock  == imp ? 'dragOver' : ''"
          @dragend="dragend($event)"
          @dragover="dragover(imp, index, $event)"
          @dragstart="dragstart(index, $event, FormData.Implementation)">
        <td :draggable="!readonly" class='WorkflowDesignerTableMoveCol'>
          <div v-if="!readonly" class='WorkflowDesignerTableMoveButton'></div>
        </td >
        <td >
          <el-select
            v-model="imp.ActionName"
            :class="validateField(notEmpty, imp.ActionName) ? 'WorkflowDesignerInputError' : ''"
            :disabled="readonly"
            :placeholder="labels.ImpAction"
            :title="validateField(notEmpty, imp.ActionName)"
            clearable
            filterable
            style="width: 100%;">
            <el-option v-for="item in actions" :key="item" :label="item" :value="item"></el-option>
          </el-select>
        </td>
        <td>
          <el-autocomplete v-model="imp.ActionParameter" :fetch-suggestions="querySearch" :placeholder="labels.ImpActionParameter" :readonly="readonly"
                           clearable style="width: 100%;" @focus="setCurrentItem(imp)"></el-autocomplete>
        </td>
        <td v-if="!readonly" class="WorkflowDesignerTableEditButtons Double">
          <el-button-group>
            <el-button :class="'WorkflowDesignerTableCodeActionsButton ' + (editItem == imp ? 'is-active' : '')"
                       @click="showjson('ActionParameter', imp)"></el-button>
            <el-button class="WorkflowDesignerTableDeleteButton" @click="removeRow(FormData.Implementation, index)"></el-button>
          </el-button-group>
        </td>
      </tr>
    </table>
  </div>
  <div v-if="expertMode" style="margin-bottom: 10px;">
    <h4 class="WorkflowDesignerTitleWithCreate Underline">
      {{labels.PreExecutionImplementation}}
      <a v-if="!readonly" @click="addActionRow(FormData.PreExecutionImplementation)">{{ButtonTextCreate}}</a>
    </h4>
    <table v-if="FormData.PreExecutionImplementation.length > 0" class="WorkflowDesignerTable">
      <tr>
        <th></th>
        <th>{{labels.ImpAction}}</th>
        <th>{{labels.ImpActionParameter}}</th>
      </tr>
      <tr v-for="(imp, index) in FormData.PreExecutionImplementation" :key="index"
          :class="dragOverIndex == index && dragOverBlock  == imp ? 'dragOver' : ''"
          @dragend="dragend($event)"
          @dragover="dragover(imp, index, $event)"
          @dragstart="dragstart(index, $event, FormData.PreExecutionImplementation)">
        <td :draggable="!readonly" class='WorkflowDesignerTableMoveCol'>
          <div v-if="!readonly" class='WorkflowDesignerTableMoveButton'></div>
        </td>
        <td>
          <el-select
            v-model="imp.ActionName"
            :class="validateField(notEmpty, imp.ActionName) ? 'WorkflowDesignerInputError' : ''"
            :disabled="readonly"
            :placeholder="labels.ImpAction"
            :title="validateField(notEmpty, imp.ActionName)"
            clearable
            filterable
            style="width: 100%;">
            <el-option v-for="item in actions" :key="item" :label="item" :value="item"></el-option>
          </el-select>
        </td>
        <td>
          <el-autocomplete v-model="imp.ActionParameter" :fetch-suggestions="querySearch" :placeholder="labels.ImpActionParameter" :readonly="readonly"
                           clearable style="width: 100%;" @focus="setCurrentItem(imp)"></el-autocomplete>
        </td>
        <td v-if="!readonly" class="WorkflowDesignerTableEditButtons Double">
          <el-button-group>
            <el-button :class="'WorkflowDesignerTableCodeActionsButton ' + (editItem == imp ? 'is-active' : '')"
                       @click="showjson('ActionParameter', imp)"></el-button>
            <el-button class="WorkflowDesignerTableDeleteButton" @click="removeRow(FormData.PreExecutionImplementation, index)"></el-button>
          </el-button-group>
        </td>
      </tr>
    </table>
  </div>
  <div v-if="expertMode" style="margin-bottom: 10px;">
    <h4 class="WorkflowDesignerTitleWithCreate Underline">
      {{labels.Annotations}}
      <a v-if="!readonly" @click="addAnnotationRow(FormData.Annotations)">{{ButtonTextCreate}}</a>
    </h4>
    <table v-if="FormData.Annotations.length > 0" class="WorkflowDesignerTable">
      <tr>
        <th></th>
        <th>{{ labels.AnnotationName }}</th>
        <th>{{ labels.AnnotationValue }}</th>
      </tr>
      <tr v-for="(annotation, index) in FormData.Annotations" :key="index"
          :class="dragOverIndex == index && dragOverBlock  == annotation ? 'dragOver' : ''"
          @dragend="dragend($event)"
          @dragover="dragover(annotation, index, $event)"
          @dragstart="dragstart(index, $event, FormData.Annotations)">
        <td :draggable="!readonly" class='WorkflowDesignerTableMoveCol'>
          <div v-if="!readonly" class='WorkflowDesignerTableMoveButton'></div>
        </td>
        <td>
          <el-form-item :inline-message="true" :prop="'Annotations.' + index + '.Name'" :rules="annotationNameRules()" class="el-form-item"
                        style="flex-grow: 1;">
            <el-input
              v-model="annotation.Name"
              :class="validateField(notEmpty, annotation.Name) ? 'WorkflowDesignerInputError' : ''"
              :placeholder="labels.AnnotationName"
              :title="validateField(notEmpty, annotation.Name)"
              clearable >
            </el-input>
          </el-form-item>
        </td>
        <td >
          <el-input
            v-model="annotation.JsonValue"
            :placeholder="labels.AnnotationValue"
            :readonly="readonly"
            clearable>
          </el-input>
        </td>
        <td v-if="!readonly" class="WorkflowDesignerTableEditButtons Double">
          <el-button-group>
            <el-button :class="'WorkflowDesignerTableCodeActionsButton ' + (editItem == annotation ? 'is-active' : '')"
                       @click="showjson('JsonValue', annotation)"></el-button>
            <el-button class="WorkflowDesignerTableDeleteButton" @click="removeRow(FormData.Annotations, index)"></el-button>
          </el-button-group>
        </td>
      </tr>
    </table>
  </div>
  <div v-if="expertMode" style="margin-bottom: 10px;">
    <h4 style="padding-bottom: 1px;border-bottom: 1px solid rgba(34,36,38,.15);">{{ labels.TimeoutsLabel }}</h4>
    <table class="WorkflowDesignerTable">
      <tr>
        <th>
          <el-tooltip :content="labels.ExecutionTimeoutWarning" placement="top-start">
            <div>
              {{ labels.ExecutionTimeoutLabel }}<i class="el-icon-warning"
                                                   style="display: contents; color: #FF3300; opacity: 0.6; margin-left: 3px;"></i>
            </div>
          </el-tooltip>
        </th>
        <th>{{ labels.TypeLabel }}</th>
        <th v-if="FormData.ExecutionTimeout.Type == 'SetActivity'">{{ labels.ActivityLabel }}</th>
        <th v-if="FormData.ExecutionTimeout.Type == 'SetState'">{{ labels.StateLabel }}</th>
        <th v-if="FormData.ExecutionTimeout.Type == 'Retry'">{{ labels.RetryCountLabel }}</th>
      </tr>
      <tr>
        <td :label="labels.ExecutionTimeoutLabel" :readonly="readonly">
          <el-input v-model="FormData.ExecutionTimeout.Timer" :class="(FormData.ExecutionTimeout.Type ? validateField(notEmpty, FormData.ExecutionTimeout.Timer) : false) ? 'WorkflowDesignerInputError' : ''"
                    :placeholder="labels.ExecutionTimeoutLabel"
                    :readonly="readonly"
                    :title="FormData.ExecutionTimeout.Type ? validateField(notEmpty, FormData.ExecutionTimeout.Timer):''"
                    clearable></el-input>
        </td>
        <td >
          <el-select
            v-model="FormData.ExecutionTimeout.Type"
            :class="(FormData.ExecutionTimeout.Timer ? validateField(notEmpty, FormData.ExecutionTimeout.Type) : false) ? 'WorkflowDesignerInputError' : ''"
            :disabled="readonly"
            :placeholder="labels.TypeLabel"
            :title="FormData.ExecutionTimeout.Timer ? validateField(notEmpty, FormData.ExecutionTimeout.Type):''"
            clearable
            filterable
            style="width: 100%;"
            @change="delete  FormData.ExecutionTimeout.NameForSet">
            <el-option key="SetActivity" :label="labels.SetActivityLabel" value="SetActivity"></el-option>
            <el-option key="SetState" :label="labels.SetStateLabel" value="SetState"></el-option>
            <el-option key="Retry" :label="labels.RetryLabel" value="Retry"></el-option>
          </el-select>
        </td>
        <td v-if="FormData.ExecutionTimeout.Type == 'SetActivity'" >
          <el-select
            v-model="FormData.ExecutionTimeout.NameForSet"
            :class="validateField(notEmpty, FormData.ExecutionTimeout.NameForSet) ? 'WorkflowDesignerInputError' : ''"
            :disabled="readonly"
            :placeholder="labels.ActivityLabel"
            :title="validateField(notEmpty, FormData.ExecutionTimeout.NameForSet)"
            clearable
            filterable
            style="width: 100%;">
            <el-option  v-for="item in activities" :key="item" :label="item" :value="item"></el-option>
          </el-select>
        </td>
        <td v-if="FormData.ExecutionTimeout.Type == 'SetState'">
          <el-select
            v-model="FormData.ExecutionTimeout.NameForSet"
            :class="validateField(notEmpty, FormData.ExecutionTimeout.NameForSet) ? 'WorkflowDesignerInputError' : ''"
            :disabled="readonly"
            :placeholder="labels.StateLabel"
            :title="validateField(notEmpty, FormData.ExecutionTimeout.NameForSet)"
            clearable
            filterable
            style="width: 100%;">
            <el-option v-for="item in states" :key="item" :label="item" :value="item"></el-option>
          </el-select>
        </td>
        <td v-if="FormData.ExecutionTimeout.Type == 'Retry'">
          <el-input-number
            v-model="FormData.ExecutionTimeout.RetryCount" :class="validateField(notEmpty, FormData.ExecutionTimeout.RetryCount) ? 'WorkflowDesignerInputError' : ''"
            :placeholder="labels.RetryCountLabel"
            :readonly="readonly"
            :title="validateField(notEmpty, FormData.ExecutionTimeout.RetryCount)" clearable controls-position="right" style="width: 100%;"></el-input-number>
        </td>
        <td v-if="!readonly" class="WorkflowDesignerTableEditButton">
          <el-button class="WorkflowDesignerTableDeleteButton" @click="FormData.ExecutionTimeout = {RetryCount:'1'}"></el-button>
        </td>
      </tr>
    </table>
    <table class="WorkflowDesignerTable">
      <tr>
        <th>{{labels.IdleTimeoutLabel}}</th>
        <th>{{labels.TypeLabel}}</th>
        <th v-if="FormData.IdleTimeout.Type == 'SetActivity'">{{labels.ActivityLabel}}</th>
        <th v-if="FormData.IdleTimeout.Type == 'SetState'">{{labels.StateLabel}}</th>
      </tr>
      <tr>
        <td :readonly="readonly">
          <el-input  v-model="FormData.IdleTimeout.Timer"
                     :class="(FormData.IdleTimeout.Type ? validateField(notEmpty, FormData.IdleTimeout.Timer) : false) ? 'WorkflowDesignerInputError' : ''"
                     :placeholder="labels.IdleTimeoutLabel"
                     :readonly="readonly"
                     :title="FormData.IdleTimeout.Type ? validateField(notEmpty, FormData.IdleTimeout.Timer):''"
                     clearable></el-input>
        </td>
        <td :readonly="readonly">
          <el-select
            v-model="FormData.IdleTimeout.Type"
            :class="(FormData.IdleTimeout.Timer ? validateField(notEmpty, FormData.IdleTimeout.Type) : false) ? 'WorkflowDesignerInputError' : ''"
            :disabled="readonly"
            :placeholder="labels.TypeLabel"
            :title="FormData.IdleTimeout.Timer ? validateField(notEmpty, FormData.IdleTimeout.Type):''"
            clearable
            filterable
            style="width: 100%;"
            @change="delete  FormData.IdleTimeout.NameForSet">
            <el-option key="SetActivity" :label="labels.SetActivityLabel" value="SetActivity"></el-option>
            <el-option key="SetState" :label="labels.SetStateLabel" value="SetState"></el-option>
          </el-select>
        </td>
        <td v-if="FormData.IdleTimeout.Type == 'SetActivity'">
          <el-select
            v-model="FormData.IdleTimeout.NameForSet"
            :class="validateField(notEmpty, FormData.IdleTimeout.NameForSet) ? 'WorkflowDesignerInputError' : ''"
            :disabled="readonly"
            :placeholder="labels.ActivityLabel"
            :title="validateField(notEmpty, FormData.IdleTimeout.NameForSet)"
            clearable
            filterable
            style="width: 100%;">
            <el-option  v-for="item in activities" :key="item" :label="item" :value="item"></el-option>
          </el-select>
        </td>
        <td v-if="FormData.IdleTimeout.Type == 'SetState'">
          <el-select
            v-model="FormData.IdleTimeout.NameForSet"
            :class="validateField(notEmpty, FormData.IdleTimeout.NameForSet) ? 'WorkflowDesignerInputError' : ''"
            :disabled="readonly"
            :placeholder="labels.StateLabel"
            :title="validateField(notEmpty, FormData.IdleTimeout.NameForSet)"
            clearable
            filterable
            style="width: 100%;">
            <el-option v-for="item in states" :key="item" :label="item"  :value="item"></el-option>
          </el-select>
        </td>
        <td v-if="!readonly" class="WorkflowDesignerTableEditButton">
          <el-button class="WorkflowDesignerTableDeleteButton" @click="FormData.IdleTimeout = {RetryCount:'1'}"></el-button>
        </td>
      </tr>
    </table>
  </div>
  <div v-if="expertMode" style="margin-bottom: 10px;">
    <h4 class="WorkflowDesignerTitleWithCreate Underline">
      {{labels.ExceptionsHandling}}
      <a v-if="!readonly" @click="addRow(FormData.ExceptionsHandlers)">{{ButtonTextCreate}}</a>
    </h4>
    <table v-if="FormData.ExceptionsHandlers.length > 0" class="WorkflowDesignerTable">
      <tr>
        <th></th>
        <th>{{labels.Exceptions}}</th>
        <th>{{labels.TypeLabel}}</th>
        <th>{{labels.ImpAction}}</th>
      </tr>
      <tr v-for="(imp, index) in FormData.ExceptionsHandlers" :key="index"
          :class="dragOverIndex == index && dragOverBlock  == imp ? 'dragOver' : ''"
          @dragend="dragend($event)"
          @dragover="dragover(imp, index, $event)"
          @dragstart="dragstart(index, $event, FormData.ExceptionsHandlers)">
        <td :draggable="!readonly" class='WorkflowDesignerTableMoveCol'>
          <div v-if="!readonly" class='WorkflowDesignerTableMoveButton'></div>
        </td >
        <td>
          <el-select
            v-model="imp.Exceptions"
            :class="validateField(notEmpty, imp.Exceptions) ? 'WorkflowDesignerInputError' : ''"
            :disabled="readonly"
            :placeholder="labels.Exceptions"
            :title="validateField(notEmpty, imp.Exceptions)"
            allow-create
            clearable
            filterable
            filterable
            multiple
            style="width: 100%;">
            <el-option key="AllExceptions" :label="labels.AllExceptions" value="*"></el-option>
          </el-select>
        </td>
        <td >
          <el-select
            v-model="imp.Type"
            :class="validateField(notEmpty, imp.Type) ? 'WorkflowDesignerInputError' : ''"
            :disabled="readonly"
            :placeholder="labels.TypeLabel"
            :title="validateField(notEmpty, imp.Type)"
            clearable
            filterable
            style="width: 100%;"
            @change="delete imp.NameForSet">
            <el-option key="SetActivity" :label="labels.SetActivityLabel" value="SetActivity"></el-option>
            <el-option key="SetState" :label="labels.SetStateLabel" value="SetState"></el-option>
            <el-option key="Retry" :label="labels.RetryLabel" value="Retry"></el-option>
            <el-option key="Ignore" :label="labels.IgnoreLabel" value="Ignore"></el-option>
          </el-select>
        </td>
        <td v-if="imp.Type == 'SetActivity'"
            :readonly="readonly">
          <el-select
            v-model="imp.NameForSet"
            :class="validateField(notEmpty, imp.NameForSet) ? 'WorkflowDesignerInputError' : ''"
            :disabled="readonly"
            :placeholder="labels.ActivityLabel"
            :title="validateField(notEmpty, imp.NameForSet)"
            clearable
            filterable
            style="width: 100%;">
            <el-option  v-for="item in activities" :key="item" :label="item" :value="item"></el-option>
          </el-select>
        </td>
        <td v-if="imp.Type == 'SetState'" >
          <el-select
            v-model="imp.NameForSet"
            :class="validateField(notEmpty, imp.NameForSet) ? 'WorkflowDesignerInputError' : ''"
            :disabled="readonly"
            :placeholder="labels.StateLabel"
            :title="validateField(notEmpty, imp.NameForSet)"
            clearable
            filterable
            style="width: 100%;">
            <el-option v-for="item in states" :key="item" :label="item" :value="item"></el-option>
          </el-select>
        </td>
        <td v-if="imp.Type == 'Retry'">
          <el-input-number v-model="imp.RetryCount" :class="validateField(notEmpty, imp.RetryCount) ? 'WorkflowDesignerInputError' : ''"
                           :placeholder="labels.RetryCountLabel"
                           :readonly="readonly"
                           :title="validateField(notEmpty, imp.RetryCount)"
                           clearable
                           controls-position="right" style="width: 100%;"></el-input-number>
        </td>
        <td v-if="imp.Type == 'Ignore' || !imp.Type">
        </td>
        <td v-if="!readonly" class="WorkflowDesignerTableEditButton">
          <el-button class="WorkflowDesignerTableDeleteButton" @click="removeRow(FormData.ExceptionsHandlers, index)"></el-button>
        </td>
      </tr>
    </table>
  </div>
  <div style="margin-bottom: 10px;">
    <el-button v-if="!readonly && !itemHasComment" circle icon="IconComment" @click="showUserComment()"></el-button>
    <h4 v-if="itemHasComment" style="padding-bottom: 1px;border-bottom: 1px solid rgba(34,36,38,.15);">{{ labels.UserComment }}</h4>
    <el-input
      v-if="itemHasComment"
      v-model="FormData.UserComment"
      :placeholder="labels.UserComment"
      :rows="5"
      type="textarea"
    >
    </el-input>
  </div>
</el-form>
<div class="WorkflowDesignerButtons">
  <el-button link @click="expertMode = !expertMode">{{ expertMode ? SwitchToDefaultMode : SwitchToExpertMode }}</el-button>
  <el-button v-if="!readonly" type="primary" @click="onSave">{{ ButtonTextSave }}</el-button>
  <el-button @click="onClose">{{ ButtonTextCancel }}</el-button>
</div>
<script type="application/javascript">
  function weatherActivity_Data(me) {
    return {
      readonly: false,
      labels: WorkflowDesignerConstants.ActivityFormLabel,
      ButtonTextSave: WorkflowDesignerConstants.ButtonTextSave,
      ButtonTextCancel: WorkflowDesignerConstants.ButtonTextCancel,
      ButtonTextCreate: WorkflowDesignerConstants.ButtonTextCreate,
      ButtonTextDelete: WorkflowDesignerConstants.ButtonTextDelete,
      SwitchToDefaultMode: WorkflowDesignerConstants.SwitchToDefaultMode,
      SwitchToExpertMode: WorkflowDesignerConstants.SwitchToExpertMode,
      expertMode: false,
      actions: [],
      activities: [],
      states: [],
      editItem: null,
      itemHasComment: false,
      FormData: {
        Implementation: [],
        PreExecutionImplementation: [],
        Annotations: [],
        ExceptionsHandlers: [],
      },
      actionsStore: me.graph.data.CodeActions,
      disableAllPersist: false,
      isIndeterminate: false,
      persists: [],
      checkedPersists: []
    }
  }
  function weatherActivity_Init(me) {
    me.VueConfig.watch = {
      actionsStore:function(val) {
        me.VueConfig.state.actions = me.graph.getActionNames();
      },
    }
    me.VueConfig.methods.onUpdate = function(item){
      debugger
      var formdata = me.VueConfig.state.FormData;
      formdata.Name = item.Name;
      formdata.ExecutionTimeout = item.ExecutionTimeout ? WorkflowDesignerCommon.clone(item.ExecutionTimeout) : {RetryCount: '1'};
      formdata.IdleTimeout = item.IdleTimeout ? WorkflowDesignerCommon.clone(item.IdleTimeout) : {RetryCount: '1'};
      // formdata.ExceptionsHandler = item.ExceptionsHandler ? WorkflowDesignerCommon.clone(item.ExceptionsHandler) : {RetryCount:"1", Exceptions:[]};
      formdata.ExceptionsHandlers = Array.isArray(item.ExceptionsHandlers) ? WorkflowDesignerCommon.clone(item.ExceptionsHandlers) : [];
      formdata.State = item.State;
      formdata.IsInitial = item.IsInitial;
      formdata.IsFinal = item.IsFinal;
      formdata.IsForSetState = item.IsForSetState;
      formdata.IsAutoSchemeUpdate = item.IsAutoSchemeUpdate;
      formdata.DisablePersistState = item.DisablePersistState;
      formdata.DisablePersistTransitionHistory = item.DisablePersistTransitionHistory;
      formdata.DisablePersistParameters = item.DisablePersistParameters;
      formdata.UserComment = item.UserComment;
      formdata.Implementation = Array.isArray(item.Implementation) ? WorkflowDesignerCommon.clone(item.Implementation) : [];
      formdata.PreExecutionImplementation = Array.isArray(item.PreExecutionImplementation) ? WorkflowDesignerCommon.clone(item.PreExecutionImplementation) : [];
      formdata.Annotations = Array.isArray(item.Annotations) ? WorkflowDesignerCommon.clone(item.Annotations) : [];
      me.linkItem = item;
      me.VueConfig.state.originalItem = WorkflowDesignerCommon.clone(formdata);
      me.VueConfig.state.prevName = item.Name;
      me.VueConfig.state.readonly = me.graph.Settings.readonly;
      me.VueConfig.state.actions = me.graph.getActionNames();
      me.VueConfig.state.itemHasComment = formdata.UserComment != null && formdata.UserComment.length > 0;
      const {activities, states} = me.VueConfig.state;
      me.graph.data.Activities.forEach(({Name, State}) => {
        if (item.Name === Name) return;
        if (!activities.includes(Name)) activities.push(Name);
        if (State && !states.includes(State)) states.push(State);
      });
      activities.sort();
      states.sort();
      me.VueConfig.state.disableAllPersist = formdata.DisablePersistState && formdata.DisablePersistTransitionHistory && formdata.DisablePersistParameters;
      me.VueConfig.state.isIndeterminate = formdata.DisablePersistState || formdata.DisablePersistTransitionHistory || formdata.DisablePersistParameters;
      me.VueConfig.state.persists = [
        me.VueConfig.state.labels.DisablePersists.DisablePersistState,
        me.VueConfig.state.labels.DisablePersists.DisablePersistParameters,
        me.VueConfig.state.labels.DisablePersists.DisablePersistTransitionHistory
      ];
      if (formdata.DisablePersistState != undefined && formdata.DisablePersistState) {
        me.VueConfig.state.checkedPersists.push(me.VueConfig.state.labels.DisablePersists.DisablePersistState);
      }
      if (formdata.DisablePersistParameters != undefined && formdata.DisablePersistParameters) {
        me.VueConfig.state.checkedPersists.push(me.VueConfig.state.labels.DisablePersists.DisablePersistParameters);
      }
      if (formdata.DisablePersistTransitionHistory != undefined && formdata.DisablePersistTransitionHistory) {
        me.VueConfig.state.checkedPersists.push(me.VueConfig.state.labels.DisablePersists.DisablePersistTransitionHistory);
      }
      var objectCorrect = me.VueConfig.methods.objectCorrect;
      if ((formdata.PreExecutionImplementation.length > 0)
        || (formdata.Annotations.length > 0)
        || objectCorrect(formdata.ExecutionTimeout.Type)
        || objectCorrect(formdata.IdleTimeout.Type)
        || formdata.ExceptionsHandlers.length > 0
        || me.VueConfig.state.disableAllPersist
        || me.VueConfig.state.isIndeterminate) {
        me.VueConfig.state.expertMode = true;
      }
    };
    me.VueConfig.methods.activityNameRules = function(){
      var res = me.requiredRule();
      var validator = function(rule, value, callback){
        var hasEqualNames = me.graph.data.Activities.find(function (a) {
          return a != me.linkItem && a.Name == value;
        });
        hasEqualNames ?  callback(new Error(rule.message)) : callback();
      };
      res.push({ validator: validator, message: WorkflowDesignerConstants.FieldMustBeUnique});
      return res;
    }
    me.VueConfig.methods.validateField = function (rules, value) {
      var value = arguments[arguments.length - 1];
      for (var i = 0; i < arguments.length - 1; i++) {
        var error = arguments[i](value);
        if (error)
          return error;
      }
    };
    me.VueConfig.methods.notEmpty = function (value) {
      var isValid = true;
      if(!me.VueConfig.methods.objectCorrect(value))
        isValid = false;
      if (Object.prototype.toString.call(value) === '[object Array]') {
        if (value.length < 1)
          isValid = false;
      }
      if (!isValid)
        return WorkflowDesignerConstants.FieldIsRequired;
    }
    me.VueConfig.methods.nameOnChange = function (value) {
      var formdata = me.VueConfig.state.FormData;
      if (formdata.State == me.VueConfig.state.prevName) {
        formdata.State = value;
      }
      me.VueConfig.state.prevName = value;
    };
    me.VueConfig.methods.onInitialChange = function(){
      var formdata = me.VueConfig.state.FormData;
      formdata.IsFinal = formdata.IsInitial ? false : formdata.IsFinal;
    };
    me.VueConfig.methods.onFinalChange = function(){
      var formdata = me.VueConfig.state.FormData;
      formdata.IsInitial = formdata.IsFinal ? false : formdata.IsInitial;
    };
    me.VueConfig.methods.handleCheckAllPersistsChange = function (val) {
      me.VueConfig.state.checkedPersists = val ? me.VueConfig.state.persists : [];
      me.VueConfig.state.isIndeterminate = false;
    };
    me.VueConfig.methods.handleCheckedPersistsChange = function (value) {
      let checkedCount = value.length;
      me.VueConfig.state.disableAllPersist = checkedCount === me.VueConfig.state.persists.length;
      me.VueConfig.state.isIndeterminate = checkedCount > 0 && checkedCount < me.VueConfig.state.persists.length;
    }
    me.VueConfig.methods.setCurrentItem = function(item) {
      this.currentItem = item;
    };
    me.VueConfig.methods.querySearch = function(queryString, cb) {
      if(me.VueConfig.state.readonly)
        return cb([]);
      var res = me.graph.getAutoCompleteSuggestions2('actionparameter', this.currentItem.ActionName, queryString);
      cb(res);
    };
    me.VueConfig.methods.validate = function () {
      var validateFunc = me.VueConfig.methods.validateField;
      var data = me.VueConfig.state.FormData;
      var notEmptyRule = me.VueConfig.methods.notEmpty;
      for (var i = 0; i < data.Implementation.length; i++) {
        var item = data.Implementation[i];
        if (validateFunc(notEmptyRule, item.ActionName)) {
          return false;
        }
      }
      for (var i = 0; i < data.PreExecutionImplementation.length; i++) {
        var item = data.PreExecutionImplementation[i];
        if (validateFunc(notEmptyRule, item.ActionName)) {
          return false;
        }
      }
      for (var i = 0; i < data.Annotations.length; i++) {
        var item = data.Annotations[i];
        if (validateFunc(notEmptyRule, item.Name)) {
          return false;
        }
      }
      if (data.ExecutionTimeout.Type) {
        if (validateFunc(notEmptyRule, data.ExecutionTimeout.Timer)) {
          return false;
        }
      }
      if (data.ExecutionTimeout.Timer) {
        if (validateFunc(notEmptyRule, data.ExecutionTimeout.Type)) {
          return false;
        }
      }
      if (data.ExecutionTimeout.Type == 'SetActivity' || data.ExecutionTimeout.Type == 'SetState') {
        if (validateFunc(notEmptyRule, data.ExecutionTimeout.NameForSet)) {
          return false;
        }
      }
      if (data.ExecutionTimeout.Type == 'Retry') {
        if (validateFunc(notEmptyRule, data.ExecutionTimeout.RetryCount)) {
          return false;
        }
      }
      if (data.IdleTimeout.Type) {
        if (validateFunc(notEmptyRule, data.IdleTimeout.Timer)) {
          return false;
        }
      }
      if (data.IdleTimeout.Timer) {
        if (validateFunc(notEmptyRule, data.IdleTimeout.Type)) {
          return false;
        }
      }
      if (typeof data.IdleTimeout.Type != 'undefined') {
        if (validateFunc(notEmptyRule, data.IdleTimeout.NameForSet)) {
          return false;
        }
      }
      for (var i = 0; i < data.ExceptionsHandlers.length; i++) {
        var item = data.ExceptionsHandlers[i];
        if (validateFunc(notEmptyRule, item.Exceptions) ||
          validateFunc(notEmptyRule, item.Type)) {
          return false;
        }
        if (item.Type == 'SetActivity' || item.Type == 'SetState') {
          if (validateFunc(notEmptyRule, item.NameForSet)) {
            return false;
          }
        }
        if (item.Type == 'Retry') {
          if (validateFunc(notEmptyRule, item.RetryCount)) {
            return false;
          }
        }
      }
      return true;
    };
    me.VueConfig.methods.showUserComment = function () {
      me.VueConfig.state.itemHasComment = true;
    }
    me.VueConfig.methods.addRow = function (items) {
      items.push({});
    };
    me.VueConfig.methods.addActionRow = function (items) {
      items.push({ActionParameter: ''});
    };
    me.VueConfig.methods.addAnnotationRow = function (items) {
      items.push({JsonValue: null});
    };
    me.VueConfig.methods.removeRow = function (items, index) {
      items.splice(index, 1);
    };
    me.VueConfig.methods.showjson = function (name, item) {
      me.VueConfig.state.editItem = item;
      me.editItem = item;
      var onSuccess = function (value) {
        if (me.editItem) {
          me.editItem[name] = value;
          me.VueConfig.state.editItem = undefined;
          delete me.editItem;
        }
      };
      var onClose = function (value) {
        me.VueConfig.state.editItem = undefined;
      };
      var params = {
        name: item['ActionName'],
        type: ['Action']
      };
      me.VueConfig.state.jsonform = me.showjson(item[name], params, onSuccess, onClose);
    };
    me.VueConfig.methods.onSave = function () {
      me.VueConfig.state.FormData.DisablePersistState = false;
      me.VueConfig.state.FormData.DisablePersistParameters = false;
      me.VueConfig.state.FormData.DisablePersistTransitionHistory = false;
      const persists = me.VueConfig.state.checkedPersists;
      for (var i = 0; i < persists.length; i++) {
        switch (persists[i]) {
          case  me.VueConfig.state.labels.DisablePersists.DisablePersistState:
            me.VueConfig.state.FormData.DisablePersistState = true;
            break;
          case me.VueConfig.state.labels.DisablePersists.DisablePersistParameters:
            me.VueConfig.state.FormData.DisablePersistParameters = true;
            break;
          case me.VueConfig.state.labels.DisablePersists.DisablePersistTransitionHistory:
            me.VueConfig.state.FormData.DisablePersistTransitionHistory = true;
            break;
        }
      }
      if (this.$refs && WorkflowDesignerCommon.validateForms(this.$refs) && me.VueConfig.methods.validate()) {
        me.onSuccess(me.VueConfig.state.FormData);
        me.onClose(true);
      }
    };
    me.VueConfig.methods.onClose = function () {
      if (me.VueConfig.state.readonly) {
        me.onClose(true);
        return;
      }
      var originalItem = me.VueConfig.state.originalItem;
      var item = me.VueConfig.state.FormData;
      if (WorkflowDesignerCommon.deepCompare(originalItem, item)) {
        me.onClose(true);
      } else {
        me.showConfirm();
        return false;
      }
    };
    me.VueConfig.methods.onCloseSave = function () {
      me.onClose(true);
    };
    me.showConfirm = function () {
      me.VueConfig.methods.showConfirm({
        title: WorkflowDesignerConstants.DialogConfirmText,
        message: WorkflowDesignerConstants.CloseWithoutSaving,
        onSuccess: function () {
          me.VueConfig.methods.onCloseSave();
        }
      });
    }
  }
</script>