调整学生请假审批
This commit is contained in:
parent
8fe418f37e
commit
c9ab421a35
@ -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);
|
||||
|
||||
@ -546,6 +546,13 @@
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/base/xs/qj/detail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "学生请假详情",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/view/routine/qd/index",
|
||||
"style": {
|
||||
|
||||
276
src/pages/base/xs/qj/components/progressList.vue
Normal file
276
src/pages/base/xs/qj/components/progressList.vue
Normal 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>
|
||||
187
src/pages/base/xs/qj/detail.vue
Normal file
187
src/pages/base/xs/qj/detail.vue
Normal 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();
|
||||
|
||||
// 从URL参数获取请假ID
|
||||
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>
|
||||
@ -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);
|
||||
|
||||
// 从URL参数获取请假ID
|
||||
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 {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user