babyhealth/pages/tasks/approvalDetail.vue
2026-02-06 20:21:10 +08:00

484 lines
12 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="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>