Files
gxtyzx-uniapp/src/packageApproval/pages/index.vue
T

392 lines
7.9 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="approval-page">
<view class="tabs">
<view
v-for="tab in tabs"
:key="tab.key"
class="tab"
:class="{ active: activeTab === tab.key }"
@click="switchTab(tab.key)"
>
{{ tab.label }}
</view>
</view>
<view v-if="activeTab === 'mine'" class="toolbar">
<picker :range="statusLabels" :value="statusIndex" @change="onStatusChange">
<view class="filter">{{ statusLabels[statusIndex] }}</view>
</picker>
<input
v-model="keywords"
class="search"
type="text"
placeholder="搜索单号或名称"
confirm-type="search"
@confirm="reload"
/>
</view>
<view v-if="activeTab !== 'mine'" class="placeholder">
<text class="placeholder-title">{{ activeLabel }}功能建设中</text>
<text class="placeholder-desc">当前版本先开放「我发起的」</text>
</view>
<view v-else class="list">
<uni-card
v-for="item in list"
:key="item.id"
:border="false"
:padding="'14px 16px'"
class="item-card"
@click="openDetail(item.id)"
>
<view class="item-head">
<text class="item-title">{{ item.title || '未命名单据' }}</text>
<text class="status" :class="'status-' + item.status">
{{ item.statusName }}
</text>
</view>
<view class="item-row">
<text class="label">单号</text>
<text class="value">{{ item.bizNo || '—' }}</text>
</view>
<view class="item-row">
<text class="label">当前节点</text>
<text class="value">{{ item.currentNodeName || '—' }}</text>
</view>
<view class="item-row">
<text class="label">提交时间</text>
<text class="value">{{ formatTime(item.submitTime) }}</text>
</view>
</uni-card>
<view v-if="!loading && list.length === 0" class="empty">
<text>暂无单据</text>
</view>
<view v-if="loading" class="loading">
<text>加载中...</text>
</view>
<view v-if="finished && list.length > 0" class="finished">
<text>没有更多了</text>
</view>
</view>
<view v-if="activeTab === 'mine'" class="fab" @click="createDraft">
<text class="fab-text">+</text>
</view>
</view>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue';
import { onLoad, onPullDownRefresh, onReachBottom, onShow } from '@dcloudio/uni-app';
import { pageMine } from '@/api/approval/instance';
import type { ApprovalItem } from '@/api/approval/model';
type TabKey = 'todo' | 'mine' | 'done';
const tabs: Array<{ key: TabKey; label: string }> = [
{ key: 'todo', label: '待办' },
{ key: 'mine', label: '我发起的' },
{ key: 'done', label: '已办' }
];
const statusOptions = [
{ value: '', label: '全部状态' },
{ value: 'DRAFT', label: '未提交' },
{ value: 'PENDING', label: '审批中' },
{ value: 'REJECTED', label: '已驳回' },
{ value: 'WITHDRAWN', label: '已撤回' },
{ value: 'APPROVED', label: '审批完成' },
{ value: 'ARCHIVED', label: '已归档' }
];
const statusLabels = statusOptions.map(option => option.label);
const activeTab = ref<TabKey>('mine');
const activeLabel = computed(
() => tabs.find(tab => tab.key === activeTab.value)?.label || ''
);
const statusIndex = ref(0);
const keywords = ref('');
const list = ref<ApprovalItem[]>([]);
const page = ref(1);
const limit = 10;
const total = ref(0);
const loading = ref(false);
const finished = ref(false);
onLoad(() => {
load(true);
});
onShow(() => {
if (activeTab.value === 'mine') {
load(true);
}
});
onPullDownRefresh(async () => {
if (activeTab.value === 'mine') {
await load(true);
}
uni.stopPullDownRefresh();
});
onReachBottom(() => {
if (activeTab.value === 'mine' && !loading.value && !finished.value) {
load(false);
}
});
function switchTab(key: TabKey) {
if (activeTab.value === key) {
return;
}
activeTab.value = key;
if (key === 'mine' && list.value.length === 0) {
load(true);
}
}
function onStatusChange(event: { detail: { value: string | number } }) {
statusIndex.value = Number(event.detail.value);
load(true);
}
async function load(reset: boolean) {
if (loading.value) {
return;
}
loading.value = true;
try {
if (reset) {
page.value = 1;
finished.value = false;
}
const result = await pageMine({
page: page.value,
limit,
keywords: keywords.value || undefined,
status: statusOptions[statusIndex.value].value || undefined
});
total.value = Number(result.count) || 0;
list.value = reset ? result.list || [] : [...list.value, ...(result.list || [])];
finished.value = list.value.length >= total.value;
if (!finished.value) {
page.value += 1;
}
} catch (error: any) {
uni.showToast({ title: error?.message || '加载失败', icon: 'none' });
} finally {
loading.value = false;
}
}
function reload() {
load(true);
}
function openDetail(id: number) {
uni.navigateTo({ url: '/packageApproval/pages/detail?id=' + id });
}
function createDraft() {
uni.navigateTo({
url: '/packageApproval/pages/form-completion?mode=create'
});
}
function formatTime(value?: string) {
if (!value) {
return '—';
}
return value.replace('T', ' ').substring(0, 19);
}
</script>
<style lang="scss">
.approval-page {
min-height: 100vh;
background-color: #f5f6f8;
padding-bottom: 140rpx;
}
.tabs {
display: flex;
background: #ffffff;
padding: 12rpx 24rpx;
position: sticky;
top: 0;
z-index: 10;
}
.tab {
flex: 1;
text-align: center;
padding: 16rpx 0;
font-size: 28rpx;
color: #606266;
border-bottom: 4rpx solid transparent;
}
.tab.active {
color: #d81e06;
font-weight: 600;
border-bottom-color: #d81e06;
}
.toolbar {
display: flex;
align-items: center;
gap: 16rpx;
padding: 16rpx 24rpx;
background: #ffffff;
}
.filter {
padding: 12rpx 20rpx;
font-size: 26rpx;
color: #303133;
background: #f2f3f5;
border-radius: 12rpx;
white-space: nowrap;
}
.search {
flex: 1;
height: 64rpx;
padding: 0 20rpx;
font-size: 26rpx;
background: #f2f3f5;
border-radius: 12rpx;
}
.list {
padding: 8rpx 0;
}
.item-card {
margin: 12rpx 16rpx;
}
.item-head {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 12rpx;
}
.item-title {
flex: 1;
font-size: 30rpx;
font-weight: 600;
color: #303133;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.status {
font-size: 24rpx;
padding: 4rpx 14rpx;
border-radius: 999rpx;
background: #f2f3f5;
color: #606266;
}
.status-DRAFT {
background: #f4f4f5;
color: #909399;
}
.status-PENDING {
background: #fff7e6;
color: #d46b08;
}
.status-REJECTED {
background: #fff1f0;
color: #cf1322;
}
.status-WITHDRAWN {
background: #f4f4f5;
color: #909399;
}
.status-APPROVED {
background: #f6ffed;
color: #389e0d;
}
.status-ARCHIVED {
background: #e6f4ff;
color: #0958d9;
}
.item-row {
display: flex;
align-items: center;
margin-top: 8rpx;
font-size: 26rpx;
}
.label {
width: 140rpx;
color: #909399;
}
.value {
flex: 1;
color: #303133;
word-break: break-all;
}
.placeholder {
padding: 160rpx 40rpx;
text-align: center;
}
.placeholder-title {
display: block;
font-size: 30rpx;
color: #606266;
}
.placeholder-desc {
display: block;
margin-top: 16rpx;
font-size: 26rpx;
color: #909399;
}
.empty,
.loading,
.finished {
padding: 40rpx;
text-align: center;
font-size: 26rpx;
color: #909399;
}
.fab {
position: fixed;
right: 40rpx;
bottom: 80rpx;
width: 100rpx;
height: 100rpx;
border-radius: 50%;
background: #d81e06;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 8rpx 20rpx rgba(216, 30, 6, 0.35);
}
.fab-text {
color: #ffffff;
font-size: 56rpx;
line-height: 1;
}
</style>