617 lines
13 KiB
Vue
617 lines
13 KiB
Vue
<template>
|
||
<view class="verify-page">
|
||
<!-- 筛选条件 -->
|
||
<view class="filter-wrapper">
|
||
<view class="filter-item">
|
||
<text class="filter-label">业务类型:</text>
|
||
<view class="filter-buttons">
|
||
<view
|
||
class="filter-btn"
|
||
:class="{ active: bstType === '' }"
|
||
@click="onBstTypeChange('')"
|
||
>
|
||
全部
|
||
</view>
|
||
<view
|
||
class="filter-btn"
|
||
:class="{ active: bstType === 'TASK' }"
|
||
@click="onBstTypeChange('TASK')"
|
||
>
|
||
任务
|
||
</view>
|
||
<view
|
||
class="filter-btn"
|
||
:class="{ active: bstType === 'UPDATE_TASK' }"
|
||
@click="onBstTypeChange('UPDATE_TASK')"
|
||
>
|
||
延期审核
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="filter-item">
|
||
<text class="filter-label">审核状态:</text>
|
||
<view class="filter-buttons">
|
||
<view
|
||
class="filter-btn"
|
||
:class="{ active: filterStatus === '' }"
|
||
@click="onStatusChange('')"
|
||
>
|
||
全部
|
||
</view>
|
||
<view
|
||
class="filter-btn"
|
||
:class="{ active: filterStatus === '1' }"
|
||
@click="onStatusChange('1')"
|
||
>
|
||
已通过
|
||
</view>
|
||
<view
|
||
class="filter-btn"
|
||
:class="{ active: filterStatus === '2' }"
|
||
@click="onStatusChange('2')"
|
||
>
|
||
已驳回
|
||
</view>
|
||
<view
|
||
class="filter-btn"
|
||
:class="{ active: filterStatus === '3' }"
|
||
@click="onStatusChange('3')"
|
||
>
|
||
待审核
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="filter-item">
|
||
<text class="filter-label">时间范围:</text>
|
||
<view class="filter-buttons">
|
||
<view
|
||
class="filter-btn date-btn"
|
||
@click="openDatePicker"
|
||
>
|
||
<text>{{ dateRangeText }}</text>
|
||
<text class="date-icon">📅</text>
|
||
</view>
|
||
<view
|
||
v-if="dateRange.length > 0"
|
||
class="filter-btn clear-btn"
|
||
@click="clearDateRange"
|
||
>
|
||
清除
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<scroll-view class="list-scroll" >
|
||
<view class="card" v-for="item in displayList" :key="item.id" @click="goDetail(item)">
|
||
<view class="card-header">
|
||
<view class="left">
|
||
<text class="badge">{{ getBstTypeText(item.bstType) }}</text>
|
||
<text class="sub">所属项目 · {{ item.projectName || '—' }}</text>
|
||
</view>
|
||
<view class="right">
|
||
<uv-tags :text="statusText(item.status)" :type="statusType(item.status)" size="mini"></uv-tags>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="card-body">
|
||
<text class="remark" v-if="item.createRemark">备注:{{ item.createRemark }}</text>
|
||
<view class="row">
|
||
<text class="label">创建时间:</text>
|
||
<text class="value">{{ item.createTime || '—' }}</text>
|
||
</view>
|
||
<!-- <view class="row">-->
|
||
<!-- <text class="label">申请人:</text>-->
|
||
<!-- <text class="value">{{ item.createName || '—' }}</text>-->
|
||
<!-- </view>-->
|
||
</view>
|
||
|
||
<view class="card-footer">
|
||
<uv-button
|
||
v-if="item.status === '3'"
|
||
type="primary"
|
||
size="small"
|
||
text="去处理"
|
||
@click.stop="goHandle(item)"
|
||
/>
|
||
<uv-button
|
||
v-else
|
||
type="info"
|
||
size="small"
|
||
:text="statusText(item.status)"
|
||
:plain="true"
|
||
:disabled="true"
|
||
/>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="empty" v-if="isEmpty && !loading">
|
||
<text>暂无数据</text>
|
||
</view>
|
||
<view class="loading" v-if="loading">
|
||
<text>加载中...</text>
|
||
</view>
|
||
<view class="load-more" v-if="list.length > 0">
|
||
<text v-if="loading">加载中...</text>
|
||
<text v-else-if="noMore">没有更多数据了</text>
|
||
<text v-else>上拉加载更多</text>
|
||
</view>
|
||
</scroll-view>
|
||
|
||
<!-- 日期范围选择弹窗 -->
|
||
<view v-if="showDatePicker" class="modal-mask" @click="closeDatePicker">
|
||
<view class="modal-content date-modal" @click.stop>
|
||
<view class="modal-title">选择日期范围</view>
|
||
<view class="date-picker-content">
|
||
<view class="date-section">
|
||
<text class="section-label">开始日期</text>
|
||
<picker
|
||
mode="date"
|
||
:value="startDate"
|
||
:start="'2020-01-01'"
|
||
:end="endDate || '2099-12-31'"
|
||
@change="handleStartDateChange"
|
||
>
|
||
<view class="picker-display">
|
||
{{ startDate || '请选择开始日期' }}
|
||
</view>
|
||
</picker>
|
||
</view>
|
||
<view class="date-section">
|
||
<text class="section-label">结束日期</text>
|
||
<picker
|
||
mode="date"
|
||
:value="endDate"
|
||
:start="startDate || '2020-01-01'"
|
||
:end="'2099-12-31'"
|
||
@change="handleEndDateChange"
|
||
>
|
||
<view class="picker-display">
|
||
{{ endDate || '请选择结束日期' }}
|
||
</view>
|
||
</picker>
|
||
</view>
|
||
</view>
|
||
<view class="modal-buttons">
|
||
<button class="modal-btn" @click="closeDatePicker">取消</button>
|
||
<button class="modal-btn primary" @click="confirmDateRange">确定</button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, computed, onMounted } from 'vue';
|
||
import { getVerifyList } from '@/api';
|
||
import { usePagination } from '@/composables';
|
||
import { onReachBottom } from '@dcloudio/uni-app'
|
||
const bstType = ref('');
|
||
const filterStatus = ref('');
|
||
const dateRange = ref([]); // [startDate, endDate] 格式: yyyy-MM-dd
|
||
const showDatePicker = ref(false);
|
||
const startDate = ref('');
|
||
const endDate = ref('');
|
||
|
||
// 使用分页组合式函数
|
||
const {
|
||
list,
|
||
loading,
|
||
noMore,
|
||
isEmpty,
|
||
getList,
|
||
loadMore,
|
||
updateParams,
|
||
queryParams
|
||
} = usePagination({
|
||
fetchData: async (params) => {
|
||
// 构建请求参数
|
||
const requestParams = {
|
||
...params,
|
||
orderByColumn: 'createTime',
|
||
isAsc: 'descending'
|
||
};
|
||
|
||
// 添加业务类型筛选
|
||
if (bstType.value) {
|
||
requestParams.bstType = bstType.value;
|
||
}
|
||
|
||
// 添加审核状态筛选
|
||
if (filterStatus.value) {
|
||
requestParams.status = filterStatus.value;
|
||
}
|
||
|
||
// 添加时间范围筛选
|
||
if (dateRange.value.length === 2) {
|
||
requestParams.createTimeList = dateRange.value;
|
||
}
|
||
|
||
const res = await getVerifyList(requestParams);
|
||
// 处理数据,添加 expireTime 字段
|
||
if (res?.rows) {
|
||
res.rows = res.rows.map(r => ({
|
||
...r,
|
||
expireTime: safeParseExpire(r.data)
|
||
}));
|
||
}
|
||
return res;
|
||
},
|
||
mode: 'loadMore',
|
||
pageSize: 20,
|
||
defaultParams: {}
|
||
});
|
||
|
||
// 日期范围显示文本
|
||
const dateRangeText = computed(() => {
|
||
if (dateRange.value.length === 2) {
|
||
return `${dateRange.value[0]} 至 ${dateRange.value[1]}`;
|
||
}
|
||
return '选择日期范围';
|
||
});
|
||
|
||
// 获取业务类型文本
|
||
const getBstTypeText = (type) => {
|
||
if (type === 'TASK') return '任务审核';
|
||
if (type === 'UPDATE_TASK') return '延期审核';
|
||
return '审核';
|
||
};
|
||
|
||
function statusText(s) {
|
||
if (s === '1') return '已通过';
|
||
if (s === '2') return '已驳回';
|
||
return '待处理';
|
||
}
|
||
|
||
function statusType(s) {
|
||
if (s === '1') return 'success';
|
||
if (s === '2') return 'error';
|
||
return 'warning';
|
||
}
|
||
|
||
const safeParseExpire = (dataField) => {
|
||
if (!dataField) return '';
|
||
try {
|
||
if (typeof dataField === 'string') {
|
||
return JSON.parse(dataField)?.expireTime || '';
|
||
}
|
||
return dataField.expireTime || '';
|
||
} catch (e) {
|
||
return '';
|
||
}
|
||
};
|
||
|
||
// 显示列表(直接使用分页的 list)
|
||
const displayList = computed(() => list.value);
|
||
|
||
// 打开日期选择器
|
||
const openDatePicker = () => {
|
||
if (dateRange.value.length === 2) {
|
||
startDate.value = dateRange.value[0];
|
||
endDate.value = dateRange.value[1];
|
||
} else {
|
||
startDate.value = '';
|
||
endDate.value = '';
|
||
}
|
||
showDatePicker.value = true;
|
||
};
|
||
|
||
// 关闭日期选择器
|
||
const closeDatePicker = () => {
|
||
showDatePicker.value = false;
|
||
};
|
||
|
||
// 开始日期改变
|
||
const handleStartDateChange = (e) => {
|
||
startDate.value = e.detail.value;
|
||
};
|
||
|
||
// 结束日期改变
|
||
const handleEndDateChange = (e) => {
|
||
endDate.value = e.detail.value;
|
||
};
|
||
|
||
// 确认日期范围
|
||
const confirmDateRange = () => {
|
||
if (startDate.value && endDate.value) {
|
||
// 确保开始日期 <= 结束日期
|
||
if (startDate.value > endDate.value) {
|
||
uni.showToast({
|
||
title: '开始日期不能大于结束日期',
|
||
icon: 'none'
|
||
});
|
||
return;
|
||
}
|
||
dateRange.value = [startDate.value, endDate.value];
|
||
closeDatePicker();
|
||
refreshList();
|
||
} else {
|
||
uni.showToast({
|
||
title: '请选择完整的日期范围',
|
||
icon: 'none'
|
||
});
|
||
}
|
||
};
|
||
|
||
// 清除日期范围
|
||
const clearDateRange = () => {
|
||
dateRange.value = [];
|
||
startDate.value = '';
|
||
endDate.value = '';
|
||
refreshList();
|
||
};
|
||
|
||
// 业务类型改变
|
||
const onBstTypeChange = (value) => {
|
||
bstType.value = value;
|
||
refreshList();
|
||
};
|
||
|
||
// 审核状态改变
|
||
const onStatusChange = (value) => {
|
||
filterStatus.value = value;
|
||
refreshList();
|
||
};
|
||
|
||
// 刷新列表
|
||
const refreshList = () => {
|
||
updateParams({});
|
||
};
|
||
|
||
const goHandle = (item) => {
|
||
// 预留处理入口
|
||
uni.showToast({ title: '去处理', icon: 'none' });
|
||
};
|
||
|
||
const goDetail = (item) => {
|
||
// 可跳转到任务详情或审批详情
|
||
};
|
||
|
||
onMounted(() => {
|
||
// 读取路由参数
|
||
const pages = getCurrentPages();
|
||
const currentPage = pages[pages.length - 1];
|
||
const options = currentPage?.options || {};
|
||
if (options.bstType) {
|
||
bstType.value = options.bstType;
|
||
}
|
||
if (options.status) {
|
||
filterStatus.value = options.status;
|
||
}
|
||
getList(true);
|
||
});
|
||
|
||
onReachBottom(()=>{
|
||
loadMore();
|
||
});
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.verify-page {
|
||
width: 100%;
|
||
background: #f5f5f5;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.filter-wrapper {
|
||
background: #fff;
|
||
padding: 12px;
|
||
border-bottom: 1px solid #f0f0f0;
|
||
}
|
||
|
||
.filter-item {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: 12px;
|
||
&:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
}
|
||
|
||
.filter-label {
|
||
font-size: 13px;
|
||
color: #666;
|
||
width: 70px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.filter-buttons {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 8px;
|
||
flex: 1;
|
||
}
|
||
|
||
.filter-btn {
|
||
padding: 6px 12px;
|
||
font-size: 12px;
|
||
color: #666;
|
||
background: #f5f5f5;
|
||
border-radius: 16px;
|
||
border: 1px solid #e0e0e0;
|
||
transition: all 0.3s;
|
||
&.active {
|
||
color: #fff;
|
||
background: #2979ff;
|
||
border-color: #2979ff;
|
||
}
|
||
&.date-btn {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
}
|
||
&.clear-btn {
|
||
background: #ffebee;
|
||
color: #f44336;
|
||
border-color: #f44336;
|
||
}
|
||
}
|
||
|
||
.date-icon {
|
||
font-size: 12px;
|
||
}
|
||
|
||
.list-scroll {
|
||
flex: 1;
|
||
}
|
||
|
||
.card {
|
||
background: #fff;
|
||
margin: 12px 12px 0 12px;
|
||
border-radius: 12px;
|
||
padding: 12px;
|
||
}
|
||
|
||
.card-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 6px;
|
||
}
|
||
|
||
.left {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
.badge {
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
color: #333;
|
||
}
|
||
|
||
.sub {
|
||
font-size: 12px;
|
||
color: #999;
|
||
}
|
||
|
||
.card-body {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 6px;
|
||
margin-top: 4px;
|
||
}
|
||
|
||
.remark {
|
||
font-size: 13px;
|
||
color: #333;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.row {
|
||
display: flex;
|
||
font-size: 12px;
|
||
color: #666;
|
||
}
|
||
|
||
.label {
|
||
width: 70px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.value {
|
||
color: #333;
|
||
}
|
||
|
||
.card-footer {
|
||
margin-top: 10px;
|
||
display: flex;
|
||
justify-content: flex-start;
|
||
}
|
||
|
||
.empty, .loading, .load-more {
|
||
text-align: center;
|
||
color: #999;
|
||
font-size: 12px;
|
||
padding: 16px 0;
|
||
}
|
||
|
||
// 日期选择弹窗样式
|
||
.modal-mask {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: rgba(0, 0, 0, 0.5);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 999;
|
||
}
|
||
|
||
.modal-content {
|
||
background: #fff;
|
||
border-radius: 12px;
|
||
width: 90%;
|
||
max-width: 500px;
|
||
max-height: 80vh;
|
||
overflow: hidden;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.modal-title {
|
||
padding: 16px;
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
text-align: center;
|
||
border-bottom: 1px solid #f0f0f0;
|
||
}
|
||
|
||
.date-picker-content {
|
||
padding: 16px;
|
||
overflow-y: auto;
|
||
flex: 1;
|
||
}
|
||
|
||
.date-section {
|
||
margin-bottom: 20px;
|
||
&:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
}
|
||
|
||
.section-label {
|
||
display: block;
|
||
font-size: 14px;
|
||
color: #333;
|
||
margin-bottom: 8px;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.picker-display {
|
||
padding: 12px;
|
||
background: #f5f5f5;
|
||
border-radius: 8px;
|
||
border: 1px solid #e0e0e0;
|
||
font-size: 14px;
|
||
color: #333;
|
||
text-align: center;
|
||
}
|
||
|
||
.modal-buttons {
|
||
display: flex;
|
||
border-top: 1px solid #f0f0f0;
|
||
}
|
||
|
||
.modal-btn {
|
||
flex: 1;
|
||
padding: 16px;
|
||
font-size: 14px;
|
||
background: #fff;
|
||
color: #666;
|
||
border: none;
|
||
border-right: 1px solid #f0f0f0;
|
||
&:last-child {
|
||
border-right: none;
|
||
}
|
||
&.primary {
|
||
color: #2979ff;
|
||
font-weight: 500;
|
||
}
|
||
}
|
||
</style>
|