调整教师请假的处理逻辑

This commit is contained in:
ywyonui 2025-09-22 18:11:59 +08:00
parent 4648a0793a
commit cf832cc3d3
17 changed files with 729 additions and 371 deletions

View File

@ -91,13 +91,6 @@ export const findQjListApi = async (params: any) => {
return await get("/api/jsQj/findPage", params);
};
/**
* ID获取默认审批人和抄送人
*/
export const getApproversByRuleId = async (ruleId: string) => {
return await get("/api/jsQj/getApproversByRuleId", { ruleId });
};
/**
* ID获取请假详情
*/

View File

@ -2,6 +2,14 @@ import { get, post } from "@/utils/request";
// 流程审批相关API接口
/**
* ID获取默认审批人和抄送人
*/
export const getSprAndCsrByRuleId = async (ruleId: string) => {
return await get("/api/lcglSet/getSprAndCsrByRuleId", { ruleId });
};
/**
* ID和业务类型获取审批流程
* @param ywId ID

View File

@ -3,19 +3,11 @@
<!-- 审批人列表 -->
<view class="section">
<view class="section-header">
<text class="section-title">审批人</text>
<text class="section-title">审批人{{ approvers.length || 0 }}</text>
<!-- 审批人选择器 -->
<BasicJsPicker
ref="approverPickerRef"
:customTrigger="true"
:multiple="true"
:excludeIds="getExcludeApproverIds()"
:defaultValue="[]"
title="选择审批人"
placeholder="请选择审批人"
searchPlaceholder="搜索审批人"
@change="handleApproverChange"
>
<BasicJsPicker ref="approverPickerRef" :customTrigger="true" :multiple="true"
:excludeIds="getExcludeApproverIds()" :defaultValue="[]" title="选择审批人" placeholder="请选择审批人"
searchPlaceholder="搜索审批人" @change="handleApproverChange">
<template #trigger>
<view class="add-btn">
<text class="add-icon">+</text>
@ -29,36 +21,19 @@
<view v-for="(spr, index) in approvers" :key="spr.id" class="list-item">
<view class="item-info">
<text class="item-name">{{ spr.jsxm }}</text>
</view>
<view class="item-actions">
</view>
<view class="item-actions" v-if="!spr.fixed">
<!-- 上移箭头 -->
<uni-icons
v-if="index > 0"
type="top"
size="18"
color="#666"
@click="moveApproverUp(index)"
class="action-icon"
></uni-icons>
<uni-icons v-if="index > configSprList.length" type="top" size="18" color="#666" @click="moveApproverUp(index)"
class="action-icon"></uni-icons>
<!-- 下移箭头 -->
<uni-icons
v-if="index < approvers.length - 1"
type="bottom"
size="18"
color="#666"
@click="moveApproverDown(index)"
class="action-icon"
></uni-icons>
<uni-icons v-if="index < approvers.length - 1" type="bottom" size="18" color="#666"
@click="moveApproverDown(index)" class="action-icon"></uni-icons>
<!-- 删除图标改为垃圾桶图标 -->
<uni-icons
type="trash"
size="18"
color="#666"
@click="removeApprover(index)"
class="action-icon"
></uni-icons>
<uni-icons type="trash" size="18" color="#666" @click="removeApprover(index)"
class="action-icon"></uni-icons>
</view>
</view>
@ -71,19 +46,10 @@
<!-- 抄送人列表 -->
<view class="section">
<view class="section-header">
<text class="section-title">抄送人</text>
<text class="section-title">抄送人{{ ccList.length || 0 }}</text>
<!-- 抄送人选择器 -->
<BasicJsPicker
ref="ccPickerRef"
:customTrigger="true"
:multiple="true"
:excludeIds="getExcludeCcIds()"
:defaultValue="[]"
title="选择抄送人"
placeholder="请选择抄送人"
searchPlaceholder="搜索抄送人"
@change="handleCcChange"
>
<BasicJsPicker ref="ccPickerRef" :customTrigger="true" :multiple="true" :excludeIds="getExcludeCcIds()"
:defaultValue="[]" title="选择抄送人" placeholder="请选择抄送人" searchPlaceholder="搜索抄送人" @change="handleCcChange">
<template #trigger>
<view class="add-btn">
<text class="add-icon">+</text>
@ -94,15 +60,21 @@
</view>
<view class="list-content">
<view v-for="(csr, index) in ccList" :key="csr.id" class="list-item">
<view v-for="(csr, index) in displayedCsrList" :key="csr.id" class="list-item">
<view class="item-info">
<text class="item-name">{{ csr.jsxm }}</text>
</view>
<view class="item-actions">
<view class="item-actions" v-if="!csr.fixed">
<text class="delete-btn" @click="removeCc(index)">删除</text>
</view>
</view>
<!-- 更多按钮 -->
<view v-if="ccList.length > 2" class="more-button" @click="toggleCsrExpanded">
<text class="more-text">
{{ csrExpanded ? '收起' : `更多(${ccList.length - 2})` }}
</text>
<text class="more-icon" :class="{ expanded: csrExpanded }"></text>
</view>
<view v-if="ccList.length === 0" class="empty-tip">
<text>暂无抄送人请添加</text>
</view>
@ -113,14 +85,10 @@
<script lang="ts" setup>
import { ref, watch, onMounted } from "vue";
import { getApproversByRuleId } from "@/api/base/jsQjApi";
import { getByYwIdAndYwTypeApi } from "@/api/base/lcglSpApi";
import { getSprAndCsrByRuleId, getByYwIdAndYwTypeApi } from "@/api/base/lcglSpApi";
import { useCommonStore } from "@/store/modules/common";
import BasicJsPicker from "@/components/BasicJsPicker/Picker.vue";
// uni
declare const uni: any;
// JsVo
interface JsInfo {
id: string;
@ -130,27 +98,43 @@ interface JsInfo {
headPic?: string;
userId?: number;
deptId?: string;
fixed?: boolean; //
}
//
const normalizeTeacherData = (item: any): JsInfo => {
const normalizeTeacherData = (item: any, fixed: boolean): JsInfo => {
return {
id: item.id || item.value,
jsxm: item.jsxm || item.label,
id: item.userId || item.id || item.value,
jsxm: item.userName || item.jsxm || item.label,
lxdh: item.lxdh,
sfzh: item.sfzh,
headPic: item.headPic,
userId: item.userId,
deptId: item.deptId,
fixed: fixed,
};
};
// ID
const uniqueById = (array: any[]) => {
return array.filter((item, index, self) =>
index === self.findIndex(t => t.id === item.id)
);
};
// userId
const uniqueByUserId = (array: any[]) => {
return array.filter((item, index, self) =>
index === self.findIndex(t => t.userId === item.userId)
);
};
//
const props = withDefaults(
defineProps<{
defaultValue?: any;
ruleId?: string;
qjId?: string; // ID
ywId?: string; // ID
}>(),
{
defaultValue: () => ({
@ -158,7 +142,7 @@ const props = withDefaults(
csrList: [],
}),
ruleId: "",
qjId: "",
ywId: "",
}
);
@ -169,6 +153,26 @@ const emit = defineEmits(["change"]);
const approvers = ref<JsInfo[]>([]);
const ccList = ref<JsInfo[]>([]);
//
const configSprList = ref<any>([]);
const configCsrList = ref<any>([]);
//
const csrExpanded = ref(false);
//
const displayedCsrList = computed(() => {
if (csrExpanded.value || ccList.value.length <= 2) {
return ccList.value;
}
return ccList.value.slice(0, 2);
});
//
const toggleCsrExpanded = () => {
csrExpanded.value = !csrExpanded.value;
};
//
const approverPickerRef = ref();
const ccPickerRef = ref();
@ -176,66 +180,18 @@ const ccPickerRef = ref();
// common store
const commonStore = useCommonStore();
//
const initData = () => {
if (props.defaultValue) {
if (
props.defaultValue.sprList &&
Array.isArray(props.defaultValue.sprList)
) {
// 使
approvers.value = props.defaultValue.sprList.map(normalizeTeacherData);
}
if (
props.defaultValue.csrList &&
Array.isArray(props.defaultValue.csrList)
) {
// 使
ccList.value = props.defaultValue.csrList.map(normalizeTeacherData);
}
}
};
//
watch(
() => props.defaultValue,
() => {
initData();
},
{ immediate: true, deep: true }
);
//
const loadDefaultApprovers = async () => {
try {
// IDLcglSp
if (props.qjId) {
await loadExistingApprovers();
} else if (props.ruleId) {
// LcglSet
await loadDefaultFromLcglSet();
}
} catch (error) {
console.error("获取默认审批人失败:", error);
uni.showToast({
title: "获取默认审批人失败",
icon: "none",
});
}
};
// LcglSp使
const loadExistingApprovers = async () => {
try {
const response = await getByYwIdAndYwTypeApi(props.qjId, "JS_QJ");
const response = await getByYwIdAndYwTypeApi(props.ywId, "JS_QJ");
if (
response &&
response.resultCode === 1 &&
response.result &&
Array.isArray(response.result)
) {
const spList = response.result;
let spList = response.result;
//
let approverList: any = [];
let ccListData: any = [];
@ -250,14 +206,27 @@ const loadExistingApprovers = async () => {
}
}
}
// userId
approverList = uniqueByUserId(approverList);
ccListData = uniqueByUserId(ccListData);
let changeFlag = false;
// 使
if (approvers.value.length === 0 && approverList.length > 0) {
approvers.value = approverList.map(normalizeTeacherData);
approvers.value = approverList.map((item:any) => {
// config
const f = configSprList.value.filter((config:any) => config.userId === item.userId);
return normalizeTeacherData(item, f && f.length > 0);
});
changeFlag = true;
}
if (ccList.value.length === 0 && ccListData.length > 0) {
ccList.value = ccListData.map(normalizeTeacherData);
ccList.value = ccListData.map((item:any) => {
// config
const f = configCsrList.value.filter((config:any) => config.userId === item.userId);
return normalizeTeacherData(item, f && f.length > 0);
});
changeFlag = true;
}
if (changeFlag) {
@ -272,30 +241,11 @@ const loadExistingApprovers = async () => {
// LcglSet
const loadDefaultFromLcglSet = async () => {
try {
const response = await getApproversByRuleId(props.ruleId);
const response = await getSprAndCsrByRuleId(props.ruleId);
if (response && response.resultCode === 1 && response.result) {
const data = response.result;
let changeFlag = false;
// 使
if (
approvers.value.length === 0 &&
data.approvers &&
Array.isArray(data.approvers)
) {
approvers.value = data.approvers.map(normalizeTeacherData);
changeFlag = true;
}
if (
ccList.value.length === 0 &&
data.ccList &&
Array.isArray(data.ccList)
) {
ccList.value = data.ccList.map(normalizeTeacherData);
changeFlag = true;
}
if (changeFlag) {
notifyChange();
}
configSprList.value = data.sprList;
configCsrList.value = data.csrList;
}
} catch (error) {
console.error("获取默认审批人失败:", error);
@ -336,8 +286,16 @@ const moveApproverDown = (index: number) => {
const handleApproverChange = (selectedTeachers: any[]) => {
if (Array.isArray(selectedTeachers)) {
// 使
const normalizedTeachers = selectedTeachers.map(normalizeTeacherData);
const normalizedTeachers = selectedTeachers.map((item:any) => {
// config
const f = configSprList.value.filter((config:any) => config.userId === item.userId);
return normalizeTeacherData(item, f && f.length > 0);
});
approvers.value.push(...normalizedTeachers);
// ID
approvers.value = uniqueById(approvers.value);
notifyChange();
uni.showToast({
title: "审批人设置成功",
@ -350,8 +308,16 @@ const handleApproverChange = (selectedTeachers: any[]) => {
const handleCcChange = (selectedTeachers: any[]) => {
if (Array.isArray(selectedTeachers)) {
// 使
const normalizedTeachers = selectedTeachers.map(normalizeTeacherData);
const normalizedTeachers = selectedTeachers.map((item:any) => {
// config
const f = configCsrList.value.filter((config:any) => config.userId === item.userId);
return normalizeTeacherData(item, f && f.length > 0);
});
ccList.value.push(...normalizedTeachers);
// ID
ccList.value = uniqueById(ccList.value);
notifyChange();
uni.showToast({
title: "抄送人设置成功",
@ -409,11 +375,19 @@ const getExcludeCcIds = () => {
//
onMounted(async () => {
try {
initData();
await loadDefaultApprovers();
//
await commonStore.getAllJsBasicInfoVo();
//
await loadDefaultFromLcglSet();
//
if (!props.ywId) {
approvers.value = configSprList.value.map((item:any) => normalizeTeacherData(item, true));
ccList.value = configCsrList.value.map((item:any) => normalizeTeacherData(item, true));
} else {
loadExistingApprovers();
}
//
console.log("SprList组件初始化成功");
} catch (error) {
console.error("SprList组件初始化失败:", error);
}
@ -520,7 +494,43 @@ onMounted(async () => {
border-radius: 12rpx;
border: 2rpx dashed #dee2e6;
}
.more-button {
display: flex;
align-items: center;
justify-content: center;
padding: 16rpx 32rpx;
margin-top: 20rpx;
margin-bottom: 40rpx;
background: #f8f9fa;
border: 2rpx solid #e9ecef;
border-radius: 12rpx;
cursor: pointer;
transition: all 0.3s ease;
&:active {
background: #e9ecef;
transform: translateY(1px);
}
.more-text {
font-size: 28rpx;
color: #007aff;
margin-right: 12rpx;
}
.more-icon {
font-size: 24rpx;
color: #007aff;
transition: transform 0.3s ease;
&.expanded {
transform: rotate(180deg);
}
}
}
}
}
}
</style>
</style>

View File

@ -28,7 +28,7 @@
</view>
<!-- 审批人 -->
<view class="info-section spr" v-if="sprSpList.length > 0">
<view class="section-title">审批人</view>
<view class="section-title">审批人{{ sprSpList.length || 0 }}</view>
<view class="sp-list">
<view v-for="spr in sprSpList" :key="spr.id" class="sp-item">
<view class="sp-info">
@ -54,14 +54,14 @@
</view>
<!-- 抄送人 -->
<view class="info-section csr" v-if="csrSpList.length > 0">
<view class="section-title">抄送人</view>
<view class="section-title">抄送人{{ csrSpList.length || 0 }}</view>
<view class="sp-list">
<view v-for="csr in displayedcsrList" :key="csr.id" class="sp-item">
<view v-for="csr in displayedCsrList" :key="csr.id" class="sp-item">
<view class="sp-info">
<text class="name">{{ csr.userName }}</text>
<text class="dept">{{ csr.deptName }}</text>
<text class="status" :class="getSprStatusClass(csr.approveStatus)">
{{ getSprStatusText(csr.approveStatus) }}
<text class="status" :class="getCsrStatusClass(csr.approveStatus)">
{{ getCsrStatusText(csr.approveStatus) }}
</text>
</view>
</view>
@ -76,7 +76,7 @@
</view>
<!-- 操作记录 -->
<view class="info-section log" v-if="logList.length > 0">
<view class="section-title">操作记录</view>
<view class="section-title">操作记录{{ logList.length || 0 }}</view>
<view class="sp-list">
<view v-for="log in displayedLogList" :key="log.id" class="sp-item">
<view class="log-header">
@ -153,7 +153,7 @@ const csrExpanded = ref(false);
const logExpanded = ref(false);
//
const displayedcsrList = computed(() => {
const displayedCsrList = computed(() => {
if (csrExpanded.value || csrSpList.value.length <= 2) {
return csrSpList.value;
}
@ -232,8 +232,11 @@ const getSprStatusText = (status: any) => {
//
const getCsrStatusClass = (status: any) => {
const statusMap: Record<string, string> = {
pending: "status-pending",
approved: "status-approved",
unread: "status-unread",
read: "status-read",
cc_sent: "status-approved",
};
return statusMap[status] || "status-default";
};
@ -241,6 +244,9 @@ const getCsrStatusClass = (status: any) => {
//
const getCsrStatusText = (status: any) => {
const statusMap: Record<string, string> = {
pending: "待抄送",
approved: "已抄送",
cc_sent: "已抄送",
unread: "未读",
read: "已读",
};

View File

@ -3,7 +3,7 @@
<!-- 原始审批人 -->
<view class="section">
<view class="section-header">
<text class="section-title">原审批人</text>
<text class="section-title">原审批人{{ srcSprList.length || 0 }}</text>
</view>
<view class="list-content">
<view v-for="(spr, index) in srcSprList" :key="index" class="list-item readonly">
@ -26,10 +26,10 @@
<!-- 原始抄送人 -->
<view class="section">
<view class="section-header">
<text class="section-title">原抄送人</text>
<text class="section-title">原抄送人{{ srcCsrList.length || 0 }}</text>
</view>
<view class="list-content">
<view v-for="(csr, index) in srcCsrList" :key="index" class="list-item readonly">
<view v-for="(csr, index) in displayedSrcCsrList" :key="index" class="list-item readonly">
<view class="item-info">
<text class="item-name">{{ csr.jsxm }}</text>
</view>
@ -38,29 +38,29 @@
<view v-if="srcCsrList.length === 0" class="empty-tip">
<text>暂无原抄送人</text>
</view>
<!-- 更多按钮 -->
<view v-if="srcCsrList.length > 2" class="more-button" @click="toggleSrcCsrExpanded">
<text class="more-text">
{{ srcCsrExpanded ? '收起' : `更多(${srcCsrList.length - 2})` }}
</text>
<text class="more-icon" :class="{ expanded: srcCsrExpanded }"></text>
</view>
</view>
</view>
<!-- 审批人列表 -->
<view class="section">
<view class="section-header">
<text class="section-title">审批转办人</text>
<text class="section-title">{{ newSprTitle }}</text>
<!-- 审批人选择器 -->
<BasicJsPicker
ref="approverPickerRef"
:customTrigger="true"
:multiple="true"
:excludeIds="getExcludeApproverIds()"
:defaultValue="approvers"
title="选择审批转办人"
placeholder="请选择审批转办人"
searchPlaceholder="搜索审批转办人"
@change="handleApproverChange"
>
<BasicJsPicker ref="approverPickerRef" :customTrigger="true" :multiple="true"
:excludeIds="getExcludeApproverIds()" :defaultValue="approvers" title="选择审批人" placeholder="请选择审批转办人"
searchPlaceholder="搜索审批人" @change="handleApproverChange">
<template #trigger>
<view class="add-btn">
<text class="add-icon">+</text>
<text class="add-text">添加审批转办人</text>
<text class="add-text">添加{{ newSprTitle }}</text>
</view>
</template>
</BasicJsPicker>
@ -73,33 +73,16 @@
</view>
<view class="item-actions">
<!-- 上移箭头 -->
<uni-icons
v-if="index > 0"
type="top"
size="18"
color="#666"
@click="moveApproverUp(index)"
class="action-icon"
></uni-icons>
<uni-icons v-if="index > 0" type="top" size="18" color="#666" @click="moveApproverUp(index)"
class="action-icon"></uni-icons>
<!-- 下移箭头 -->
<uni-icons
v-if="index < approvers.length - 1"
type="bottom"
size="18"
color="#666"
@click="moveApproverDown(index)"
class="action-icon"
></uni-icons>
<uni-icons v-if="index < approvers.length - 1" type="bottom" size="18" color="#666"
@click="moveApproverDown(index)" class="action-icon"></uni-icons>
<!-- 删除图标改为垃圾桶图标 -->
<uni-icons
type="trash"
size="18"
color="#666"
@click="removeApprover(index)"
class="action-icon"
></uni-icons>
<uni-icons type="trash" size="18" color="#666" @click="removeApprover(index)"
class="action-icon"></uni-icons>
</view>
</view>
@ -112,23 +95,14 @@
<!-- 抄送人列表 -->
<view class="section">
<view class="section-header">
<text class="section-title">新增抄送人</text>
<text class="section-title">{{ newCsrTitle }}</text>
<!-- 抄送人选择器 -->
<BasicJsPicker
ref="ccPickerRef"
:customTrigger="true"
:multiple="true"
:excludeIds="getExcludeCcIds()"
:defaultValue="ccList"
title="选择抄送人"
placeholder="请选择抄送人"
searchPlaceholder="搜索抄送人"
@change="handleCcChange"
>
<BasicJsPicker ref="ccPickerRef" :customTrigger="true" :multiple="true" :excludeIds="getExcludeCcIds()"
:defaultValue="ccList" title="选择抄送人" placeholder="请选择抄送人" searchPlaceholder="搜索抄送人" @change="handleCcChange">
<template #trigger>
<view class="add-btn">
<text class="add-icon">+</text>
<text class="add-text">添加抄送人</text>
<text class="add-text">添加{{ newCsrTitle }}</text>
</view>
</template>
</BasicJsPicker>
@ -159,6 +133,15 @@ import { useDataStore } from "@/store/modules/data";
import BasicJsPicker from "@/components/BasicJsPicker/Picker.vue";
const { getLcgl } = useDataStore();
//
const props = withDefaults(defineProps<{
newSprTitle?: string;
newCsrTitle?: string;
}>(), {
newSprTitle: "审批转办人",
newCsrTitle: "新抄送人"
});
// JsVo
interface JsInfo {
id: string;
@ -207,6 +190,23 @@ const ccPickerRef = ref();
// common store
const commonStore = useCommonStore();
//
const srcCsrExpanded = ref(false);
//
const displayedSrcCsrList = computed(() => {
if (srcCsrExpanded.value || srcCsrList.value.length <= 2) {
return srcCsrList.value;
}
return srcCsrList.value.slice(0, 2);
});
//
const toggleSrcCsrExpanded = () => {
srcCsrExpanded.value = !srcCsrExpanded.value;
};
//
const getSprStatusClass = (status: any) => {
const statusMap: Record<string, string> = {
@ -238,19 +238,19 @@ const getSprStatusText = (status: any) => {
const initData = (jsList: any) => {
const srcSprSpList = getLcgl.sprSpList || [];
const srcCsrSpList = getLcgl.csrSpList || [];
//
srcSprList.value = srcSprSpList.map((sp: any) => {
const js = jsList.find((js: any) => js.userId === sp.userId);
return js ? normalizeTeacherData(js, sp) : {};
});
//
srcCsrList.value = srcCsrSpList.map((sp: any) => {
const js = jsList.find((js: any) => js.userId === sp.userId);
return js ? normalizeTeacherData(js, sp) : {};
});
// spListuserId, jsListuserId
approvers.value = [];
ccList.value = [];
@ -375,11 +375,11 @@ onMounted(async () => {
}
});
const getNewSprList = () => {
const getNewSprList = () => {
return approvers.value;
};
const getNewCsrList = () => {
const getNewCsrList = () => {
return ccList.value;
};
@ -494,22 +494,22 @@ defineExpose({
background-color: #f9f9f9;
color: #8c8c8c;
}
&.status-skipped {
background-color: #f0f5ff;
color: #2f54eb;
}
&.status-stop {
background-color: #fff1f0;
color: #ff4d4f;
}
&.status-transfer {
background-color: #f9f0ff;
color: #722ed1;
}
&.status-default {
background-color: #fafafa;
color: #595959;
@ -521,11 +521,11 @@ defineExpose({
display: flex;
align-items: center;
gap: 16rpx;
.action-icon {
padding: 6rpx;
border-radius: 4rpx;
&:active {
background-color: #eee;
}
@ -555,6 +555,42 @@ defineExpose({
border-radius: 12rpx;
border: 2rpx dashed #ebedf0;
}
.more-button {
display: flex;
align-items: center;
justify-content: center;
padding: 16rpx 32rpx;
margin-top: 20rpx;
margin-bottom: 40rpx;
background: #f8f9fa;
border: 2rpx solid #e9ecef;
border-radius: 12rpx;
cursor: pointer;
transition: all 0.3s ease;
&:active {
background: #e9ecef;
transform: translateY(1px);
}
.more-text {
font-size: 28rpx;
color: #007aff;
margin-right: 12rpx;
}
.more-icon {
font-size: 24rpx;
color: #007aff;
transition: transform 0.3s ease;
&.expanded {
transform: rotate(180deg);
}
}
}
}
}
}

View File

@ -113,7 +113,7 @@ onLoad(async (data: any) => {
const xsQj = res.result || {};
if (xsQj.spResult != "A" && getXxts && getXxts.dbZt === "A") {
uni.reLaunch({ url: '/pages/base/xs/qj/detail' });
// const flag = await XkTfPageUtils.updateXxts();
const flag = await XkTfPageUtils.updateXxts();
} else {
nextTick(() => {
setData(xsQj);

View File

@ -0,0 +1,176 @@
<template>
<u-popup :show="dlgFlag" mode="bottom" width="calc(100% - 60rpx)" :closeOnClickOverlay="false" @close="closeDlg"
class="transfer-popup">
<view class="popup-content">
<view class="popup-header">
<view class="popup-title">转办设置</view>
</view>
<view class="popup-body">
<view class="section">
<view class="section-header">
<text class="section-title">转办原因</text>
</view>
<view class="list-content">
<u-input v-model="spRemark" type="textarea" placeholder="请填写转办原因" :autoHeight="true" maxlength="200"
class="remark-input" />
</view>
</view>
<TransferSpCsMgr ref="transferSpCsMgrRef" />
</view>
<view class="popup-actions flex-row justify-end mt-4">
<u-button class="mr-2" @click="closeDlg">取消</u-button>
<u-button type="primary" @click="submit">确定</u-button>
</view>
</view>
</u-popup>
</template>
<script setup lang="ts">
import { ref } from "vue";
import TransferSpCsMgr from "@/components/TransferSpCsMgr/index.vue"
// emit
const emit = defineEmits(["submit"]);
const transferSpCsMgrRef = ref<any>(null);
const dlgFlag = ref(false);
const spRemark = ref("");
const showDlg = (type: string) => {
dlgFlag.value = true;
};
const closeDlg = () => {
dlgFlag.value = false;
};
//
const submit = () => {
if (!spRemark.value || !spRemark.value.trim()) {
uni.showToast({ title: "请填写转办原因", icon: "none" });
return;
}
const newSprList = transferSpCsMgrRef.value.getNewSprList();
const newCsrList = transferSpCsMgrRef.value.getNewCsrList();
if (!newSprList.length) {
uni.showToast({ title: "请选择审批转办人", icon: "none" });
return;
}
emit('submit', { newSprList, newCsrList, spRemark: spRemark.value });
}
defineExpose({
showDlg,
closeDlg
});
</script>
<style lang="scss" scoped>
.transfer-popup {
width: calc(100% - 60rpx);
max-height: 90vh;
}
.popup-content {
display: flex;
flex-direction: column;
max-height: 90vh;
}
.popup-header {
flex-shrink: 0;
padding: 30rpx;
border-bottom: 1rpx solid #ebedf0;
.popup-title {
font-size: 32rpx;
font-weight: bold;
text-align: center;
color: #262626;
}
}
.popup-body {
flex: 1;
overflow-y: auto;
padding: 30rpx;
max-height: calc(90vh - 200rpx);
.flex-row {
margin-bottom: 30rpx;
.label {
font-size: 28rpx;
color: #262626;
margin-bottom: 16rpx;
}
}
.remark-input {
display: flex;
align-items: flex-start; /* 垂直上对齐 */
justify-content: flex-start; /* 水平居左对齐 */
input {
height: 160rpx;
}
}
}
.popup-actions {
flex-shrink: 0;
padding: 30rpx;
border-top: 1rpx solid #ebedf0;
}
.transfer-form {
background: #fff;
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.section {
margin-bottom: 40rpx;
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
.section-title {
font-size: 32rpx;
font-weight: bold;
color: #262626;
}
}
.list-content {
}
}
.bottom-actions {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: white;
padding: 15px;
border-top: 1px solid #eee;
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.1);
display: flex;
gap: 15px;
z-index: 1000;
}
.bottom-actions .u-button {
flex: 1;
height: 44px;
border-radius: 8px;
font-size: 16px;
font-weight: 500;
}
</style>

View File

@ -7,6 +7,9 @@
<view class="flex-row items-center pb-5">
<u-button text="终止" class="ml-15 mr-7" :plain="true" @click="showDlg('stop')" />
<u-button text="转办" class="mr-15 mr-7" :plain="true" @click="showTransfer" />
</view>
<view class="flex-row items-center pb-5">
<u-button text="协调代课教师" class="mr-15 mr-7" :plain="true" @click="showXtDlg" />
</view>
<!-- 驳回弹窗 -->
<u-popup :show="dlgFlag" mode="center" :closeOnClickOverlay="false" @close="closeDlg">
@ -24,12 +27,13 @@
</view>
</u-popup>
<YwTransfer ref="transferRef" @submit="handleTransfer" />
<XtDkJs ref="xtDkJsRef" @submit="handleXtDkJs" />
</view>
</template>
<script lang="ts" setup>
import YwTransfer from "../YwTransfer/index.vue";
import XtDkJs from "../XtDkJs/index.vue";
//
const props = withDefaults(defineProps<{
@ -43,6 +47,7 @@ const props = withDefaults(defineProps<{
rejectValue?: string, //
approvedRemark?: string //
autoToMessage?: boolean //
showXt?: boolean
}>(), {
spApi: async (params: any) => {},
transferApi: async (params: any) => {},
@ -53,13 +58,15 @@ const props = withDefaults(defineProps<{
approvedValue: 'approved',
rejectValue: 'rejected',
approvedRemark: '同意',
autoToMessage: true
autoToMessage: true,
showXt: false
});
// emit
const emit = defineEmits(['submit', 'reject', 'stop', 'transfer'])
const transferRef = ref<any>(null);
const xtDkJsRef = ref<any>(null);
const dlgFlag = ref(false);
const dlgType = ref("");
@ -90,6 +97,10 @@ const showTransfer = () => {
transferRef.value.showDlg();
};
const showXtDlg = () => {
};
const submit = async () => {
const params = {
...props.params
@ -142,6 +153,26 @@ const handleTransfer = async (data: any) => {
}
};
//
const handleXtDkJs = async (data: any) => {
const params = {
...props.params
};
const newSprList = data.newSprList || [];
const newCsrList = data.newCsrList || [];
params.spRemark = data.spRemark;
params.zbrIds = newSprList.map((item: any) => item.userId).join(",");
params.csrIds = newCsrList.map((item: any) => item.userId).join(",");
uni.showLoading({ title: "正在转办..." });
await props.transferApi(params);
transferRef.value.closeDlg();
uni.hideLoading();
emit('transfer');
if (props.autoToMessage) {
goToMessage();
}
};
const handleDlgSubmit = async () => {
if (!spRemark.value || !spRemark.value.trim()) {
uni.showToast({ title: "请填写" + dlgTips.value, icon: "none" });

View File

@ -19,10 +19,10 @@
<text class="label">代课老师:</text>
<view class="value">{{ item.jsName }}</view>
</view>
<view class="info-row">
<!-- <view class="info-row">
<text class="label">确认状态:</text>
<view class="value">{{ item.statusLabel }}</view>
</view>
</view> -->
</view>
</view>
</view>

View File

@ -72,6 +72,10 @@ watch(
);
const init = async () => {
if (!props.qjId) {
console.log("初始化的qjId为空");
return;
}
const res = await findQjById({ id: props.qjId });
qjData.value = (res && res.result) ? res.result : {};
if (props.dbFlag) {

View File

@ -102,10 +102,12 @@
</template>
<script setup lang="ts">
import { getPkkbByJsRangeTimeApi } from "@/api/base/jsQjApi";
import { getPkkbByJsRangeTimeApi, findDkPageApi } from "@/api/base/jsQjApi";
import BasicJsPicker from "@/components/BasicJsPicker/Picker.vue";
import { useUserStore } from "@/store/modules/user";
import { useCommonStore } from "@/store/modules/common";
const { getJs } = useUserStore();
const { getAllJsBasicInfoVo } = useCommonStore();
//
const props = withDefaults(
@ -114,6 +116,7 @@ const props = withDefaults(
}>(),
{
data: () => ({
qjId: "",
jsId: "",
qjkstime: "", //
qjjstime: "", //
@ -165,6 +168,10 @@ const switchTab = (index: number) => {
};
const getPkkbList = async () => {
//
if (!props.data.qjjstime || !props.data.qjkstime) {
return;
}
const res = await getPkkbByJsRangeTimeApi({
jsId: props.data.jsId,
startTime: props.data.qjkstime,
@ -177,6 +184,7 @@ const getPkkbList = async () => {
srcData[key] = {
dkJsId: item.dkJsId,
dkJsName: item.dkJsName,
userId: item.userId
};
});
const kmMap: any = {};
@ -189,13 +197,14 @@ const getPkkbList = async () => {
if (src) {
item.dkJsId = src.dkJsId;
item.dkJsName = src.dkJsName;
item.userId = src.userId;
} else {
item.dkJsId = "";
item.dkJsName = "";
item.userId = "";
}
kmMap[item.pkId] = kmMap[item.pkId] || {
kmMap[item.pkMc] = kmMap[item.pkMc] || {
pkMc: item.pkMc,
pkId: item.pkId,
};
return item;
});
@ -206,11 +215,13 @@ const getPkkbList = async () => {
const changeJsByTy = (selected: any, item: any) => {
item.dkJsId = selected.value;
item.dkJsName = selected.label;
item.userId = selected.userId;
const newList = dkList.value.map((dk: any) => {
return {
...dk,
dkJsId: item.dkJsId,
dkJsName: item.dkJsName,
userId: item.userId
};
});
dkList.value = newList;
@ -227,10 +238,11 @@ const changeJsByTy = (selected: any, item: any) => {
const changeJsByKm = (selected: any, item: any) => {
item.dkJsId = selected.value;
item.dkJsName = selected.label;
item.userId = selected.userId;
//
const newList = dkList.value.map((dk: any) => {
if (dk.pkId === item.pkId) {
return { ...dk, dkJsId: item.dkJsId, dkJsName: item.dkJsName };
if (dk.pkMc === item.pkMc) {
return { ...dk, dkJsId: item.dkJsId, dkJsName: item.dkJsName, userId: item.userId };
}
return dk;
});
@ -240,6 +252,7 @@ const changeJsByKm = (selected: any, item: any) => {
const changeJs = (selected: any, item: any) => {
item.dkJsId = selected.value;
item.dkJsName = selected.label;
item.userId = selected.userId;
};
const validate = async () => {
@ -262,6 +275,38 @@ const validate = async () => {
return true;
};
const initDkList = async () => {
const resJs = await getAllJsBasicInfoVo();
const jsList = resJs.result || [];
const res: any = await findDkPageApi({
qjId: props.data.qjId,
page: 1,
rows: 1000,
});
const kmMap: any = {};
const rows = (res && (res.rows || res.result || res.data)) || [];
dkList.value = rows.map((item: any) => {
item.dktime = item.dktime.split(" ")[0];
item.jcmc = jsTypeMc[item.jcType] + "第" + item.jc + "节";
const xq: number = item.xq - 1;
item.xqLabel = wdNameList.value[xq];
const js = jsList.find((js: any) => js.id === item.jsId);
if (js) {
item.dkJsId = js.id;
item.dkJsName = js.jsxm;
item.userId = js.userId;
}
kmMap[item.pkMc] = kmMap[item.pkMc] || {
pkMc: item.pkMc,
};
return item;
});
// kmMapvalue
kmDkList.value = Object.values(kmMap);
};
// ref
const getDkList = () => {
return dkList.value;
@ -272,6 +317,7 @@ defineExpose({
getPkkbList,
validate,
getDkList,
initDkList,
});
</script>

View File

@ -7,9 +7,8 @@
</template>
<template #spcs>
<BasicSpCsMgr
ruleId="yfzc_js_qj"
:qjId="formData.id"
:defaultValue="defSpCs"
rule-id="yfzc_js_qj"
:yw-id="formData.id"
@change="onSpCsChange"
/>
</template>
@ -18,7 +17,7 @@
<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="navigateBack" />
<u-button text="取消" class="ml-15 mr-7" :plain="true" @click="goBack" />
<u-button text="提交" class="mr-15 mr-7" type="primary" @click="submit" />
</view>
</view>
@ -29,7 +28,6 @@
<script setup lang="ts">
import JsQjDkEdit from "./jsQjDkEdit.vue"
import BasicSpCsMgr from "@/components/BasicSpCsMgr/index.vue"
import { navigateBack } from "@/utils/uniapp";
import { useForm } from "@/components/BasicForm/hooks/useForm";
import { jsQjSqApi, jsQjCxtjApi } from "@/api/base/jsQjApi";
import { showToast } from "@/utils/uniapp";
@ -62,14 +60,6 @@ let formData = ref<any>({
jsId: getJs.id,
});
const defSpCs = computed(() => {
return {
sprList: formData.value.sprList,
csrList: formData.value.csrList,
}
})
const dkRef = ref<any>(null);
if (typeof props.data.dkfs === "string") {
@ -206,9 +196,13 @@ const updateDk = () => {
});
};
//
setValue(props.data)
updateDk();
const initDk = () => {
nextTick(() => {
if (dkRef.value) {
dkRef.value.initDkList();
}
});
}
const submit = async () => {
const fd = await getValue();
@ -216,9 +210,6 @@ const submit = async () => {
return;
}
const params = { ...fd };
// /
params.sprList = formData.value.sprList || [];
params.csrList = formData.value.csrList || [];
if (fd.dkfs === 0) {
const dkList = dkRef.value.getDkList();
if (!dkList.length) {
@ -243,10 +234,15 @@ const submit = async () => {
params.dkList = [];
}
}
// /
params.sprList = formData.value.sprList || [];
params.csrList = formData.value.csrList || [];
let submitApi = jsQjSqApi;
if (props.data && props.data.id) {
params.id = props.data.id;
submitApi = jsQjCxtjApi
params.id = props.data.id;
params.jsId = getJs.id;
params.jsName = getJs.jsxm;
} else {
params.id = null;
params.jsId = getJs.id;
@ -270,6 +266,23 @@ const onSpCsChange = (payload: any) => {
formData.value.csrList = Array.isArray(payload.csrList) ? payload.csrList : [];
}
};
const goBack = () => {
if (props.data && props.data.id) {
uni.reLaunch({ url: "/pages/base/message/index" });
} else {
uni.reLaunch({ url: "/pages/base/service/index" });
}
};
onMounted(() => {
if (props.data && props.data.id) {
//
setValue(props.data);
initDk();
}
});
</script>
<style lang="scss" scoped>

View File

@ -53,7 +53,10 @@ const [register, { reload }] = useLayout({
//
const goToDetail = (item: any | null) => {
setData(item);
setData({
...item,
from: 'list'
});
let url = '/pages/view/hr/jsQj/detail'; // 使
uni.navigateTo({ url });
};

View File

@ -1,76 +1,117 @@
<template>
<BasicLayout>
<view class="p-15">
<view class="info-card">
<view class="card-header">
<text class="card-title">重新提交请假申请</text>
</view>
<view class="card-content">
<text class="info-text">您的请假申请已被驳回请修改后重新提交</text>
</view>
</view>
<view class="leave-page">
<!-- 选项卡 -->
<BasicTabs
class="leave-tabs"
ref="tabsRef"
:list="tabList"
bar-width="60px"
scroll-count="4"
:current="curTabIndex"
@change="switchTab"
/>
<view class="leave-edit" v-if="curTabIndex === 0">
<!-- 使用请假申请组件 -->
<JsQjEdit :data="qjData" />
<JsQjEdit :data="qjData" v-if="qjData && qjData.id" />
</view>
</BasicLayout>
<view class="leave-list" v-else>
<view class="mt-15">
<LcglSp :yw-id="qjId" yw-type="JS_QJ" />
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { findQjById } from "@/api/base/jsQjApi";
import { onLoad } from "@dcloudio/uni-app";
import { useUserStore } from "@/store/modules/user";
import { useDataStore } from "@/store/modules/data";
import { ref } from "vue";
import JsQjEdit from "./components/jsQjEdit.vue";
import LcglSp from "@/components/LcglSp/index.vue";
import JsQjEdit from "./components/jsQjEdit.vue"
import { QjPageUtils } from "@/utils/qjPageUtils";
import { findQjById } from "@/api/base/jsQjApi";
const qjId = ref<string>();
const { getJs } = useUserStore();
const { getData, getXxts } = useDataStore();
//
const tabList = ref([
{ name: "重新提交", id: "leave-edit" },
{ name: "审批流程", id: "leave-detail" },
])
const curTabIndex = ref(0)
//
const switchTab = (index: number) => {
curTabIndex.value = index
}
const dbFlag = ref(false);
const qjId = ref('');
const qjData = ref<any>({});
//
const loadQjData = async () => {
try {
const result:any = await findQjById({ id: qjId.value });
if (result.code === 1) {
qjData.value = result.data;
}
} catch (error) {
console.error('获取请假信息失败:', error);
}
const spParams = computed(() => {
return {
xxtsId: getXxts.id,
ywId: qjId.value
};
});
const loadData = async () => {
const res = await findQjById({ id: qjId.value });
qjData.value = (res && res.result) ? res.result : {};
qjData.value.dkfs = parseInt(qjData.value.dkfs);
};
onLoad((options:any) => {
if (options.qjId) {
qjId.value = options.qjId;
loadQjData();
onLoad(async (data?: any) => {
const ret = await QjPageUtils.init(data);
if (!ret || !ret.success) {
return;
}
qjId.value = ret.qjId;
dbFlag.value = ret.dbFlag;
loadData();
});
</script>
<style lang="scss" scoped>
.info-card {
background: white;
border-radius: 8px;
margin-bottom: 15px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.card-header {
padding: 15px;
border-bottom: 1px solid #eee;
.leave-page {
flex: 1 0 1px;
display: flex;
flex-direction: column;
height: 100vh;
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
.card-title {
font-size: 16px;
font-weight: bold;
color: #333;
.leave-tabs {
flex: 0 0 45px;
background: #fff;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
z-index: 10;
}
}
.card-content {
padding: 15px;
.info-text {
font-size: 14px;
color: #666;
line-height: 1.5;
.leave-edit,
.leave-list {
flex: 1 0 1px;
position: relative;
overflow-y: auto;
}
.leave-edit {
background: #fff;
margin: 20rpx;
border-radius: 16rpx;
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.1);
}
.leave-list {
background: #fff;
margin: 20rpx;
border-radius: 16rpx;
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.1);
}
}
</style>

View File

@ -1,41 +1,22 @@
<template>
<BasicLayout>
<template #top>
<view>
<BasicTabs
class="detail-tabs"
:list="tabList"
bar-width="60px"
scroll-count="4"
:current="curTabIndex"
@change="switchTab"
/>
</view>
</template>
<view class="qj-detail">
<!-- 请假信息 -->
<view v-show="curTabIndex === 0">
<JsQjDetailInfo
:qjId="qjId"
:dbFlag="dbFlag"
@loadQjData="handleQjDataLoaded"
/>
<view>
<JsQjDetailInfo :qjId="qjId" :dbFlag="dbFlag" @loadQjData="handleQjDataLoaded" />
</view>
<!-- 代课信息 -->
<view v-show="curTabIndex === 1">
<view>
<view v-if="!showDkFlag" class="empty-dk">
<view>{{ showDkEmptyLabel }}</view>
</view>
<JsQjDetailDk v-else
:qjId="qjId"
@loadDkList="handleDkListLoaded"
/>
<JsQjDetailDk v-else :qjId="qjId" @loadDkList="handleDkListLoaded" />
</view>
<!-- 审批流程 -->
<view v-show="curTabIndex === 2">
<LcglSpList :yw-id="qjId" yw-type="JS_QJ" />
<view class="mt-15">
<LcglSp :yw-id="qjId" yw-type="JS_QJ" />
</view>
</view>
<template #bottom>
@ -53,34 +34,27 @@ import { useDataStore } from "@/store/modules/data";
import { ref, computed } from "vue";
import JsQjDetailInfo from "./components/jsQjDetailInfo.vue";
import JsQjDetailDk from "./components/jsQjDetailDk.vue";
import LcglSpList from "@/components/LcglSpList/index.vue";
import LcglSp from "@/components/LcglSp/index.vue";
import { onLoad } from "@dcloudio/uni-app";
import { QjPageUtils } from "@/utils/qjPageUtils";
import { xxtsFindByIdApi } from "@/api/base/xxtsApi";
const { getData, setData } = useDataStore();
const tabList = ref<any>([
{ name: "请假信息", id: "tab-qj" },
{ name: "代课信息", id: "tab-dk" },
{ name: "审批流程", id: "tab-sp" }
]);
const curTabIndex = ref(0);
const { getData, setData, getXxts } = useDataStore();
const dbFlag = ref(false);
const qjId = computed(() => getData.id);
const qjId = ref('');
const showDkFlag = ref(false);
const showDkEmptyLabel = ref('');
// Tab
const switchTab = (index: number) => {
curTabIndex.value = index;
};
const handleQjDataLoaded = (data: any) => {
setData(data);
data = data || {};
const dkfs = typeof(data.dkfs) === "string" ? parseInt(data.dkfs) : (data.dkfs || 2);
const bpmStatus = typeof(data.bpmStatus) === "string" ? parseInt(data.bpmStatus) : (data.bpmStatus || 1);
showDkFlag.value = dkfs === 0 || (dkfs === 1 && bpmStatus > 4);
if (data.spResult != "A" && getXxts && getXxts.dbZt === "A") {
QjPageUtils.updateXxts();
}
setData(data);
const dkfs = typeof (data.dkfs) === "string" ? parseInt(data.dkfs) : (data.dkfs || 2);
const bpmStatus = typeof (data.bpmStatus) === "string" ? parseInt(data.bpmStatus) : (data.bpmStatus || 1);
showDkFlag.value = dkfs === 0 || (dkfs === 1 && bpmStatus > 4);
if (dkfs === 1) {
showDkEmptyLabel.value = "等待教科处协调";
} else if (dkfs === 2) {
@ -93,11 +67,31 @@ const handleDkListLoaded = (list: any[]) => {
};
const goHome = () => {
setData({});
uni.reLaunch({
url: '/pages/base/service/index',
});
};
onLoad(async (data) => {
if (getData && getData.id && (
getData.from === "xxts-B" || getData.from === "list"
)) {
qjId.value = getData.id;
//
return;
}
const ret = await QjPageUtils.init({
...data,
detailFlag: true,
});
if (!ret || !ret.success) {
return;
}
dbFlag.value = ret.dbFlag;
qjId.value = ret.qjId;
});
</script>
<style lang="scss" scoped>

View File

@ -43,12 +43,6 @@ import { QjPageUtils } from "@/utils/qjPageUtils";
const { getJs } = useUserStore();
const { setData, getXxts } = useDataStore();
const tabList = ref<any>([
{ name: "请假信息", id: "tab-qj" },
{ name: "代课信息", id: "tab-dk" },
{ name: "审批流程", id: "tab-sp" }
]);
const curTabIndex = ref(0);
const dbFlag = ref(false);
const qjId = ref('');
@ -62,14 +56,12 @@ const spParams = computed(() => {
};
});
// Tab
const switchTab = (index: number) => {
curTabIndex.value = index;
};
const handleQjDataLoaded = (data: any) => {
setData(data);
data = data || {};
setData(data);
if (data.spResult != "A" && getXxts && getXxts.dbZt === "A") {
uni.reLaunch({ url: '/pages/base/xs/qj/detail' });
}
const dkfs = typeof(data.dkfs) === "string" ? parseInt(data.dkfs) : (data.dkfs || 2);
showDkFlag.value = dkfs === 0;
if (dkfs === 1) {

View File

@ -31,15 +31,19 @@ export const QjPageUtils = {
const xxtsRes = await xxtsFindByIdApi({ id: data.id });
if (xxtsRes && xxtsRes.result) {
const xxts = xxtsRes.result;
setXxts(xxts);
ret.qjId = xxts.xxzbId;
// 检查待办状态
if (xxts.dbZt === "B") {
setData({ id: xxts.xxzbId });
url = "/pages/view/hr/jsQj/detail";
uni.reLaunch({ url });
ret.success = false;
} else {
setXxts(xxts);
ret.qjId = xxts.xxzbId;
// 消息推送状态为B
setData({ id: xxts.xxzbId, from: "xxts-B" });
if (!data.detailFlag) {
url = "/pages/view/hr/jsQj/detail";
uni.reLaunch({ url });
ret.success = false;
} else {
ret.success = true;
}
}
} else {
uni.showToast({
@ -68,6 +72,7 @@ export const QjPageUtils = {
if (!getXxts || !getXxts.id) {
return false;
}
console.log("更新待办状态getXxts", getXxts);
// 更新待办状态
await xxtsSaveApi({
id: getXxts.id,