调整学生请假审批

This commit is contained in:
ywyonui 2025-08-28 00:57:52 +08:00
parent 8fe418f37e
commit c9ab421a35
5 changed files with 539 additions and 27 deletions

View File

@ -179,6 +179,16 @@ export const xsQjSpApi = async (params: any) => {
return await post("/api/xsQj/sp", params);
};
// 获取学生请假审批流程
export const getXsQjApprovalProcessApi = async (ywId: string, ywType: string = 'XS_QJ') => {
return await get("/api/lcglSp/getByYwIdAndYwType", { ywId, ywType });
};
// 获取学生请假详情
export const getXsQjDetailApi = async (id: string) => {
return await get("/api/xsQj/getDetail", { id });
};
// 获取所有班级
export const bjFindAllApi = async (params: any) => {
return await get("/api/bj/findAll", params);

View File

@ -546,6 +546,13 @@
"enablePullDownRefresh": false
}
},
{
"path": "pages/base/xs/qj/detail",
"style": {
"navigationBarTitleText": "学生请假详情",
"enablePullDownRefresh": false
}
},
{
"path": "pages/view/routine/qd/index",
"style": {

View File

@ -0,0 +1,276 @@
<template>
<view class="approval-progress">
<view class="progress-title">
<text class="applicant-name">审批进度</text>
</view>
<view class="divider"></view>
<view class="progress-list">
<view class="progress-item" v-for="(approver, index) in approvalList" :key="index">
<view class="progress-item-row">
<view class="item-avatar">
<image
:src="approver.avatar || '/static/base/home/11222.png'"
class="w-full h-full"
></image>
</view>
<view class="item-middle">
<text class="item-name">{{ approver.userName }}</text>
<text class="item-detail">{{ getSpTypeText(approver.spType) }}</text>
</view>
<view class="item-right">
<text class="item-time" v-if="approver.approveTime">{{ formatTime(approver.approveTime) }}</text>
<text class="item-status" :class="getStatusClass(approver.approveStatus)">
{{ getStatusText(approver.approveStatus) }}
</text>
</view>
</view>
<view class="progress-item-line" v-if="index < approvalList.length - 1"></view>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import dayjs from "dayjs";
import { getXsQjApprovalProcessApi } from "@/api/base/server";
//
const props = withDefaults(defineProps<{
qjId: string
}>(), {
qjId: ''
});
//
const approvalList = ref<any[]>([]);
//
const loadApprovalProcess = async () => {
if (!props.qjId) return;
try {
// API
const res = await getXsQjApprovalProcessApi(props.qjId, 'XS_QJ');
if (res.resultCode === 1 && res.result) {
// sort
approvalList.value = res.result.map((item: any) => ({
userName: item.userName || getDefaultUserName(item.spType),
spType: item.spType,
approveStatus: item.approveStatus,
approveTime: item.approveTime,
approveRemark: item.approveRemark,
avatar: item.avatar || '/static/base/home/11222.png'
}));
} else {
loadMockData();
}
} catch (error) {
console.error('获取审批流程失败:', error);
// API使
loadMockData();
}
};
//
const getDefaultUserName = (spType: string) => {
switch (spType) {
case 'SQ': return '学生';
case 'SP': return '班主任';
case 'CC': return '家长';
default: return '未知';
}
};
//
const loadMockData = () => {
const mockData = [
{
userName: '学生',
spType: 'SQ',
approveStatus: 'approved',
approveTime: new Date(),
approveRemark: '申请人提交',
avatar: '/static/base/home/11222.png'
},
{
userName: '班主任',
spType: 'SP',
approveStatus: 'pending',
approveTime: null,
approveRemark: '待审批',
avatar: '/static/base/home/11222.png'
}
];
approvalList.value = mockData;
};
//
const getSpTypeText = (spType: string) => {
switch (spType) {
case 'SQ': return '申请人';
case 'SP': return '审批人';
case 'CC': return '抄送人';
default: return '';
}
};
//
const getStatusText = (status: string) => {
switch (status) {
case 'apply': return '已申请';
case 'pending': return '待处理';
case 'approved': return '已同意';
case 'rejected': return '已拒绝';
case 'cc_sent': return '已抄送';
default: return '未知';
}
};
//
const getStatusClass = (status: string) => {
switch (status) {
case 'pending': return 'status-pending';
case 'approved': return 'status-approved';
case 'rejected': return 'status-rejected';
case 'cc_sent': return 'status-cc';
default: return '';
}
};
//
const formatTime = (time: string | Date) => {
if (!time) return '';
return dayjs(time).format('YYYY-MM-DD HH:mm');
};
// qjId
watch(() => props.qjId, (newVal) => {
if (newVal) {
loadApprovalProcess();
}
});
//
if (props.qjId) {
loadApprovalProcess();
}
</script>
<style lang="scss" scoped>
.approval-progress {
margin: 15px;
background-color: #fff;
border-radius: 8px;
padding: 15px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
.progress-title {
font-size: 16px;
font-weight: bold;
color: #333;
margin-bottom: 10px;
.applicant-name {
font-size: 16px;
font-weight: bold;
color: #333;
}
}
.divider {
height: 1px;
background-color: #eee;
margin-bottom: 15px;
}
.progress-list {
display: flex;
flex-direction: column;
.progress-item {
position: relative;
.progress-item-row {
display: flex;
align-items: center;
padding: 10px 0;
.item-avatar {
width: 40px;
height: 40px;
border-radius: 50%;
overflow: hidden;
margin-right: 12px;
flex-shrink: 0;
}
.item-middle {
flex: 1;
display: flex;
flex-direction: column;
.item-name {
font-size: 14px;
color: #333;
font-weight: 500;
margin-bottom: 4px;
}
.item-detail {
font-size: 12px;
color: #999;
}
}
.item-right {
display: flex;
flex-direction: column;
align-items: flex-end;
.item-time {
font-size: 12px;
color: #999;
margin-bottom: 4px;
}
.item-status {
font-size: 12px;
padding: 2px 8px;
border-radius: 10px;
&.status-pending {
background-color: #fff7e6;
color: #fa8c16;
}
&.status-approved {
background-color: #f6ffed;
color: #52c41a;
}
&.status-rejected {
background-color: #fff2f0;
color: #ff4d4f;
}
&.status-cc {
background-color: #f0f5ff;
color: #1890ff;
}
}
}
}
.progress-item-line {
height: 20px;
width: 2px;
background-color: #e8e8e8;
margin-left: 19px;
margin-top: -10px;
margin-bottom: -10px;
}
}
}
}
</style>

View File

@ -0,0 +1,187 @@
<template>
<BasicLayout>
<view class="qj-detail">
<!-- 请假信息卡片 -->
<view class="info-card">
<view class="card-header">
<text class="applicant-name">{{ qjData.xsxm }}提交的学生请假</text>
</view>
<view class="divider"></view>
<view class="card-body">
<view class="info-row">
<text class="label">学生姓名:</text>
<text class="value">{{ qjData.xsxm }}</text>
</view>
<view class="info-row">
<text class="label">家长姓名:</text>
<text class="value">{{ qjData.jzxm }}</text>
</view>
<view class="info-row">
<text class="label">请假类型:</text>
<text class="value">{{ qjData.qjlx }}</text>
</view>
<view class="info-row">
<text class="label">开始时间:</text>
<text class="value">{{ qjData.qjkstime }}</text>
</view>
<view class="info-row">
<text class="label">结束时间:</text>
<text class="value">{{ qjData.qjjstime }}</text>
</view>
<view class="info-row">
<text class="label">请假时长:</text>
<text class="value">{{ qjData.qjsc }}</text>
</view>
<view class="info-row">
<text class="label">是否离校:</text>
<text class="value">{{ qjData.sflx === 1 ? '是' : '否' }}</text>
</view>
<view class="info-column">
<text class="label">请假事由:</text>
<text class="value">{{ qjData.qjsy }}</text>
</view>
<view class="info-row">
<text class="label">申请时间:</text>
<text class="value">{{ qjData.createdTime }}</text>
</view>
</view>
</view>
<!-- 审批流程 -->
<ProgressList :qjId="qjId" />
</view>
<template #bottom>
<view class="white-bg-color py-5">
<view class="flex-row items-center pb-10 pt-5">
<u-button
text="返回首页"
class="ml-15 mr-7"
:plain="true"
@click="goHome"
/>
</view>
</view>
</template>
</BasicLayout>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { useDataStore } from "@/store/modules/data";
import { getXsQjDetailApi } from "@/api/base/server";
import ProgressList from "./components/progressList.vue";
const { getData } = useDataStore();
// URLID
const qjId = getData.id || '';
//
const qjData = ref<any>({});
//
const loadQjDetail = async () => {
if (!qjId) {
console.error('请假ID不能为空');
return;
}
try {
const res = await getXsQjDetailApi(qjId);
if (res.resultCode === 1 && res.result) {
qjData.value = res.result;
}
} catch (error) {
console.error('获取请假详情失败:', error);
}
};
const goHome = () => {
uni.reLaunch({ url: '/pages/index/index' });
};
//
const navigateBack = () => {
uni.navigateBack();
};
//
onMounted(() => {
loadQjDetail();
});
</script>
<style lang="scss" scoped>
.qj-detail {
background-color: #f5f7fa;
}
.info-card {
margin: 15px;
background-color: #fff;
border-radius: 8px;
padding: 15px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
.card-header {
font-size: 16px;
font-weight: bold;
color: #333;
margin-bottom: 10px;
.applicant-name {
font-size: 16px;
font-weight: bold;
color: #333;
}
}
.divider {
height: 1px;
background-color: #eee;
margin-bottom: 15px;
}
.card-body {
padding: 15px;
.info-row {
display: flex;
margin-bottom: 10px;
.label {
font-size: 14px;
color: #bbb;
width: 70px;
flex-shrink: 0;
margin-right: 8px;
}
.value {
font-size: 14px;
color: #333;
flex: 1;
}
}
.info-column {
display: flex;
flex-direction: column;
.label {
font-size: 14px;
color: #bbb;
flex-shrink: 0;
margin-right: 8px;
width: 100%;
margin-bottom: 5px;
}
.value {
font-size: 14px;
color: #333;
flex: 1;
}
}
}
}
</style>

View File

@ -4,7 +4,7 @@
<!-- 请假信息卡片 -->
<view class="info-card">
<view class="card-header">
<text class="applicant-name" v-if="dbFlag">{{ xxtsData.dbZy }}</text>
<text class="applicant-name" v-if="dbFlag">{{ xxtsData.xxzy }}</text>
<text class="applicant-name" v-else>学生{{ qjData.xsxm }}的请假申请</text>
</view>
<view class="divider"></view>
@ -35,13 +35,16 @@
</view>
</view>
</view>
<!-- 请假信息卡片 -->
<!-- 审批意见卡片 -->
<view class="info-card">
<view class="card-header">
<text class="applicant-name">审批意见</text>
</view>
<view class="divider"></view>
<BasicForm @register="register" />
<view class="card-body">
<BasicForm @register="register" />
</view>
</view>
</view>
@ -75,16 +78,20 @@ import { useUserStore } from "@/store/modules/user";
import { useDataStore } from "@/store/modules/data";
import { ref, nextTick } from "vue";
import { xxtsFindByIdApi, xxtsBlApi } from "@/api/base/server";
import ProgressList from "./components/progressList.vue";
const { getJs, loginByOpenId } = useUserStore();
const { getData, setXxts, setData, getXxts } = useDataStore();
const dbFlag = ref(false);
// URLID
const qjId = getData.id || '';
const [register, { getValue }] = useForm({
schema: [
{
field: "flag",
field: "spStatus",
label: "审批意见",
component: "BasicCheckbox",
required: true,
@ -93,13 +100,13 @@ const [register, { getValue }] = useForm({
},
componentProps: {
data: [
{ value: 2, text: "同意" },
{ value: 1, text: "拒绝" },
{ value: 'approved', text: "同意" },
{ value: 'rejected', text: "拒绝" },
],
},
},
{
field: "comment",
field: "spYj",
label: "审批说明",
component: "BasicInput",
required: true,
@ -108,6 +115,7 @@ const [register, { getValue }] = useForm({
},
componentProps: {
type: "textarea",
placeholder: "请输入审批说明",
},
},
],
@ -115,25 +123,50 @@ const [register, { getValue }] = useForm({
//
const qjData = computed(() => getData || {});
const xxtsData = computed(() => getXxts || {});
const xxtsData = ref<any>({})
const submit = async () => {
const params = {
qjId: qjData.value.id,
jsId: getJs.id,
spStatus: 2,
spYj: "同意",
};
uni.showLoading({
title: "提交中...",
});
const res = await xsQjSpApi(params);
//
if (dbFlag.value) {
await xxtsBlApi({ id: xxtsData.value.id });
try {
const formData = await getValue();
if (!formData.spStatus || !formData.spYj) {
uni.showToast({
title: '请填写完整的审批信息',
icon: 'none'
});
return;
}
const params = {
qjId: qjData.value.id,
jsId: getJs.id,
spStatus: formData.spStatus,
spYj: formData.spYj,
};
uni.showLoading({
title: "提交中...",
});
const res = await xsQjSpApi(params);
uni.hideLoading();
uni.showToast({
title: '审批提交成功',
icon: 'success'
});
setTimeout(() => {
navigateBack();
}, 1500);
} catch (error) {
uni.hideLoading();
uni.showToast({
title: '提交失败,请重试',
icon: 'none'
});
console.error('审批提交失败:', error);
}
uni.hideLoading();
navigateBack();
};
onLoad(async (data: any) => {
@ -153,7 +186,7 @@ onLoad(async (data: any) => {
const xxtsRes = await xxtsFindByIdApi({ id: data.id });
if (xxtsRes && xxtsRes.result) {
const xxts = xxtsRes.result;
xxtsData.value = xxts;
//
if (xxts.dbZt === "B") {
setData({ id: xxts.xxzbId });
@ -172,7 +205,7 @@ onLoad(async (data: any) => {
} catch (error) {
console.error("获取待办信息失败", error);
// Xxts退
const xxtsData = getXxts();
const xxtsData = getXxts;
if (xxtsData && xxtsData.dbZt === "B") {
setData({ id: data.id });
let url = "/pages/base/xs/qj/detail";
@ -222,7 +255,6 @@ onLoad(async (data: any) => {
margin-bottom: 15px;
}
.card-body {
padding: 15px;
@ -244,6 +276,7 @@ onLoad(async (data: any) => {
flex: 1;
}
}
.info-column {
display: flex;
flex-direction: column;
@ -264,7 +297,6 @@ onLoad(async (data: any) => {
}
}
}
}
.bottom-action {