审批调整

This commit is contained in:
hebo 2025-09-23 21:53:36 +08:00
parent 2d2314c336
commit dea3be0d4a
3 changed files with 498 additions and 472 deletions

View File

@ -145,12 +145,6 @@ export function getGwFlowByIdApi(id: string) {
return get(`/api/gw/getGwFlowById?id=${id}`); return get(`/api/gw/getGwFlowById?id=${id}`);
} }
/**
*
*/
export function gwTransferApi(params: any) {
return post('/api/gw/transfer', params);
}
/** /**
* *
@ -158,3 +152,47 @@ export function gwTransferApi(params: any) {
export function findUserTodosApi(approveStatus: string, jsId: string, pageNum: number = 1, pageSize: number = 20) { export function findUserTodosApi(approveStatus: string, jsId: string, pageNum: number = 1, pageSize: number = 20) {
return get('/api/gw/findUserTodos', { approveStatus, jsId, pageNum, pageSize }); return get('/api/gw/findUserTodos', { approveStatus, jsId, pageNum, pageSize });
} }
// ===== 新增:统一的流程接口 =====
/**
*
*/
export function gwSqApi(params: any) {
return post('/api/gw/sq', params);
}
/**
*
*/
export function gwSpApi(params: any) {
return post('/api/gw/sp', params);
}
/**
*
*/
export function gwTransferApi(params: any) {
return post('/api/gw/transfer', params);
}
/**
*
*/
export function gwStopApi(params: any) {
return post('/api/gw/stop', params);
}
/**
*
*/
export function gwCxtjApi(params: any) {
return post('/api/gw/cxtj', params);
}
/**
*
*/
export function gwXtApi(params: any) {
return post('/api/gw/xt', params);
}

View File

@ -82,136 +82,9 @@
</view> </view>
</view> </view>
<!-- 当前处理人 -->
<view class="approver-section">
<view class="section-title">当前审批人</view>
<view class="approver-list">
<view
v-for="approver in approvers"
:key="approver.id"
class="approver-item"
>
<view class="approver-info">
<text class="order">{{ approver.order }}</text>
<text class="name">{{ approver.userName }}</text>
<text class="dept">{{ approver.deptName }}</text>
<text class="status" :class="getApproverStatusClass(approver.approveStatus)">
{{ getApproverStatusText(approver.approveStatus) }}
</text>
</view> </view>
<!-- 显示审批意见和审批时间 --> <!-- 审批流程 -->
<view class="approver-detail" v-if="approver.approveRemark || approver.approveTime"> <LcglSp :yw-id="gwInfo.id" yw-type="GW"/>
<view class="approval-info" v-if="approver.approveRemark">
<text class="info-label">审批意见</text>
<text class="info-value">{{ approver.approveRemark }}</text>
</view>
<view class="approval-info" v-if="approver.approveTime">
<text class="info-label">审批时间</text>
<text class="info-value">{{ formatTime(approver.approveTime) }}</text>
</view>
</view>
</view>
</view>
</view>
<!-- 抄送人 -->
<view class="cc-section">
<view class="section-title">抄送人</view>
<view class="cc-list">
<view
v-for="ccUser in displayedCcUsers"
:key="ccUser.id"
class="cc-item"
>
<view class="cc-info">
<text class="name">{{ ccUser.userName }}</text>
<text class="dept">{{ ccUser.deptName }}</text>
<text class="status" :class="getCCStatusClass(ccUser.status)">
{{ getCCStatusText(ccUser.status) }}
</text>
</view>
</view>
</view>
<!-- 更多按钮 -->
<view
v-if="ccUsers.length > 2"
class="more-button"
@click="toggleCcExpanded"
>
<text class="more-text">
{{ ccExpanded ? '收起' : `更多(${ccUsers.length - 2})` }}
</text>
<text class="more-icon" :class="{ expanded: ccExpanded }"></text>
</view>
</view>
<!-- 操作记录 -->
<view class="log-section">
<view class="section-title">操作记录</view>
<view class="log-list">
<view
v-for="log in displayedOperationLogs"
:key="log.id"
class="log-item"
>
<view class="log-header">
<text class="operator">{{ log.operatorName }}</text>
<text class="time">{{ formatTime(log.operationTime) }}</text>
</view>
<view class="log-content">
<text class="content">{{ log.operationContent }}</text>
</view>
<view class="log-detail" v-if="log.beforeChange || log.afterChange">
<u-button
text="详情"
size="mini"
@click="showLogDetail(log)"
/>
</view>
</view>
</view>
<!-- 更多按钮 -->
<view
v-if="operationLogs.length > 2"
class="more-button"
@click="toggleLogExpanded"
>
<text class="more-text">
{{ logExpanded ? '收起' : `更多(${operationLogs.length - 2})` }}
</text>
<text class="more-icon" :class="{ expanded: logExpanded }"></text>
</view>
</view>
</view>
<!-- 底部固定按钮 -->
<view class="bottom-actions" v-if="canCurrentUserOperate">
<!-- 驳回按钮暂时隐藏 -->
<!-- <u-button
text="驳回"
type="error"
size="large"
@click="handleReject"
/> -->
<u-button
text="转办"
type="warning"
size="large"
@click="handleTransfer"
/>
<u-button
text="同意"
type="primary"
size="large"
@click="handleApprove"
/>
</view>
<!-- 操作记录详情弹窗 --> <!-- 操作记录详情弹窗 -->
@ -219,7 +92,7 @@
<view class="detail-modal"> <view class="detail-modal">
<view class="detail-header"> <view class="detail-header">
<text class="detail-title">操作详情</text> <text class="detail-title">操作详情</text>
<u-button text="关闭" @click="showLogDetailModal = false" /> <u-button text="关闭" @click="showLogDetailModal = false"/>
</view> </view>
<view class="detail-content"> <view class="detail-content">
<view class="detail-item" v-if="currentLog.beforeChange"> <view class="detail-item" v-if="currentLog.beforeChange">
@ -279,18 +152,32 @@
</view> </view>
</view> </view>
</u-popup> </u-popup>
<template #bottom>
<YwConfirm
:spApi="gwSpApi"
:stopApi="gwStopApi"
:transferApi="gwTransferApi"
:params="spParams"
:showXt="false"
:showReject="true"
:showTransfer="true"
:showApprove="true"
:showReturn="false"
:showStop="false"
:showXtDk="false"/>
</template>
</BasicLayout> </BasicLayout>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted, computed } from "vue"; import {ref, onMounted, computed} from "vue";
import BasicLayout from "@/components/BasicLayout/Layout.vue"; import BasicLayout from "@/components/BasicLayout/Layout.vue";
import { navigateTo } from "@/utils/uniapp"; import {navigateTo} from "@/utils/uniapp";
import { getGwFlowByIdApi, gwApproveApi } from "@/api/routine/gw"; import {getGwFlowByIdApi, gwApproveApi, gwSpApi, gwTransferApi, gwStopApi} from "@/api/routine/gw";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { useUserStore } from "@/store/modules/user"; import {useUserStore} from "@/store/modules/user";
import { imagUrl } from "@/utils"; import {imagUrl} from "@/utils";
import { import {
isVideo, isVideo,
isImage, isImage,
@ -300,6 +187,16 @@ import {
previewImage as previewImageUtil, previewImage as previewImageUtil,
downloadFile downloadFile
} from "@/utils/filePreview"; } from "@/utils/filePreview";
import LcglSp from "@/components/LcglSp/index.vue";
import YwConfirm from "@/pages/components/YwConfirm/index.vue";
import {useDataStore} from "@/store/modules/data";
const { setData, getXxts } = useDataStore();
const spParams = computed(() => {
return{
xxtsId: getXxts.id,
ywId: gwInfo.value.id
}
})
// //
interface GwInfo { interface GwInfo {
@ -365,9 +262,6 @@ interface OperationLog {
} }
// //
const gwId = ref(""); const gwId = ref("");
@ -379,7 +273,7 @@ const downloadFileName = ref('');
const isDownloading = ref(false); const isDownloading = ref(false);
// store // store
const { getUser, getJs } = useUserStore(); const {getUser, getJs} = useUserStore();
// //
const gwInfo = ref<GwInfo>({} as GwInfo); const gwInfo = ref<GwInfo>({} as GwInfo);
@ -517,19 +411,6 @@ const getGwInfo = async () => {
}; };
// //
const showLogDetail = (log: OperationLog) => { const showLogDetail = (log: OperationLog) => {
currentLog.value = log; currentLog.value = log;
@ -589,7 +470,7 @@ const handleTransfer = () => {
// //
uni.setStorageSync('transferData', { uni.setStorageSync('transferData', {
xxtsInfo: { id: gwId.value }, xxtsInfo: {id: gwId.value},
gwInfo: gwInfo.value, gwInfo: gwInfo.value,
approvers: approvers.value, approvers: approvers.value,
ccUsers: ccUsers.value // ccUsers: ccUsers.value //
@ -874,7 +755,7 @@ const downloadFileAction = (file: FileInfo, event?: Event) => {
// //
const originalFileName = getOriginalFileName(file); const originalFileName = getOriginalFileName(file);
console.log('下载文件:', { finalUrl, originalFileName, file }); console.log('下载文件:', {finalUrl, originalFileName, file});
// //
showDownloadPathModal(finalUrl, originalFileName); showDownloadPathModal(finalUrl, originalFileName);
@ -1077,12 +958,12 @@ const confirmDownload = () => {
// //
const systemInfo = uni.getSystemInfoSync(); const systemInfo = uni.getSystemInfoSync();
const { platform } = systemInfo; const {platform} = systemInfo;
// //
const isH5 = platform === 'web' || typeof window !== 'undefined'; const isH5 = platform === 'web' || typeof window !== 'undefined';
console.log('平台检测:', { platform, systemInfo, isH5 }); console.log('平台检测:', {platform, systemInfo, isH5});
if (isH5) { if (isH5) {
console.log('使用H5下载方式'); console.log('使用H5下载方式');
@ -1117,7 +998,7 @@ const confirmDownload = () => {
// H5 // H5
const downloadForH5 = (url: string, fileName: string) => { const downloadForH5 = (url: string, fileName: string) => {
try { try {
console.log('H5下载开始:', { url, fileName }); console.log('H5下载开始:', {url, fileName});
// 使 // 使
forceDownload(url, fileName); forceDownload(url, fileName);
@ -1132,7 +1013,7 @@ const downloadForH5 = (url: string, fileName: string) => {
// //
const forceDownload = (url: string, fileName: string) => { const forceDownload = (url: string, fileName: string) => {
try { try {
console.log('尝试强制下载:', { url, fileName }); console.log('尝试强制下载:', {url, fileName});
// 1: 使fetchblob // 1: 使fetchblob
fetch(url, { fetch(url, {
@ -1196,7 +1077,7 @@ const tryAlternativeDownload = (url: string, fileName: string) => {
xhr.open('GET', url, true); xhr.open('GET', url, true);
xhr.responseType = 'blob'; xhr.responseType = 'blob';
xhr.onload = function() { xhr.onload = function () {
if (xhr.status === 200) { if (xhr.status === 200) {
const blob = xhr.response; const blob = xhr.response;
const blobUrl = window.URL.createObjectURL(blob); const blobUrl = window.URL.createObjectURL(blob);
@ -1226,7 +1107,7 @@ const tryAlternativeDownload = (url: string, fileName: string) => {
} }
}; };
xhr.onerror = function() { xhr.onerror = function () {
console.error('XMLHttpRequest下载失败'); console.error('XMLHttpRequest下载失败');
console.log('调用手动下载提示'); console.log('调用手动下载提示');
showManualDownloadModal(url, fileName); showManualDownloadModal(url, fileName);
@ -1503,10 +1384,25 @@ onMounted(() => {
border-radius: 4px; border-radius: 4px;
font-size: 12px; font-size: 12px;
&.status-draft { background: #f0f0f0; color: #666; } &.status-draft {
&.status-pending { background: #fff7e6; color: #fa8c16; } background: #f0f0f0;
&.status-approved { background: #f6ffed; color: #52c41a; } color: #666;
&.status-rejected { background: #fff2f0; color: #ff4d4f; } }
&.status-pending {
background: #fff7e6;
color: #fa8c16;
}
&.status-approved {
background: #f6ffed;
color: #52c41a;
}
&.status-rejected {
background: #fff2f0;
color: #ff4d4f;
}
} }
.urgency-tag { .urgency-tag {
@ -1514,9 +1410,20 @@ onMounted(() => {
border-radius: 4px; border-radius: 4px;
font-size: 12px; font-size: 12px;
&.urgency-normal { background: #f0f0f0; color: #666; } &.urgency-normal {
&.urgency-high { background: #fff7e6; color: #fa8c16; } background: #f0f0f0;
&.urgency-urgent { background: #fff2f0; color: #ff4d4f; } color: #666;
}
&.urgency-high {
background: #fff7e6;
color: #fa8c16;
}
&.urgency-urgent {
background: #fff2f0;
color: #ff4d4f;
}
} }
.file-item { .file-item {
@ -1635,12 +1542,35 @@ onMounted(() => {
border-radius: 4px; border-radius: 4px;
font-size: 12px; font-size: 12px;
&.status-pending { background: #fff7e6; color: #fa8c16; } &.status-pending {
&.status-approved { background: #f6ffed; color: #52c41a; } background: #fff7e6;
&.status-rejected { background: #fff2f0; color: #ff4d4f; } color: #fa8c16;
&.status-skipped { background: #f0f0f0; color: #666; } }
&.status-unread { background: #fff7e6; color: #fa8c16; }
&.status-read { background: #f6ffed; color: #52c41a; } &.status-approved {
background: #f6ffed;
color: #52c41a;
}
&.status-rejected {
background: #fff2f0;
color: #ff4d4f;
}
&.status-skipped {
background: #f0f0f0;
color: #666;
}
&.status-unread {
background: #fff7e6;
color: #fa8c16;
}
&.status-read {
background: #f6ffed;
color: #52c41a;
}
} }
} }
} }
@ -1949,7 +1879,11 @@ onMounted(() => {
} }
@keyframes spin { @keyframes spin {
0% { transform: rotate(0deg); } 0% {
100% { transform: rotate(360deg); } transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
} }
</style> </style>

View File

@ -119,6 +119,7 @@ import { onShow } from "@dcloudio/uni-app";
import { navigateTo } from "@/utils/uniapp"; import { navigateTo } from "@/utils/uniapp";
import BasicSearch from "@/components/BasicSearch/Search.vue"; import BasicSearch from "@/components/BasicSearch/Search.vue";
import { gwFindPageApi, findUserTodosApi } from "@/api/routine/gw"; import { gwFindPageApi, findUserTodosApi } from "@/api/routine/gw";
import { gwSqApi, gwSpApi, gwTransferApi, gwStopApi, gwCxtjApi, gwXtApi } from "@/api/routine/gw";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { imagUrl } from "@/utils"; import { imagUrl } from "@/utils";
import { useUserStore } from "@/store/modules/user"; import { useUserStore } from "@/store/modules/user";
@ -148,6 +149,7 @@ interface GwListItem {
fileFormat?: string; fileFormat?: string;
files?: FileInfo[]; files?: FileInfo[];
spZbqd?: string; // ID spZbqd?: string; // ID
spResult?: string; // A-B-C-D-
tjrtime?: string; // tjrtime?: string; //
[key: string]: any; [key: string]: any;
} }
@ -278,8 +280,8 @@ const loadUserTodos = async (approveStatus: string, jsId: string) => {
loading.value = true; loading.value = true;
uni.showLoading({ title: '加载中...' }); uni.showLoading({ title: '加载中...' });
// findUserTodos // findUserTodos 使
const response = await findUserTodosApi(approveStatus, jsId, 1, 1000); // pageSize const response = await findUserTodosApi(approveStatus, jsId, 1, 50); // 使
// - // -
const result = (response as any).data || response; const result = (response as any).data || response;
@ -374,27 +376,58 @@ const getStatusText = (status: string) => {
return statusMap[status] || "未知"; return statusMap[status] || "未知";
}; };
// // -
const getButtonText = (item: GwListItem) => { const getButtonText = (item: GwListItem) => {
const currentTeacherId = getCurrentTeacherId(); const currentTeacherId = getCurrentTeacherId();
const { gwStatus, spZbqd } = item; const { gwStatus, spZbqd, spResult } = item;
// IDspZbqdB"" // ""
if (currentTeacherId && spZbqd && gwStatus === 'B') { if (currentTeacherId && spZbqd && gwStatus === 'B' && activeTab.value === 'pending') {
const approverIds = spZbqd.split(',').map(id => id.trim()); const approverIds = spZbqd.split(',').map(id => id.trim());
if (approverIds.includes(currentTeacherId)) { if (approverIds.includes(currentTeacherId)) {
return '审批'; return '审批';
} }
} }
//
if (activeTab.value === 'approved') {
if (spResult === 'B') {
return '已同意';
} else if (spResult === 'C') {
return '已驳回';
} else if (spResult === 'D') {
return '已终止';
}
}
// "" // ""
return '详情'; return '详情';
}; };
// // -
const getButtonClass = (item: GwListItem) => { const getButtonClass = (item: GwListItem) => {
const buttonText = getButtonText(item); const buttonText = getButtonText(item);
return buttonText === '审批' ? 'action-button-approve' : 'action-button-detail'; const currentTeacherId = getCurrentTeacherId();
const { gwStatus, spZbqd, spResult } = item;
//
if (buttonText === '审批') {
return 'action-button-approve';
}
//
if (activeTab.value === 'approved') {
if (spResult === 'B') {
return 'action-button-approved'; //
} else if (spResult === 'C') {
return 'action-button-rejected'; //
} else if (spResult === 'D') {
return 'action-button-stopped'; //
}
}
//
return 'action-button-detail';
}; };
// //
@ -877,7 +910,10 @@ onUnmounted(() => {
// //
.action-button-approve, .action-button-approve,
.action-button-detail { .action-button-detail,
.action-button-approved,
.action-button-rejected,
.action-button-stopped {
padding: 4px 8px !important; padding: 4px 8px !important;
border-radius: 12px !important; border-radius: 12px !important;
font-size: 12px !important; font-size: 12px !important;
@ -901,6 +937,24 @@ onUnmounted(() => {
border: none !important; border: none !important;
} }
.action-button-approved {
background: linear-gradient(135deg, #66bb6a 0%, #4caf50 100%) !important;
color: white !important;
border: none !important;
}
.action-button-rejected {
background: linear-gradient(135deg, #ef5350 0%, #e53935 100%) !important;
color: white !important;
border: none !important;
}
.action-button-stopped {
background: linear-gradient(135deg, #9e9e9e 0%, #757575 100%) !important;
color: white !important;
border: none !important;
}
// //
@media (max-width: 375px) { @media (max-width: 375px) {
.query-component { .query-component {