484 lines
12 KiB
Vue
484 lines
12 KiB
Vue
<template>
|
||
<view class="main-data-page">
|
||
<!-- 使用子头部组件 -->
|
||
<sub-header
|
||
title="审批详情"
|
||
@goBack="goBack"
|
||
@showMoreOptions="showMoreOptions"
|
||
/>
|
||
<view class="main-container">
|
||
<!-- 审批详情卡片 -->
|
||
<view class="detail-card">
|
||
<!-- 标题和状态 -->
|
||
<view class="detail-header">
|
||
<view class="detail-title">{{ approvalDetail.title }}</view>
|
||
<view class="detail-status" :class="approvalDetail.status">
|
||
{{ getStatusText(approvalDetail.status) }}
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 基本信息 -->
|
||
<view class="detail-section">
|
||
<view class="section-title">基本信息</view>
|
||
<view class="info-list">
|
||
<view class="info-item">
|
||
<text class="info-label">申请人:</text>
|
||
<text class="info-value">{{ approvalDetail.applicant }}</text>
|
||
</view>
|
||
<view class="info-item">
|
||
<text class="info-label">申请时间:</text>
|
||
<text class="info-value">{{ approvalDetail.applyTime }}</text>
|
||
</view>
|
||
<view class="info-item">
|
||
<text class="info-label">业务类型:</text>
|
||
<text class="info-value">{{ approvalDetail.businessType }}</text>
|
||
</view>
|
||
<view class="info-item">
|
||
<text class="info-label">优先级:</text>
|
||
<view class="priority-tag" :class="approvalDetail.priority">
|
||
{{ getPriorityText(approvalDetail.priority) }}
|
||
</view>
|
||
</view>
|
||
<view class="info-item" v-if="approvalDetail.amount">
|
||
<text class="info-label">申请金额:</text>
|
||
<text class="info-value amount">{{ approvalDetail.amount }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 审批信息 -->
|
||
<view class="detail-section">
|
||
<view class="section-title">审批信息</view>
|
||
<view class="info-list">
|
||
<view class="info-item">
|
||
<text class="info-label">审批人:</text>
|
||
<text class="info-value">{{ approvalDetail.approver || '待分配' }}</text>
|
||
</view>
|
||
<view class="info-item">
|
||
<text class="info-label">审批时间:</text>
|
||
<text class="info-value">{{ approvalDetail.approveTime || '待审批' }}</text>
|
||
</view>
|
||
<view class="info-item" v-if="approvalDetail.remark">
|
||
<text class="info-label">审批意见:</text>
|
||
<text class="info-value">{{ approvalDetail.remark }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 操作按钮 -->
|
||
<view class="action-buttons" v-if="approvalDetail.status === 'pending'">
|
||
<button class="action-btn reject-btn" @click="handleApproval('rejected')">
|
||
拒绝
|
||
</button>
|
||
<button class="action-btn approve-btn" @click="handleApproval('approved')">
|
||
通过
|
||
</button>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 理由输入弹窗 -->
|
||
<view v-if="showReasonDialog" class="reason-mask" @click.self="closeReasonDialog">
|
||
<view class="reason-modal">
|
||
<view class="reason-title">{{ actionType === 'approved' ? '通过理由' : '拒绝理由' }}</view>
|
||
<textarea class="reason-textarea" v-model="reasonText" placeholder="请输入审批理由(必填)" auto-height :maxlength="200" />
|
||
<view class="reason-tip">最多200字,需填写后才能提交</view>
|
||
<view class="reason-actions">
|
||
<button class="reason-btn cancel" @click="closeReasonDialog">取消</button>
|
||
<button class="reason-btn confirm" @click="submitApproval">提交</button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, onMounted } from "vue";
|
||
import SubHeader from "../components/subHeader.vue";
|
||
|
||
// 审批详情数据
|
||
const approvalDetail = ref({
|
||
id: '',
|
||
title: '',
|
||
status: '',
|
||
applicant: '',
|
||
applyTime: '',
|
||
businessType: '',
|
||
priority: '',
|
||
amount: '',
|
||
approver: '',
|
||
approveTime: '',
|
||
remark: ''
|
||
});
|
||
|
||
// 获取状态文本
|
||
const getStatusText = (status) => {
|
||
const statusMap = {
|
||
pending: '待审批',
|
||
approved: '已通过',
|
||
rejected: '已拒绝'
|
||
};
|
||
return statusMap[status] || status;
|
||
};
|
||
|
||
// 获取优先级文本
|
||
const getPriorityText = (priority) => {
|
||
const priorityMap = {
|
||
high: '高',
|
||
medium: '中',
|
||
low: '低'
|
||
};
|
||
return priorityMap[priority] || priority;
|
||
};
|
||
|
||
// 理由弹框状态
|
||
const showReasonDialog = ref(false);
|
||
const actionType = ref('approved'); // 'approved' | 'rejected'
|
||
const reasonText = ref('');
|
||
|
||
// 处理审批操作(先弹出理由框)
|
||
const handleApproval = (action) => {
|
||
actionType.value = action;
|
||
reasonText.value = '';
|
||
showReasonDialog.value = true;
|
||
};
|
||
|
||
const closeReasonDialog = () => {
|
||
showReasonDialog.value = false;
|
||
};
|
||
|
||
const submitApproval = () => {
|
||
const text = reasonText.value.trim();
|
||
if (!text) {
|
||
uni.showToast({ title: '请先填写理由', icon: 'none' });
|
||
return;
|
||
}
|
||
|
||
const action = actionType.value;
|
||
const actionText = action === 'approved' ? '通过' : '拒绝';
|
||
|
||
// 这里可以调用API提交备注与状态
|
||
approvalDetail.value.status = action;
|
||
approvalDetail.value.approveTime = new Date().toLocaleString('zh-CN');
|
||
approvalDetail.value.approver = '当前用户'; // 实际应为当前登录用户
|
||
approvalDetail.value.remark = text;
|
||
|
||
showReasonDialog.value = false;
|
||
uni.showToast({ title: `已${actionText}`, icon: 'success' });
|
||
|
||
setTimeout(() => {
|
||
uni.navigateBack();
|
||
}, 1200);
|
||
};
|
||
|
||
// 头部相关方法
|
||
const goBack = () => {
|
||
uni.navigateBack();
|
||
};
|
||
|
||
const showMoreOptions = () => {
|
||
uni.showActionSheet({
|
||
itemList: ["刷新", "返回首页"],
|
||
success: (res) => {
|
||
switch (res.tapIndex) {
|
||
case 0:
|
||
// 刷新数据
|
||
uni.showToast({ title: "数据已刷新", icon: "success" });
|
||
break;
|
||
case 1:
|
||
uni.reLaunch({
|
||
url: "/pages/index/index",
|
||
});
|
||
uni.showToast({ title: "已返回工作台", icon: "success" });
|
||
break;
|
||
}
|
||
},
|
||
});
|
||
};
|
||
|
||
// 页面加载时获取参数
|
||
onMounted(() => {
|
||
const pages = getCurrentPages();
|
||
const currentPage = pages[pages.length - 1];
|
||
const options = currentPage.options;
|
||
|
||
// 从URL参数中获取数据
|
||
approvalDetail.value = {
|
||
id: options.id || '',
|
||
title: decodeURIComponent(options.title || ''),
|
||
status: options.status || '',
|
||
applicant: decodeURIComponent(options.applicant || ''),
|
||
applyTime: decodeURIComponent(options.applyTime || ''),
|
||
businessType: decodeURIComponent(options.businessType || ''),
|
||
priority: options.priority || '',
|
||
amount: options.amount ? decodeURIComponent(options.amount) : '',
|
||
approver: '待分配',
|
||
approveTime: '',
|
||
remark: ''
|
||
};
|
||
});
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
@import "../../static/css/style.scss";
|
||
|
||
// 变量定义
|
||
$border-radius: 16rpx;
|
||
$border-radius-small: 12rpx;
|
||
$spacing-small: 12rpx;
|
||
$spacing-medium: 20rpx;
|
||
$spacing-large: 30rpx;
|
||
$transition: all 0.3s ease;
|
||
|
||
.main-data-page {
|
||
background: var(--background);
|
||
min-height: 100vh;
|
||
padding-top: calc(var(--status-bar-height) + 88rpx);
|
||
padding-bottom: 40rpx;
|
||
|
||
.main-container {
|
||
padding: 24rpx;
|
||
}
|
||
}
|
||
|
||
// 详情卡片
|
||
.detail-card {
|
||
background: var(--surface);
|
||
border-radius: $border-radius;
|
||
box-shadow: var(--shadow);
|
||
border: 1rpx solid var(--border-light);
|
||
overflow: hidden;
|
||
}
|
||
|
||
// 详情头部
|
||
.detail-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: flex-start;
|
||
padding: $spacing-large;
|
||
border-bottom: 1rpx solid var(--border-light);
|
||
|
||
.detail-title {
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
color: var(--title-color);
|
||
flex: 1;
|
||
margin-right: $spacing-medium;
|
||
}
|
||
|
||
.detail-status {
|
||
padding: 8rpx 16rpx;
|
||
border-radius: 20rpx;
|
||
font-size: 24rpx;
|
||
font-weight: 500;
|
||
white-space: nowrap;
|
||
|
||
&.pending {
|
||
background: var(--warning-light);
|
||
color: var(--warning);
|
||
}
|
||
|
||
&.approved {
|
||
background: var(--success-light);
|
||
color: var(--success);
|
||
}
|
||
|
||
&.rejected {
|
||
background: var(--error-light);
|
||
color: var(--error);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 详情区域
|
||
.detail-section {
|
||
padding: $spacing-large;
|
||
border-bottom: 1rpx solid var(--border-light);
|
||
|
||
&:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.section-title {
|
||
font-size: 28rpx;
|
||
font-weight: 600;
|
||
color: var(--title-color);
|
||
margin-bottom: $spacing-medium;
|
||
}
|
||
}
|
||
|
||
// 信息列表
|
||
.info-list {
|
||
.info-item {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-bottom: $spacing-small;
|
||
|
||
&:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.info-label {
|
||
font-size: 26rpx;
|
||
color: var(--text-muted);
|
||
width: 140rpx;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.info-value {
|
||
font-size: 26rpx;
|
||
color: var(--text-color);
|
||
flex: 1;
|
||
|
||
&.amount {
|
||
color: var(--orange);
|
||
font-weight: 600;
|
||
}
|
||
}
|
||
|
||
.priority-tag {
|
||
padding: 6rpx 12rpx;
|
||
border-radius: $border-radius-small;
|
||
font-size: 22rpx;
|
||
font-weight: 500;
|
||
|
||
&.high {
|
||
background: var(--error-light);
|
||
color: var(--error);
|
||
}
|
||
|
||
&.medium {
|
||
background: var(--warning-light);
|
||
color: var(--warning);
|
||
}
|
||
|
||
&.low {
|
||
background: var(--success-light);
|
||
color: var(--success);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 操作按钮
|
||
.action-buttons {
|
||
display: flex;
|
||
gap: $spacing-medium;
|
||
padding: $spacing-large;
|
||
|
||
.action-btn {
|
||
flex: 1;
|
||
height: 80rpx;
|
||
border-radius: $border-radius;
|
||
font-size: 28rpx;
|
||
font-weight: 600;
|
||
border: none;
|
||
transition: $transition;
|
||
|
||
&.reject-btn {
|
||
background: var(--error-light);
|
||
color: var(--error);
|
||
border: 1rpx solid var(--error);
|
||
|
||
&:active {
|
||
background: var(--error);
|
||
color: var(--white);
|
||
}
|
||
}
|
||
|
||
&.approve-btn {
|
||
background: var(--success-light);
|
||
color: var(--success);
|
||
border: 1rpx solid var(--success);
|
||
|
||
&:active {
|
||
background: var(--success);
|
||
color: var(--white);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 支持安全区域的设备
|
||
@supports (padding: max(0px)) {
|
||
.main-data-page {
|
||
padding-top: calc(
|
||
var(--status-bar-height) + 88rpx + env(safe-area-inset-top)
|
||
);
|
||
}
|
||
}
|
||
|
||
// 理由弹窗样式
|
||
.reason-mask {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: rgba(0, 0, 0, 0.45);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 10000;
|
||
}
|
||
|
||
.reason-modal {
|
||
width: 86%;
|
||
background: var(--surface);
|
||
border-radius: $border-radius;
|
||
box-shadow: var(--shadow-lg);
|
||
border: 1rpx solid var(--border-light);
|
||
padding: $spacing-large;
|
||
}
|
||
|
||
.reason-title {
|
||
font-size: 30rpx;
|
||
font-weight: 600;
|
||
color: var(--title-color);
|
||
margin-bottom: $spacing-medium;
|
||
}
|
||
|
||
.reason-textarea {
|
||
width: 100%;
|
||
min-height: 160rpx;
|
||
background: var(--surface-hover);
|
||
border: 1rpx solid var(--border-light);
|
||
border-radius: $border-radius-small;
|
||
padding: $spacing-medium;
|
||
font-size: 26rpx;
|
||
color: var(--text-color);
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.reason-tip {
|
||
margin-top: $spacing-small;
|
||
font-size: 22rpx;
|
||
color: var(--text-muted);
|
||
}
|
||
|
||
.reason-actions {
|
||
margin-top: $spacing-large;
|
||
display: flex;
|
||
gap: $spacing-medium;
|
||
}
|
||
|
||
.reason-btn {
|
||
flex: 1;
|
||
height: 76rpx;
|
||
border-radius: $border-radius;
|
||
font-size: 28rpx;
|
||
font-weight: 600;
|
||
border: none;
|
||
}
|
||
|
||
.reason-btn.cancel {
|
||
background: var(--button-secondary);
|
||
color: var(--text-color);
|
||
}
|
||
|
||
.reason-btn.confirm {
|
||
background: var(--primary-color);
|
||
color: var(--white);
|
||
}
|
||
</style>
|
||
|