Просмотр исходного кода

feat: 发起流程页面-增加搜索框、前端搜索功能完成;右侧展示所有流程,通过滚动或者点击,可以切换分类;鼠标hover时,展示描述

GoldenZqqq 9 месяцев назад
Родитель
Сommit
cd69659c93
1 измененных файлов с 227 добавлено и 56 удалено
  1. 227 56
      src/views/bpm/processInstance/create/index.vue

+ 227 - 56
src/views/bpm/processInstance/create/index.vue

@@ -1,46 +1,74 @@
 <template>
   <!-- 第一步,通过流程定义的列表,选择对应的流程 -->
-  <ContentWrap
-    class="process-definition-container position-relative pb-20px"
-    v-if="!selectProcessDefinition"
-    v-loading="loading"
-  >
-    <el-row :gutter="20" class="!flex-nowrap">
-      <el-col :span="5">
-        <div class="flex flex-col">
-          <div
-            v-for="category in categoryList"
-            :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 }}
+  <template v-if="!selectProcessDefinition">
+    <el-input
+      v-model="currentSearchKey"
+      class="!w-50% mb-15px"
+      placeholder="请输入流程名称"
+      clearable
+      @keyup.enter="handleQuery"
+      @clear="handleClear"
+    >
+      <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 categoryList"
+              :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>
-        </div>
-      </el-col>
-      <el-col :span="19">
-        <h3 class="text-16px font-bold mb-10px mt-5px">{{ categoryActive.name }}</h3>
-        <div class="grid grid-cols-3 gap3" v-if="categoryProcessDefinitionList.length">
-          <el-card
-            v-for="definition in categoryProcessDefinitionList"
-            :key="definition.id"
-            shadow="hover"
-            class="cursor-pointer definition-item-card"
-            @click="handleSelect(definition)"
-          >
-            <template #default>
-              <div class="flex">
-                <el-image :src="definition.icon" class="w-32px h-32px" />
-                <el-text class="!ml-10px" size="large">{{ definition.name }}</el-text>
+        </el-col>
+        <el-col :span="19">
+          <el-scrollbar ref="scrollWrapper" height="700">
+            <div
+              class="mb-20px pl-10px"
+              v-for="(definitions, title) in processDefinitionGroup"
+              :key="title"
+              :ref="`category-${title}`"
+            >
+              <h3 class="text-18px font-bold mb-10px mt-5px">{{ title }}</h3>
+              <div class="grid grid-cols-3 gap3">
+                <el-tooltip
+                  v-for="definition in definitions"
+                  :key="definition.id"
+                  :content="definition.description"
+                  placement="top"
+                >
+                  <el-card
+                    shadow="hover"
+                    class="cursor-pointer definition-item-card"
+                    @click="handleSelect(definition)"
+                  >
+                    <template #default>
+                      <div class="flex">
+                        <el-image :src="definition.icon" class="w-32px h-32px" />
+                        <el-text class="!ml-10px" size="large">{{ definition.name }}</el-text>
+                      </div>
+                    </template>
+                  </el-card>
+                </el-tooltip>
               </div>
-            </template>
-          </el-card>
-        </div>
-        <el-empty v-else />
-      </el-col>
-    </el-row>
-  </ContentWrap>
+            </div>
+          </el-scrollbar>
+        </el-col>
+      </el-row>
+      <el-empty class="!py-200px" :image-size="200" description="没有找到搜索结果" v-else />
+    </ContentWrap>
+  </template>
 
   <!-- 第二步,填写表单,进行流程的提交 -->
   <ProcessDefinitionDetail
@@ -56,12 +84,14 @@ import * as DefinitionApi from '@/api/bpm/definition'
 import * as ProcessInstanceApi from '@/api/bpm/processInstance'
 import { CategoryApi } from '@/api/bpm/category'
 import ProcessDefinitionDetail from './ProcessDefinitionDetail.vue'
+import { groupBy } from 'lodash-es'
 
 defineOptions({ name: 'BpmProcessInstanceCreate' })
 
+const { proxy } = getCurrentInstance() as any
 const route = useRoute() // 路由
 const message = useMessage() // 消息
-
+const currentSearchKey = ref('') // 当前搜索关键字
 const processInstanceId: any = route.query.processInstanceId
 const loading = ref(true) // 加载中
 const categoryList: any = ref([]) // 分类的列表
@@ -71,15 +101,10 @@ const processDefinitionList = ref([]) // 流程定义的列表
 const getList = async () => {
   loading.value = true
   try {
-    // 流程分类
-    categoryList.value = await CategoryApi.getCategorySimpleList()
-    if (categoryList.value.length > 0) {
-      categoryActive.value = categoryList.value[0]
-    }
-    // 流程定义
-    processDefinitionList.value = await DefinitionApi.getProcessDefinitionList({
-      suspensionState: 1
-    })
+    // 所有流程分类数据
+    await getCategoryList()
+    // 所有流程定义数据
+    await getDefinitionList()
 
     // 如果 processInstanceId 非空,说明是重新发起
     if (processInstanceId?.length > 0) {
@@ -102,11 +127,149 @@ const getList = async () => {
   }
 }
 
-/** 选中分类对应的流程定义列表 */
-const categoryProcessDefinitionList: any = computed(() => {
-  return processDefinitionList.value.filter(
-    (item: any) => item.category == categoryActive.value.code
-  )
+// 获取所有流程分类数据
+const getCategoryList = async () => {
+  try {
+    // 流程分类
+    categoryList.value = await CategoryApi.getCategorySimpleList()
+    if (categoryList.value.length > 0) {
+      categoryActive.value = categoryList.value[0]
+    }
+  } finally {
+  }
+}
+
+// 获取所有流程定义数据
+const getDefinitionList = async () => {
+  try {
+    // 流程定义
+    processDefinitionList.value = await DefinitionApi.getProcessDefinitionList({
+      suspensionState: 1
+    })
+    /* 测试数据 */
+    // processDefinitionList.value = [
+    //   {
+    //     id: 'business:3:fab1dceb-95be-11ef-8c7d-00a6181404fd',
+    //     version: 3,
+    //     name: '商务管理',
+    //     key: 'business',
+    //     icon: 'https://picsum.photos/200?r=2',
+    //     description: '商务管理',
+    //     category: 'test0',
+    //     categoryName: '分类0',
+    //     formType: 10,
+    //     formId: 27,
+    //     formName: null,
+    //     formConf:
+    //       '{"form":{"inline":false,"hideRequiredAsterisk":false,"labelPosition":"right","size":"default","labelWidth":"100px"},"resetBtn":{"show":false,"innerText":"重置"},"submitBtn":{"show":false,"innerText":"提交"}}',
+    //     formFields: [
+    //       '{"type":"input","field":"F1yrm2sosxgeabc","title":"请假原因","info":"","$required":false,"props":{"type":"text","placeholder":"请输入123"},"_fc_id":"id_Fhrbm2sosxgeacc","name":"ref_Festm2sosxgeadc","display":true,"hidden":false,"_fc_drag_tag":"input"}',
+    //       '{"type":"radio","field":"F9r3m2sp1b34aec","title":"请假类型","info":"","$required":false,"props":{"_optionType":2},"_fc_id":"id_F4nwm2sp1b34afc","name":"ref_Fkodm2sp1b34agc","display":true,"hidden":false,"_fc_drag_tag":"radio","options":[{"label":"事假","value":"1"},{"label":"婚假","value":"2"},{"label":"丧假","value":"3"}]}',
+    //       '{"type":"datePicker","field":"Finom2tsbwbpadc","title":"请假时间段","info":"","$required":false,"props":{"type":"datetimerange"},"_fc_id":"id_F028m2tsbwbpaec","name":"ref_F0okm2tsbwbpafc","display":true,"hidden":false,"_fc_drag_tag":"dateRange"}'
+    //     ],
+    //     formCustomCreatePath: '',
+    //     formCustomViewPath: '',
+    //     suspensionState: 1,
+    //     deploymentTime: null,
+    //     bpmnXml: null,
+    //     startUserSelectTasks: null
+    //   },
+    //   {
+    //     id: 'oa_leave:1:6e5ac269-5f87-11ef-bdb6-00a6181404fd',
+    //     version: 1,
+    //     name: 'oa_leave',
+    //     key: 'oa_leave',
+    //     icon: null,
+    //     description: 'oa_leave',
+    //     category: 'etst',
+    //     categoryName: '分类1',
+    //     formType: 20,
+    //     formId: null,
+    //     formName: null,
+    //     formConf: null,
+    //     formFields: null,
+    //     formCustomCreatePath: '/bpm/oa/leave/create',
+    //     formCustomViewPath: '/bpm/oa/leave/detail',
+    //     suspensionState: 1,
+    //     deploymentTime: null,
+    //     bpmnXml: null,
+    //     startUserSelectTasks: null
+    //   },
+    //   {
+    //     id: 'oa_leave:3:c9d06889-94fd-11ef-bf08-00a6181404fd',
+    //     version: 3,
+    //     name: '请假流程',
+    //     key: 'oa_leave',
+    //     icon: 'https://picsum.photos/200?r=1',
+    //     description: '请假流程',
+    //     category: 'test3',
+    //     categoryName: '分类3',
+    //     formType: 10,
+    //     formId: 27,
+    //     formName: null,
+    //     formConf:
+    //       '{"form":{"inline":false,"hideRequiredAsterisk":false,"labelPosition":"right","size":"default","labelWidth":"100px"},"resetBtn":{"show":false,"innerText":"重置"},"submitBtn":{"show":true,"innerText":"提交"}}',
+    //     formFields: [
+    //       '{"type":"input","field":"F1yrm2sosxgeabc","title":"请假原因","info":"","$required":false,"props":{"type":"text","placeholder":"请输入123"},"_fc_id":"id_Fhrbm2sosxgeacc","name":"ref_Festm2sosxgeadc","display":true,"hidden":false,"_fc_drag_tag":"input"}',
+    //       '{"type":"radio","field":"F9r3m2sp1b34aec","title":"请假类型","info":"","$required":false,"props":{"_optionType":2},"_fc_id":"id_F4nwm2sp1b34afc","name":"ref_Fkodm2sp1b34agc","display":true,"hidden":false,"_fc_drag_tag":"radio","options":[{"label":"事假","value":"1"},{"label":"婚假","value":"2"},{"label":"丧假","value":"3"}]}'
+    //     ],
+    //     formCustomCreatePath: 'bpm/oa/leave/create',
+    //     formCustomViewPath: 'bpm/oa/leave/create',
+    //     suspensionState: 1,
+    //     deploymentTime: null,
+    //     bpmnXml: null,
+    //     startUserSelectTasks: null
+    //   }
+    // ]
+    /* 测试数据 */
+    // processDefinitionList.value = [
+    //   ...processDefinitionList.value,
+    //   ...processDefinitionList.value,
+    //   ...processDefinitionList.value,
+    //   ...processDefinitionList.value,
+    //   ...processDefinitionList.value,
+    //   ...processDefinitionList.value,
+    //   ...processDefinitionList.value,
+    //   ...processDefinitionList.value,
+    //   ...processDefinitionList.value,
+    //   ...processDefinitionList.value,
+    //   ...processDefinitionList.value,
+    //   ...processDefinitionList.value,
+    //   ...processDefinitionList.value,
+    //   ...processDefinitionList.value,
+    //   ...processDefinitionList.value,
+    //   ...processDefinitionList.value
+    // ]
+    // 初始化过滤列表为全部流程定义
+    filteredProcessDefinitionList.value = processDefinitionList.value
+  } finally {
+  }
+}
+
+const filteredProcessDefinitionList = ref([]) // 用于存储搜索过滤后的流程定义
+// 直接进行前端搜索
+const handleQuery = () => {
+  if (currentSearchKey.value.trim()) {
+    // 如果有搜索关键字,进行过滤
+    filteredProcessDefinitionList.value = processDefinitionList.value.filter(
+      (definition: any) =>
+        definition.name.toLowerCase().includes(currentSearchKey.value.toLowerCase()) // 假设搜索依据是流程定义的名称
+    )
+  } else {
+    // 如果没有搜索关键字,恢复所有数据
+    filteredProcessDefinitionList.value = processDefinitionList.value
+  }
+}
+
+// 监听input `clearable` 事件
+const handleClear = () => {
+  filteredProcessDefinitionList.value = processDefinitionList.value
+}
+
+// 流程定义的分组
+const processDefinitionGroup: any = computed(() => {
+  if (!processDefinitionList.value?.length) return {}
+  return groupBy(filteredProcessDefinitionList.value, 'categoryName')
 })
 
 // ========== 表单相关 ==========
@@ -122,8 +285,16 @@ const handleSelect = async (row, formVariables?) => {
   processDefinitionDetailRef.value?.initProcessInfo(row, formVariables)
 }
 // 左侧分类切换
-const handleCategoryClick = (val: number) => {
-  categoryActive.value = val
+const handleCategoryClick = (category) => {
+  categoryActive.value = category
+  const categoryRef = proxy.$refs[`category-${category.name}`] // 获取点击分类对应的 DOM 元素
+  if (categoryRef?.length) {
+    const scrollWrapper = proxy.$refs.scrollWrapper // 获取右侧滚动容器
+    const categoryOffsetTop = categoryRef[0].offsetTop
+
+    // 滚动到对应位置
+    scrollWrapper.scrollTo({ top: categoryOffsetTop, behavior: 'smooth' })
+  }
 }
 
 /** 初始化 */