feat:优化 bpm 相关代码,重点处理 back 逻辑,search 表单
This commit is contained in:
@@ -92,6 +92,7 @@ import { onLoad } from '@dcloudio/uni-app'
|
||||
import { computed, ref } from 'vue'
|
||||
import { getCategorySimpleList } from '@/api/bpm/category'
|
||||
import { getProcessDefinitionList } from '@/api/bpm/definition'
|
||||
import { navigateBackPlus } from '@/utils';
|
||||
|
||||
definePage({
|
||||
style: {
|
||||
@@ -148,7 +149,7 @@ const groupedDefinitions = computed<Record<string, ProcessDefinition[]>>(() => {
|
||||
|
||||
/** 返回上一页 */
|
||||
function handleBack() {
|
||||
uni.navigateBack()
|
||||
navigateBackPlus('/pages/bpm/index')
|
||||
}
|
||||
|
||||
/** 搜索 */
|
||||
|
||||
@@ -43,9 +43,14 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { computed, reactive, ref } from 'vue'
|
||||
import { approveTask, rejectTask } from '@/api/bpm/task'
|
||||
import { navigateBackPlus } from '@/utils'
|
||||
|
||||
const props = defineProps<{
|
||||
id?: string | any
|
||||
pass?: string | any
|
||||
}>()
|
||||
|
||||
definePage({
|
||||
style: {
|
||||
@@ -54,19 +59,27 @@ definePage({
|
||||
},
|
||||
})
|
||||
|
||||
const taskId = ref('')
|
||||
const pass = ref(true) // true: 同意, false: 拒绝
|
||||
const taskId = computed(() => props.id || '')
|
||||
const isPass = computed(() => props.pass !== 'false') // true: 同意, false: 拒绝
|
||||
const submitting = ref(false)
|
||||
const formData = reactive({
|
||||
reason: '',
|
||||
})
|
||||
|
||||
/** 是否为同意操作 */
|
||||
const isApprove = computed(() => pass.value)
|
||||
const isApprove = computed(() => isPass.value)
|
||||
|
||||
/** 返回上一页 */
|
||||
function handleBack() {
|
||||
uni.navigateBack()
|
||||
navigateBackPlus(`/pages-bpm/processInstance/detail/index?id=${taskId.value}`)
|
||||
}
|
||||
|
||||
/** 初始化校验 */
|
||||
if (!props.id) {
|
||||
uni.showToast({
|
||||
title: '参数错误',
|
||||
icon: 'none',
|
||||
})
|
||||
}
|
||||
|
||||
/** 校验表单 */
|
||||
@@ -92,7 +105,7 @@ async function handleSubmit() {
|
||||
try {
|
||||
const api = isApprove.value ? approveTask : rejectTask
|
||||
const result = await api({
|
||||
id: taskId.value,
|
||||
id: taskId.value as string,
|
||||
reason: formData.reason,
|
||||
})
|
||||
if (result) {
|
||||
@@ -104,25 +117,10 @@ async function handleSubmit() {
|
||||
uni.navigateBack()
|
||||
}, 1500)
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
} catch (error) {
|
||||
console.error('[audit] 审批失败:', error)
|
||||
}
|
||||
finally {
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 初始化 */
|
||||
onLoad((options) => {
|
||||
if (!options?.id) {
|
||||
uni.showToast({
|
||||
title: '参数错误',
|
||||
icon: 'none',
|
||||
})
|
||||
return
|
||||
}
|
||||
taskId.value = options.id
|
||||
pass.value = options.pass !== 'false' // 默认为同意
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -117,6 +117,7 @@ import { getProcessInstance } from '@/api/bpm/processInstance'
|
||||
import { getTaskListByProcessInstanceId } from '@/api/bpm/task'
|
||||
import { useUserStore } from '@/store'
|
||||
import { formatDateTime, formatPast } from '@/utils/date'
|
||||
import { navigateBackPlus } from '@/utils';
|
||||
|
||||
definePage({
|
||||
style: {
|
||||
@@ -135,8 +136,9 @@ const orderAsc = ref(true)
|
||||
const runningTask = computed(() => {
|
||||
return tasks.value.find((task) => {
|
||||
// 待处理状态
|
||||
if (task.status !== 1 && task.status !== 6)
|
||||
if (task.status !== 1 && task.status !== 6) {
|
||||
return false
|
||||
}
|
||||
// 当前用户是处理人
|
||||
return task.assigneeUser?.id === userStore.userInfo?.id
|
||||
})
|
||||
@@ -146,12 +148,15 @@ const runningTask = computed(() => {
|
||||
const sortedTasks = computed(() => {
|
||||
const list = [...tasks.value].filter(t => t.status !== 4) // 过滤已取消
|
||||
list.sort((a, b) => {
|
||||
if (a.endTime && b.endTime)
|
||||
if (a.endTime && b.endTime) {
|
||||
return orderAsc.value ? a.endTime - b.endTime : b.endTime - a.endTime
|
||||
if (a.endTime)
|
||||
}
|
||||
if (a.endTime) {
|
||||
return orderAsc.value ? -1 : 1
|
||||
if (b.endTime)
|
||||
}
|
||||
if (b.endTime) {
|
||||
return orderAsc.value ? 1 : -1
|
||||
}
|
||||
return orderAsc.value ? a.createTime - b.createTime : b.createTime - a.createTime
|
||||
})
|
||||
return list
|
||||
@@ -159,7 +164,7 @@ const sortedTasks = computed(() => {
|
||||
|
||||
/** 返回上一页 */
|
||||
function handleBack() {
|
||||
uni.navigateBack()
|
||||
navigateBackPlus('/pages/bpm/index')
|
||||
}
|
||||
|
||||
/** 切换排序 */
|
||||
@@ -184,54 +189,68 @@ function getStatusText(status?: number) {
|
||||
|
||||
/** 获取状态标签类型 */
|
||||
function getStatusType(status?: number): 'default' | 'primary' | 'success' | 'warning' | 'danger' {
|
||||
if ([1, 6, 7].includes(status ?? 0))
|
||||
if ([1, 6, 7].includes(status ?? 0)) {
|
||||
return 'primary'
|
||||
if (status === 2)
|
||||
}
|
||||
if (status === 2) {
|
||||
return 'success'
|
||||
if (status === 3)
|
||||
}
|
||||
if (status === 3) {
|
||||
return 'danger'
|
||||
if (status === 4 || status === 5)
|
||||
}
|
||||
if (status === 4 || status === 5) {
|
||||
return 'warning'
|
||||
}
|
||||
return 'default'
|
||||
}
|
||||
|
||||
/** 获取任务圆点样式 */
|
||||
function getTaskDotClass(task: Task) {
|
||||
if ([1, 6, 7].includes(task.status))
|
||||
if ([1, 6, 7].includes(task.status)) {
|
||||
return 'bg-[#1890ff]'
|
||||
if (task.status === 2)
|
||||
}
|
||||
if (task.status === 2) {
|
||||
return 'bg-[#52c41a]'
|
||||
if (task.status === 3)
|
||||
}
|
||||
if (task.status === 3) {
|
||||
return 'bg-[#ff4d4f]'
|
||||
if (task.status === 5)
|
||||
}
|
||||
if (task.status === 5) {
|
||||
return 'bg-[#faad14]'
|
||||
}
|
||||
return 'bg-[#d9d9d9]'
|
||||
}
|
||||
|
||||
/** 获取状态文本样式 */
|
||||
function getStatusTextClass(status: number) {
|
||||
if ([1, 6, 7].includes(status))
|
||||
if ([1, 6, 7].includes(status)) {
|
||||
return 'text-[#1890ff]'
|
||||
if (status === 2)
|
||||
}
|
||||
if (status === 2) {
|
||||
return 'text-[#52c41a]'
|
||||
if (status === 3)
|
||||
}
|
||||
if (status === 3) {
|
||||
return 'text-[#ff4d4f]'
|
||||
if (status === 5)
|
||||
}
|
||||
if (status === 5) {
|
||||
return 'text-[#faad14]'
|
||||
}
|
||||
return 'text-[#999]'
|
||||
}
|
||||
|
||||
/** 同意 */
|
||||
function handleApprove() {
|
||||
if (!runningTask.value)
|
||||
if (!runningTask.value) {
|
||||
return
|
||||
}
|
||||
uni.navigateTo({ url: `/pages-bpm/processInstance/detail/audit/index?id=${runningTask.value.id}&pass=true` })
|
||||
}
|
||||
|
||||
/** 拒绝 */
|
||||
function handleReject() {
|
||||
if (!runningTask.value)
|
||||
if (!runningTask.value) {
|
||||
return
|
||||
}
|
||||
uni.navigateTo({ url: `/pages-bpm/processInstance/detail/audit/index?id=${runningTask.value.id}&pass=false` })
|
||||
}
|
||||
|
||||
|
||||
@@ -1,54 +1,55 @@
|
||||
<template>
|
||||
<view class="bpm-list">
|
||||
<view
|
||||
v-for="item in list"
|
||||
:key="item.id"
|
||||
class="bpm-card"
|
||||
@click="handleDetail(item)"
|
||||
>
|
||||
<view class="bpm-card-content">
|
||||
<view class="bpm-card-header">
|
||||
<view class="bpm-card-title">
|
||||
{{ item.processInstanceName }}
|
||||
</view>
|
||||
<wd-tag type="primary" plain>
|
||||
查看详情
|
||||
</wd-tag>
|
||||
</view>
|
||||
<view v-if="item.summary?.length" class="bpm-summary">
|
||||
<view v-for="(s, idx) in item.summary" :key="idx" class="bpm-summary-item">
|
||||
<text class="text-[#999]">{{ s.key }}:</text>
|
||||
<text>{{ s.value }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="bpm-card-info">
|
||||
<view class="bpm-user">
|
||||
<view class="bpm-avatar">
|
||||
{{ item.startUser.nickname?.[0] || '?' }}
|
||||
</view>
|
||||
<text class="bpm-nickname">{{ item.startUser.nickname }}</text>
|
||||
</view>
|
||||
<text class="bpm-time">{{ formatDateTime(item.createTime) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="loadMoreState !== 'loading' && list.length === 0" class="bpm-empty">
|
||||
<wd-status-tip image="content" tip="暂无抄送任务" />
|
||||
</view>
|
||||
<wd-loadmore
|
||||
v-if="list.length > 0"
|
||||
:state="loadMoreState"
|
||||
@reload="loadMore"
|
||||
/>
|
||||
|
||||
<!-- 搜索弹窗 -->
|
||||
<view>
|
||||
<!-- 搜索组件 -->
|
||||
<CopySearchForm
|
||||
v-model="searchPopupVisible"
|
||||
:search-params="queryParams"
|
||||
@search="handleSearch"
|
||||
@reset="handleReset"
|
||||
/>
|
||||
|
||||
<view class="bpm-list">
|
||||
<view
|
||||
v-for="item in list"
|
||||
:key="item.id"
|
||||
class="bpm-card"
|
||||
@click="handleDetail(item)"
|
||||
>
|
||||
<view class="bpm-card-content">
|
||||
<view class="bpm-card-header">
|
||||
<view class="bpm-card-title">
|
||||
{{ item.processInstanceName }}
|
||||
</view>
|
||||
<wd-tag type="primary" plain>
|
||||
查看详情
|
||||
</wd-tag>
|
||||
</view>
|
||||
<view v-if="item.summary?.length" class="bpm-summary">
|
||||
<view v-for="(s, idx) in item.summary" :key="idx" class="bpm-summary-item">
|
||||
<text class="text-[#999]">{{ s.key }}:</text>
|
||||
<text>{{ s.value }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="bpm-card-info">
|
||||
<view class="bpm-user">
|
||||
<view class="bpm-avatar">
|
||||
{{ item.startUser.nickname?.[0] || '?' }}
|
||||
</view>
|
||||
<text class="bpm-nickname">{{ item.startUser.nickname }}</text>
|
||||
</view>
|
||||
<text class="bpm-time">{{ formatDateTime(item.createTime) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="loadMoreState !== 'loading' && list.length === 0" class="bpm-empty">
|
||||
<wd-status-tip image="content" tip="暂无抄送任务" />
|
||||
</view>
|
||||
<wd-loadmore
|
||||
v-if="list.length > 0"
|
||||
:state="loadMoreState"
|
||||
@reload="loadMore"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -64,17 +65,13 @@ import CopySearchForm from './copy-search-form.vue'
|
||||
import './index.scss'
|
||||
|
||||
const props = defineProps<{
|
||||
searchVisible?: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:searchVisible': [value: boolean]
|
||||
active?: boolean
|
||||
}>()
|
||||
|
||||
const total = ref(0)
|
||||
const list = ref<ProcessInstanceCopy[]>([])
|
||||
const loadMoreState = ref<LoadMoreState>('loading')
|
||||
const searchPopupVisible = ref(false)
|
||||
const isFirstLoad = ref(true)
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
@@ -133,16 +130,18 @@ onReachBottom(() => {
|
||||
loadMore()
|
||||
})
|
||||
|
||||
watch(() => props.searchVisible, (val) => {
|
||||
searchPopupVisible.value = val ?? false
|
||||
})
|
||||
|
||||
watch(searchPopupVisible, (val) => {
|
||||
emit('update:searchVisible', val)
|
||||
/** 监听激活状态,刷新数据 */
|
||||
watch(() => props.active, (val) => {
|
||||
if (val && !isFirstLoad.value) {
|
||||
queryParams.pageNo = 1
|
||||
list.value = []
|
||||
getList()
|
||||
}
|
||||
})
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(() => {
|
||||
getList()
|
||||
isFirstLoad.value = false
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
<template>
|
||||
<!-- 搜索框入口 -->
|
||||
<wd-search
|
||||
:placeholder="searchPlaceholder"
|
||||
:hide-cancel="true"
|
||||
disabled
|
||||
@click="visible = true"
|
||||
/>
|
||||
|
||||
<!-- 搜索弹窗 -->
|
||||
<wd-popup
|
||||
v-model="visible"
|
||||
position="top"
|
||||
@@ -93,19 +102,26 @@ export interface CopySearchFormData {
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: boolean
|
||||
searchParams?: Partial<CopySearchFormData>
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [value: boolean]
|
||||
'search': [data: CopySearchFormData]
|
||||
'reset': []
|
||||
search: [data: CopySearchFormData]
|
||||
reset: []
|
||||
}>()
|
||||
|
||||
const visible = computed({
|
||||
get: () => props.modelValue,
|
||||
set: (val: boolean) => emit('update:modelValue', val),
|
||||
const visible = ref(false)
|
||||
|
||||
/** 搜索条件 placeholder 拼接 */
|
||||
const searchPlaceholder = computed(() => {
|
||||
const conditions: string[] = []
|
||||
if (props.searchParams?.processInstanceName) {
|
||||
conditions.push(`名称:${props.searchParams.processInstanceName}`)
|
||||
}
|
||||
if (props.searchParams?.createTime?.[0] && props.searchParams?.createTime?.[1]) {
|
||||
conditions.push(`时间:${formatDate(props.searchParams.createTime[0])}~${formatDate(props.searchParams.createTime[1])}`)
|
||||
}
|
||||
return conditions.length > 0 ? conditions.join(' | ') : '搜索抄送任务'
|
||||
})
|
||||
|
||||
const formData = reactive<CopySearchFormData>({
|
||||
@@ -141,7 +157,7 @@ function handleEndCancel() {
|
||||
}
|
||||
|
||||
/** 监听弹窗打开,同步外部参数 */
|
||||
watch(() => props.modelValue, (val) => {
|
||||
watch(visible, (val) => {
|
||||
if (val && props.searchParams) {
|
||||
formData.processInstanceName = props.searchParams.processInstanceName
|
||||
formData.createTime = props.searchParams.createTime ?? [undefined, undefined]
|
||||
|
||||
@@ -1,52 +1,53 @@
|
||||
<template>
|
||||
<view class="bpm-list">
|
||||
<view
|
||||
v-for="item in list"
|
||||
:key="item.id"
|
||||
class="bpm-card"
|
||||
@click="handleDetail(item)"
|
||||
>
|
||||
<view class="bpm-card-content">
|
||||
<view class="bpm-card-header">
|
||||
<view class="bpm-card-title">
|
||||
{{ item.processInstance?.name }}
|
||||
</view>
|
||||
<dict-tag :type="DICT_TYPE.BPM_TASK_STATUS" :value="item.status" />
|
||||
</view>
|
||||
<view v-if="item.processInstance?.summary?.length" class="bpm-summary">
|
||||
<view v-for="(s, idx) in item.processInstance.summary" :key="idx" class="bpm-summary-item">
|
||||
<text class="text-[#999]">{{ s.key }}:</text>
|
||||
<text>{{ s.value }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="bpm-card-info">
|
||||
<view class="bpm-user">
|
||||
<view class="bpm-avatar">
|
||||
{{ item.processInstance?.startUser?.nickname?.[0] || '?' }}
|
||||
</view>
|
||||
<text class="bpm-nickname">{{ item.processInstance?.startUser?.nickname }}</text>
|
||||
</view>
|
||||
<text class="bpm-time">{{ formatDateTime(item.createTime) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="loadMoreState !== 'loading' && list.length === 0" class="bpm-empty">
|
||||
<wd-status-tip image="content" tip="暂无已办任务" />
|
||||
</view>
|
||||
<wd-loadmore
|
||||
v-if="list.length > 0"
|
||||
:state="loadMoreState"
|
||||
@reload="loadMore"
|
||||
/>
|
||||
|
||||
<!-- 搜索弹窗 -->
|
||||
<view>
|
||||
<!-- 搜索组件 -->
|
||||
<DoneSearchForm
|
||||
v-model="searchPopupVisible"
|
||||
:search-params="queryParams"
|
||||
@search="handleSearch"
|
||||
@reset="handleReset"
|
||||
/>
|
||||
|
||||
<view class="bpm-list">
|
||||
<view
|
||||
v-for="item in list"
|
||||
:key="item.id"
|
||||
class="bpm-card"
|
||||
@click="handleDetail(item)"
|
||||
>
|
||||
<view class="bpm-card-content">
|
||||
<view class="bpm-card-header">
|
||||
<view class="bpm-card-title">
|
||||
{{ item.processInstance?.name }}
|
||||
</view>
|
||||
<dict-tag :type="DICT_TYPE.BPM_TASK_STATUS" :value="item.status" />
|
||||
</view>
|
||||
<view v-if="item.processInstance?.summary?.length" class="bpm-summary">
|
||||
<view v-for="(s, idx) in item.processInstance.summary" :key="idx" class="bpm-summary-item">
|
||||
<text class="text-[#999]">{{ s.key }}:</text>
|
||||
<text>{{ s.value }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="bpm-card-info">
|
||||
<view class="bpm-user">
|
||||
<view class="bpm-avatar">
|
||||
{{ item.processInstance?.startUser?.nickname?.[0] || '?' }}
|
||||
</view>
|
||||
<text class="bpm-nickname">{{ item.processInstance?.startUser?.nickname }}</text>
|
||||
</view>
|
||||
<text class="bpm-time">{{ formatDateTime(item.createTime) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="loadMoreState !== 'loading' && list.length === 0" class="bpm-empty">
|
||||
<wd-status-tip image="content" tip="暂无已办任务" />
|
||||
</view>
|
||||
<wd-loadmore
|
||||
v-if="list.length > 0"
|
||||
:state="loadMoreState"
|
||||
@reload="loadMore"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -63,17 +64,13 @@ import DoneSearchForm from './done-search-form.vue'
|
||||
import './index.scss'
|
||||
|
||||
const props = defineProps<{
|
||||
searchVisible?: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:searchVisible': [value: boolean]
|
||||
active?: boolean
|
||||
}>()
|
||||
|
||||
const total = ref(0)
|
||||
const list = ref<Task[]>([])
|
||||
const loadMoreState = ref<LoadMoreState>('loading')
|
||||
const searchPopupVisible = ref(false)
|
||||
const isFirstLoad = ref(true)
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
@@ -132,16 +129,18 @@ onReachBottom(() => {
|
||||
loadMore()
|
||||
})
|
||||
|
||||
watch(() => props.searchVisible, (val) => {
|
||||
searchPopupVisible.value = val ?? false
|
||||
})
|
||||
|
||||
watch(searchPopupVisible, (val) => {
|
||||
emit('update:searchVisible', val)
|
||||
/** 监听激活状态,刷新数据 */
|
||||
watch(() => props.active, (val) => {
|
||||
if (val && !isFirstLoad.value) {
|
||||
queryParams.pageNo = 1
|
||||
list.value = []
|
||||
getList()
|
||||
}
|
||||
})
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(() => {
|
||||
getList()
|
||||
isFirstLoad.value = false
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
<template>
|
||||
<!-- 搜索框入口 -->
|
||||
<wd-search
|
||||
:placeholder="searchPlaceholder"
|
||||
:hide-cancel="true"
|
||||
disabled
|
||||
@click="visible = true"
|
||||
/>
|
||||
|
||||
<!-- 搜索弹窗 -->
|
||||
<wd-popup
|
||||
v-model="visible"
|
||||
position="top"
|
||||
@@ -126,7 +135,7 @@ import type { ProcessDefinition } from '@/api/bpm/definition'
|
||||
import { computed, onMounted, reactive, ref, watch } from 'vue'
|
||||
import { getCategorySimpleList } from '@/api/bpm/category'
|
||||
import { getProcessDefinitionList } from '@/api/bpm/definition'
|
||||
import { getIntDictOptions } from '@/hooks/useDict'
|
||||
import { getDictLabel, getIntDictOptions } from '@/hooks/useDict'
|
||||
import { DICT_TYPE } from '@/utils/constants'
|
||||
import { formatDate } from '@/utils/date'
|
||||
|
||||
@@ -140,19 +149,29 @@ export interface DoneSearchFormData {
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: boolean
|
||||
searchParams?: Partial<DoneSearchFormData>
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [value: boolean]
|
||||
'search': [data: DoneSearchFormData]
|
||||
'reset': []
|
||||
search: [data: DoneSearchFormData]
|
||||
reset: []
|
||||
}>()
|
||||
|
||||
const visible = computed({
|
||||
get: () => props.modelValue,
|
||||
set: (val: boolean) => emit('update:modelValue', val),
|
||||
const visible = ref(false)
|
||||
|
||||
/** 搜索条件 placeholder 拼接 */
|
||||
const searchPlaceholder = computed(() => {
|
||||
const conditions: string[] = []
|
||||
if (props.searchParams?.name) {
|
||||
conditions.push(`名称:${props.searchParams.name}`)
|
||||
}
|
||||
if (props.searchParams?.status !== undefined && props.searchParams.status !== -1) {
|
||||
conditions.push(`状态:${getDictLabel(DICT_TYPE.BPM_TASK_STATUS, props.searchParams.status)}`)
|
||||
}
|
||||
if (props.searchParams?.createTime?.[0] && props.searchParams?.createTime?.[1]) {
|
||||
conditions.push(`时间:${formatDate(props.searchParams.createTime[0])}~${formatDate(props.searchParams.createTime[1])}`)
|
||||
}
|
||||
return conditions.length > 0 ? conditions.join(' | ') : '搜索已办任务'
|
||||
})
|
||||
|
||||
const categoryList = ref<Category[]>([])
|
||||
@@ -211,7 +230,7 @@ async function getProcessDefinitions() {
|
||||
}
|
||||
|
||||
/** 监听弹窗打开,同步外部参数 */
|
||||
watch(() => props.modelValue, (val) => {
|
||||
watch(visible, (val) => {
|
||||
if (val && props.searchParams) {
|
||||
formData.name = props.searchParams.name
|
||||
formData.processDefinitionKey = props.searchParams.processDefinitionKey
|
||||
|
||||
@@ -1,61 +1,63 @@
|
||||
<template>
|
||||
<view class="bpm-list">
|
||||
<view
|
||||
v-for="item in list"
|
||||
:key="item.id"
|
||||
class="bpm-card"
|
||||
@click="handleDetail(item)"
|
||||
>
|
||||
<view class="bpm-card-content">
|
||||
<view class="bpm-card-header">
|
||||
<view class="bpm-card-title">
|
||||
{{ item.name }}
|
||||
</view>
|
||||
<dict-tag :type="DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS" :value="item.status" />
|
||||
</view>
|
||||
<view v-if="item.summary?.length" class="bpm-summary">
|
||||
<view v-for="(s, idx) in item.summary" :key="idx" class="bpm-summary-item">
|
||||
<text class="text-[#999]">{{ s.key }}:</text>
|
||||
<text>{{ s.value }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="bpm-card-info">
|
||||
<view class="bpm-user">
|
||||
<view class="bpm-avatar">
|
||||
{{ userNickname?.[0] }}
|
||||
</view>
|
||||
<text class="bpm-nickname">{{ userNickname }}</text>
|
||||
</view>
|
||||
<text class="bpm-time">{{ formatDateTime(item.startTime) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view v-if="loadMoreState !== 'loading' && list.length === 0" class="bpm-empty">
|
||||
<wd-status-tip image="content" tip="暂无发起的流程" />
|
||||
</view>
|
||||
<!-- 加载更多 -->
|
||||
<wd-loadmore
|
||||
v-if="list.length > 0"
|
||||
:state="loadMoreState"
|
||||
@reload="loadMore"
|
||||
/>
|
||||
|
||||
<!-- 搜索弹窗 -->
|
||||
<view>
|
||||
<!-- 搜索组件 -->
|
||||
<MySearchForm
|
||||
v-model="searchPopupVisible"
|
||||
:search-params="queryParams"
|
||||
@search="handleSearch"
|
||||
@reset="handleReset"
|
||||
/>
|
||||
|
||||
<!-- 新增按钮 -->
|
||||
<view
|
||||
class="fixed bottom-100rpx right-32rpx z-10 h-100rpx w-100rpx flex items-center justify-center rounded-full bg-[#1890ff] shadow-lg"
|
||||
@click="handleCreate"
|
||||
>
|
||||
<wd-icon name="add" size="24px" color="#fff" />
|
||||
<view class="bpm-list">
|
||||
<view
|
||||
v-for="item in list"
|
||||
:key="item.id"
|
||||
class="bpm-card"
|
||||
@click="handleDetail(item)"
|
||||
>
|
||||
<view class="bpm-card-content">
|
||||
<view class="bpm-card-header">
|
||||
<view class="bpm-card-title">
|
||||
{{ item.name }}
|
||||
</view>
|
||||
<dict-tag :type="DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS" :value="item.status" />
|
||||
</view>
|
||||
<view v-if="item.summary?.length" class="bpm-summary">
|
||||
<view v-for="(s, idx) in item.summary" :key="idx" class="bpm-summary-item">
|
||||
<text class="text-[#999]">{{ s.key }}:</text>
|
||||
<text>{{ s.value }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="bpm-card-info">
|
||||
<view class="bpm-user">
|
||||
<view class="bpm-avatar">
|
||||
{{ userNickname?.[0] }}
|
||||
</view>
|
||||
<text class="bpm-nickname">{{ userNickname }}</text>
|
||||
</view>
|
||||
<text class="bpm-time">{{ formatDateTime(item.startTime) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view v-if="loadMoreState !== 'loading' && list.length === 0" class="bpm-empty">
|
||||
<wd-status-tip image="content" tip="暂无发起的流程" />
|
||||
</view>
|
||||
<!-- 加载更多 -->
|
||||
<wd-loadmore
|
||||
v-if="list.length > 0"
|
||||
:state="loadMoreState"
|
||||
@reload="loadMore"
|
||||
/>
|
||||
|
||||
<!-- 新增按钮 -->
|
||||
<!-- TODO @AI:换成 wd-fat:要注意,可能高度不对;晚点在改; -->
|
||||
<view
|
||||
class="fixed bottom-100rpx right-32rpx z-10 h-100rpx w-100rpx flex items-center justify-center rounded-full bg-[#1890ff] shadow-lg"
|
||||
@click="handleCreate"
|
||||
>
|
||||
<wd-icon name="add" size="24px" color="#fff" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
@@ -74,11 +76,7 @@ import MySearchForm from './my-search-form.vue'
|
||||
import './index.scss'
|
||||
|
||||
const props = defineProps<{
|
||||
searchVisible?: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:searchVisible': [value: boolean]
|
||||
active?: boolean
|
||||
}>()
|
||||
|
||||
const userStore = useUserStore()
|
||||
@@ -87,7 +85,7 @@ const userNickname = computed(() => userStore.userInfo?.nickname || '')
|
||||
const total = ref(0)
|
||||
const list = ref<ProcessInstance[]>([])
|
||||
const loadMoreState = ref<LoadMoreState>('loading')
|
||||
const searchPopupVisible = ref(false)
|
||||
const isFirstLoad = ref(true)
|
||||
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
@@ -157,16 +155,18 @@ onReachBottom(() => {
|
||||
loadMore()
|
||||
})
|
||||
|
||||
watch(() => props.searchVisible, (val) => {
|
||||
searchPopupVisible.value = val ?? false
|
||||
})
|
||||
|
||||
watch(searchPopupVisible, (val) => {
|
||||
emit('update:searchVisible', val)
|
||||
/** 监听激活状态,刷新数据 */
|
||||
watch(() => props.active, (val) => {
|
||||
if (val && !isFirstLoad.value) {
|
||||
queryParams.pageNo = 1
|
||||
list.value = []
|
||||
getList()
|
||||
}
|
||||
})
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(() => {
|
||||
getList()
|
||||
isFirstLoad.value = false
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
<template>
|
||||
<!-- 搜索框入口 -->
|
||||
<wd-search
|
||||
:placeholder="searchPlaceholder"
|
||||
:hide-cancel="true"
|
||||
disabled
|
||||
@click="visible = true"
|
||||
/>
|
||||
|
||||
<!-- 搜索弹窗 -->
|
||||
<wd-popup
|
||||
v-model="visible"
|
||||
position="top"
|
||||
@@ -126,7 +135,7 @@ import type { ProcessDefinition } from '@/api/bpm/definition'
|
||||
import { computed, onMounted, reactive, ref, watch } from 'vue'
|
||||
import { getCategorySimpleList } from '@/api/bpm/category'
|
||||
import { getProcessDefinitionList } from '@/api/bpm/definition'
|
||||
import { getIntDictOptions } from '@/hooks/useDict'
|
||||
import { getDictLabel, getIntDictOptions } from '@/hooks/useDict'
|
||||
import { DICT_TYPE } from '@/utils/constants'
|
||||
import { formatDate } from '@/utils/date'
|
||||
|
||||
@@ -140,19 +149,29 @@ export interface MySearchFormData {
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: boolean
|
||||
searchParams?: Partial<MySearchFormData>
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [value: boolean]
|
||||
'search': [data: MySearchFormData]
|
||||
'reset': []
|
||||
search: [data: MySearchFormData]
|
||||
reset: []
|
||||
}>()
|
||||
|
||||
const visible = computed({
|
||||
get: () => props.modelValue,
|
||||
set: (val: boolean) => emit('update:modelValue', val),
|
||||
const visible = ref(false)
|
||||
|
||||
/** 搜索条件 placeholder 拼接 */
|
||||
const searchPlaceholder = computed(() => {
|
||||
const conditions: string[] = []
|
||||
if (props.searchParams?.name) {
|
||||
conditions.push(`名称:${props.searchParams.name}`)
|
||||
}
|
||||
if (props.searchParams?.status !== undefined && props.searchParams.status !== -1) {
|
||||
conditions.push(`状态:${getDictLabel(DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS, props.searchParams.status)}`)
|
||||
}
|
||||
if (props.searchParams?.createTime?.[0] && props.searchParams?.createTime?.[1]) {
|
||||
conditions.push(`时间:${formatDate(props.searchParams.createTime[0])}~${formatDate(props.searchParams.createTime[1])}`)
|
||||
}
|
||||
return conditions.length > 0 ? conditions.join(' | ') : '搜索我的流程'
|
||||
})
|
||||
|
||||
const categoryList = ref<Category[]>([])
|
||||
@@ -211,7 +230,7 @@ async function getProcessDefinitions() {
|
||||
}
|
||||
|
||||
/** 监听弹窗打开,同步外部参数 */
|
||||
watch(() => props.modelValue, (val) => {
|
||||
watch(visible, (val) => {
|
||||
if (val && props.searchParams) {
|
||||
formData.name = props.searchParams.name
|
||||
formData.processDefinitionId = props.searchParams.processDefinitionId
|
||||
|
||||
@@ -1,63 +1,64 @@
|
||||
<template>
|
||||
<view class="bpm-list">
|
||||
<view
|
||||
v-for="item in list"
|
||||
:key="item.id"
|
||||
class="bpm-card"
|
||||
@click="handleDetail(item)"
|
||||
>
|
||||
<view class="bpm-card-content">
|
||||
<view class="bpm-card-header">
|
||||
<view class="bpm-card-title">
|
||||
{{ item.processInstance?.name }}
|
||||
</view>
|
||||
<wd-tag type="primary" plain>
|
||||
待审批
|
||||
</wd-tag>
|
||||
</view>
|
||||
<view v-if="item.processInstance?.summary?.length" class="bpm-summary">
|
||||
<view v-for="(s, idx) in item.processInstance.summary" :key="idx" class="bpm-summary-item">
|
||||
<text class="text-[#999]">{{ s.key }}:</text>
|
||||
<text>{{ s.value }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="bpm-card-info">
|
||||
<view class="bpm-user">
|
||||
<view class="bpm-avatar">
|
||||
{{ item.processInstance?.startUser?.nickname?.[0] || '?' }}
|
||||
</view>
|
||||
<text class="bpm-nickname">{{ item.processInstance?.startUser?.nickname }}</text>
|
||||
</view>
|
||||
<text class="bpm-time--warning">{{ formatPast(item.createTime) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="bpm-actions">
|
||||
<view class="bpm-action-btn" @click.stop="handleReject(item)">
|
||||
<text>拒绝</text>
|
||||
</view>
|
||||
<view class="bpm-action-btn" @click.stop="handleApprove(item)">
|
||||
<text>同意</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 加载更多 -->
|
||||
<view v-if="loadMoreState !== 'loading' && list.length === 0" class="bpm-empty">
|
||||
<wd-status-tip image="content" tip="暂无待办任务" />
|
||||
</view>
|
||||
<wd-loadmore
|
||||
v-if="list.length > 0"
|
||||
:state="loadMoreState"
|
||||
@reload="loadMore"
|
||||
/>
|
||||
|
||||
<!-- 搜索弹窗 -->
|
||||
<view>
|
||||
<!-- 搜索组件 -->
|
||||
<TodoSearchForm
|
||||
v-model="searchPopupVisible"
|
||||
:search-params="queryParams"
|
||||
@search="handleSearch"
|
||||
@reset="handleReset"
|
||||
/>
|
||||
|
||||
<view class="bpm-list">
|
||||
<view
|
||||
v-for="item in list"
|
||||
:key="item.id"
|
||||
class="bpm-card"
|
||||
@click="handleDetail(item)"
|
||||
>
|
||||
<view class="bpm-card-content">
|
||||
<view class="bpm-card-header">
|
||||
<view class="bpm-card-title">
|
||||
{{ item.processInstance?.name }}
|
||||
</view>
|
||||
<wd-tag type="primary" plain>
|
||||
待审批
|
||||
</wd-tag>
|
||||
</view>
|
||||
<view v-if="item.processInstance?.summary?.length" class="bpm-summary">
|
||||
<view v-for="(s, idx) in item.processInstance.summary" :key="idx" class="bpm-summary-item">
|
||||
<text class="text-[#999]">{{ s.key }}:</text>
|
||||
<text>{{ s.value }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="bpm-card-info">
|
||||
<view class="bpm-user">
|
||||
<view class="bpm-avatar">
|
||||
{{ item.processInstance?.startUser?.nickname?.[0] || '?' }}
|
||||
</view>
|
||||
<text class="bpm-nickname">{{ item.processInstance?.startUser?.nickname }}</text>
|
||||
</view>
|
||||
<text class="bpm-time--warning">{{ formatPast(item.createTime) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="bpm-actions">
|
||||
<view class="bpm-action-btn" @click.stop="handleReject(item)">
|
||||
<text>拒绝</text>
|
||||
</view>
|
||||
<view class="bpm-action-btn" @click.stop="handleApprove(item)">
|
||||
<text>同意</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 加载更多 -->
|
||||
<view v-if="loadMoreState !== 'loading' && list.length === 0" class="bpm-empty">
|
||||
<wd-status-tip image="content" tip="暂无待办任务" />
|
||||
</view>
|
||||
<wd-loadmore
|
||||
v-if="list.length > 0"
|
||||
:state="loadMoreState"
|
||||
@reload="loadMore"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -73,17 +74,13 @@ import TodoSearchForm from './todo-search-form.vue'
|
||||
import './index.scss'
|
||||
|
||||
const props = defineProps<{
|
||||
searchVisible?: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:searchVisible': [value: boolean]
|
||||
active?: boolean
|
||||
}>()
|
||||
|
||||
const total = ref(0)
|
||||
const list = ref<Task[]>([])
|
||||
const loadMoreState = ref<LoadMoreState>('loading')
|
||||
const searchPopupVisible = ref(false)
|
||||
const isFirstLoad = ref(true)
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
@@ -152,16 +149,18 @@ onReachBottom(() => {
|
||||
loadMore()
|
||||
})
|
||||
|
||||
watch(() => props.searchVisible, (val) => {
|
||||
searchPopupVisible.value = val ?? false
|
||||
})
|
||||
|
||||
watch(searchPopupVisible, (val) => {
|
||||
emit('update:searchVisible', val)
|
||||
/** 监听激活状态,刷新数据 */
|
||||
watch(() => props.active, (val) => {
|
||||
if (val && !isFirstLoad.value) {
|
||||
queryParams.pageNo = 1
|
||||
list.value = []
|
||||
getList()
|
||||
}
|
||||
})
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(() => {
|
||||
getList()
|
||||
isFirstLoad.value = false
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
<template>
|
||||
<!-- 搜索框入口 -->
|
||||
<wd-search
|
||||
:placeholder="searchPlaceholder"
|
||||
:hide-cancel="true"
|
||||
disabled
|
||||
@click="visible = true"
|
||||
/>
|
||||
|
||||
<!-- 搜索弹窗 -->
|
||||
<wd-popup
|
||||
v-model="visible"
|
||||
position="top"
|
||||
@@ -126,7 +135,7 @@ import type { ProcessDefinition } from '@/api/bpm/definition'
|
||||
import { computed, onMounted, reactive, ref, watch } from 'vue'
|
||||
import { getCategorySimpleList } from '@/api/bpm/category'
|
||||
import { getProcessDefinitionList } from '@/api/bpm/definition'
|
||||
import { getIntDictOptions } from '@/hooks/useDict'
|
||||
import { getDictLabel, getIntDictOptions } from '@/hooks/useDict'
|
||||
import { DICT_TYPE } from '@/utils/constants'
|
||||
import { formatDate } from '@/utils/date'
|
||||
|
||||
@@ -140,19 +149,29 @@ export interface TodoSearchFormData {
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: boolean
|
||||
searchParams?: Partial<TodoSearchFormData>
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:modelValue': [value: boolean]
|
||||
'search': [data: TodoSearchFormData]
|
||||
'reset': []
|
||||
search: [data: TodoSearchFormData]
|
||||
reset: []
|
||||
}>()
|
||||
|
||||
const visible = computed({
|
||||
get: () => props.modelValue,
|
||||
set: (val: boolean) => emit('update:modelValue', val),
|
||||
const visible = ref(false)
|
||||
|
||||
/** 搜索条件 placeholder 拼接 */
|
||||
const searchPlaceholder = computed(() => {
|
||||
const conditions: string[] = []
|
||||
if (props.searchParams?.name) {
|
||||
conditions.push(`名称:${props.searchParams.name}`)
|
||||
}
|
||||
if (props.searchParams?.status !== undefined && props.searchParams.status !== -1) {
|
||||
conditions.push(`状态:${getDictLabel(DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS, props.searchParams.status)}`)
|
||||
}
|
||||
if (props.searchParams?.createTime?.[0] && props.searchParams?.createTime?.[1]) {
|
||||
conditions.push(`时间:${formatDate(props.searchParams.createTime[0])}~${formatDate(props.searchParams.createTime[1])}`)
|
||||
}
|
||||
return conditions.length > 0 ? conditions.join(' | ') : '搜索待办任务'
|
||||
})
|
||||
|
||||
const categoryList = ref<Category[]>([])
|
||||
@@ -211,7 +230,7 @@ async function getProcessDefinitions() {
|
||||
}
|
||||
|
||||
/** 监听弹窗打开,同步外部参数 */
|
||||
watch(() => props.modelValue, (val) => {
|
||||
watch(visible, (val) => {
|
||||
if (val && props.searchParams) {
|
||||
formData.name = props.searchParams.name
|
||||
formData.processDefinitionKey = props.searchParams.processDefinitionKey
|
||||
|
||||
@@ -4,13 +4,7 @@
|
||||
<wd-navbar
|
||||
title="审批"
|
||||
placeholder safe-area-inset-top fixed
|
||||
>
|
||||
<template #right>
|
||||
<view class="flex items-center" @click="searchVisible = !searchVisible">
|
||||
<wd-icon name="search" size="20px" />
|
||||
</view>
|
||||
</template>
|
||||
</wd-navbar>
|
||||
/>
|
||||
|
||||
<!-- Tabs 区域 -->
|
||||
<view class="bg-white">
|
||||
@@ -22,26 +16,10 @@
|
||||
</wd-tabs>
|
||||
</view>
|
||||
<!-- 列表内容 -->
|
||||
<TodoList
|
||||
v-show="tabType === 'todo'"
|
||||
:search-visible="searchVisible && tabType === 'todo'"
|
||||
@update:search-visible="searchVisible = $event"
|
||||
/>
|
||||
<DoneList
|
||||
v-show="tabType === 'done'"
|
||||
:search-visible="searchVisible && tabType === 'done'"
|
||||
@update:search-visible="searchVisible = $event"
|
||||
/>
|
||||
<MyList
|
||||
v-show="tabType === 'my'"
|
||||
:search-visible="searchVisible && tabType === 'my'"
|
||||
@update:search-visible="searchVisible = $event"
|
||||
/>
|
||||
<CopyList
|
||||
v-show="tabType === 'copy'"
|
||||
:search-visible="searchVisible && tabType === 'copy'"
|
||||
@update:search-visible="searchVisible = $event"
|
||||
/>
|
||||
<TodoList v-show="tabType === 'todo'" :active="tabType === 'todo'" />
|
||||
<DoneList v-show="tabType === 'done'" :active="tabType === 'done'" />
|
||||
<MyList v-show="tabType === 'my'" :active="tabType === 'my'" />
|
||||
<CopyList v-show="tabType === 'copy'" :active="tabType === 'copy'" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -63,12 +41,10 @@ definePage({
|
||||
const tabTypes: string[] = ['todo', 'done', 'my', 'copy']
|
||||
const tabIndex = ref(0)
|
||||
const tabType = computed<string>(() => tabTypes[tabIndex.value])
|
||||
const searchVisible = ref(false)
|
||||
|
||||
/** Tab 切换 */
|
||||
function handleTabChange({ index }: { index: number }) {
|
||||
tabIndex.value = index
|
||||
searchVisible.value = false
|
||||
}
|
||||
|
||||
/** 初始化:根据 tab 参数设置默认 tab */
|
||||
|
||||
Reference in New Issue
Block a user