优化审批功能
This commit is contained in:
1 parent
184d5b0403
commit
395c7f717e
15 files changed
+2997
-16
No files matched your search
@@ -0,0 +1,309 @@
|
||||
<template>
|
||||
<view class="page">
|
||||
<!-- 顶部:搜索(系统导航栏已带返回按钮 + 标题) -->
|
||||
<view class="toolbar">
|
||||
<view class="search-box">
|
||||
<FaIcon name="magnifying-glass" color="#94a3b8" :size="16" />
|
||||
<input
|
||||
class="search-input"
|
||||
v-model="keyword"
|
||||
placeholder="搜索单据标题"
|
||||
confirm-type="search"
|
||||
@confirm="onSearch"
|
||||
/>
|
||||
<text v-if="keyword" class="search-clear" @tap="clearKeyword">清空</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="stat-bar">
|
||||
<text class="stat-text">共 {{ total }} 条待办</text>
|
||||
</view>
|
||||
|
||||
<!-- 待办列表 -->
|
||||
<scroll-view class="list-scroll" scroll-y :show-scrollbar="false" @scrolltolower="loadMore">
|
||||
<view v-if="loading && !list.length" class="state-box">
|
||||
<text class="state-text">加载中...</text>
|
||||
</view>
|
||||
<view v-else-if="!list.length" class="state-box">
|
||||
<u-empty mode="list" text="暂无待我审批的任务" />
|
||||
</view>
|
||||
<block v-else>
|
||||
<view
|
||||
v-for="task in list"
|
||||
:key="task.id"
|
||||
class="todo-card"
|
||||
hover-class="card-active"
|
||||
@tap="openDetail(task.instance)"
|
||||
>
|
||||
<view class="card-top">
|
||||
<text class="card-title">{{ task.instance && task.instance.biz_title ? task.instance.biz_title : '(无标题)' }}</text>
|
||||
<u-tag
|
||||
:text="task.instance && task.instance.status_label || ''"
|
||||
:type="task.instance && task.instance.status_tag || 'info'"
|
||||
size="mini"
|
||||
/>
|
||||
</view>
|
||||
<view class="card-meta">
|
||||
<text class="meta-text">节点:{{ task.node_name || '-' }}</text>
|
||||
<text class="meta-text">发起人:{{ task.instance && task.instance.submit_user_name || '-' }}</text>
|
||||
</view>
|
||||
<view class="card-meta">
|
||||
<text class="meta-text">单据:{{ task.instance && task.instance.biz_type || '-' }}</text>
|
||||
<text class="meta-text">提交:{{ task.instance && (task.instance.submit_time || task.instance.create_time) || '-' }}</text>
|
||||
</view>
|
||||
<view class="card-meta" v-if="task.deadline">
|
||||
<text class="meta-text">截止:{{ task.deadline }}</text>
|
||||
</view>
|
||||
<!-- card-actions 不接管 tap,只把"查看详情"按钮的 tap 单独拦在按钮层,
|
||||
这样 card 主体其它区域的 tap 仍能正常冒泡到 .todo-card 触发跳转 -->
|
||||
<view class="card-actions">
|
||||
<view class="op-btn op-view" hover-class="btn-active" @tap.stop="openDetail(task.instance)">查看详情</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="finished" class="load-done">
|
||||
<text>{{ list.length ? '没有更多了' : '' }}</text>
|
||||
</view>
|
||||
<view v-else class="more-tip" @tap="loadMore">
|
||||
<text>加载更多</text>
|
||||
</view>
|
||||
</block>
|
||||
<view class="bottom-space" />
|
||||
</scroll-view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { onLoad, onShow } from '@dcloudio/uni-app'
|
||||
import {
|
||||
fetchApprovalTodoList
|
||||
} from '@/api/approval.js'
|
||||
import { isLoggedIn } from '@/utils/auth.js'
|
||||
|
||||
const PAGE_SIZE = 20
|
||||
|
||||
const list = ref([])
|
||||
const total = ref(0)
|
||||
const page = ref(1)
|
||||
const loading = ref(false)
|
||||
const loadingMore = ref(false)
|
||||
const finished = ref(false)
|
||||
const keyword = ref('')
|
||||
|
||||
onLoad(() => {
|
||||
if (!isLoggedIn()) {
|
||||
uni.reLaunch({ url: '/pages/login/login' })
|
||||
return
|
||||
}
|
||||
refresh()
|
||||
})
|
||||
|
||||
onShow(() => {
|
||||
if (isLoggedIn() && list.value.length === 0) refresh()
|
||||
})
|
||||
|
||||
async function refresh() {
|
||||
page.value = 1
|
||||
finished.value = false
|
||||
list.value = []
|
||||
await loadPage()
|
||||
}
|
||||
|
||||
async function loadMore() {
|
||||
if (finished.value || loadingMore.value) return
|
||||
loadingMore.value = true
|
||||
page.value += 1
|
||||
await loadPage()
|
||||
}
|
||||
|
||||
async function loadPage() {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await fetchApprovalTodoList({
|
||||
page: page.value,
|
||||
pageSize: PAGE_SIZE,
|
||||
keyword: keyword.value.trim()
|
||||
})
|
||||
const rows = (data.list || []).filter(t => {
|
||||
if (!keyword.value) return true
|
||||
const title = t.instance && t.instance.biz_title ? String(t.instance.biz_title) : ''
|
||||
return title.toLowerCase().includes(keyword.value.toLowerCase())
|
||||
})
|
||||
list.value = page.value === 1 ? rows : [...list.value, ...rows]
|
||||
total.value = data.total || 0
|
||||
finished.value = list.value.length >= total.value
|
||||
} catch (e) {
|
||||
if (page.value > 1) page.value -= 1
|
||||
uni.showToast({ title: '加载失败', icon: 'none' })
|
||||
} finally {
|
||||
loading.value = false
|
||||
loadingMore.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function onSearch() { refresh() }
|
||||
|
||||
function clearKeyword() {
|
||||
keyword.value = ''
|
||||
refresh()
|
||||
}
|
||||
|
||||
function openDetail(instance) {
|
||||
if (!instance || !instance.id) {
|
||||
uni.showToast({ title: '单据数据异常', icon: 'none' })
|
||||
return
|
||||
}
|
||||
uni.navigateTo({ url: `/pages/approval/detail?id=${instance.id}` })
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@/src/styles/page-common.scss';
|
||||
|
||||
.page {
|
||||
min-height: 100vh;
|
||||
background: $color-bg-page;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* ====== 顶部搜索 ====== */
|
||||
.toolbar {
|
||||
flex-shrink: 0;
|
||||
padding: 20rpx $page-padding-x;
|
||||
background: $color-card;
|
||||
box-shadow: $shadow-header;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
height: 72rpx;
|
||||
padding: 0 24rpx;
|
||||
background: $color-bg-page;
|
||||
border-radius: $radius-full;
|
||||
box-shadow: $shadow-card;
|
||||
|
||||
.search-input {
|
||||
flex: 1;
|
||||
font-size: 26rpx;
|
||||
color: $color-text;
|
||||
}
|
||||
|
||||
.search-clear {
|
||||
font-size: 24rpx;
|
||||
color: $color-text-muted;
|
||||
}
|
||||
}
|
||||
|
||||
/* ====== 计数条 ====== */
|
||||
.stat-bar {
|
||||
flex-shrink: 0;
|
||||
padding: 16rpx $page-padding-x;
|
||||
background: $color-card;
|
||||
border-bottom: 1rpx solid $color-divider;
|
||||
}
|
||||
|
||||
.stat-text {
|
||||
font-size: 26rpx;
|
||||
color: $color-text-secondary;
|
||||
}
|
||||
|
||||
/* ====== 列表 ====== */
|
||||
.list-scroll {
|
||||
flex: 1;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.todo-card {
|
||||
@include card;
|
||||
margin: 16rpx $page-padding-x 0;
|
||||
padding: 24rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10rpx;
|
||||
}
|
||||
|
||||
.card-active { opacity: 0.85; }
|
||||
|
||||
.card-top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: $color-text;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.card-meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.meta-text {
|
||||
font-size: 24rpx;
|
||||
color: $color-text-muted;
|
||||
}
|
||||
|
||||
.card-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 16rpx;
|
||||
padding-top: 14rpx;
|
||||
border-top: 1rpx solid $color-divider;
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
|
||||
.op-btn {
|
||||
padding: 14rpx 36rpx;
|
||||
border-radius: $radius-md;
|
||||
font-size: 26rpx;
|
||||
font-weight: 500;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.op-view {
|
||||
background: $color-primary;
|
||||
}
|
||||
.btn-active { opacity: 0.85; }
|
||||
|
||||
.more-tip {
|
||||
padding: 12rpx 0 4rpx;
|
||||
text-align: center;
|
||||
font-size: 24rpx;
|
||||
color: $color-primary;
|
||||
}
|
||||
|
||||
.load-done {
|
||||
padding: 12rpx 0 4rpx;
|
||||
text-align: center;
|
||||
font-size: 24rpx;
|
||||
color: $color-text-muted;
|
||||
}
|
||||
|
||||
.state-box {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 140rpx 0;
|
||||
}
|
||||
|
||||
.state-text {
|
||||
font-size: 28rpx;
|
||||
color: $color-text-muted;
|
||||
}
|
||||
|
||||
.bottom-space {
|
||||
height: 40rpx;
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user