From a1d7e81859f554f3a53680cc35f0f49bf1f77098 Mon Sep 17 00:00:00 2001
From: wwf <1971391498@qq.com>
Date: 星期四, 14 五月 2026 14:37:02 +0800
Subject: [PATCH] 导入项目

---
 src/views/bpm/processInstance/create/ProcessDefinitionDetail.vue |  357 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 357 insertions(+), 0 deletions(-)

diff --git a/src/views/bpm/processInstance/create/ProcessDefinitionDetail.vue b/src/views/bpm/processInstance/create/ProcessDefinitionDetail.vue
new file mode 100644
index 0000000..c1ad017
--- /dev/null
+++ b/src/views/bpm/processInstance/create/ProcessDefinitionDetail.vue
@@ -0,0 +1,357 @@
+<template>
+  <ContentWrap :bodyStyle="{ padding: '10px 20px 0' }">
+    <div class="processInstance-wrap-main">
+      <el-scrollbar>
+        <div class="text-#878c93 h-15px">娴佺▼锛歿{ selectProcessDefinition.name }}</div>
+        <el-divider class="!my-8px" />
+
+        <!-- 涓棿涓昏鍐呭 tab 鏍� -->
+        <el-tabs v-model="activeTab">
+          <!-- 琛ㄥ崟淇℃伅 -->
+          <el-tab-pane label="琛ㄥ崟濉啓" name="form">
+            <div class="form-scroll-area" v-loading="processInstanceStartLoading">
+              <el-scrollbar>
+                <el-row>
+                  <el-col :span="17">
+                    <form-create
+                      :rule="detailForm.rule"
+                      v-model:api="fApi"
+                      v-model="detailForm.value"
+                      :option="detailForm.option"
+                      @submit="submitForm"
+                    />
+                  </el-col>
+
+                  <el-col :span="6" :offset="1">
+                    <!-- 娴佺▼鏃堕棿绾� -->
+                    <ProcessInstanceTimeline
+                      ref="timelineRef"
+                      :activity-nodes="activityNodes"
+                      :show-status-icon="false"
+                      @select-user-confirm="selectUserConfirm"
+                    />
+                  </el-col>
+                </el-row>
+              </el-scrollbar>
+            </div>
+          </el-tab-pane>
+          <!-- 娴佺▼鍥� -->
+          <el-tab-pane label="娴佺▼鍥�" name="diagram">
+            <div class="form-scroll-area">
+              <!-- BPMN 娴佺▼鍥鹃瑙� -->
+              <ProcessInstanceBpmnViewer
+                :bpmn-xml="bpmnXML"
+                v-if="BpmModelType.BPMN === selectProcessDefinition.modelType"
+              />
+
+              <!-- Simple 娴佺▼鍥鹃瑙� -->
+              <ProcessInstanceSimpleViewer
+                :simple-json="simpleJson"
+                v-if="BpmModelType.SIMPLE === selectProcessDefinition.modelType"
+              />
+            </div>
+          </el-tab-pane>
+        </el-tabs>
+
+        <!-- 搴曢儴鎿嶄綔鏍� -->
+        <div class="b-t-solid border-t-1px border-[var(--el-border-color)]">
+          <!-- 鎿嶄綔鏍忔寜閽� -->
+          <div
+            v-if="activeTab === 'form'"
+            class="h-50px bottom-10 text-14px flex items-center color-#32373c dark:color-#fff font-bold btn-container"
+          >
+            <el-button plain type="success" @click="submitForm">
+              <Icon icon="ep:select" />&nbsp; 鍙戣捣
+            </el-button>
+            <el-button plain type="danger" @click="handleCancel">
+              <Icon icon="ep:close" />&nbsp; 鍙栨秷
+            </el-button>
+          </div>
+        </div>
+      </el-scrollbar>
+    </div>
+  </ContentWrap>
+</template>
+<script lang="ts" setup>
+import { decodeFields, setConfAndFields2 } from '@/utils/formCreate'
+import { BpmModelType, BpmModelFormType } from '@/utils/constants'
+import {
+  CandidateStrategy,
+  NodeId,
+  FieldPermissionType
+} from '@/components/SimpleProcessDesignerV2/src/consts'
+import ProcessInstanceBpmnViewer from '../detail/ProcessInstanceBpmnViewer.vue'
+import ProcessInstanceSimpleViewer from '../detail/ProcessInstanceSimpleViewer.vue'
+import ProcessInstanceTimeline from '../detail/ProcessInstanceTimeline.vue'
+import type { ApiAttrs } from '@form-create/element-ui/types/config'
+import { useTagsViewStore } from '@/store/modules/tagsView'
+import * as ProcessInstanceApi from '@/api/bpm/processInstance'
+import * as DefinitionApi from '@/api/bpm/definition'
+import { ApprovalNodeInfo } from '@/api/bpm/processInstance'
+import formCreate from '@form-create/element-ui'
+
+defineOptions({ name: 'ProcessDefinitionDetail' })
+const props = defineProps<{
+  selectProcessDefinition: any
+}>()
+const emit = defineEmits(['cancel'])
+const processInstanceStartLoading = ref(false) // 娴佺▼瀹炰緥鍙戣捣涓�
+const { push, currentRoute } = useRouter() // 璺敱
+const message = useMessage() // 娑堟伅寮圭獥
+const { delView } = useTagsViewStore() // 瑙嗗浘鎿嶄綔
+
+const detailForm: any = ref({
+  rule: [],
+  option: {},
+  value: {}
+}) // 娴佺▼琛ㄥ崟璇︽儏
+const fApi = ref<ApiAttrs>()
+// 鎸囧畾瀹℃壒浜�
+const startUserSelectTasks: any = ref([]) // 鍙戣捣浜洪渶瑕侀�夋嫨瀹℃壒浜烘垨鎶勯�佷汉鐨勪换鍔″垪琛�
+const startUserSelectAssignees = ref({}) // 鍙戣捣浜洪�夋嫨瀹℃壒浜虹殑鏁版嵁
+const tempStartUserSelectAssignees = ref({}) // 鍘嗗彶鍙戣捣浜洪�夋嫨瀹℃壒浜虹殑鏁版嵁锛岀敤浜庢瘡娆¤〃鍗曞彉鏇存椂锛屼复鏃朵繚瀛�
+const bpmnXML: any = ref(null) // BPMN 鏁版嵁
+const simpleJson = ref<string | undefined>() // Simple 璁捐鍣ㄦ暟鎹� json 鏍煎紡
+
+const activeTab = ref('form') // 褰撳墠鐨� Tab
+const activityNodes = ref<ProcessInstanceApi.ApprovalNodeInfo[]>([]) // 瀹℃壒鑺傜偣淇℃伅
+
+/** 璁剧疆琛ㄥ崟淇℃伅銆佽幏鍙栨祦绋嬪浘鏁版嵁 **/
+const initProcessInfo = async (row: any, formVariables?: any) => {
+  // 閲嶇疆鎸囧畾瀹℃壒浜�
+  startUserSelectTasks.value = []
+  startUserSelectAssignees.value = {}
+
+  // 鎯呭喌涓�锛氭祦绋嬭〃鍗�
+  if (row.formType == BpmModelFormType.NORMAL) {
+    // 璁剧疆琛ㄥ崟
+    // 娉ㄦ剰锛氶渶瑕佷粠 formVariables 涓紝绉婚櫎涓嶅湪 row.formFields 鐨勫�笺��
+    // 鍘熷洜鏄細鍚庣杩斿洖鐨� formVariables 閲岄潰锛屼細鏈変竴浜涢潪琛ㄥ崟鐨勪俊鎭�備緥濡傝锛屾煇涓祦绋嬭妭鐐圭殑瀹℃壒浜恒��
+    //        杩欐牱锛屽氨鍙兘瀵艰嚧涓�涓祦绋嬭瀹℃壒涓嶉�氳繃鍚庯紝閲嶆柊鍙戣捣鏃讹紝浼氱洿鎺ュ悗绔姤閿欙紒锛侊紒
+    const formApi = formCreate.create(decodeFields(row.formFields))
+    const allowedFields = formApi.fields()
+    for (const key in formVariables) {
+      if (!allowedFields.includes(key)) {
+        delete formVariables[key]
+      }
+    }
+    setConfAndFields2(detailForm, row.formConf, row.formFields, formVariables)
+
+    await nextTick()
+    fApi.value?.btn.show(false) // 闅愯棌鎻愪氦鎸夐挳
+
+    // 鑾峰彇娴佺▼瀹℃壒淇℃伅,褰撳啀娆″彂璧锋椂锛屾祦绋嬪鎵硅妭鐐硅鏍规嵁鍘熷琛ㄥ崟鍙傛暟棰勬祴鍑烘潵
+    await getApprovalDetail({
+      id: row.id,
+      processVariablesStr: JSON.stringify(formVariables)
+    })
+
+    // 鍔犺浇娴佺▼鍥�
+    const processDefinitionDetail = await DefinitionApi.getProcessDefinition(row.id)
+    if (processDefinitionDetail) {
+      bpmnXML.value = processDefinitionDetail.bpmnXml
+      simpleJson.value = processDefinitionDetail.simpleModel
+    }
+    // 鎯呭喌浜岋細涓氬姟琛ㄥ崟
+  } else if (row.formCustomCreatePath) {
+    await push({
+      path: row.formCustomCreatePath
+    })
+    // 杩欓噷鏆傛椂鏃犻渶鍔犺浇娴佺▼鍥撅紝鍥犱负璺冲嚭鍒板彟澶栦釜 Tab锛�
+  }
+}
+
+/** 棰勬祴娴佺▼鑺傜偣浼氬洜涓鸿緭鍏ョ殑鍙傛暟鍊艰�屼骇鐢熸柊鐨勯娴嬬粨鏋滃�硷紝鎵�浠ラ渶閲嶆柊棰勬祴涓�娆� */
+watch(
+  detailForm.value,
+  (newValue) => {
+    if (newValue && Object.keys(newValue.value).length > 0) {
+      // 璁板綍涔嬪墠鐨勮妭鐐瑰鎵逛汉
+      tempStartUserSelectAssignees.value = startUserSelectAssignees.value
+      startUserSelectAssignees.value = {}
+      // 鍔犺浇鏈�鏂扮殑瀹℃壒璇︽儏
+      getApprovalDetail({
+        id: props.selectProcessDefinition.id,
+        processVariablesStr: JSON.stringify(newValue.value) // 瑙e喅 GET 鏃犳硶浼犻�掑璞$殑闂锛屽悗绔� String 鍐嶈浆 JSON
+      })
+    }
+  },
+  {
+    immediate: true
+  }
+)
+
+/** 鑾峰彇瀹℃壒璇︽儏 */
+const getApprovalDetail = async (row: any) => {
+  try {
+    // TODO 鑾峰彇瀹℃壒璇︽儏锛岃缃� activityId 涓哄彂璧蜂汉鑺傜偣锛堜负浜嗚幏鍙栧瓧娈垫潈闄愩�傛殏鏃跺彧瀵� Simple 璁捐鍣ㄦ湁鏁堬級锛汙jason锛氳繖閲屽彲浠ュ幓鎺� activityId 涔堬紵
+    const data = await ProcessInstanceApi.getApprovalDetail({
+      processDefinitionId: row.id,
+      activityId: NodeId.START_USER_NODE_ID,
+      processVariablesStr: row.processVariablesStr // 瑙e喅 GET 鏃犳硶浼犻�掑璞$殑闂锛屽悗绔� String 鍐嶈浆 JSON
+    })
+
+    if (!data) {
+      message.error('鏌ヨ涓嶅埌瀹℃壒璇︽儏淇℃伅锛�')
+      return
+    }
+    // 鑾峰彇瀹℃壒鑺傜偣锛屾樉绀� Timeline 鐨勬暟鎹�
+    activityNodes.value = data.activityNodes
+
+    // 鑾峰彇鍙戣捣浜鸿嚜閫夌殑浠诲姟
+    startUserSelectTasks.value = data.activityNodes?.filter(
+      (node: ApprovalNodeInfo) => CandidateStrategy.START_USER_SELECT === node.candidateStrategy
+    )
+    // 鎭㈠涔嬪墠鐨勯�夋嫨瀹℃壒浜�
+    if (startUserSelectTasks.value?.length > 0) {
+      for (const node of startUserSelectTasks.value) {
+        if (
+          tempStartUserSelectAssignees.value[node.id] &&
+          tempStartUserSelectAssignees.value[node.id].length > 0
+        ) {
+          startUserSelectAssignees.value[node.id] = tempStartUserSelectAssignees.value[node.id]
+        } else {
+          startUserSelectAssignees.value[node.id] = []
+        }
+      }
+    }
+
+    // 鑾峰彇琛ㄥ崟瀛楁鏉冮檺
+    const formFieldsPermission = data.formFieldsPermission
+    // 璁剧疆琛ㄥ崟瀛楁鏉冮檺
+    if (formFieldsPermission) {
+      Object.keys(formFieldsPermission).forEach((item) => {
+        setFieldPermission(item, formFieldsPermission[item])
+      })
+    }
+  } finally {
+  }
+}
+
+/**
+ * 璁剧疆琛ㄥ崟鏉冮檺
+ */
+const setFieldPermission = (field: string, permission: string) => {
+  if (permission === FieldPermissionType.READ) {
+    // 1. 璁剧疆瀛楁涓哄彧璇�
+    //@ts-ignore
+    fApi.value?.disabled(true, field)
+    // 2. 鍙瀛楁锛� 鍘绘帀楠岃瘉瑙勫垯
+    //  fApi.value?.updateValidate(field, []); 杩欎釜鏂规硶璨屼技涓嶈捣浣滅敤锛�
+    try {
+      //@ts-ignore
+      const rule = fApi.value?.getRule(field)
+      if (rule) {
+        // 蹇呭~楠岃瘉璁剧疆涓篺alse
+        rule.$required = false
+        // 娓呯┖鎵�鏈夐獙璇佽鍒�
+        if (rule.validate) {
+          rule.validate = []
+        }
+      }
+    } catch (error) {
+      console.warn('淇敼瀛楁楠岃瘉瑙勫垯澶辫触:', error)
+    }
+  }
+  if (permission === FieldPermissionType.WRITE) {
+    //@ts-ignore
+    fApi.value?.disabled(false, field)
+  }
+  if (permission === FieldPermissionType.NONE) {
+    //@ts-ignore
+    fApi.value?.hidden(true, field)
+  }
+}
+
+/** 鎻愪氦鎸夐挳 */
+const submitForm = async () => {
+  if (!fApi.value || !props.selectProcessDefinition) {
+    return
+  }
+  
+  try {
+    // 娴佺▼琛ㄥ崟鏍¢獙
+    await fApi.value.validate()
+  } catch (error) {
+    // 濡傛灉楠岃瘉澶辫触锛屾鏌ユ槸鍚︽槸鍙瀛楁鐨勯獙璇侀敊璇�
+    console.warn('琛ㄥ崟楠岃瘉澶辫触:', error)
+    return
+  }
+  // 濡傛灉鏈夋寚瀹氬鎵逛汉锛岄渶瑕佹牎楠�
+  if (startUserSelectTasks.value?.length > 0) {
+    for (const userTask of startUserSelectTasks.value) {
+      if (
+        Array.isArray(startUserSelectAssignees.value[userTask.id]) &&
+        startUserSelectAssignees.value[userTask.id].length === 0
+      )
+        return message.warning(`璇烽�夋嫨${userTask.name}鐨勫�欓�変汉`)
+    }
+  }
+
+  // 鎻愪氦璇锋眰
+  processInstanceStartLoading.value = true
+  try {
+    await ProcessInstanceApi.createProcessInstance({
+      processDefinitionId: props.selectProcessDefinition.id,
+      variables: detailForm.value.value,
+      startUserSelectAssignees: startUserSelectAssignees.value
+    })
+    // 鎻愮ず
+    message.success('鍙戣捣娴佺▼鎴愬姛')
+    // 璺宠浆鍥炲幓
+    delView(unref(currentRoute))
+    await push({
+      name: 'BpmProcessInstanceMy'
+    })
+  } finally {
+    processInstanceStartLoading.value = false
+  }
+}
+
+/** 鍙栨秷鍙戣捣瀹℃壒 */
+const handleCancel = () => {
+  emit('cancel')
+}
+
+/** 閫夋嫨鍙戣捣浜� */
+const selectUserConfirm = (id: string, userList: any[]) => {
+  startUserSelectAssignees.value[id] = userList?.map((item: any) => item.id)
+}
+
+defineExpose({ initProcessInfo })
+</script>
+
+<style lang="scss" scoped>
+$wrap-padding-height: 20px;
+$wrap-margin-height: 15px;
+$button-height: 51px;
+$process-header-height: 105px;
+
+.processInstance-wrap-main {
+  height: calc(
+    100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 35px
+  );
+  max-height: calc(
+    100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 35px
+  );
+  overflow: auto;
+
+  .form-scroll-area {
+    height: calc(
+      100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 35px -
+        $process-header-height - 40px
+    );
+    max-height: calc(
+      100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 35px -
+        $process-header-height - 40px
+    );
+    overflow: auto;
+  }
+}
+
+.form-box {
+  :deep(.el-card) {
+    border: none;
+  }
+}
+</style>

--
Gitblit v1.8.0