feat: 发起流程页面-增加搜索框、前端搜索功能完成;右侧展示所有流程,通过滚动或者点击,可以切换分类;鼠标hover时,展示描述
							parent
							
								
									5121d5694d
								
							
						
					
					
						commit
						cd69659c93
					
				| 
						 | 
				
			
			@ -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' })
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/** 初始化 */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue