Browse Source

整合仿钉钉流程设计器 https://github.com/StavinLi/Workflow-Vue3

jason 1 năm trước cách đây
mục cha
commit
07dc725857

+ 237 - 0
src/components/SimpleProcessDesigner/src/addNode.vue

@@ -0,0 +1,237 @@
+/* stylelint-disable order/properties-order */
+<template>
+  <div class="add-node-btn-box">
+    <div class="add-node-btn">
+      <el-popover placement="right-start" v-model="visible" width="auto">
+        <div class="add-node-popover-body">
+          <a class="add-node-popover-item approver" @click="addType(1)">
+            <div class="item-wrapper">
+              <span class="iconfont"></span>
+            </div>
+            <p>审批人</p>
+          </a>
+          <a class="add-node-popover-item notifier" @click="addType(2)">
+            <div class="item-wrapper">
+              <span class="iconfont"></span>
+            </div>
+            <p>抄送人</p>
+          </a>
+          <a class="add-node-popover-item condition" @click="addType(4)">
+            <div class="item-wrapper">
+              <span class="iconfont"></span>
+            </div>
+            <p>条件分支</p>
+          </a>
+        </div>
+        <template #reference>
+          <button class="btn" type="button">
+            <span class="iconfont"></span>
+          </button>
+        </template>
+      </el-popover>
+    </div>
+  </div>
+</template>
+<script setup>
+import { ref } from 'vue'
+let props = defineProps({
+  childNodeP: {
+    type: Object,
+    default: () => ({})
+  }
+})
+let emits = defineEmits(['update:childNodeP'])
+let visible = ref(false)
+const addType = (type) => {
+  visible.value = false
+  if (type != 4) {
+    var data
+    if (type == 1) {
+      data = {
+        nodeName: '审核人',
+        error: true,
+        type: 1,
+        settype: 1,
+        selectMode: 0,
+        selectRange: 0,
+        directorLevel: 1,
+        examineMode: 1,
+        noHanderAction: 1,
+        examineEndDirectorLevel: 0,
+        childNode: props.childNodeP,
+        nodeUserList: []
+      }
+    } else if (type == 2) {
+      data = {
+        nodeName: '抄送人',
+        type: 2,
+        ccSelfSelectFlag: 1,
+        childNode: props.childNodeP,
+        nodeUserList: []
+      }
+    }
+    emits('update:childNodeP', data)
+  } else {
+    emits('update:childNodeP', {
+      nodeName: '路由',
+      type: 4,
+      childNode: null,
+      conditionNodes: [
+        {
+          nodeName: '条件1',
+          error: true,
+          type: 3,
+          priorityLevel: 1,
+          conditionList: [],
+          nodeUserList: [],
+          childNode: props.childNodeP
+        },
+        {
+          nodeName: '条件2',
+          type: 3,
+          priorityLevel: 2,
+          conditionList: [],
+          nodeUserList: [],
+          childNode: null
+        }
+      ]
+    })
+  }
+}
+</script>
+<style scoped lang="scss">
+.add-node-btn-box {
+  width: 240px;
+  display: inline-flex;
+  -ms-flex-negative: 0;
+  flex-shrink: 0;
+  -webkit-box-flex: 1;
+  -ms-flex-positive: 1;
+  position: relative;
+
+  &:before {
+    content: '';
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    z-index: -1;
+    margin: auto;
+    width: 2px;
+    height: 100%;
+    background-color: #cacaca;
+  }
+
+  .add-node-btn {
+    user-select: none;
+    width: 240px;
+    padding: 20px 0 32px;
+    display: flex;
+    -webkit-box-pack: center;
+    justify-content: center;
+    flex-shrink: 0;
+    -webkit-box-flex: 1;
+    flex-grow: 1;
+
+    .btn {
+      outline: none;
+      box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.1);
+      width: 30px;
+      height: 30px;
+      background: #3296fa;
+      border-radius: 50%;
+      position: relative;
+      border: none;
+      line-height: 30px;
+      -webkit-transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
+      transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
+
+      .iconfont {
+        color: #fff;
+        font-size: 16px;
+      }
+
+      &:hover {
+        transform: scale(1.3);
+        box-shadow: 0 13px 27px 0 rgba(0, 0, 0, 0.1);
+      }
+
+      &:active {
+        transform: none;
+        background: #1e83e9;
+        box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.1);
+      }
+    }
+  }
+}
+
+.add-node-popover-body {
+  display: flex;
+
+  .add-node-popover-item {
+    margin-right: 10px;
+    cursor: pointer;
+    text-align: center;
+    flex: 1;
+    color: #191f25 !important;
+
+    .item-wrapper {
+      user-select: none;
+      display: inline-block;
+      width: 80px;
+      height: 80px;
+      margin-bottom: 5px;
+      background: #fff;
+      border: 1px solid #e2e2e2;
+      border-radius: 50%;
+      transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
+
+      .iconfont {
+        font-size: 35px;
+        line-height: 80px;
+      }
+    }
+
+    &.approver {
+      .item-wrapper {
+        color: #ff943e;
+      }
+    }
+
+    &.notifier {
+      .item-wrapper {
+        color: #3296fa;
+      }
+    }
+
+    &.condition {
+      .item-wrapper {
+        color: #15bc83;
+      }
+    }
+
+    &:hover {
+      .item-wrapper {
+        background: #3296fa;
+        box-shadow: 0 10px 20px 0 rgba(50, 150, 250, 0.4);
+      }
+
+      .iconfont {
+        color: #fff;
+      }
+    }
+
+    &:active {
+      .item-wrapper {
+        box-shadow: none;
+        background: #eaeaea;
+      }
+
+      .iconfont {
+        color: inherit;
+      }
+    }
+  }
+}
+</style>

+ 283 - 0
src/components/SimpleProcessDesigner/src/drawer/approverDrawer.vue

@@ -0,0 +1,283 @@
+<template>
+  <el-drawer
+    :append-to-body="true"
+    title="审批人设置"
+    v-model="visible"
+    class="set_promoter"
+    :show-close="false"
+    :size="550"
+    :before-close="saveApprover"
+  >
+    <div class="demo-drawer__content">
+      <div class="drawer_content">
+        <div class="approver_content">
+          <el-radio-group v-model="approverConfig.settype" class="clear" @change="changeType">
+            <el-radio v-for="{ value, label } in setTypes" :key="value" :label="value">{{
+              label
+            }}</el-radio>
+          </el-radio-group>
+          <el-button type="primary" @click="addApprover" v-if="approverConfig.settype == 1"
+            >添加/修改成员</el-button
+          >
+          <p class="selected_list" v-if="approverConfig.settype == 1">
+            <span v-for="(item, index) in approverConfig.nodeUserList" :key="index"
+              >{{ item.name }}
+              <img
+                src="@/assets/images/add-close1.png"
+                @click="removeEle(approverConfig.nodeUserList, item, 'targetId')"
+              />
+            </span>
+            <a
+              v-if="approverConfig.nodeUserList.length != 0"
+              @click="approverConfig.nodeUserList = []"
+              >清除</a
+            >
+          </p>
+        </div>
+        <div class="approver_manager" v-if="approverConfig.settype == 2">
+          <p>
+            <span>发起人的:</span>
+            <select v-model="approverConfig.directorLevel">
+              <option v-for="item in directorMaxLevel" :value="item" :key="item"
+                >{{ item == 1 ? '直接' : '第' + item + '级' }}主管</option
+              >
+            </select>
+          </p>
+          <p class="tip">找不到主管时,由上级主管代审批</p>
+        </div>
+        <div class="approver_self" v-if="approverConfig.settype == 5">
+          <p>该审批节点设置“发起人自己”后,审批人默认为发起人</p>
+        </div>
+        <div class="approver_self_select" v-show="approverConfig.settype == 4">
+          <el-radio-group v-model="approverConfig.selectMode" style="width: 100%">
+            <el-radio v-for="{ value, label } in selectModes" :label="value" :key="value">{{
+              label
+            }}</el-radio>
+          </el-radio-group>
+          <h3>选择范围</h3>
+          <el-radio-group
+            v-model="approverConfig.selectRange"
+            style="width: 100%"
+            @change="changeRange"
+          >
+            <el-radio v-for="{ value, label } in selectRanges" :label="value" :key="value">{{
+              label
+            }}</el-radio>
+          </el-radio-group>
+          <template v-if="approverConfig.selectRange == 2 || approverConfig.selectRange == 3">
+            <el-button type="primary" @click="addApprover" v-if="approverConfig.selectRange == 2"
+              >添加/修改成员</el-button
+            >
+            <el-button type="primary" @click="addRoleApprover" v-else>添加/修改角色</el-button>
+            <p class="selected_list">
+              <span v-for="(item, index) in approverConfig.nodeUserList" :key="index"
+                >{{ item.name }}
+                <img
+                  src="@/assets/images/add-close1.png"
+                  @click="removeEle(approverConfig.nodeUserList, item, 'targetId')"
+                />
+              </span>
+              <a
+                v-if="approverConfig.nodeUserList.length != 0 && approverConfig.selectRange != 1"
+                @click="approverConfig.nodeUserList = []"
+                >清除</a
+              >
+            </p>
+          </template>
+        </div>
+        <div class="approver_manager" v-if="approverConfig.settype == 7">
+          <p>审批终点</p>
+          <p style="padding-bottom: 20px">
+            <span>发起人的:</span>
+            <select v-model="approverConfig.examineEndDirectorLevel">
+              <option v-for="item in directorMaxLevel" :value="item" :key="item"
+                >{{ item == 1 ? '最高' : '第' + item }}层级主管</option
+              >
+            </select>
+          </p>
+        </div>
+        <div
+          class="approver_some"
+          v-if="
+            (approverConfig.settype == 1 && approverConfig.nodeUserList.length > 1) ||
+            approverConfig.settype == 2 ||
+            (approverConfig.settype == 4 && approverConfig.selectMode == 2)
+          "
+        >
+          <p>多人审批时采用的审批方式</p>
+          <el-radio-group v-model="approverConfig.examineMode" class="clear">
+            <el-radio :label="1">依次审批</el-radio>
+            <br />
+            <el-radio :label="2" v-if="approverConfig.settype != 2"
+              >会签(须所有审批人同意)</el-radio
+            >
+          </el-radio-group>
+        </div>
+        <div
+          class="approver_some"
+          v-if="approverConfig.settype == 2 || approverConfig.settype == 7"
+        >
+          <p>审批人为空时</p>
+          <el-radio-group v-model="approverConfig.noHanderAction" class="clear">
+            <el-radio :label="1">自动审批通过/不允许发起</el-radio>
+            <br />
+            <el-radio :label="2">转交给审核管理员</el-radio>
+          </el-radio-group>
+        </div>
+      </div>
+      <div class="demo-drawer__footer clear">
+        <el-button type="primary" @click="saveApprover">确 定</el-button>
+        <el-button @click="closeDrawer">取 消</el-button>
+      </div>
+    </div>
+  </el-drawer>
+</template>
+<script lang="ts" setup>
+import { ref, watch, computed } from 'vue'
+import { useWorkFlowStoreWithOut } from '@/store/modules/simpleWorkflow'
+import { setTypes, selectModes, selectRanges } from '../util'
+import { removeEle, setApproverStr } from '../util'
+let props = defineProps({
+  directorMaxLevel: {
+    type: Number,
+    default: 0
+  }
+})
+let approverConfig = ref({})
+let approverVisible = ref(false)
+let approverRoleVisible = ref(false)
+let checkedRoleList = ref([])
+let checkedList = ref([])
+let store = useWorkFlowStoreWithOut()
+let { setApproverConfig, setApprover } = store
+let approverConfig1 = computed(() => store.approverConfig1)
+let approverDrawer = computed(() => store.approverDrawer)
+let visible = computed({
+  get() {
+    return approverDrawer.value
+  },
+  set() {
+    closeDrawer()
+  }
+})
+watch(approverConfig1, (val: any) => {
+  approverConfig.value = val.value
+})
+let changeRange = () => {
+  approverConfig.value.nodeUserList = []
+}
+const changeType = (val) => {
+  approverConfig.value.nodeUserList = []
+  approverConfig.value.examineMode = 1
+  approverConfig.value.noHanderAction = 2
+  if (val == 2) {
+    approverConfig.value.directorLevel = 1
+  } else if (val == 4) {
+    approverConfig.value.selectMode = 1
+    approverConfig.value.selectRange = 1
+  } else if (val == 7) {
+    approverConfig.value.examineEndDirectorLevel = 1
+  }
+}
+const addApprover = () => {
+  approverVisible.value = true
+  checkedList.value = approverConfig.value.nodeUserList
+}
+const addRoleApprover = () => {
+  approverRoleVisible.value = true
+  checkedRoleList.value = approverConfig.value.nodeUserList
+}
+const sureApprover = (data) => {
+  approverConfig.value.nodeUserList = data
+  approverVisible.value = false
+}
+const sureRoleApprover = (data) => {
+  approverConfig.value.nodeUserList = data
+  approverRoleVisible.value = false
+}
+const saveApprover = () => {
+  approverConfig.value.error = !setApproverStr(approverConfig.value)
+  setApproverConfig({
+    value: approverConfig.value,
+    flag: true,
+    id: approverConfig1.value.id
+  })
+  closeDrawer()
+}
+const closeDrawer = () => {
+  setApprover(false)
+}
+</script>
+<style lang="scss" scoped>
+.set_promoter {
+  .approver_content {
+    padding-bottom: 10px;
+    border-bottom: 1px solid #f2f2f2;
+  }
+
+  .approver_self_select,
+  .approver_content {
+    .el-button {
+      margin-bottom: 20px;
+    }
+  }
+
+  .approver_content,
+  .approver_some,
+  .approver_self_select {
+    .el-radio-group {
+      display: unset;
+    }
+
+    .el-radio {
+      width: 27%;
+      margin-bottom: 20px;
+      height: 16px;
+    }
+  }
+
+  .approver_manager p {
+    line-height: 32px;
+  }
+
+  .approver_manager select {
+    width: 420px;
+    height: 32px;
+    background: rgba(255, 255, 255, 1);
+    border-radius: 4px;
+    border: 1px solid rgba(217, 217, 217, 1);
+  }
+
+  .approver_manager p.tip {
+    margin: 10px 0 22px 0;
+    font-size: 12px;
+    line-height: 16px;
+    color: #f8642d;
+  }
+
+  .approver_self {
+    padding: 28px 20px;
+  }
+
+  .approver_self_select,
+  .approver_manager,
+  .approver_content,
+  .approver_some {
+    padding: 20px 20px 0;
+  }
+
+  .approver_manager p:first-of-type,
+  .approver_some p {
+    line-height: 19px;
+    font-size: 14px;
+    margin-bottom: 14px;
+  }
+
+  .approver_self_select h3 {
+    margin: 5px 0 20px;
+    font-size: 14px;
+    font-weight: bold;
+    line-height: 19px;
+  }
+}
+</style>

+ 298 - 0
src/components/SimpleProcessDesigner/src/nodeWrap.vue

@@ -0,0 +1,298 @@
+<!-- eslint-disable vue/no-mutating-props -->
+<!--
+ * @Date: 2022-09-21 14:41:53
+ * @LastEditors: StavinLi 495727881@qq.com
+ * @LastEditTime: 2023-05-24 15:20:24
+ * @FilePath: /Workflow-Vue3/src/components/nodeWrap.vue
+-->
+<template>
+     <div class="node-wrap" v-if="nodeConfig.type < 3">
+      <div class="node-wrap-box" :class="(nodeConfig.type == 0 ? 'start-node ' : '') +(isTried && nodeConfig.error ? 'active error' : '')">
+          <div class="title" :style="`background: rgb(${bgColors[nodeConfig.type]});`">
+            <span v-if="nodeConfig.type == 0">{{ nodeConfig.nodeName }}</span>
+            <template v-else>
+              <span class="iconfont">{{nodeConfig.type == 1?'':''}}</span>
+              <input
+                v-if="isInput"
+                type="text"
+                class="ant-input editable-title-input"
+                @blur="blurEvent()"
+                @focus="$event.currentTarget.select()"
+                v-focus
+                v-model="nodeConfig.nodeName"
+                :placeholder="defaultText"
+              />
+              <span v-else class="editable-title" @click="clickEvent()">{{ nodeConfig.nodeName }}</span>
+              <i class="anticon anticon-close close" @click="delNode"></i>
+            </template>
+          </div>
+          <div class="content" @click="setPerson">
+            <div class="text">
+                <span class="placeholder" v-if="!showText">请选择{{defaultText}}</span>
+                {{showText}}
+            </div>
+            <i class="anticon anticon-right arrow"></i>
+          </div>
+          <div class="error_tip" v-if="isTried && nodeConfig.error">
+            <i class="anticon anticon-exclamation-circle"></i>
+          </div>
+      </div>
+      <addNode v-model:childNodeP="nodeConfig.childNode" />
+    </div>
+    <div class="branch-wrap" v-if="nodeConfig.type == 4">
+    <div class="branch-box-wrap">
+      <div class="branch-box">
+        <button class="add-branch" @click="addTerm">添加条件</button>
+        <div class="col-box" v-for="(item, index) in nodeConfig.conditionNodes" :key="index">
+          <div class="condition-node">
+            <div class="condition-node-box">
+              <div class="auto-judge" :class="isTried && item.error ? 'error active' : ''">
+                <div class="sort-left" v-if="index != 0" @click="arrTransfer(index, -1)">&lt;</div>
+                <div class="title-wrapper">
+                  <input
+                    v-if="isInputList[index]"
+                    type="text"
+                    class="ant-input editable-title-input"
+                    @blur="blurEvent(index)"
+                    @focus="$event.currentTarget.select()"
+                    v-focus
+                    v-model="item.nodeName"
+                  />
+                  <span v-else class="editable-title" @click="clickEvent(index)">{{ item.nodeName }}</span>
+                  <span class="priority-title" @click="setPerson(item.priorityLevel)">优先级{{ item.priorityLevel }}</span>
+                  <i class="anticon anticon-close close" @click="delTerm(index)"></i>
+                </div>
+                <div class="sort-right" v-if="index != nodeConfig.conditionNodes.length - 1" @click="arrTransfer(index)">&gt;</div>
+                <div class="content" @click="setPerson(item.priorityLevel)">{{ conditionStr(nodeConfig, index) }}</div>
+                <div class="error_tip" v-if="isTried && item.error">
+                    <i class="anticon anticon-exclamation-circle"></i>
+                </div>
+              </div>
+              <addNode v-model:childNodeP="item.childNode" />
+            </div>
+          </div>
+          <nodeWrap v-if="item.childNode" v-model:nodeConfig="item.childNode" />
+          <template v-if="index == 0">
+            <div class="top-left-cover-line"></div>
+            <div class="bottom-left-cover-line"></div>
+          </template>
+          <template v-if="index == nodeConfig.conditionNodes.length - 1">
+            <div class="top-right-cover-line"></div>
+            <div class="bottom-right-cover-line"></div>
+          </template>
+        </div>
+      </div>
+      <addNode v-model:childNodeP="nodeConfig.childNode" />
+    </div>
+  </div>
+    <nodeWrap v-if="nodeConfig.childNode" v-model:nodeConfig="nodeConfig.childNode" />
+</template>
+<script  setup>
+import addNode from './addNode.vue'
+import { onMounted, ref, watch, getCurrentInstance, computed } from 'vue'
+import {
+  arrToStr,
+  conditionStr,
+  setApproverStr,
+  copyerStr,
+  bgColors,
+  placeholderList
+} from './util'
+import { useWorkFlowStoreWithOut } from '@/store/modules/simpleWorkflow'
+let _uid = getCurrentInstance().uid
+
+let props = defineProps({
+  nodeConfig: {
+    type: Object,
+    default: () => ({})
+  },
+  flowPermission: {
+    type: Object,
+    // eslint-disable-next-line vue/require-valid-default-prop
+    default: () => []
+  }
+})
+
+let defaultText = computed(() => {
+  return placeholderList[props.nodeConfig.type]
+})
+let showText = computed(() => {
+  if (props.nodeConfig.type == 0) return arrToStr(props.flowPermission) || '所有人'
+  if (props.nodeConfig.type == 1) return setApproverStr(props.nodeConfig)
+  return copyerStr(props.nodeConfig)
+})
+
+let isInputList = ref([])
+let isInput = ref(false)
+const resetConditionNodesErr = () => {
+  for (var i = 0; i < props.nodeConfig.conditionNodes.length; i++) {
+    // eslint-disable-next-line vue/no-mutating-props
+    props.nodeConfig.conditionNodes[i].error =
+      conditionStr(props.nodeConfig, i) == '请设置条件' &&
+      i != props.nodeConfig.conditionNodes.length - 1
+  }
+}
+onMounted(() => {
+  if (props.nodeConfig.type == 1) {
+    // eslint-disable-next-line vue/no-mutating-props
+    props.nodeConfig.error = !setApproverStr(props.nodeConfig)
+  } else if (props.nodeConfig.type == 2) {
+    // eslint-disable-next-line vue/no-mutating-props
+    props.nodeConfig.error = !copyerStr(props.nodeConfig)
+  } else if (props.nodeConfig.type == 4) {
+    resetConditionNodesErr()
+  }
+})
+let emits = defineEmits(['update:flowPermission', 'update:nodeConfig'])
+let store = useWorkFlowStoreWithOut()
+let {
+  setPromoter,
+  setApprover,
+  setCopyer,
+  setCondition,
+  setFlowPermission,
+  setApproverConfig,
+  setCopyerConfig,
+  setConditionsConfig
+} = store
+let isTried = computed(() => store.isTried)
+let flowPermission1 = computed(() => store.flowPermission1)
+let approverConfig1 = computed(() => store.approverConfig1)
+let copyerConfig1 = computed(() => store.copyerConfig1)
+let conditionsConfig1 = computed(() => store.conditionsConfig1)
+watch(flowPermission1, (flow) => {
+  if (flow.flag && flow.id === _uid) {
+    emits('update:flowPermission', flow.value)
+  }
+})
+watch(approverConfig1, (approver) => {
+  if (approver.flag && approver.id === _uid) {
+    emits('update:nodeConfig', approver.value)
+  }
+})
+watch(copyerConfig1, (copyer) => {
+  if (copyer.flag && copyer.id === _uid) {
+    emits('update:nodeConfig', copyer.value)
+  }
+})
+watch(conditionsConfig1, (condition) => {
+  if (condition.flag && condition.id === _uid) {
+    emits('update:nodeConfig', condition.value)
+  }
+})
+
+const clickEvent = (index) => {
+  if (index || index === 0) {
+    isInputList.value[index] = true
+  } else {
+    isInput.value = true
+  }
+}
+const blurEvent = (index) => {
+  if (index || index === 0) {
+    isInputList.value[index] = false
+    // eslint-disable-next-line vue/no-mutating-props
+    props.nodeConfig.conditionNodes[index].nodeName =
+      props.nodeConfig.conditionNodes[index].nodeName || '条件'
+  } else {
+    isInput.value = false
+    // eslint-disable-next-line vue/no-mutating-props
+    props.nodeConfig.nodeName = props.nodeConfig.nodeName || defaultText
+  }
+}
+const delNode = () => {
+  emits('update:nodeConfig', props.nodeConfig.childNode)
+}
+const addTerm = () => {
+  let len = props.nodeConfig.conditionNodes.length + 1
+  // eslint-disable-next-line vue/no-mutating-props
+  props.nodeConfig.conditionNodes.push({
+    nodeName: '条件' + len,
+    type: 3,
+    priorityLevel: len,
+    conditionList: [],
+    nodeUserList: [],
+    childNode: null
+  })
+  resetConditionNodesErr()
+  emits('update:nodeConfig', props.nodeConfig)
+}
+const delTerm = (index) => {
+  // eslint-disable-next-line vue/no-mutating-props
+  props.nodeConfig.conditionNodes.splice(index, 1)
+  props.nodeConfig.conditionNodes.map((item, index) => {
+    item.priorityLevel = index + 1
+    item.nodeName = `条件${index + 1}`
+  })
+  resetConditionNodesErr()
+  emits('update:nodeConfig', props.nodeConfig)
+  if (props.nodeConfig.conditionNodes.length == 1) {
+    if (props.nodeConfig.childNode) {
+      if (props.nodeConfig.conditionNodes[0].childNode) {
+        reData(props.nodeConfig.conditionNodes[0].childNode, props.nodeConfig.childNode)
+      } else {
+        // eslint-disable-next-line vue/no-mutating-props
+        props.nodeConfig.conditionNodes[0].childNode = props.nodeConfig.childNode
+      }
+    }
+    emits('update:nodeConfig', props.nodeConfig.conditionNodes[0].childNode)
+  }
+}
+const reData = (data, addData) => {
+  if (!data.childNode) {
+    data.childNode = addData
+  } else {
+    reData(data.childNode, addData)
+  }
+}
+const setPerson = (priorityLevel) => {
+  var { type } = props.nodeConfig
+  if (type == 0) {
+    setPromoter(true)
+    setFlowPermission({
+      value: props.flowPermission,
+      flag: false,
+      id: _uid
+    })
+  } else if (type == 1) {
+    setApprover(true)
+    setApproverConfig({
+      value: {
+        ...JSON.parse(JSON.stringify(props.nodeConfig)),
+        ...{ settype: props.nodeConfig.settype ? props.nodeConfig.settype : 1 }
+      },
+      flag: false,
+      id: _uid
+    })
+  } else if (type == 2) {
+    setCopyer(true)
+    setCopyerConfig({
+      value: JSON.parse(JSON.stringify(props.nodeConfig)),
+      flag: false,
+      id: _uid
+    })
+  } else {
+    setCondition(true)
+    setConditionsConfig({
+      value: JSON.parse(JSON.stringify(props.nodeConfig)),
+      priorityLevel,
+      flag: false,
+      id: _uid
+    })
+  }
+}
+const arrTransfer = (index, type = 1) => {
+  //向左-1,向右1
+  // eslint-disable-next-line vue/no-mutating-props
+  props.nodeConfig.conditionNodes[index] = props.nodeConfig.conditionNodes.splice(
+    index + type,
+    1,
+    props.nodeConfig.conditionNodes[index]
+  )[0]
+  props.nodeConfig.conditionNodes.map((item, index) => {
+    item.priorityLevel = index + 1
+  })
+  resetConditionNodesErr()
+  emits('update:nodeConfig', props.nodeConfig)
+}
+</script>

+ 165 - 0
src/components/SimpleProcessDesigner/src/util.ts

@@ -0,0 +1,165 @@
+/**
+ * todo
+ */
+export const arrToStr = (arr?: [{ name: string }]) => {
+  if (arr) {
+    return arr
+      .map((item) => {
+        return item.name
+      })
+      .toString()
+  }
+}
+
+export const setApproverStr = (nodeConfig: any) => {
+  if (nodeConfig.settype == 1) {
+    if (nodeConfig.nodeUserList.length == 1) {
+      return nodeConfig.nodeUserList[0].name
+    } else if (nodeConfig.nodeUserList.length > 1) {
+      if (nodeConfig.examineMode == 1) {
+        return arrToStr(nodeConfig.nodeUserList)
+      } else if (nodeConfig.examineMode == 2) {
+        return nodeConfig.nodeUserList.length + '人会签'
+      }
+    }
+  } else if (nodeConfig.settype == 2) {
+    const level =
+      nodeConfig.directorLevel == 1 ? '直接主管' : '第' + nodeConfig.directorLevel + '级主管'
+    if (nodeConfig.examineMode == 1) {
+      return level
+    } else if (nodeConfig.examineMode == 2) {
+      return level + '会签'
+    }
+  } else if (nodeConfig.settype == 4) {
+    if (nodeConfig.selectRange == 1) {
+      return '发起人自选'
+    } else {
+      if (nodeConfig.nodeUserList.length > 0) {
+        if (nodeConfig.selectRange == 2) {
+          return '发起人自选'
+        } else {
+          return '发起人从' + nodeConfig.nodeUserList[0].name + '中自选'
+        }
+      } else {
+        return ''
+      }
+    }
+  } else if (nodeConfig.settype == 5) {
+    return '发起人自己'
+  } else if (nodeConfig.settype == 7) {
+    return '从直接主管到通讯录中级别最高的第' + nodeConfig.examineEndDirectorLevel + '个层级主管'
+  }
+}
+
+export const copyerStr = (nodeConfig: any) => {
+  if (nodeConfig.nodeUserList.length != 0) {
+    return arrToStr(nodeConfig.nodeUserList)
+  } else {
+    if (nodeConfig.ccSelfSelectFlag == 1) {
+      return '发起人自选'
+    }
+  }
+}
+export const conditionStr = (nodeConfig, index) => {
+  const { conditionList, nodeUserList } = nodeConfig.conditionNodes[index]
+  if (conditionList.length == 0) {
+    return index == nodeConfig.conditionNodes.length - 1 &&
+      nodeConfig.conditionNodes[0].conditionList.length != 0
+      ? '其他条件进入此流程'
+      : '请设置条件'
+  } else {
+    let str = ''
+    for (let i = 0; i < conditionList.length; i++) {
+      const {
+        columnId,
+        columnType,
+        showType,
+        showName,
+        optType,
+        zdy1,
+        opt1,
+        zdy2,
+        opt2,
+        fixedDownBoxValue
+      } = conditionList[i]
+      if (columnId == 0) {
+        if (nodeUserList.length != 0) {
+          str += '发起人属于:'
+          str +=
+            nodeUserList
+              .map((item) => {
+                return item.name
+              })
+              .join('或') + ' 并且 '
+        }
+      }
+      if (columnType == 'String' && showType == '3') {
+        if (zdy1) {
+          str += showName + '属于:' + dealStr(zdy1, JSON.parse(fixedDownBoxValue)) + ' 并且 '
+        }
+      }
+      if (columnType == 'Double') {
+        if (optType != 6 && zdy1) {
+          const optTypeStr = ['', '<', '>', '≤', '=', '≥'][optType]
+          str += `${showName} ${optTypeStr} ${zdy1} 并且 `
+        } else if (optType == 6 && zdy1 && zdy2) {
+          str += `${zdy1} ${opt1} ${showName} ${opt2} ${zdy2} 并且 `
+        }
+      }
+    }
+    return str ? str.substring(0, str.length - 4) : '请设置条件'
+  }
+}
+
+export const dealStr = (str: string, obj) => {
+  const arr = []
+  const list = str.split(',')
+  for (const elem in obj) {
+    list.map((item) => {
+      if (item == elem) {
+        arr.push(obj[elem].value)
+      }
+    })
+  }
+  return arr.join('或')
+}
+
+export const removeEle = (arr, elem, key = 'id') => {
+  let includesIndex
+  arr.map((item, index) => {
+    if (item[key] == elem[key]) {
+      includesIndex = index
+    }
+  })
+  arr.splice(includesIndex, 1)
+}
+
+export const bgColors = ['87, 106, 149', '255, 148, 62', '50, 150, 250']
+export const placeholderList = ['发起人', '审核人', '抄送人']
+export const setTypes = [
+  { value: 1, label: '指定成员' },
+  { value: 2, label: '主管' },
+  { value: 4, label: '发起人自选' },
+  { value: 5, label: '发起人自己' },
+  { value: 7, label: '连续多级主管' }
+]
+
+export const selectModes = [
+  { value: 1, label: '选一个人' },
+  { value: 2, label: '选多个人' }
+]
+
+export const selectRanges = [
+  { value: 1, label: '全公司' },
+  { value: 2, label: '指定成员' },
+  { value: 3, label: '指定角色' }
+]
+
+export const optTypes = [
+  { value: '1', label: '小于' },
+  { value: '2', label: '大于' },
+  { value: '3', label: '小于等于' },
+  { value: '4', label: '等于' },
+  { value: '5', label: '大于等于' },
+  { value: '6', label: '介于两个数之间' }
+]

+ 1292 - 0
src/components/SimpleProcessDesigner/theme/workflow.css

@@ -0,0 +1,1292 @@
+
+.clearfix {
+    zoom: 1
+}
+
+.clearfix:after,
+.clearfix:before {
+    content: "";
+    display: table
+}
+
+.clearfix:after {
+    clear: both
+}
+
+@font-face {
+    font-family: anticon;
+    font-display: fallback;
+    src: url("https://at.alicdn.com/t/font_148784_v4ggb6wrjmkotj4i.eot");
+    src: url("https://at.alicdn.com/t/font_148784_v4ggb6wrjmkotj4i.woff") format("woff"), url("https://at.alicdn.com/t/font_148784_v4ggb6wrjmkotj4i.ttf") format("truetype"), url("https://at.alicdn.com/t/font_148784_v4ggb6wrjmkotj4i.svg#iconfont") format("svg")
+}
+
+.anticon {
+    display: inline-block;
+    font-style: normal;
+    vertical-align: baseline;
+    text-align: center;
+    text-transform: none;
+    line-height: 1;
+    text-rendering: optimizeLegibility;
+    -webkit-font-smoothing: antialiased;
+    -moz-osx-font-smoothing: grayscale
+}
+
+.anticon:before {
+    display: block;
+    font-family: anticon!important
+}
+.anticon-close:before {
+  content: "\E633"
+}
+.anticon-right:before {
+    content: "\E61F"
+}
+.anticon-exclamation-circle{
+    color: rgb(242, 86, 67)
+}
+.anticon-exclamation-circle:before {
+    content: "\E62C"
+}
+
+.anticon-left:before {
+    content: "\E620"
+}
+
+.anticon-close-circle:before {
+    content: "\E62E"
+}
+  
+.ant-btn {
+    line-height: 1.5;
+    display: inline-block;
+    font-weight: 400;
+    text-align: center;
+    touch-action: manipulation;
+    cursor: pointer;
+    background-image: none;
+    border: 1px solid transparent;
+    white-space: nowrap;
+    padding: 0 15px;
+    font-size: 14px;
+    border-radius: 4px;
+    height: 32px;
+    user-select: none;
+    transition: all .3s cubic-bezier(.645, .045, .355, 1);
+    position: relative;
+    color: rgba(0, 0, 0, .65);
+    background-color: #fff;
+    border-color: #d9d9d9
+}
+
+.ant-btn>.anticon {
+    line-height: 1
+}
+
+.ant-btn,
+.ant-btn:active,
+.ant-btn:focus {
+    outline: 0
+}
+
+.ant-btn>a:only-child {
+    color: currentColor
+}
+
+.ant-btn>a:only-child:after {
+    content: "";
+    position: absolute;
+    top: 0;
+    left: 0;
+    bottom: 0;
+    right: 0;
+    background: transparent
+}
+
+.ant-btn:focus,
+.ant-btn:hover {
+    color: #40a9ff;
+    background-color: #fff;
+    border-color: #40a9ff
+}
+
+.ant-btn:focus>a:only-child,
+.ant-btn:hover>a:only-child {
+    color: currentColor
+}
+
+.ant-btn:focus>a:only-child:after,
+.ant-btn:hover>a:only-child:after {
+    content: "";
+    position: absolute;
+    top: 0;
+    left: 0;
+    bottom: 0;
+    right: 0;
+    background: transparent
+}
+
+.ant-btn.active,
+.ant-btn:active {
+    color: #096dd9;
+    background-color: #fff;
+    border-color: #096dd9
+}
+
+.ant-btn.active>a:only-child,
+.ant-btn:active>a:only-child {
+    color: currentColor
+}
+
+.ant-btn.active>a:only-child:after,
+.ant-btn:active>a:only-child:after {
+    content: "";
+    position: absolute;
+    top: 0;
+    left: 0;
+    bottom: 0;
+    right: 0;
+    background: transparent
+}
+
+.ant-btn.active,
+.ant-btn:active,
+.ant-btn:focus,
+.ant-btn:hover {
+    background: #fff;
+    text-decoration: none
+}
+
+.ant-btn>i,
+.ant-btn>span {
+    pointer-events: none
+}
+
+.ant-btn:before {
+    position: absolute;
+    top: -1px;
+    left: -1px;
+    bottom: -1px;
+    right: -1px;
+    background: #fff;
+    opacity: .35;
+    content: "";
+    border-radius: inherit;
+    z-index: 1;
+    transition: opacity .2s;
+    pointer-events: none;
+    display: none
+}
+
+.ant-btn .anticon {
+    transition: margin-left .3s cubic-bezier(.645, .045, .355, 1)
+}
+
+.ant-btn:active>span,
+.ant-btn:focus>span {
+    position: relative
+}
+
+.ant-btn>.anticon+span,
+.ant-btn>span+.anticon {
+    margin-left: 8px
+}
+
+.ant-input {
+    font-family: Chinese Quote, -apple-system, BlinkMacSystemFont, Segoe UI, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Helvetica Neue, Helvetica, Arial, sans-serif;
+    font-variant: tabular-nums;
+    box-sizing: border-box;
+    margin: 0;
+    padding: 0;
+    list-style: none;
+    position: relative;
+    display: inline-block;
+    padding: 4px 11px;
+    width: 100%;
+    height: 32px;
+    font-size: 14px;
+    line-height: 1.5;
+    color: rgba(0, 0, 0, .65);
+    background-color: #fff;
+    background-image: none;
+    border: 1px solid #d9d9d9;
+    border-radius: 4px;
+    transition: all .3s
+}
+
+.ant-input::-moz-placeholder {
+    color: #bfbfbf;
+    opacity: 1
+}
+
+.ant-input:-ms-input-placeholder {
+    color: #bfbfbf
+}
+
+.ant-input::-webkit-input-placeholder {
+    color: #bfbfbf
+}
+
+.ant-input:focus,
+.ant-input:hover {
+    border-color: #40a9ff;
+    border-right-width: 1px!important
+}
+
+.ant-input:focus {
+    outline: 0;
+    box-shadow: 0 0 0 2px rgba(24, 144, 255, .2)
+}
+
+textarea.ant-input {
+    max-width: 100%;
+    height: auto;
+    vertical-align: bottom;
+    transition: all .3s, height 0s;
+    min-height: 32px
+}
+
+a,
+abbr,
+acronym,
+address,
+applet,
+article,
+aside,
+audio,
+b,
+big,
+blockquote,
+body,
+canvas,
+caption,
+center,
+cite,
+code,
+dd,
+del,
+details,
+dfn,
+div,
+dl,
+dt,
+em,
+fieldset,
+figcaption,
+figure,
+footer,
+form,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+header,
+hgroup,
+html,
+i,
+iframe,
+img,
+ins,
+kbd,
+label,
+legend,
+li,
+mark,
+menu,
+nav,
+object,
+ol,
+p,
+pre,
+q,
+s,
+samp,
+section,
+small,
+span,
+strike,
+strong,
+sub,
+summary,
+sup,
+table,
+tbody,
+td,
+tfoot,
+th,
+thead,
+time,
+tr,
+tt,
+u,
+ul,
+var,
+video {
+    margin: 0;
+    padding: 0;
+    border: 0;
+    outline: 0;
+    font-size: 100%;
+    font: inherit;
+    vertical-align: baseline
+}
+
+*,
+:after,
+:before {
+    -webkit-box-sizing: border-box;
+    -moz-box-sizing: border-box;
+    box-sizing: border-box
+}
+
+html {
+    font-family: sans-serif;
+    -ms-text-size-adjust: 100%;
+    -webkit-text-size-adjust: 100%
+}
+
+body,
+html {
+    font-size: 14px
+}
+
+body {
+    font-family: Microsoft Yahei, Lucida Grande, Lucida Sans Unicode, Helvetica, Arial, Verdana, sans-serif;
+    line-height: 1.6;
+    background-color: #fff;
+    position: static!important;
+    -webkit-tap-highlight-color: rgba(0, 0, 0, 0)
+}
+
+ol,
+ul {
+    list-style-type: none
+}
+
+b,
+strong {
+    font-weight: 700
+}
+
+img {
+    border: 0
+}
+
+button,
+input,
+select,
+textarea {
+    font-family: inherit;
+    font-size: 100%;
+    margin: 0
+}
+
+textarea {
+    overflow: auto;
+    vertical-align: top;
+    -webkit-appearance: none
+}
+
+button,
+input {
+    line-height: normal
+}
+
+button,
+select {
+    text-transform: none
+}
+
+button,
+html input[type=button],
+input[type=reset],
+input[type=submit] {
+    -webkit-appearance: button;
+    cursor: pointer
+}
+
+input[type=search] {
+    -webkit-appearance: textfield;
+    -moz-box-sizing: content-box;
+    -webkit-box-sizing: content-box;
+    box-sizing: content-box
+}
+
+input[type=search]::-webkit-search-cancel-button,
+input[type=search]::-webkit-search-decoration {
+    -webkit-appearance: none
+}
+
+button::-moz-focus-inner,
+input::-moz-focus-inner {
+    border: 0;
+    padding: 0
+}
+
+table {
+    width: 100%;
+    border-spacing: 0;
+    border-collapse: collapse
+}
+
+table,
+td,
+th {
+    border: 0
+}
+
+td,
+th {
+    padding: 0;
+    vertical-align: top
+}
+
+th {
+    font-weight: 700;
+    text-align: left
+}
+
+thead th {
+    white-space: nowrap
+}
+
+a {
+    text-decoration: none;
+    cursor: pointer;
+    color: #3296fa
+}
+
+a:active,
+a:hover {
+    outline: 0;
+    color: #3296fa
+}
+
+small {
+    font-size: 80%
+}
+
+body,
+html {
+    font-size: 12px!important;
+    color: #191f25!important;
+    background: #f6f6f6!important
+}
+
+.wrap {
+    display: -webkit-box;
+    display: -ms-flexbox;
+    display: flex;
+    -webkit-box-orient: vertical;
+    -webkit-box-direction: normal;
+    -ms-flex-direction: column;
+    flex-direction: column;
+    height: 100%
+}
+
+@font-face {
+    font-family: IconFont;
+    src: url("//at.alicdn.com/t/font_135284_ph2thxxbzgf.eot");
+    src: url("//at.alicdn.com/t/font_135284_ph2thxxbzgf.eot?#iefix") format("embedded-opentype"), url("//at.alicdn.com/t/font_135284_ph2thxxbzgf.woff") format("woff"), url("//at.alicdn.com/t/font_135284_ph2thxxbzgf.ttf") format("truetype"), url("//at.alicdn.com/t/font_135284_ph2thxxbzgf.svg#IconFont") format("svg")
+}
+
+.iconfont {
+    font-family: IconFont!important;
+    font-size: 16px;
+    font-style: normal;
+    -webkit-font-smoothing: antialiased;
+    -webkit-text-stroke-width: .2px;
+    -moz-osx-font-smoothing: grayscale
+}
+
+.fd-nav {
+    position: fixed;
+    top: 0;
+    left: 0;
+    right: 0;
+    z-index: 997;
+    width: 100%;
+    height: 60px;
+    font-size: 14px;
+    color: #fff;
+    background: #3296fa;
+    display: flex;
+    align-items: center
+}
+
+.fd-nav>* {
+    flex: 1;
+    width: 100%
+}
+
+.fd-nav .fd-nav-left {
+    display: -webkit-box;
+    display: flex;
+    align-items: center
+}
+
+.fd-nav .fd-nav-center {
+    flex: none;
+    width: 600px;
+    text-align: center
+}
+
+.fd-nav .fd-nav-right {
+    display: flex;
+    align-items: center;
+    justify-content: flex-end;
+    text-align: right
+}
+
+.fd-nav .fd-nav-back {
+    display: inline-block;
+    width: 60px;
+    height: 60px;
+    font-size: 22px;
+    border-right: 1px solid #1583f2;
+    text-align: center;
+    cursor: pointer
+}
+
+.fd-nav .fd-nav-back:hover {
+    background: #5af
+}
+
+.fd-nav .fd-nav-back:active {
+    background: #1583f2
+}
+
+.fd-nav .fd-nav-back .anticon {
+    line-height: 60px
+}
+
+.fd-nav .fd-nav-title {
+    width: 0;
+    flex: 1;
+    overflow: hidden;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    padding: 0 15px
+}
+
+.fd-nav a {
+    color: #fff;
+    margin-left: 12px
+}
+
+.fd-nav .button-publish {
+    min-width: 80px;
+    margin-left: 4px;
+    margin-right: 15px;
+    color: #3296fa;
+    border-color: #fff
+}
+
+.fd-nav .button-publish.ant-btn:focus,
+.fd-nav .button-publish.ant-btn:hover {
+    color: #3296fa;
+    border-color: #fff;
+    box-shadow: 0 10px 20px 0 rgba(0, 0, 0, .3)
+}
+
+.fd-nav .button-publish.ant-btn:active {
+    color: #3296fa;
+    background: #d6eaff;
+    box-shadow: none
+}
+
+.fd-nav .button-preview {
+    min-width: 80px;
+    margin-left: 16px;
+    margin-right: 4px;
+    color: #fff;
+    border-color: #fff;
+    background: transparent
+}
+
+.fd-nav .button-preview.ant-btn:focus,
+.fd-nav .button-preview.ant-btn:hover {
+    color: #fff;
+    border-color: #fff;
+    background: #59acfc
+}
+
+.fd-nav .button-preview.ant-btn:active {
+    color: #fff;
+    border-color: #fff;
+    background: #2186ef
+}
+
+.fd-nav-content {
+    position: fixed;
+    top: 60px;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    z-index: 1;
+    overflow-x: hidden;
+    overflow-y: auto;
+    padding-bottom: 30px
+}
+
+.error-modal-desc {
+    font-size: 13px;
+    color: rgba(25, 31, 37, .56);
+    line-height: 22px;
+    margin-bottom: 14px
+}
+
+.error-modal-list {
+    height: 200px;
+    overflow-y: auto;
+    margin-right: -25px;
+    padding-right: 25px
+}
+
+.error-modal-item {
+    padding: 10px 20px;
+    line-height: 21px;
+    background: #f6f6f6;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 8px;
+    border-radius: 4px
+}
+
+.error-modal-item-label {
+    flex: none;
+    font-size: 15px;
+    color: rgba(25, 31, 37, .56);
+    padding-right: 10px
+}
+
+.error-modal-item-content {
+    text-align: right;
+    flex: 1;
+    font-size: 13px;
+    color: #191f25
+}
+
+#body.blur {
+    -webkit-filter: blur(3px);
+    filter: blur(3px)
+}
+
+.zoom {
+    display: flex;
+    position: fixed;
+    -webkit-box-align: center;
+    -ms-flex-align: center;
+    align-items: center;
+    -webkit-box-pack: justify;
+    -ms-flex-pack: justify;
+    justify-content: space-between;
+    height: 40px;
+    width: 125px;
+    right: 40px;
+    margin-top: 30px;
+    z-index: 10
+}
+
+.zoom .zoom-in,
+.zoom .zoom-out {
+    width: 30px;
+    height: 30px;
+    background: #fff;
+    color: #c1c1cd;
+    cursor: pointer;
+    background-size: 100%;
+    background-repeat: no-repeat
+}
+
+.zoom .zoom-out {
+    background-image: url(https://gw.alicdn.com/tfs/TB1s0qhBHGYBuNjy0FoXXciBFXa-90-90.png)
+}
+
+.zoom .zoom-out.disabled {
+    opacity: .5
+}
+
+.zoom .zoom-in {
+    background-image: url(https://gw.alicdn.com/tfs/TB1UIgJBTtYBeNjy1XdXXXXyVXa-90-90.png)
+}
+
+.zoom .zoom-in.disabled {
+    opacity: .5
+}
+
+.auto-judge:hover .editable-title,
+.node-wrap-box:hover .editable-title {
+    border-bottom: 1px dashed #fff
+}
+
+.auto-judge:hover .editable-title.editing,
+.node-wrap-box:hover .editable-title.editing {
+    text-decoration: none;
+    border: 1px solid #d9d9d9
+}
+
+.auto-judge:hover .editable-title {
+    border-color: #15bc83
+}
+
+.editable-title {
+    line-height: 15px;
+    overflow: hidden;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    border-bottom: 1px dashed transparent
+}
+
+.editable-title:before {
+    content: "";
+    position: absolute;
+    top: 0;
+    left: 0;
+    bottom: 0;
+    right: 40px
+}
+
+.editable-title:hover {
+    border-bottom: 1px dashed #fff
+}
+
+.editable-title-input {
+    flex: none;
+    height: 18px;
+    padding-left: 4px;
+    text-indent: 0;
+    font-size: 12px;
+    line-height: 18px;
+    z-index: 1
+}
+
+.editable-title-input:hover {
+    text-decoration: none
+}
+
+.ant-btn {
+    position: relative
+}
+
+.node-wrap-box {
+    display: -webkit-inline-box;
+    display: -ms-inline-flexbox;
+    display: inline-flex;
+    -webkit-box-orient: vertical;
+    -webkit-box-direction: normal;
+    -ms-flex-direction: column;
+    flex-direction: column;
+    position: relative;
+    width: 220px;
+    min-height: 72px;
+    -ms-flex-negative: 0;
+    flex-shrink: 0;
+    background: #fff;
+    border-radius: 4px;
+    cursor: pointer
+}
+
+.node-wrap-box:after {
+    pointer-events: none;
+    content: "";
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    z-index: 2;
+    border-radius: 4px;
+    border: 1px solid transparent;
+    transition: all .1s cubic-bezier(.645, .045, .355, 1);
+    box-shadow: 0 2px 5px 0 rgba(0, 0, 0, .1)
+}
+
+.node-wrap-box.active:after,
+.node-wrap-box:active:after,
+.node-wrap-box:hover:after {
+    border: 1px solid #3296fa;
+    box-shadow: 0 0 6px 0 rgba(50, 150, 250, .3)
+}
+
+.node-wrap-box.active .close,
+.node-wrap-box:active .close,
+.node-wrap-box:hover .close {
+    display: block
+}
+
+.node-wrap-box.error:after {
+    border: 1px solid #f25643;
+    box-shadow: 0 2px 5px 0 rgba(0, 0, 0, .1)
+}
+
+.node-wrap-box .title {
+    position: relative;
+    display: flex;
+    align-items: center;
+    padding-left: 16px;
+    padding-right: 30px;
+    width: 100%;
+    height: 24px;
+    line-height: 24px;
+    font-size: 12px;
+    color: #fff;
+    text-align: left;
+    background: #576a95;
+    border-radius: 4px 4px 0 0
+}
+
+.node-wrap-box .title .iconfont {
+    font-size: 12px;
+    margin-right: 5px
+}
+
+.node-wrap-box .placeholder {
+    color: #bfbfbf
+}
+
+.node-wrap-box .close {
+    display: none;
+    position: absolute;
+    right: 10px;
+    top: 50%;
+    transform: translateY(-50%);
+    width: 20px;
+    height: 20px;
+    font-size: 14px;
+    color: #fff;
+    border-radius: 50%;
+    text-align: center;
+    line-height: 20px
+}
+
+.node-wrap-box .content {
+    position: relative;
+    font-size: 14px;
+    padding: 16px;
+    padding-right: 30px
+}
+
+.node-wrap-box .content .text {
+    overflow: hidden;
+    text-overflow: ellipsis;
+    display: -webkit-box;
+    -webkit-line-clamp: 3;
+    -webkit-box-orient: vertical
+}
+
+.node-wrap-box .content .arrow {
+    position: absolute;
+    right: 10px;
+    top: 50%;
+    transform: translateY(-50%);
+    width: 20px;
+    height: 14px;
+    font-size: 14px;
+    color: #979797
+}
+
+.start-node.node-wrap-box .content .text {
+    display: block;
+    white-space: nowrap
+}
+
+.node-wrap-box:before {
+    content: "";
+    position: absolute;
+    top: -12px;
+    left: 50%;
+    -webkit-transform: translateX(-50%);
+    transform: translateX(-50%);
+    width: 0;
+    height: 4px;
+    border-style: solid;
+    border-width: 8px 6px 4px;
+    border-color: #cacaca transparent transparent;
+    background: #f5f5f7
+}
+
+.node-wrap-box.start-node:before {
+    content: none
+}
+
+.top-left-cover-line {
+    left: -1px
+}
+
+.top-left-cover-line,
+.top-right-cover-line {
+    position: absolute;
+    height: 8px;
+    width: 50%;
+    background-color: #f5f5f7;
+    top: -4px
+}
+
+.top-right-cover-line {
+    right: -1px
+}
+
+.bottom-left-cover-line {
+    left: -1px
+}
+
+.bottom-left-cover-line,
+.bottom-right-cover-line {
+    position: absolute;
+    height: 8px;
+    width: 50%;
+    background-color: #f5f5f7;
+    bottom: -4px
+}
+
+.bottom-right-cover-line {
+    right: -1px
+}
+
+.dingflow-design {
+    width: 100%;
+    background-color: #f5f5f7;
+    overflow: auto;
+    position: absolute;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    top: 0
+}
+
+.dingflow-design .box-scale {
+    transform: scale(1);
+    display: inline-block;
+    position: relative;
+    width: 100%;
+    padding: 54.5px 0;
+    -webkit-box-align: start;
+    -ms-flex-align: start;
+    align-items: flex-start;
+    -webkit-box-pack: center;
+    -ms-flex-pack: center;
+    justify-content: center;
+    -ms-flex-wrap: wrap;
+    flex-wrap: wrap;
+    min-width: -webkit-min-content;
+    min-width: -moz-min-content;
+    min-width: min-content;
+    background-color: #f5f5f7;
+    transform-origin: 50% 0px 0px;
+}
+
+.dingflow-design .node-wrap {
+    flex-direction: column;
+    -webkit-box-pack: start;
+    -ms-flex-pack: start;
+    justify-content: flex-start;
+    -webkit-box-align: center;
+    -ms-flex-align: center;
+    align-items: center;
+    -ms-flex-wrap: wrap;
+    flex-wrap: wrap;
+    -webkit-box-flex: 1;
+    -ms-flex-positive: 1;
+    padding: 0 50px;
+    position: relative
+}
+
+.dingflow-design .branch-wrap,
+.dingflow-design .node-wrap {
+    display: inline-flex;
+    width: 100%
+}
+
+.dingflow-design .branch-box-wrap {
+    display: flex;
+    -webkit-box-orient: vertical;
+    -webkit-box-direction: normal;
+    -ms-flex-direction: column;
+    flex-direction: column;
+    -ms-flex-wrap: wrap;
+    flex-wrap: wrap;
+    -webkit-box-align: center;
+    -ms-flex-align: center;
+    align-items: center;
+    min-height: 270px;
+    width: 100%;
+    -ms-flex-negative: 0;
+    flex-shrink: 0
+}
+
+.dingflow-design .branch-box {
+    display: flex;
+    overflow: visible;
+    min-height: 180px;
+    height: auto;
+    border-bottom: 2px solid #ccc;
+    border-top: 2px solid #ccc;
+    position: relative;
+    margin-top: 15px
+}
+
+.dingflow-design .branch-box .col-box {
+    background: #f5f5f7
+}
+
+.dingflow-design .branch-box .col-box:before {
+    content: "";
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    z-index: 0;
+    margin: auto;
+    width: 2px;
+    height: 100%;
+    background-color: #cacaca
+}
+
+.dingflow-design .add-branch {
+    border: none;
+    outline: none;
+    user-select: none;
+    justify-content: center;
+    font-size: 12px;
+    padding: 0 10px;
+    height: 30px;
+    line-height: 30px;
+    border-radius: 15px;
+    color: #3296fa;
+    background: #fff;
+    box-shadow: 0 2px 4px 0 rgba(0, 0, 0, .1);
+    position: absolute;
+    top: -16px;
+    left: 50%;
+    transform: translateX(-50%);
+    transform-origin: center center;
+    cursor: pointer;
+    z-index: 1;
+    display: inline-flex;
+    align-items: center;
+    -webkit-transition: all .3s cubic-bezier(.645, .045, .355, 1);
+    transition: all .3s cubic-bezier(.645, .045, .355, 1)
+}
+
+.dingflow-design .add-branch:hover {
+    transform: translateX(-50%) scale(1.1);
+    box-shadow: 0 8px 16px 0 rgba(0, 0, 0, .1)
+}
+
+.dingflow-design .add-branch:active {
+    transform: translateX(-50%);
+    box-shadow: none
+}
+
+.dingflow-design .col-box {
+    display: inline-flex;
+    -webkit-box-orient: vertical;
+    -webkit-box-direction: normal;
+    flex-direction: column;
+    -webkit-box-align: center;
+    align-items: center;
+    position: relative
+}
+
+.dingflow-design .condition-node {
+    min-height: 220px
+}
+
+.dingflow-design .condition-node,
+.dingflow-design .condition-node-box {
+    display: inline-flex;
+    -webkit-box-orient: vertical;
+    -webkit-box-direction: normal;
+    flex-direction: column;
+    -webkit-box-flex: 1
+}
+
+.dingflow-design .condition-node-box {
+    padding-top: 30px;
+    padding-right: 50px;
+    padding-left: 50px;
+    -webkit-box-pack: center;
+    justify-content: center;
+    -webkit-box-align: center;
+    align-items: center;
+    flex-grow: 1;
+    position: relative
+}
+
+.dingflow-design .condition-node-box:before {
+    content: "";
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    margin: auto;
+    width: 2px;
+    height: 100%;
+    background-color: #cacaca
+}
+
+.dingflow-design .auto-judge {
+    position: relative;
+    width: 220px;
+    min-height: 72px;
+    background: #fff;
+    border-radius: 4px;
+    padding: 14px 19px;
+    cursor: pointer
+}
+
+.dingflow-design .auto-judge:after {
+    pointer-events: none;
+    content: "";
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    z-index: 2;
+    border-radius: 4px;
+    border: 1px solid transparent;
+    transition: all .1s cubic-bezier(.645, .045, .355, 1);
+    box-shadow: 0 2px 5px 0 rgba(0, 0, 0, .1)
+}
+
+.dingflow-design .auto-judge.active:after,
+.dingflow-design .auto-judge:active:after,
+.dingflow-design .auto-judge:hover:after {
+    border: 1px solid #3296fa;
+    box-shadow: 0 0 6px 0 rgba(50, 150, 250, .3)
+}
+
+.dingflow-design .auto-judge.active .close,
+.dingflow-design .auto-judge:active .close,
+.dingflow-design .auto-judge:hover .close {
+    display: block
+}
+
+.dingflow-design .auto-judge.error:after {
+    border: 1px solid #f25643;
+    box-shadow: 0 2px 5px 0 rgba(0, 0, 0, .1)
+}
+
+.dingflow-design .auto-judge .title-wrapper {
+    position: relative;
+    font-size: 12px;
+    color: #15bc83;
+    text-align: left;
+    line-height: 16px
+}
+
+.dingflow-design .auto-judge .title-wrapper .editable-title {
+    display: inline-block;
+    max-width: 120px;
+    overflow: hidden;
+    white-space: nowrap;
+    text-overflow: ellipsis
+}
+
+.dingflow-design .auto-judge .title-wrapper .priority-title {
+    display: inline-block;
+    float: right;
+    margin-right: 10px;
+    color: rgba(25, 31, 37, .56)
+}
+
+.dingflow-design .auto-judge .placeholder {
+    color: #bfbfbf
+}
+
+.dingflow-design .auto-judge .close {
+    display: none;
+    position: absolute;
+    right: -10px;
+    top: -10px;
+    width: 20px;
+    height: 20px;
+    font-size: 14px;
+    color: rgba(0, 0, 0, .25);
+    border-radius: 50%;
+    text-align: center;
+    line-height: 20px;
+    z-index: 2
+}
+
+.dingflow-design .auto-judge .content {
+    font-size: 14px;
+    color: #191f25;
+    text-align: left;
+    margin-top: 6px;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    display: -webkit-box;
+    -webkit-line-clamp: 3;
+    -webkit-box-orient: vertical
+}
+
+.dingflow-design .auto-judge .sort-left,
+.dingflow-design .auto-judge .sort-right {
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    display: none;
+    z-index: 1
+}
+
+.dingflow-design .auto-judge .sort-left {
+    left: 0;
+    border-right: 1px solid #f6f6f6
+}
+
+.dingflow-design .auto-judge .sort-right {
+    right: 0;
+    border-left: 1px solid #f6f6f6
+}
+
+.dingflow-design .auto-judge:hover .sort-left,
+.dingflow-design .auto-judge:hover .sort-right {
+    display: flex;
+    align-items: center
+}
+
+.dingflow-design .auto-judge .sort-left:hover,
+.dingflow-design .auto-judge .sort-right:hover {
+    background: #efefef
+}
+
+.dingflow-design .end-node {
+    border-radius: 50%;
+    font-size: 14px;
+    color: rgba(25, 31, 37, .4);
+    text-align: left
+}
+
+.dingflow-design .end-node .end-node-circle {
+    width: 10px;
+    height: 10px;
+    margin: auto;
+    border-radius: 50%;
+    background: #dbdcdc
+}
+
+.dingflow-design .end-node .end-node-text {
+    margin-top: 5px;
+    text-align: center
+}
+
+.approval-setting {
+    border-radius: 2px;
+    margin: 20px 0;
+    position: relative;
+    background: #fff
+}
+
+.ant-btn {
+    position: relative
+}
+
+

+ 55 - 0
src/store/modules/simpleWorkflow.ts

@@ -0,0 +1,55 @@
+import { store } from '../index'
+import { defineStore } from 'pinia'
+
+export const useWorkFlowStore = defineStore('simpleWorkflow', {
+  state: () => ({
+    tableId: '',
+    isTried: false,
+    promoterDrawer: false,
+    flowPermission1: {},
+    approverDrawer: false,
+    approverConfig1: {},
+    copyerDrawer: false,
+    copyerConfig1: {},
+    conditionDrawer: false,
+    conditionsConfig1: {
+      conditionNodes: []
+    }
+  }),
+  actions: {
+    setTableId(payload) {
+      this.tableId = payload
+    },
+    setIsTried(payload) {
+      this.isTried = payload
+    },
+    setPromoter(payload) {
+      this.promoterDrawer = payload
+    },
+    setFlowPermission(payload) {
+      this.flowPermission1 = payload
+    },
+    setApprover(payload) {
+      this.approverDrawer = payload
+    },
+    setApproverConfig(payload) {
+      this.approverConfig1 = payload
+    },
+    setCopyer(payload) {
+      this.copyerDrawer = payload
+    },
+    setCopyerConfig(payload) {
+      this.copyerConfig1 = payload
+    },
+    setCondition(payload) {
+      this.conditionDrawer = payload
+    },
+    setConditionsConfig(payload) {
+      this.conditionsConfig1 = payload
+    }
+  }
+})
+
+export const useWorkFlowStoreWithOut = () => {
+  return useWorkFlowStore(store)
+}