diff --git a/src/api/bpm/definition/index.ts b/src/api/bpm/definition/index.ts
index 61352a5..6858e51 100644
--- a/src/api/bpm/definition/index.ts
+++ b/src/api/bpm/definition/index.ts
@@ -6,6 +6,7 @@ export interface ProcessDefinition {
key: string
name: string
description?: string
+ icon?: string
category: string
formType?: number
formId?: number
diff --git a/src/pages-bpm/processInstance/create/index.vue b/src/pages-bpm/processInstance/create/index.vue
index 54ea74c..a64ad6d 100644
--- a/src/pages-bpm/processInstance/create/index.vue
+++ b/src/pages-bpm/processInstance/create/index.vue
@@ -9,78 +9,75 @@
/>
-
-
-
+
-
-
-
- {{ item.name }}
-
-
-
+
+
+
-
- {{ getCategoryName(category as string) }}
-
+
+ {{ item.name }}
-
+
+
-
+ {{ getIconText(definition.name) }}
- {{ item.name }}
+ {{ definition.name }}
+
+ 该分类下暂无流程
+
-
+
@@ -91,7 +88,7 @@
import type { Category } from '@/api/bpm/category'
import type { ProcessDefinition } from '@/api/bpm/definition'
import { onLoad } from '@dcloudio/uni-app'
-import { computed, ref } from 'vue'
+import { computed, nextTick, ref } from 'vue'
import { useToast } from 'wot-design-uni'
import { getCategorySimpleList } from '@/api/bpm/category'
import { getProcessDefinitionList } from '@/api/bpm/definition'
@@ -121,23 +118,29 @@ definePage({
const toast = useToast()
const searchName = ref('')
-const activeIndex = ref(0)
-const scrollIntoView = ref('')
+const activeCategory = ref('')
const categoryList = ref([])
-const definitionList = ref([])
-const expandedCategories = ref>({})
+const categoryPositions = ref<{ code: string, top: number }[]>([]) // 分类区域位置信息(用于滚动时自动切换 tab)
+const scrollIntoView = ref('')
+const isTabClicking = ref(false) // 是否正在通过点击 tab 触发滚动(避免滚动事件反向更新 tab)
-/** 图标配置 */
-// TODO @芋艿:【流程定义图标】支持显示流程定义的自定义图标 definition.icon
-// 对应 vben 第 175-189 行:优先显示 definition.icon,无图标时显示流程名称前两个字
-// TODO @AI:优化下,图标使用 vben 对应的逻辑;
-const iconConfig = [
- { icon: 'warning', color: '#D98469' },
- { icon: 'heart', color: '#7BC67C' },
- { icon: 'cart', color: '#4A7FEB' },
- { icon: 'home', color: '#4A7FEB' },
- { icon: 'location', color: '#4A9DEB' },
-]
+const definitionList = ref([])
+
+/** 根据流程名称获取图标背景色 */
+function getIconColor(name: string): string {
+ const iconColors = ['#D98469', '#7BC67C', '#4A7FEB', '#9B7FEB', '#4A9DEB']
+ // 根据名称 hashcode 取模选择颜色
+ let hash = 0
+ for (let i = 0; i < name.length; i++) {
+ hash = (hash * 31 + name.charCodeAt(i)) | 0
+ }
+ return iconColors[Math.abs(hash) % iconColors.length]
+}
+
+/** 获取流程名称的前两个字符作为图标文字 */
+function getIconText(name: string): string {
+ return name?.slice(0, 2) || ''
+}
/** 过滤后的流程定义 */
const filteredDefinitions = computed(() => {
@@ -159,13 +162,7 @@ const groupedDefinitions = computed>(() => {
grouped[item.category] = []
grouped[item.category].push(item)
})
- // 按 categoryList 顺序排序
- const ordered: Record = {}
- categoryList.value.forEach((cat) => {
- if (grouped[cat.code])
- ordered[cat.code] = grouped[cat.code]
- })
- return ordered
+ return grouped
})
/** 返回上一页 */
@@ -174,46 +171,63 @@ function handleBack() {
}
/** 搜索 */
-function handleSearch() {
- // 搜索时展开所有分类
- categoryList.value.forEach((cat) => {
- expandedCategories.value[cat.code] = true
+async function handleSearch() {
+ // 搜索后重新计算分类位置
+ await nextTick()
+ updateCategoryPositions()
+}
+
+/** Tab 点击 */
+function handleTabClick({ name }: { index: number, name: string }) {
+ isTabClicking.value = true
+ // 滚动到对应分类
+ scrollIntoView.value = ''
+ nextTick(() => {
+ scrollIntoView.value = `category-${name}`
+ // 300ms 后恢复滚动监听
+ setTimeout(() => {
+ isTabClicking.value = false
+ }, 300)
})
}
-/** 切换分类 */
-// TODO @AI:目前有个 bug;滚动到为止后,选中的 category 不会变;
-function switchCategory(index: number) {
- activeIndex.value = index
- const category = categoryList.value[index]
- if (category) {
- expandedCategories.value[category.code] = true
- // 滚动到对应分类
- scrollIntoView.value = ''
- setTimeout(() => {
- scrollIntoView.value = `category-${category.code}`
- }, 50)
+/** 滚动事件 - 自动切换 tab */
+function handleScroll(e: { detail: { scrollTop: number } }) {
+ if (isTabClicking.value || categoryPositions.value.length === 0) {
+ return
+ }
+ // 找到当前滚动位置对应的分类
+ const scrollTop = e.detail.scrollTop
+ for (let i = categoryPositions.value.length - 1; i >= 0; i--) {
+ if (scrollTop >= categoryPositions.value[i].top - 20) {
+ if (activeCategory.value !== categoryPositions.value[i].code) {
+ activeCategory.value = categoryPositions.value[i].code
+ }
+ break
+ }
}
}
-/** 切换分类展开/收起 */
-function toggleCategory(code: string) {
- expandedCategories.value[code] = !expandedCategories.value[code]
-}
-
-/** 获取分类名称 */
-function getCategoryName(code: string) {
- return categoryList.value.find(item => item.code === code)?.name || code
-}
-
-/** 获取图标名称 */
-function getIconName(index: number) {
- return iconConfig[index % iconConfig.length].icon
-}
-
-/** 获取图标颜色 */
-function getIconColor(index: number) {
- return iconConfig[index % iconConfig.length].color
+/** 更新分类区域位置信息 */
+function updateCategoryPositions() {
+ const query = uni.createSelectorQuery()
+ query.selectAll('.category-section').boundingClientRect()
+ query.exec((res) => {
+ if (res && res[0]) {
+ const positions: { code: string, top: number }[] = []
+ const firstTop = res[0][0]?.top || 0
+ res[0].forEach((item: { top: number, dataset?: { category?: string } }, index: number) => {
+ const cat = categoryList.value[index]
+ if (cat) {
+ positions.push({
+ code: cat.code,
+ top: item.top - firstTop,
+ })
+ }
+ })
+ categoryPositions.value = positions
+ }
+ })
}
/** 选择流程定义 */
@@ -231,10 +245,6 @@ function handleSelect(item: ProcessDefinition) {
/** 加载分类列表 */
async function loadCategoryList() {
categoryList.value = await getCategorySimpleList()
- // 默认展开所有分类
- categoryList.value.forEach((cat) => {
- expandedCategories.value[cat.code] = true
- })
}
/** 加载流程定义列表 */
@@ -245,5 +255,17 @@ async function loadDefinitionList() {
/** 初始化 */
onLoad(async () => {
await Promise.all([loadCategoryList(), loadDefinitionList()])
+ // 默认选中第一个分类
+ if (categoryList.value.length > 0) {
+ activeCategory.value = categoryList.value[0].code
+ }
+ // 等待 DOM 渲染后计算分类位置
+ await nextTick()
+ setTimeout(() => {
+ updateCategoryPositions()
+ }, 100)
})
+
+