审批管理,添加条件筛选

This commit is contained in:
WindowBird 2025-11-14 10:01:12 +08:00
parent 1a8c41c096
commit 5466a0534a
3 changed files with 464 additions and 65 deletions

View File

@ -1,5 +1,36 @@
// 审批相关 API // 审批相关 API
export const getVerifyList = (params = {}) => { export const getVerifyList = (params = {}) => {
// 处理 createTimeList 参数,如果是数组则转换为重复参数格式
const processedParams = { ...params };
if (Array.isArray(processedParams.createTimeList)) {
const tempList = processedParams.createTimeList;
// 删除原来的 createTimeList因为我们要手动处理
delete processedParams.createTimeList;
// 构建查询字符串
let queryString = '';
tempList.forEach(date => {
queryString += `&createTimeList=${encodeURIComponent(date)}`;
});
return uni.$uv.http.get(`/bst/verify/list?${queryString}`, {
params: {
pageNum: 1,
pageSize: 20,
orderByColumn: 'createTime',
isAsc: 'descending',
bstType: 'UPDATE_TASK',
...processedParams
},
custom: {
auth: true
}
});
}
// 如果不是数组,正常处理
return uni.$uv.http.get('/bst/verify/list', { return uni.$uv.http.get('/bst/verify/list', {
params: { params: {
pageNum: 1, pageNum: 1,
@ -7,12 +38,10 @@ export const getVerifyList = (params = {}) => {
orderByColumn: 'createTime', orderByColumn: 'createTime',
isAsc: 'descending', isAsc: 'descending',
bstType: 'UPDATE_TASK', bstType: 'UPDATE_TASK',
...params ...processedParams
}, },
custom: { custom: {
auth: true auth: true
} }
}); });
}; };

View File

@ -1,14 +1,92 @@
<template> <template>
<view class="verify-page"> <view class="verify-page">
<view class="tabs-wrapper"> <!-- 筛选条件 -->
<uv-tabs :list="tabs" :current="currentTab" @click="onTabClick"></uv-tabs> <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> </view>
<scroll-view class="list-scroll" scroll-y @scrolltolower="loadMore"> <scroll-view class="list-scroll" scroll-y @scrolltolower="loadMore">
<view class="card" v-for="item in displayList" :key="item.id" @click="goDetail(item)"> <view class="card" v-for="item in displayList" :key="item.id" @click="goDetail(item)">
<view class="card-header"> <view class="card-header">
<view class="left"> <view class="left">
<text class="badge">申请延期</text> <text class="badge">{{ getBstTypeText(item.bstType) }}</text>
<text class="sub">所属项目 · {{ item.projectName || '—' }}</text> <text class="sub">所属项目 · {{ item.projectName || '—' }}</text>
</view> </view>
<view class="right"> <view class="right">
@ -19,8 +97,8 @@
<view class="card-body"> <view class="card-body">
<text class="remark" v-if="item.createRemark">{{ item.createRemark }}</text> <text class="remark" v-if="item.createRemark">{{ item.createRemark }}</text>
<view class="row"> <view class="row">
<text class="label">截止时间</text> <text class="label">创建时间</text>
<text class="value">{{ item.expireTime || '—' }}</text> <text class="value">{{ item.createTime || '—' }}</text>
</view> </view>
<view class="row"> <view class="row">
<text class="label">申请人</text> <text class="label">申请人</text>
@ -47,51 +125,150 @@
</view> </view>
</view> </view>
<view class="empty" v-if="!loading && displayList.length === 0"> <view class="empty" v-if="isEmpty && !loading">
<text>暂无数据</text> <text>暂无数据</text>
</view> </view>
<view class="loading" v-if="loading"> <view class="loading" v-if="loading">
<text>加载中...</text> <text>加载中...</text>
</view> </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> </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> </view>
</template> </template>
<script setup> <script setup>
import { ref, computed, onMounted } from 'vue'; import { ref, computed, onMounted } from 'vue';
import { getVerifyList } from '@/api'; import { getVerifyList } from '@/api';
import { usePagination } from '@/composables';
const tabs = [ const bstType = ref('');
{ name: '待我处理', value: 'pending' }, const filterStatus = ref('');
{ name: '我已处理', value: 'done' } const dateRange = ref([]); // [startDate, endDate] : yyyy-MM-dd
]; const showDatePicker = ref(false);
const currentTab = ref(0); const startDate = ref('');
const endDate = ref('');
const pageNum = ref(1); // 使
const pageSize = ref(20); const {
const total = ref(0); list,
const rawList = ref([]); loading,
const loading = ref(false); noMore,
const bstType = ref('UPDATE_TASK'); 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) { function statusText(s) {
if (s === '1') return '已通过'; if (s === '1') return '已通过';
if (s === '2') return '已驳回'; if (s === '2') return '已驳回';
return '待处理'; return '待处理';
} }
function statusType(s) { function statusType(s) {
if (s === '1') return 'success'; if (s === '1') return 'success';
if (s === '2') return 'error'; if (s === '2') return 'error';
return 'warning'; return 'warning';
} }
const displayList = computed(() => {
if (currentTab.value === 0) {
return rawList.value.filter(i => String(i.status) === '3');
}
return rawList.value.filter(i => String(i.status) !== '3');
});
const safeParseExpire = (dataField) => { const safeParseExpire = (dataField) => {
if (!dataField) return ''; if (!dataField) return '';
try { try {
@ -104,41 +281,81 @@ const safeParseExpire = (dataField) => {
} }
}; };
const fetchList = async () => { // 使 list
if (loading.value) return; const displayList = computed(() => list.value);
loading.value = true;
try { //
const res = await getVerifyList({ const openDatePicker = () => {
pageNum: pageNum.value, if (dateRange.value.length === 2) {
pageSize: pageSize.value, startDate.value = dateRange.value[0];
orderByColumn: 'createTime', endDate.value = dateRange.value[1];
isAsc: 'descending', } else {
bstType: bstType.value startDate.value = '';
}); endDate.value = '';
const rows = res?.rows || []; }
total.value = res?.total || 0; showDatePicker.value = true;
const mapped = rows.map(r => ({ };
...r,
expireTime: safeParseExpire(r.data) //
})); const closeDatePicker = () => {
if (pageNum.value === 1) { showDatePicker.value = false;
rawList.value = mapped; };
} else {
rawList.value = rawList.value.concat(mapped); //
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;
} }
} finally { dateRange.value = [startDate.value, endDate.value];
loading.value = false; closeDatePicker();
refreshList();
} else {
uni.showToast({
title: '请选择完整的日期范围',
icon: 'none'
});
} }
}; };
const loadMore = () => { //
if (rawList.value.length >= total.value) return; const clearDateRange = () => {
pageNum.value += 1; dateRange.value = [];
fetchList(); startDate.value = '';
endDate.value = '';
refreshList();
}; };
const onTabClick = (tab) => { //
currentTab.value = tab.index; const onBstTypeChange = (value) => {
bstType.value = value;
refreshList();
};
//
const onStatusChange = (value) => {
filterStatus.value = value;
refreshList();
};
//
const refreshList = () => {
updateParams({});
}; };
const goHandle = (item) => { const goHandle = (item) => {
@ -158,85 +375,238 @@ onMounted(() => {
if (options.bstType) { if (options.bstType) {
bstType.value = options.bstType; bstType.value = options.bstType;
} }
fetchList(); if (options.status) {
filterStatus.value = options.status;
}
getList(true);
}); });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.verify-page { .verify-page {
width: 100%; width: 100%;
background: #f5f5f5; background: #f5f5f5;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
.tabs-wrapper {
.filter-wrapper {
background: #fff; 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 { .list-scroll {
flex: 1; flex: 1;
} }
.card { .card {
background: #fff; background: #fff;
margin: 12px 12px 0 12px; margin: 12px 12px 0 12px;
border-radius: 12px; border-radius: 12px;
padding: 12px; padding: 12px;
} }
.card-header { .card-header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
margin-bottom: 6px; margin-bottom: 6px;
} }
.left { .left {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 8px; gap: 8px;
} }
.badge { .badge {
font-size: 14px; font-size: 14px;
font-weight: 600; font-weight: 600;
color: #333; color: #333;
} }
.sub { .sub {
font-size: 12px; font-size: 12px;
color: #999; color: #999;
} }
.card-body { .card-body {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 6px; gap: 6px;
margin-top: 4px; margin-top: 4px;
} }
.remark { .remark {
font-size: 13px; font-size: 13px;
color: #333; color: #333;
line-height: 1.6; line-height: 1.6;
} }
.row { .row {
display: flex; display: flex;
font-size: 12px; font-size: 12px;
color: #666; color: #666;
} }
.label { .label {
width: 70px; width: 70px;
flex-shrink: 0; flex-shrink: 0;
} }
.value { .value {
color: #333; color: #333;
} }
.card-footer { .card-footer {
margin-top: 10px; margin-top: 10px;
display: flex; display: flex;
justify-content: flex-start; justify-content: flex-start;
} }
.empty, .loading {
.empty, .loading, .load-more {
text-align: center; text-align: center;
color: #999; color: #999;
font-size: 12px; font-size: 12px;
padding: 16px 0; 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> </style>

View File

@ -11,7 +11,7 @@ export const Request = () => {
uni.$uv.http.setConfig((config) => { uni.$uv.http.setConfig((config) => {
/* config 为默认全局配置*/ /* config 为默认全局配置*/
config.baseURL = 'http://192.168.1.5:4001'; /* 根域名 */ config.baseURL = 'http://192.168.1.5:4001'; /* 根域名 */
config.baseURL = 'https://pm.ccttiot.com/prod-api'; /* 根域名 */ // config.baseURL = 'https://pm.ccttiot.com/prod-api'; /* 根域名 */
return config return config
}) })