<script setup lang="ts">
|
import { computed, ref } from "vue";
|
import { onPullDownRefresh, onReachBottom, onShow } from "@dcloudio/uni-app";
|
import { getSimpleDictDataList, type DictData } from "../../../../api/crm";
|
import { getProcessInstanceManagerPage, type BpmProcessInstance } from "../../../../api/bpm";
|
import { useAuth } from "../../../../stores/auth";
|
import { DICT, dateTimeText, dictLabel, summaryInline } from "../../shared";
|
import { statusToneClass } from "../../../../utils/status";
|
const { hasPermission, state }=useAuth(); const keyword=ref(""); const list=ref<BpmProcessInstance[]>([]); const dicts=ref<DictData[]>([]); const pageNo=ref(1); const total=ref(0); const loading=ref(false); const error=ref("");
|
const canDetail=computed(()=>hasPermission("bpm:process-instance:query")); const hasMore=computed(()=>list.value.length<total.value);
|
function statusText(row:BpmProcessInstance){return dictLabel(dicts.value,DICT.BPM_PROCESS_INSTANCE_STATUS,row.status,"--")}
|
function statusClass(row:BpmProcessInstance){return statusToneClass(row.status,statusText(row))}
|
async function load(reset=false){ if(!state.accessToken||loading.value)return; if(reset){pageNo.value=1;list.value=[]} loading.value=true; error.value=""; try{ if(!dicts.value.length)dicts.value=await getSimpleDictDataList(state.accessToken); const data=await getProcessInstanceManagerPage(state.accessToken,{pageNo:pageNo.value,pageSize:15,name:keyword.value.trim()||undefined}); total.value=data.total||0; list.value=reset?(data.list||[]):[...list.value,...(data.list||[])]; pageNo.value+=1;}catch(e){error.value=e instanceof Error?e.message:"流程实例加载失败"}finally{loading.value=false}}
|
function goDetail(row:BpmProcessInstance){ if(!canDetail.value||!row.id)return; uni.navigateTo({url:`/pages/bpm/process-instance/detail?id=${encodeURIComponent(row.id)}`}); }
|
onShow(()=>load(true)); onPullDownRefresh(async()=>{await load(true);uni.stopPullDownRefresh()}); onReachBottom(()=>{if(hasMore.value)load()});
|
</script>
|
<template><view class="page"><view class="search"><input v-model="keyword" placeholder="搜索流程名称" confirm-type="search" @confirm="load(true)"/><text @click="load(true)">搜索</text></view><view v-if="error" class="state error" @click="load(true)">{{ error }}</view><view v-for="row in list" :key="row.id" class="card"><view class="head"><view><text class="name">{{ row.name || '流程实例' }}</text><text>{{ row.processDefinition?.name || row.processDefinitionKey || '暂无流程定义' }}</text></view><text :class="['tag',statusClass(row)]">{{ statusText(row) }}</text></view><view class="summary">{{ summaryInline(row.summary) || '暂无摘要' }}</view><view class="grid"><text>发起人:{{ row.startUser?.nickname || '--' }}</text><text>分类:{{ row.categoryName || row.category || '--' }}</text><text>开始时间:{{ dateTimeText(row.startTime || row.createTime) }}</text><text>结束时间:{{ dateTimeText(row.endTime) }}</text></view><view class="actions"><button v-if="canDetail" @click="goDetail(row)">详情</button></view></view><view v-if="!list.length&&!loading" class="state">暂无流程实例</view><view v-else class="more">{{ hasMore ? (loading?'加载中...':'上拉加载更多') : '没有更多了' }}</view></view></template>
|
<style lang="scss" scoped>.page{min-height:100vh;padding:20rpx 30rpx 70rpx;box-sizing:border-box;background:#f3f6fb}.search{display:flex;align-items:center;height:80rpx;padding:0 20rpx;margin-bottom:18rpx;background:#fff;border-radius:16rpx;box-shadow:0 8rpx 20rpx rgba(43,69,107,.06)}.search input{flex:1;font-size:27rpx}.search text{color:var(--color-primary, #1769d8);font-size:24rpx}.card{padding:24rpx 22rpx 20rpx;margin-bottom:18rpx;background:#fff;border-radius:16rpx;box-shadow:0 10rpx 24rpx rgba(43,69,107,.08)}.head{display:flex;align-items:flex-start;justify-content:space-between;gap:16rpx}.head>view{min-width:0;display:flex;flex:1;flex-direction:column;gap:8rpx}.name{overflow:hidden;color:#20334c;font-size:30rpx;font-weight:700;text-overflow:ellipsis;white-space:nowrap}.head text:last-child{color:#78879b;font-size:23rpx}.tag{flex:none;padding:6rpx 16rpx;font-size:22rpx;border-radius:999rpx}.tag-success,.tag-approved,.tag-finished{color:#0e9663;background:#e9fbf2;border:1rpx solid #b8eed4}.tag-draft,.tag-warning{color:#c78210;background:#fff8e8;border:1rpx solid #f2d396}.tag-danger,.tag-cancelled{color:#c65050;background:#fff0f0;border:1rpx solid #efb8b8}.tag-info,.tag-progress{color:var(--color-primary, #1769d8);background:var(--color-primary-soft, #eef6ff);border:1rpx solid #cfe2ff}.summary{margin-top:14rpx;padding:14rpx 16rpx;color:#4e617b;font-size:24rpx;line-height:1.5;background:#f7faff;border-radius:12rpx}.grid{display:grid;grid-template-columns:1fr 1fr;gap:10rpx;padding:16rpx 0;margin-top:16rpx;color:#66778c;font-size:23rpx;border-top:1rpx solid #edf1f6;border-bottom:1rpx solid #edf1f6}.grid text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.actions{display:flex;justify-content:flex-end;padding-top:16rpx}.actions button{min-height:50rpx;padding:0 16rpx;margin:0;color:var(--color-primary, #1769d8);font-size:22rpx;line-height:48rpx;background:var(--color-primary-soft, #f5f8ff);border:1rpx solid var(--color-primary-border, #8ab3ee);border-radius:8rpx}.actions button::after{border:0}.state,.more{display:block;padding:45rpx 0;color:#8795a8;font-size:26rpx;text-align:center}.error{color:#c65050}</style>
|