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/index.vue |  321 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 321 insertions(+), 0 deletions(-)

diff --git a/src/views/bpm/processInstance/create/index.vue b/src/views/bpm/processInstance/create/index.vue
new file mode 100644
index 0000000..2c714d1
--- /dev/null
+++ b/src/views/bpm/processInstance/create/index.vue
@@ -0,0 +1,321 @@
+<template>
+  <!-- 绗竴姝ワ紝閫氳繃娴佺▼瀹氫箟鐨勫垪琛紝閫夋嫨瀵瑰簲鐨勬祦绋� -->
+  <template v-if="!selectProcessDefinition">
+    <el-input
+      v-model="searchName"
+      class="!w-50% mb-15px"
+      placeholder="璇疯緭鍏ユ祦绋嬪悕绉�"
+      clearable
+      @input="handleQuery"
+      @clear="handleQuery"
+    >
+      <template #prefix>
+        <Icon icon="ep:search" />
+      </template>
+    </el-input>
+    <ContentWrap
+      :class="{ 'process-definition-container': filteredProcessDefinitionList?.length }"
+      class="position-relative pb-20px h-700px"
+      v-loading="loading"
+    >
+      <el-row v-if="filteredProcessDefinitionList?.length" :gutter="20" class="!flex-nowrap">
+        <el-col :span="5">
+          <div class="flex flex-col">
+            <div
+              v-for="category in availableCategories"
+              :key="category.code"
+              class="flex items-center p-10px cursor-pointer text-14px rounded-md"
+              :class="categoryActive.code === category.code ? 'text-#3e7bff bg-#e8eeff' : ''"
+              @click="handleCategoryClick(category)"
+            >
+              {{ category.name }}
+            </div>
+          </div>
+        </el-col>
+        <el-col :span="19">
+          <el-scrollbar ref="scrollWrapper" height="700" @scroll="handleScroll">
+            <div
+              class="mb-20px pl-10px"
+              v-for="(definitions, categoryCode) in processDefinitionGroup"
+              :key="categoryCode"
+              :ref="`category-${categoryCode}`"
+            >
+              <h3 class="text-18px font-bold mb-10px mt-5px">
+                {{ getCategoryName(categoryCode as any) }}
+              </h3>
+              <div class="grid grid-cols-3 gap3">
+                <el-tooltip
+                  v-for="definition in definitions"
+                  :key="definition.id"
+                  :content="definition.description"
+                  :disabled="!definition.description || definition.description.trim().length === 0"
+                  placement="top"
+                >
+                  <el-card
+                    shadow="hover"
+                    class="cursor-pointer definition-item-card"
+                    @click="handleSelect(definition)"
+                  >
+                    <template #default>
+                      <div class="flex">
+                        <el-image
+                          v-if="definition.icon"
+                          :src="definition.icon"
+                          class="w-32px h-32px"
+                        />
+                        <div v-else class="flow-icon">
+                          <span style="font-size: 12px; color: #fff">
+                            {{ subString(definition.name, 0, 2) }}
+                          </span>
+                        </div>
+                        <el-text class="!ml-10px" size="large">{{ definition.name }}</el-text>
+                      </div>
+                    </template>
+                  </el-card>
+                </el-tooltip>
+              </div>
+            </div>
+          </el-scrollbar>
+        </el-col>
+      </el-row>
+      <el-empty class="!py-200px" :image-size="200" description="娌℃湁鎵惧埌鎼滅储缁撴灉" v-else />
+    </ContentWrap>
+  </template>
+
+  <!-- 绗簩姝ワ紝濉啓琛ㄥ崟锛岃繘琛屾祦绋嬬殑鎻愪氦 -->
+  <ProcessDefinitionDetail
+    v-else
+    ref="processDefinitionDetailRef"
+    :selectProcessDefinition="selectProcessDefinition"
+    @cancel="selectProcessDefinition = undefined"
+  />
+</template>
+
+<script lang="ts" setup>
+import * as DefinitionApi from '@/api/bpm/definition'
+import * as ProcessInstanceApi from '@/api/bpm/processInstance'
+import { CategoryApi, CategoryVO } from '@/api/bpm/category'
+import ProcessDefinitionDetail from './ProcessDefinitionDetail.vue'
+import { groupBy } from 'lodash-es'
+import { subString } from '@/utils/index'
+
+defineOptions({ name: 'BpmProcessInstanceCreate' })
+
+const { proxy } = getCurrentInstance() as any
+const route = useRoute() // 璺敱
+const message = useMessage() // 娑堟伅
+
+const searchName = ref('') // 褰撳墠鎼滅储鍏抽敭瀛�
+const processInstanceId: any = route.query.processInstanceId // 娴佺▼瀹炰緥缂栧彿銆傚満鏅細閲嶆柊鍙戣捣鏃�
+const loading = ref(true) // 鍔犺浇涓�
+const categoryList: any = ref([]) // 鍒嗙被鐨勫垪琛�
+const categoryActive: any = ref({}) // 閫変腑鐨勫垎绫�
+const processDefinitionList = ref([]) // 娴佺▼瀹氫箟鐨勫垪琛�
+
+/** 鏌ヨ鍒楄〃 */
+const getList = async () => {
+  loading.value = true
+  try {
+    // 鎵�鏈夋祦绋嬪垎绫绘暟鎹�
+    await getCategoryList()
+    // 鎵�鏈夋祦绋嬪畾涔夋暟鎹�
+    await getProcessDefinitionList()
+
+    // 濡傛灉 processInstanceId 闈炵┖锛岃鏄庢槸閲嶆柊鍙戣捣
+    if (processInstanceId?.length > 0) {
+      const processInstance = await ProcessInstanceApi.getProcessInstance(processInstanceId)
+      if (!processInstance) {
+        message.error('閲嶆柊鍙戣捣娴佺▼澶辫触锛屽師鍥狅細娴佺▼瀹炰緥涓嶅瓨鍦�')
+        return
+      }
+      const processDefinition = processDefinitionList.value.find(
+        (item: any) => item.key == processInstance.processDefinition?.key
+      )
+      if (!processDefinition) {
+        message.error('閲嶆柊鍙戣捣娴佺▼澶辫触锛屽師鍥狅細娴佺▼瀹氫箟涓嶅瓨鍦�')
+        return
+      }
+      await handleSelect(processDefinition, processInstance.formVariables)
+    }
+  } finally {
+    loading.value = false
+  }
+}
+
+/** 鑾峰彇鎵�鏈夋祦绋嬪垎绫绘暟鎹� */
+const getCategoryList = async () => {
+  try {
+    // 娴佺▼鍒嗙被
+    categoryList.value = await CategoryApi.getCategorySimpleList()
+  } finally {
+  }
+}
+
+/** 鑾峰彇鎵�鏈夋祦绋嬪畾涔夋暟鎹� */
+const getProcessDefinitionList = async () => {
+  try {
+    // 娴佺▼瀹氫箟
+    processDefinitionList.value = await DefinitionApi.getProcessDefinitionList({
+      suspensionState: 1
+    })
+    // 鍒濆鍖栬繃婊ゅ垪琛ㄤ负鍏ㄩ儴娴佺▼瀹氫箟
+    filteredProcessDefinitionList.value = processDefinitionList.value
+
+    // 鍦ㄨ幏鍙栧畬鎵�鏈夋暟鎹悗锛岃缃涓�涓湁鏁堝垎绫讳负婵�娲荤姸鎬�
+    if (availableCategories.value.length > 0 && !categoryActive.value?.code) {
+      categoryActive.value = availableCategories.value[0]
+    }
+  } finally {
+  }
+}
+
+/** 鎼滅储娴佺▼ */
+const filteredProcessDefinitionList = ref([]) // 鐢ㄤ簬瀛樺偍鎼滅储杩囨护鍚庣殑娴佺▼瀹氫箟
+const handleQuery = () => {
+  if (searchName.value.trim()) {
+    // 濡傛灉鏈夋悳绱㈠叧閿瓧锛岃繘琛岃繃婊�
+    filteredProcessDefinitionList.value = processDefinitionList.value.filter(
+      (definition: any) => definition.name.toLowerCase().includes(searchName.value.toLowerCase()) // 鍋囪鎼滅储渚濇嵁鏄祦绋嬪畾涔夌殑鍚嶇О
+    )
+  } else {
+    // 濡傛灉娌℃湁鎼滅储鍏抽敭瀛楋紝鎭㈠鎵�鏈夋暟鎹�
+    filteredProcessDefinitionList.value = processDefinitionList.value
+  }
+}
+
+/** 娴佺▼瀹氫箟鐨勫垎缁� */
+const processDefinitionGroup: any = computed(() => {
+  if (!processDefinitionList.value?.length) {
+    return {}
+  }
+
+  const grouped = groupBy(filteredProcessDefinitionList.value, 'category')
+  // 鎸夌収 categoryList 鐨勯『搴忛噸鏂扮粍缁囨暟鎹�
+  const orderedGroup = {}
+  categoryList.value.forEach((category: any) => {
+    if (grouped[category.code]) {
+      orderedGroup[category.code] = grouped[category.code]
+    }
+  })
+  return orderedGroup
+})
+
+/** 宸︿晶鍒嗙被鍒囨崲 */
+const handleCategoryClick = (category: any) => {
+  categoryActive.value = category
+  const categoryRef = proxy.$refs[`category-${category.code}`] // 鑾峰彇鐐瑰嚮鍒嗙被瀵瑰簲鐨� DOM 鍏冪礌
+  if (categoryRef?.length) {
+    const scrollWrapper = proxy.$refs.scrollWrapper // 鑾峰彇鍙充晶婊氬姩瀹瑰櫒
+    const categoryOffsetTop = categoryRef[0].offsetTop
+
+    // 婊氬姩鍒板搴斾綅缃�
+    scrollWrapper.scrollTo({ top: categoryOffsetTop, behavior: 'smooth' })
+  }
+}
+
+/** 閫氳繃鍒嗙被 code 鑾峰彇瀵瑰簲鐨勫悕绉� */
+const getCategoryName = (categoryCode: string) => {
+  return categoryList.value?.find((ctg: any) => ctg.code === categoryCode)?.name
+}
+
+// ========== 琛ㄥ崟鐩稿叧 ==========
+const selectProcessDefinition = ref() // 閫夋嫨鐨勬祦绋嬪畾涔�
+const processDefinitionDetailRef = ref()
+
+/** 澶勭悊閫夋嫨娴佺▼鐨勬寜閽搷浣� **/
+const handleSelect = async (row, formVariables?) => {
+  // 璁剧疆閫夋嫨鐨勬祦绋�
+  selectProcessDefinition.value = row
+  // 鍒濆鍖栨祦绋嬪畾涔夎鎯�
+  await nextTick()
+  processDefinitionDetailRef.value?.initProcessInfo(row, formVariables)
+}
+
+/** 澶勭悊婊氬姩浜嬩欢锛屽拰宸︿晶鍒嗙被鑱斿姩 */
+const handleScroll = (e: any) => {
+  // 鐩存帴浣跨敤浜嬩欢瀵硅薄鑾峰彇婊氬姩浣嶇疆
+  const scrollTop = e.scrollTop
+
+  // 鑾峰彇鎵�鏈夊垎绫诲尯鍩熺殑浣嶇疆淇℃伅
+  const categoryPositions = categoryList.value
+    .map((category: CategoryVO) => {
+      const categoryRef = proxy.$refs[`category-${category.code}`]
+      if (categoryRef?.[0]) {
+        return {
+          code: category.code,
+          offsetTop: categoryRef[0].offsetTop,
+          height: categoryRef[0].offsetHeight
+        }
+      }
+      return null
+    })
+    .filter(Boolean)
+
+  // 鏌ユ壘褰撳墠婊氬姩浣嶇疆瀵瑰簲鐨勫垎绫�
+  let currentCategory = categoryPositions[0]
+  for (const position of categoryPositions) {
+    // 涓轰簡鏇村ソ鐨勭敤鎴蜂綋楠岋紝鍙互娣诲姞涓�涓紦鍐插尯鍩燂紙姣斿 50px锛�
+    if (scrollTop >= position.offsetTop - 50) {
+      currentCategory = position
+    } else {
+      break
+    }
+  }
+
+  // 鏇存柊褰撳墠 active 鐨勫垎绫�
+  if (currentCategory && categoryActive.value.code !== currentCategory.code) {
+    categoryActive.value = categoryList.value.find(
+      (c: CategoryVO) => c.code === currentCategory.code
+    )
+  }
+}
+
+/** 杩囨护鍑烘湁娴佺▼鐨勫垎绫诲垪琛ㄣ�傜洰鐨勶細鍙睍绀烘湁娴佺▼鐨勫垎绫� */
+const availableCategories = computed(() => {
+  if (!categoryList.value?.length || !processDefinitionGroup.value) {
+    return []
+  }
+
+  // 鑾峰彇鎵�鏈夋湁娴佺▼鐨勫垎绫讳唬鐮�
+  const availableCategoryCodes = Object.keys(processDefinitionGroup.value)
+
+  // 杩囨护鍑烘湁娴佺▼鐨勫垎绫�
+  return categoryList.value.filter((category: CategoryVO) =>
+    availableCategoryCodes.includes(category.code)
+  )
+})
+
+/** 鍒濆鍖� */
+onMounted(() => {
+  getList()
+})
+</script>
+
+<style lang="scss" scoped>
+.flow-icon {
+  display: flex;
+  width: 32px;
+  height: 32px;
+  margin-right: 10px;
+  background-color: var(--el-color-primary);
+  border-radius: 0.25rem;
+  align-items: center;
+  justify-content: center;
+}
+
+.process-definition-container::before {
+  position: absolute;
+  left: 20.8%;
+  height: 100%;
+  border-left: 1px solid #e6e6e6;
+  content: '';
+}
+
+:deep() {
+  .definition-item-card {
+    .el-card__body {
+      padding: 14px;
+    }
+  }
+}
+</style>

--
Gitblit v1.8.0