1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| <script setup lang="ts">
| import { computed, ref } from "vue";
| import { onPullDownRefresh, onReachBottom, onShow } from "@dcloudio/uni-app";
| import { deleteProcessExpression, getProcessExpressionPage, type BpmProcessExpression } from "../../../api/bpm";
| import { useAuth } from "../../../stores/auth";
| import { fmt, statusClass, statusText, toast } from "../admin-shared";
| const { hasPermission, state }=useAuth(); const keyword=ref(""); const list=ref<BpmProcessExpression[]>([]); const pageNo=ref(1); const total=ref(0); const loading=ref(false); const error=ref(""); const hasMore=ref(true);
| const canCreate=computed(()=>hasPermission("bpm:process-expression:create")); const canUpdate=computed(()=>hasPermission("bpm:process-expression:update")); const canDelete=computed(()=>hasPermission("bpm:process-expression:delete"));
| async function load(reset=false){ if(!state.accessToken||loading.value)return; if(reset){pageNo.value=1;list.value=[];hasMore.value=true} if(!hasMore.value)return; loading.value=true; error.value=""; try{ const data=await getProcessExpressionPage(state.accessToken,{pageNo:pageNo.value,pageSize:20,name:keyword.value.trim()||undefined}); list.value=reset?(data.list||[]):[...list.value,...(data.list||[])]; total.value=data.total||0; hasMore.value=list.value.length<total.value; pageNo.value+=1;}catch(e){error.value=e instanceof Error?e.message:"流程表达式加载失败"}finally{loading.value=false}}
| function go(id?:number,mode:"edit"|"detail"="edit"){uni.navigateTo({url:`/pages/bpm/process-expression/form${id?`?id=${id}&mode=${mode}`:""}`})}
| function remove(id?:number){ if(!canDelete.value||!id||!state.accessToken)return; uni.showModal({title:"确认删除",content:"删除后不可恢复",success:async r=>{ if(!r.confirm||!state.accessToken)return; try{ await deleteProcessExpression(state.accessToken,id); uni.showToast({title:"已删除",icon:"success"}); load(true); }catch(e){toast(e instanceof Error?e.message:"删除失败");} }});}
| onShow(()=>load(true)); onPullDownRefresh(async()=>{await load(true);uni.stopPullDownRefresh()}); onReachBottom(()=>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="item in list" :key="item.id" class="card"><view class="head"><view><text class="name">{{ item.name }}</text><text>{{ fmt(item.expression) }}</text></view><text :class="['tag',statusClass(item.status)]">{{ statusText(item.status) }}</text></view><view class="grid"><text>创建时间:{{ fmt(item.createTime) }}</text></view><view class="actions"><button @click="go(item.id,'detail')">详情</button><button v-if="canUpdate" @click="go(item.id)">编辑</button><button v-if="canDelete" class="danger" @click="remove(item.id)">删除</button></view></view><view v-if="!list.length&&!loading" class="state">暂无流程表达式</view><view v-else class="more">{{ hasMore ? (loading?'加载中...':'上拉加载更多') : '没有更多了' }}</view><button v-if="canCreate" class="fab" @click="go()"><view /></button></view></template>
| <style lang="scss" scoped>.page{min-height:100vh;padding: 20rpx 30rpx 132rpx;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-on{color:#0e9663;background:#e9fbf2;border:1rpx solid #b8eed4}.tag-off{color:#66778c;background:#f6f8fb;border:1rpx solid #dce4ee}.grid{display:grid;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;flex-wrap:wrap;justify-content:flex-end;gap:12rpx;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 .danger{color:#c65050;background:#fff7f7;border-color:#e3a0a0}.actions button::after,.fab::after{border:0}.state,.more{display:block;padding:45rpx 0;color:#8795a8;font-size:26rpx;text-align:center}.error{color:#c65050}.fab{position:fixed;right:32rpx;bottom:56rpx;display:flex;width:100rpx;height:100rpx;align-items:center;justify-content:center;padding:0;margin:0;background:var(--color-primary, #1769d8);border-radius:50%;box-shadow:0 14rpx 28rpx rgba(23,105,216,.32)}.fab view{position:relative;width:38rpx;height:38rpx}.fab view::before,.fab view::after{position:absolute;content:"";background:#fff;border-radius:3rpx}.fab view::before{top:17rpx;left:0;width:38rpx;height:4rpx}.fab view::after{top:0;left:17rpx;width:4rpx;height:38rpx}</style>
|
|