调整教师请假逻辑
This commit is contained in:
parent
ef933f954c
commit
68fd30a282
@ -16,6 +16,11 @@ export const jsFindAll = async () => {
|
||||
return await get("/api/js/findAllBasicInfo");
|
||||
};
|
||||
|
||||
// 所有教师基础信息(Vo版本)
|
||||
export const jsFindAllBasicInfoVo = async () => {
|
||||
return await get("/api/js/findAllBasicInfoVo");
|
||||
};
|
||||
|
||||
// 所有职务
|
||||
export const zwFindAllApi = async () => {
|
||||
return await get("/api/zw/findAll");
|
||||
|
||||
@ -4,26 +4,26 @@
|
||||
import { get, post } from "@/utils/request";
|
||||
|
||||
/**
|
||||
* 申请
|
||||
* 教师申请请假
|
||||
*/
|
||||
export const jsQjSqApi = async (params: any) => {
|
||||
return await post("/api/jsQj/sq", params);
|
||||
};
|
||||
|
||||
/**
|
||||
* 审批
|
||||
*/
|
||||
export const jsQjSpApi = async (params: any) => {
|
||||
return await post("/api/jsQj/sp", params);
|
||||
};
|
||||
|
||||
/**
|
||||
* 重新提交
|
||||
* 教师重新提交请假
|
||||
*/
|
||||
export const jsQjCxtjApi = async (params: any) => {
|
||||
return await post("/api/jsQj/cxtj", params);
|
||||
};
|
||||
|
||||
/**
|
||||
* 教师审批请假申请
|
||||
*/
|
||||
export const jsQjSpApi = async (params: any) => {
|
||||
return await post("/api/jsQj/sp", params);
|
||||
};
|
||||
|
||||
/**
|
||||
* 教务处确认
|
||||
*/
|
||||
@ -39,19 +39,12 @@ export const jsQjJwcXtApi = async (params: any) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* 代课确认
|
||||
* 代课教师确认
|
||||
*/
|
||||
export const jsQjDkQrApi = async (params: any) => {
|
||||
return await post("/api/jsQj/dk/qr", params);
|
||||
};
|
||||
|
||||
/**
|
||||
* 转办
|
||||
*/
|
||||
export const jsQjZbApi = async (params: any) => {
|
||||
return await post("/api/jsQj/zb", params);
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询请假信息
|
||||
*/
|
||||
@ -89,8 +82,39 @@ export const findQjListApi = async (params: any) => {
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询请假流程处理历史
|
||||
* 根据规则ID获取默认审批人和抄送人
|
||||
*/
|
||||
export const getQjActivitiHistoryApi = async (params: any) => {
|
||||
return await get("/activiti/history/historicFlow", params);
|
||||
export const getApproversByRuleId = async (ruleId: string) => {
|
||||
return await get("/api/jsQj/getApproversByRuleId", { ruleId });
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据ID获取请假详情
|
||||
*/
|
||||
export const findQjByIdApi = async (params: { id: string }) => {
|
||||
return await get("/api/jsQj/findById", params);
|
||||
};
|
||||
|
||||
// 保留旧的接口方法以兼容现有代码,但标记为废弃
|
||||
/**
|
||||
* @deprecated 使用 jsQjSqApi 替代
|
||||
*/
|
||||
export const jsQjSaveOrUpdateApi = async (params: any) => {
|
||||
return await post("/api/jsQj/sq", params);
|
||||
};
|
||||
|
||||
/**
|
||||
* @deprecated 使用 jsQjSpApi 替代
|
||||
*/
|
||||
export const jsQjApproveApi = async (params: any) => {
|
||||
return await post("/api/jsQj/sp", params);
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取教师请假审批流程
|
||||
* @param ywId 业务ID
|
||||
* @param ywType 业务类型
|
||||
*/
|
||||
export const getJsQjApprovalProcessApi = (ywId: string, ywType: string = 'JS_QJ') => {
|
||||
return get("/api/lcglSp/getByYwIdAndYwType", { ywId, ywType });
|
||||
};
|
||||
|
||||
64
src/api/base/lcglSpApi.ts
Normal file
64
src/api/base/lcglSpApi.ts
Normal file
@ -0,0 +1,64 @@
|
||||
import { get, post } from "@/utils/request";
|
||||
|
||||
// 流程审批相关API接口
|
||||
|
||||
/**
|
||||
* 根据业务ID和业务类型获取审批流程
|
||||
* @param ywId 业务ID
|
||||
* @param ywType 业务类型
|
||||
*/
|
||||
export function getByYwIdAndYwTypeApi(ywId: string, ywType: string) {
|
||||
return get('/api/lcglSp/getByYwIdAndYwType', { ywId, ywType });
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据业务ID、业务类型和审批类型获取审批流程
|
||||
* @param ywId 业务ID
|
||||
* @param ywType 业务类型
|
||||
* @param spType 审批类型
|
||||
*/
|
||||
export function getByYwIdAndYwTypeAndSpTypeApi(ywId: string, ywType: string, spType: string) {
|
||||
return get('/api/lcglSp/getByYwIdAndYwTypeAndSpType', { ywId, ywType, spType });
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询审批流程
|
||||
* @param params 查询参数
|
||||
*/
|
||||
export function lcglSpFindPageApi(params: {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
ywId?: string;
|
||||
ywType?: string;
|
||||
spType?: string;
|
||||
approveStatus?: string;
|
||||
}) {
|
||||
return get('/api/lcglSp/findPage', params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增/修改审批流程
|
||||
* @param params 审批流程数据
|
||||
*/
|
||||
export function lcglSpSaveApi(params: {
|
||||
id?: string;
|
||||
ywId: string;
|
||||
ywType: string;
|
||||
userId: string;
|
||||
userName: string;
|
||||
spType: string;
|
||||
approveStatus?: string;
|
||||
approveRemark?: string;
|
||||
setId?: string;
|
||||
sort?: number;
|
||||
}) {
|
||||
return post('/api/lcglSp/save', params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 逻辑删除审批流程
|
||||
* @param params 删除参数
|
||||
*/
|
||||
export function lcglSpLogicDeleteApi(params: { id: string }) {
|
||||
return post('/api/lcglSp/logicDelete', params);
|
||||
}
|
||||
@ -1,12 +1,18 @@
|
||||
<template>
|
||||
<view class="basic-js-picker">
|
||||
<view class="js-selected" @click="showPicker">
|
||||
<view class="js-picker">
|
||||
<!-- 自定义触发器slot -->
|
||||
<view v-if="customTrigger" @click="showPicker">
|
||||
<slot name="trigger" :selectedList="selectedList" :getShowSelectedName="getShowSelectedName"></slot>
|
||||
</view>
|
||||
<!-- 默认触发器 -->
|
||||
<view v-else class="js-selected" @click="showPicker">
|
||||
<view class="js-selected-name">
|
||||
<text class="data" v-if="selectedList && selectedList.length">{{ getShowSelectedName() }}</text>
|
||||
<text class="data" style="color: #999;" v-else>{{ placeholder }}</text>
|
||||
</view>
|
||||
<uni-icons type="arrowright" size="16" color="#999"></uni-icons>
|
||||
</view>
|
||||
|
||||
<u-popup :show="showPopup" @close="showPopup=false">
|
||||
<view class="js-picker-popup">
|
||||
<view class="js-picker-header">
|
||||
@ -14,7 +20,7 @@
|
||||
<view class="js-picker-title">{{ title }}</view>
|
||||
<view class="js-ok-btn" @click="handleOk">确定</view>
|
||||
</view>
|
||||
<view class="js-picker-search" v-if="showSearch">
|
||||
<view class="js-picker-search">
|
||||
<BasicSearch @change="handleSearch" :showAction="false" height="36" :placeholder="searchPlaceholder"/>
|
||||
</view>
|
||||
<view class="js-list">
|
||||
@ -32,13 +38,13 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, nextTick } from 'vue';
|
||||
import { useCommonStore } from "@/store/modules/common";
|
||||
const { getAllJs } = useCommonStore();
|
||||
const { getAllJsBasicInfoVo } = useCommonStore();
|
||||
|
||||
// 接收外部传入属性
|
||||
const props = withDefaults(defineProps<{
|
||||
modelValue?: any,
|
||||
defaultValue?: any,
|
||||
defualtValue?: any,
|
||||
parentData?: any,
|
||||
multiple?: boolean,
|
||||
// 排除id列表
|
||||
@ -47,23 +53,23 @@ const props = withDefaults(defineProps<{
|
||||
title?: string,
|
||||
placeholder?: string,
|
||||
searchPlaceholder?: string,
|
||||
showSearch?: boolean
|
||||
// 自定义触发器
|
||||
customTrigger?: boolean
|
||||
}>(), {
|
||||
modelValue: null,
|
||||
defaultValue: null,
|
||||
defualtValue: null,
|
||||
parentData: null,
|
||||
multiple: false,
|
||||
excludeIds: [],
|
||||
title: '请选择教师',
|
||||
placeholder: '请选择老师',
|
||||
placeholder: '请选择教师',
|
||||
searchPlaceholder: '输入教师名称查询',
|
||||
showSearch: true
|
||||
customTrigger: false
|
||||
});
|
||||
|
||||
// 定义一个上级传入的emit响应事件用于接收数据变更
|
||||
const emit = defineEmits(['change', 'update:modelValue'])
|
||||
const emit = defineEmits(['change'])
|
||||
|
||||
const targetScrollTop = ref(0); // 用于绑定 scroll-top
|
||||
const targetScrollTop = ref(0); // 新增:用于绑定 scroll-top
|
||||
const showPopup = ref(false);
|
||||
|
||||
const jsListAll = ref<any>([]);
|
||||
@ -72,22 +78,8 @@ let searchKey = "";
|
||||
|
||||
const selectedList = ref<any>([]);
|
||||
|
||||
// 计算属性,用于 v-model 双向绑定
|
||||
const currentValue = computed({
|
||||
get() {
|
||||
return props.modelValue;
|
||||
},
|
||||
set(value) {
|
||||
emit('update:modelValue', value);
|
||||
}
|
||||
});
|
||||
|
||||
// 添加一个内部状态来跟踪当前值
|
||||
const internalValue = ref(props.modelValue || props.defaultValue);
|
||||
|
||||
const getShowSelectedName = () => {
|
||||
const names = selectedList.value.map((item: any) => item.label).join(",");
|
||||
return names;
|
||||
return selectedList.value.map((item: any) => item.label).join(",");
|
||||
};
|
||||
|
||||
const handleSearch = (value: any) => {
|
||||
@ -99,19 +91,24 @@ const handleSearch = (value: any) => {
|
||||
const handleSelect = (item: any) => {
|
||||
if (props.multiple) {
|
||||
// 多选模式:切换选中状态
|
||||
const index = selectedList.value.findIndex((selected: any) => selected.value === item.value);
|
||||
if (index > -1) {
|
||||
// 取消选择:从选中列表中移除
|
||||
selectedList.value.splice(index, 1);
|
||||
const isSelected = selectedList.value.some((selected: any) => selected.value === item.value);
|
||||
if (isSelected) {
|
||||
// 取消选择
|
||||
const index = selectedList.value.findIndex((selected: any) => selected.value === item.value);
|
||||
if (index > -1) {
|
||||
selectedList.value.splice(index, 1);
|
||||
}
|
||||
item.selected = false;
|
||||
} else {
|
||||
// 新增选择:添加到选中列表末尾(保持选择顺序)
|
||||
// 添加选择
|
||||
selectedList.value.push(item);
|
||||
item.selected = true;
|
||||
}
|
||||
} else {
|
||||
// 单选模式:清除其他选中状态,设置当前选中
|
||||
jsList.value.forEach((listItem: any) => listItem.selected = false);
|
||||
// 单选模式,清除其他选中状态
|
||||
for (const listItem of jsList.value) {
|
||||
listItem.selected = false;
|
||||
}
|
||||
selectedList.value = [item];
|
||||
item.selected = true;
|
||||
}
|
||||
@ -123,217 +120,132 @@ const handleCancel = () => {
|
||||
|
||||
const handleOk = () => {
|
||||
showPopup.value = false;
|
||||
let result;
|
||||
if (props.multiple) {
|
||||
result = selectedList.value;
|
||||
emit("change", selectedList.value, props.parentData);
|
||||
} else {
|
||||
result = selectedList.value[0] || null;
|
||||
emit("change", selectedList.value[0] || null, props.parentData);
|
||||
}
|
||||
|
||||
// 更新 v-model 值
|
||||
currentValue.value = result;
|
||||
|
||||
// 触发 change 事件
|
||||
emit("change", result, props.parentData);
|
||||
};
|
||||
}
|
||||
|
||||
const showPicker = () => {
|
||||
showPopup.value = true;
|
||||
rebuildJsList();
|
||||
nextTick(() => {
|
||||
showPopup.value = true;
|
||||
});
|
||||
};
|
||||
|
||||
const rebuildJsList = () => {
|
||||
jsList.value = [];
|
||||
jsListAll.value.forEach((item: any) => {
|
||||
if (props.excludeIds && props.excludeIds.length && props.excludeIds.includes(item.id)) {
|
||||
return;
|
||||
}
|
||||
// 判断教师姓名jsxm包含搜索关键词
|
||||
if (!searchKey || item.jsxm.includes(searchKey)) {
|
||||
jsList.value.push({
|
||||
label: item.jsxm,
|
||||
value: item.id,
|
||||
headPic: item.headPic,
|
||||
selected: false
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 恢复选中状态
|
||||
restoreSelection();
|
||||
};
|
||||
|
||||
const restoreSelection = () => {
|
||||
if (!selectedList.value.length) {
|
||||
if (!jsListAll.value || !Array.isArray(jsListAll.value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 按照selectedList的顺序恢复选中状态,保持选择顺序
|
||||
jsList.value.forEach((item: any) => {
|
||||
const isSelected = selectedList.value.some((selected: any) => selected.value === item.value);
|
||||
item.selected = isSelected;
|
||||
});
|
||||
// 使用for...of确保同步执行
|
||||
for (const item of jsListAll.value) {
|
||||
// 检查item是否有效
|
||||
if (!item || !item.id || !item.jsxm) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检查是否在排除列表中
|
||||
if (props.excludeIds && Array.isArray(props.excludeIds) && props.excludeIds.includes(item.id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 判断教师姓名jsxm包含搜索关键词
|
||||
if (!searchKey || item.jsxm.includes(searchKey)) {
|
||||
jsList.value.push({
|
||||
...item,
|
||||
label: item.jsxm,
|
||||
value: item.id,
|
||||
selected: false
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 暴露方法给父组件
|
||||
defineExpose({
|
||||
showPicker,
|
||||
open: showPicker,
|
||||
close: () => showPopup.value = false
|
||||
showPicker
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
const res = await getAllJs()
|
||||
jsListAll.value = res.result || [];
|
||||
try {
|
||||
const res = await getAllJsBasicInfoVo()
|
||||
if (res && res.result && Array.isArray(res.result)) {
|
||||
jsListAll.value = res.result;
|
||||
|
||||
rebuildJsList();
|
||||
// 确保rebuildJsList完全执行完成
|
||||
await new Promise<void>((resolve) => {
|
||||
rebuildJsList();
|
||||
// 使用nextTick确保DOM更新完成
|
||||
nextTick(() => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
// 等待下一个tick确保jsList已经构建完成
|
||||
await nextTick();
|
||||
|
||||
// 设置默认值
|
||||
const initialValue = internalValue.value || props.defaultValue;
|
||||
|
||||
if (initialValue) {
|
||||
setInitialValue(initialValue);
|
||||
}
|
||||
});
|
||||
|
||||
// 设置初始值的函数
|
||||
const setInitialValue = (value: any) => {
|
||||
if (!jsList.value.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (props.multiple) {
|
||||
if (!Array.isArray(value) || !value.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
selectedList.value = [];
|
||||
|
||||
// 先清空所有选中状态
|
||||
jsList.value.forEach((item: any) => {
|
||||
item.selected = false;
|
||||
});
|
||||
|
||||
// 处理不同类型的输入值
|
||||
let processedValue = value;
|
||||
|
||||
// 如果输入的是对象数组(如{0: {value: '1001', label: '张三'}, 1: {value: '1002', label: '李四'}})
|
||||
if (value[0] && typeof value[0] === 'object' && (value[0].value || value[0].id)) {
|
||||
processedValue = value.map((item: any) => item.value || item.id);
|
||||
}
|
||||
|
||||
// 按照传入值的顺序设置选中状态,保持选择顺序
|
||||
selectedList.value = [];
|
||||
|
||||
// 遍历传入的值,按照顺序查找对应的项目
|
||||
processedValue.forEach((id: any) => {
|
||||
const foundItem = jsList.value.find((item: any) => item.value === id || item.id === id);
|
||||
if (foundItem) {
|
||||
foundItem.selected = true;
|
||||
selectedList.value.push(foundItem);
|
||||
}
|
||||
});
|
||||
|
||||
// 清空其他未选中的项目
|
||||
jsList.value.forEach((item: any) => {
|
||||
if (!processedValue.includes(item.value) && !processedValue.includes(item.id)) {
|
||||
item.selected = false;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// 单选模式
|
||||
|
||||
// 清空所有选中状态
|
||||
jsList.value.forEach((item: any) => {
|
||||
item.selected = false;
|
||||
});
|
||||
|
||||
// 处理不同类型的输入值
|
||||
let processedValue = value;
|
||||
if (value && typeof value === 'object' && (value.value || value.id)) {
|
||||
processedValue = value.value || value.id;
|
||||
}
|
||||
|
||||
// 检查value是否匹配当前项的value或id
|
||||
const targetItem = jsList.value.find((item: any) => item.value === processedValue || item.id === processedValue);
|
||||
if (targetItem) {
|
||||
targetItem.selected = true;
|
||||
selectedList.value = [targetItem];
|
||||
}
|
||||
}
|
||||
|
||||
// 如果selectedList仍然为空,尝试从jsListAll中查找
|
||||
if (selectedList.value.length === 0 && value && Array.isArray(value)) {
|
||||
selectedList.value = [];
|
||||
|
||||
// 处理不同类型的输入值
|
||||
let processedValue = value;
|
||||
if (value[0] && typeof value[0] === 'object' && (value[0].value || value[0].id)) {
|
||||
processedValue = value.map((item: any) => item.value || item.id);
|
||||
}
|
||||
|
||||
processedValue.forEach((id: any) => {
|
||||
const foundItem = jsListAll.value.find((item: any) => item.id === id);
|
||||
if (foundItem) {
|
||||
const itemForList = {
|
||||
label: foundItem.jsxm,
|
||||
value: foundItem.id,
|
||||
headPic: foundItem.headPic,
|
||||
selected: true
|
||||
};
|
||||
selectedList.value.push(itemForList);
|
||||
|
||||
// 同时更新jsList中对应项的选中状态
|
||||
const jsListItem = jsList.value.find((item: any) => item.value === foundItem.id);
|
||||
if (jsListItem) {
|
||||
jsListItem.selected = true;
|
||||
// 设置默认值
|
||||
if (props.defualtValue) {
|
||||
if (props.multiple) {
|
||||
if (Array.isArray(props.defualtValue) && props.defualtValue.length > 0) {
|
||||
selectedList.value = [];
|
||||
// 使用for...of确保同步执行
|
||||
for (const item of jsList.value) {
|
||||
if (props.defualtValue.includes(item.value)) {
|
||||
item.selected = true;
|
||||
selectedList.value.push(item);
|
||||
} else {
|
||||
item.selected = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 使用for...of确保同步执行
|
||||
for (const item of jsList.value) {
|
||||
if (item.value === props.defualtValue) {
|
||||
item.selected = true;
|
||||
selectedList.value = [item];
|
||||
} else {
|
||||
item.selected = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 监听 modelValue 变化,同步到内部状态
|
||||
watch(() => props.modelValue, (newValue) => {
|
||||
if (newValue !== internalValue.value) {
|
||||
internalValue.value = newValue;
|
||||
// 立即尝试设置值,如果jsList还没准备好,会在jsList变化时再次尝试
|
||||
if (jsList.value.length > 0) {
|
||||
setInitialValue(newValue);
|
||||
} else {
|
||||
console.warn('JsPicker: 获取教师数据失败或数据格式不正确');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('JsPicker初始化失败:', error);
|
||||
}
|
||||
}, { immediate: true });
|
||||
|
||||
// 监听 defaultValue 变化
|
||||
watch(() => props.defaultValue, (newValue) => {
|
||||
if (newValue && jsList.value.length > 0) {
|
||||
setInitialValue(newValue);
|
||||
}
|
||||
}, { immediate: false });
|
||||
|
||||
// 监听 jsList 变化,当jsList构建完成后设置初始值
|
||||
watch(() => jsList.value, (newJsList) => {
|
||||
if (newJsList.length > 0) {
|
||||
const initialValue = internalValue.value || props.defaultValue;
|
||||
|
||||
if (initialValue) {
|
||||
setInitialValue(initialValue);
|
||||
}
|
||||
}
|
||||
}, { immediate: false });
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.basic-js-picker {
|
||||
.js-picker {
|
||||
flex: 1;
|
||||
.js-selected {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
justify-content: space-between;
|
||||
padding: 20rpx 30rpx;
|
||||
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
|
||||
border-radius: 12rpx;
|
||||
border: 1rpx solid #e9ecef;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:active {
|
||||
transform: translateY(2rpx);
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.js-selected-name {
|
||||
flex: 1;
|
||||
|
||||
.data {
|
||||
font-size: 30rpx;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
.js-picker-popup {
|
||||
|
||||
477
src/components/BasicSpCsMgr/index.vue
Normal file
477
src/components/BasicSpCsMgr/index.vue
Normal file
@ -0,0 +1,477 @@
|
||||
<template>
|
||||
<view class="spr-list-container">
|
||||
<!-- 审批人列表 -->
|
||||
<view class="section">
|
||||
<view class="section-header">
|
||||
<text class="section-title">审批人</text>
|
||||
<!-- 审批人选择器 -->
|
||||
<BasicJsPicker
|
||||
ref="approverPickerRef"
|
||||
:customTrigger="true"
|
||||
:multiple="true"
|
||||
:excludeIds="getExcludeApproverIds()"
|
||||
:defualtValue="[]"
|
||||
title="选择审批人"
|
||||
placeholder="请选择审批人"
|
||||
searchPlaceholder="搜索审批人"
|
||||
@change="handleApproverChange"
|
||||
>
|
||||
<template #trigger>
|
||||
<view class="add-btn">
|
||||
<text class="add-icon">+</text>
|
||||
<text class="add-text">添加审批人</text>
|
||||
</view>
|
||||
</template>
|
||||
</BasicJsPicker>
|
||||
</view>
|
||||
|
||||
<view class="list-content">
|
||||
<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">
|
||||
<text class="delete-btn" @click="removeApprover(index)">删除</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="approvers.length === 0" class="empty-tip">
|
||||
<text>暂无审批人,请添加</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 抄送人列表 -->
|
||||
<view class="section">
|
||||
<view class="section-header">
|
||||
<text class="section-title">抄送人</text>
|
||||
<!-- 抄送人选择器 -->
|
||||
<BasicJsPicker
|
||||
ref="ccPickerRef"
|
||||
:customTrigger="true"
|
||||
:multiple="true"
|
||||
:excludeIds="getExcludeCcIds()"
|
||||
:defualtValue="[]"
|
||||
title="选择抄送人"
|
||||
placeholder="请选择抄送人"
|
||||
searchPlaceholder="搜索抄送人"
|
||||
@change="handleCcChange"
|
||||
>
|
||||
<template #trigger>
|
||||
<view class="add-btn">
|
||||
<text class="add-icon">+</text>
|
||||
<text class="add-text">添加抄送人</text>
|
||||
</view>
|
||||
</template>
|
||||
</BasicJsPicker>
|
||||
</view>
|
||||
|
||||
<view class="list-content">
|
||||
<view v-for="(csr, index) in ccList" :key="csr.id" class="list-item">
|
||||
<view class="item-info">
|
||||
<text class="item-name">{{ csr.jsxm }}</text>
|
||||
</view>
|
||||
<view class="item-actions">
|
||||
<text class="delete-btn" @click="removeCc(index)">删除</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="ccList.length === 0" class="empty-tip">
|
||||
<text>暂无抄送人,请添加</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch, onMounted } from "vue";
|
||||
import { getApproversByRuleId } from "@/api/base/jsQjApi";
|
||||
import { 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;
|
||||
jsxm: string;
|
||||
lxdh?: string;
|
||||
sfzh?: string;
|
||||
headPic?: string;
|
||||
userId?: number;
|
||||
deptId?: string;
|
||||
}
|
||||
|
||||
// 数据转换公共接口
|
||||
const normalizeTeacherData = (item: any): JsInfo => {
|
||||
return {
|
||||
id: item.id || item.value,
|
||||
jsxm: item.jsxm || item.label,
|
||||
lxdh: item.lxdh,
|
||||
sfzh: item.sfzh,
|
||||
headPic: item.headPic,
|
||||
userId: item.userId,
|
||||
deptId: item.deptId,
|
||||
};
|
||||
};
|
||||
|
||||
// 接收外部传入属性并设置默认值
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
defaultValue?: any;
|
||||
ruleId?: string;
|
||||
qjId?: string; // 请假ID,用于重新提交时获取已有审批人
|
||||
}>(),
|
||||
{
|
||||
defaultValue: () => ({
|
||||
sprList: [],
|
||||
csrList: [],
|
||||
}),
|
||||
ruleId: "",
|
||||
qjId: "",
|
||||
}
|
||||
);
|
||||
|
||||
// 定义一个上级传入的emit响应事件用于接收数据变更
|
||||
const emit = defineEmits(["change"]);
|
||||
|
||||
// 响应式数据 - 使用defaultValue进行初始化
|
||||
const approvers = ref<JsInfo[]>([]);
|
||||
const ccList = ref<JsInfo[]>([]);
|
||||
|
||||
// 教师选择器引用
|
||||
const approverPickerRef = ref();
|
||||
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 {
|
||||
// 如果有请假ID,优先从LcglSp表获取已有审批人信息
|
||||
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");
|
||||
if (
|
||||
response &&
|
||||
response.resultCode === 1 &&
|
||||
response.result &&
|
||||
Array.isArray(response.result)
|
||||
) {
|
||||
const spList = response.result;
|
||||
|
||||
// 分离审批人和抄送人
|
||||
let approverList: any = [];
|
||||
let ccListData: any = [];
|
||||
|
||||
// 使用for...of确保同步执行
|
||||
for (const item of spList) {
|
||||
if (item && item.userId && item.userName) {
|
||||
if (item.spType === "SP") {
|
||||
approverList.push(item);
|
||||
} else if (item.spType === "CC") {
|
||||
ccListData.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
let changeFlag = false;
|
||||
// 如果当前没有审批人或抄送人,则使用已有数据
|
||||
if (approvers.value.length === 0 && approverList.length > 0) {
|
||||
approvers.value = approverList.map(normalizeTeacherData);
|
||||
changeFlag = true;
|
||||
}
|
||||
if (ccList.value.length === 0 && ccListData.length > 0) {
|
||||
ccList.value = ccListData.map(normalizeTeacherData);
|
||||
changeFlag = true;
|
||||
}
|
||||
if (changeFlag) {
|
||||
notifyChange();
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("获取已有审批人失败:", error);
|
||||
}
|
||||
};
|
||||
|
||||
// 从LcglSet表获取默认配置
|
||||
const loadDefaultFromLcglSet = async () => {
|
||||
try {
|
||||
const response = await getApproversByRuleId(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();
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("获取默认审批人失败:", error);
|
||||
}
|
||||
};
|
||||
|
||||
// 通知上级数据变更
|
||||
const notifyChange = () => {
|
||||
emit("change", {
|
||||
sprList: approvers.value,
|
||||
csrList: ccList.value,
|
||||
});
|
||||
};
|
||||
|
||||
// 处理审批人选择变更
|
||||
const handleApproverChange = (selectedTeachers: any[]) => {
|
||||
if (Array.isArray(selectedTeachers)) {
|
||||
// 使用公共接口确保数据格式一致
|
||||
const normalizedTeachers = selectedTeachers.map(normalizeTeacherData);
|
||||
approvers.value.push(...normalizedTeachers);
|
||||
notifyChange();
|
||||
uni.showToast({
|
||||
title: "审批人设置成功",
|
||||
icon: "success",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 处理抄送人选择变更
|
||||
const handleCcChange = (selectedTeachers: any[]) => {
|
||||
if (Array.isArray(selectedTeachers)) {
|
||||
// 使用公共接口确保数据格式一致
|
||||
const normalizedTeachers = selectedTeachers.map(normalizeTeacherData);
|
||||
ccList.value.push(...normalizedTeachers);
|
||||
notifyChange();
|
||||
uni.showToast({
|
||||
title: "抄送人设置成功",
|
||||
icon: "success",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 删除审批人
|
||||
const removeApprover = (index: number) => {
|
||||
uni.showModal({
|
||||
title: "确认删除",
|
||||
content: `确定要删除审批人"${approvers.value[index].jsxm}"吗?`,
|
||||
success: (res: any) => {
|
||||
if (res.confirm) {
|
||||
approvers.value.splice(index, 1);
|
||||
notifyChange();
|
||||
uni.showToast({
|
||||
title: "删除成功",
|
||||
icon: "success",
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// 删除抄送人
|
||||
const removeCc = (index: number) => {
|
||||
uni.showModal({
|
||||
title: "确认删除",
|
||||
content: `确定要删除抄送人"${ccList.value[index].jsxm}"吗?`,
|
||||
success: (res: any) => {
|
||||
if (res.confirm) {
|
||||
ccList.value.splice(index, 1);
|
||||
notifyChange();
|
||||
uni.showToast({
|
||||
title: "删除成功",
|
||||
icon: "success",
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// 获取排除的审批人ID列表
|
||||
const getExcludeApproverIds = () => {
|
||||
return approvers.value.map((item) => item.id).filter((id) => id);
|
||||
};
|
||||
|
||||
// 获取排除的抄送人ID列表
|
||||
const getExcludeCcIds = () => {
|
||||
return ccList.value.map((item) => item.id).filter((id) => id);
|
||||
};
|
||||
|
||||
// 组件挂载时加载默认数据
|
||||
onMounted(async () => {
|
||||
try {
|
||||
initData();
|
||||
await loadDefaultApprovers();
|
||||
|
||||
// 预加载教师数据
|
||||
await commonStore.getAllJsBasicInfoVo();
|
||||
} catch (error) {
|
||||
console.error("SprList组件初始化失败:", error);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.spr-list-container {
|
||||
padding: 20rpx;
|
||||
|
||||
.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: #333;
|
||||
}
|
||||
|
||||
.add-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
padding: 12rpx 24rpx;
|
||||
background: #ffffff;
|
||||
border-radius: 8rpx;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:active {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.add-icon {
|
||||
color: #1890ff;
|
||||
font-size: 28rpx;
|
||||
margin-right: 8rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.add-text {
|
||||
color: #1890ff;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.list-content {
|
||||
.list-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 24rpx;
|
||||
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
|
||||
border-radius: 12rpx;
|
||||
margin-bottom: 16rpx;
|
||||
border: 1rpx solid #e9ecef;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-2rpx);
|
||||
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.item-info {
|
||||
flex: 1;
|
||||
|
||||
.item-name {
|
||||
font-size: 30rpx;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.item-actions {
|
||||
.delete-btn {
|
||||
color: #ff6b6b;
|
||||
font-size: 28rpx;
|
||||
padding: 8rpx 16rpx;
|
||||
background: rgba(255, 107, 107, 0.1);
|
||||
border-radius: 6rpx;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:active {
|
||||
background: rgba(255, 107, 107, 0.2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.empty-tip {
|
||||
text-align: center;
|
||||
padding: 60rpx 0;
|
||||
color: #999;
|
||||
font-size: 28rpx;
|
||||
background: #f8f9fa;
|
||||
border-radius: 12rpx;
|
||||
border: 2rpx dashed #dee2e6;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -1,217 +0,0 @@
|
||||
<template>
|
||||
<view class="js-picker">
|
||||
<view class="js-selected" @click="showPicker">
|
||||
<view class="js-selected-name">
|
||||
<text class="data" v-if="selectedList && selectedList.length">{{ getShowSelectedName() }}</text>
|
||||
<text class="data" style="color: #999;" v-else>请选择老师</text>
|
||||
</view>
|
||||
<uni-icons type="arrowright" size="16" color="#999"></uni-icons>
|
||||
</view>
|
||||
<u-popup :show="showPopup" @close="showPopup=false">
|
||||
<view class="js-picker-popup">
|
||||
<view class="js-picker-header">
|
||||
<view class="js-cancel-btn" @click="handleCancel">取消</view>
|
||||
<view class="js-picker-title">请选择教师</view>
|
||||
<view class="js-ok-btn" @click="handleOk">确定</view>
|
||||
</view>
|
||||
<view class="js-picker-search">
|
||||
<BasicSearch @change="handleSearch" :showAction="false" height="36" :placeholder="'输入教师名称查询'"/>
|
||||
</view>
|
||||
<view class="js-list">
|
||||
<scroll-view scroll-y style="max-height: 60vh" :scroll-top="targetScrollTop">
|
||||
<view class="js-item" v-for="(item, index) in jsList" :key="index"
|
||||
:class="{ selected: item.selected }" @click="handleSelect(item)">
|
||||
<view class="js-name">{{ item.label }}</view>
|
||||
<BasicIcon type="checkmarkempty" v-if="item.selected" color="#333" />
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useCommonStore } from "@/store/modules/common";
|
||||
const { getAllJs } = useCommonStore();
|
||||
|
||||
// 接收外部传入属性
|
||||
const props = withDefaults(defineProps<{
|
||||
defualtValue?: any,
|
||||
parentData?: any,
|
||||
multiple?: boolean,
|
||||
// 排除id列表
|
||||
excludeIds?: any
|
||||
}>(), {
|
||||
defualtValue: null,
|
||||
parentData: null,
|
||||
multiple: false,
|
||||
excludeIds: []
|
||||
});
|
||||
|
||||
// 定义一个上级传入的emit响应事件用于接收数据变更
|
||||
const emit = defineEmits(['change'])
|
||||
|
||||
const targetScrollTop = ref(0); // 新增:用于绑定 scroll-top
|
||||
const showPopup = ref(false);
|
||||
|
||||
const jsListAll = ref<any>([]);
|
||||
const jsList = ref<any>([]);
|
||||
let searchKey = "";
|
||||
|
||||
const selectedList = ref<any>([]);
|
||||
|
||||
const getShowSelectedName = () => {
|
||||
return selectedList.value.map((item: any) => item.label).join(",");
|
||||
};
|
||||
|
||||
const handleSearch = (value: any) => {
|
||||
searchKey = value;
|
||||
rebuildJsList();
|
||||
return
|
||||
};
|
||||
|
||||
const handleSelect = (item: any) => {
|
||||
if (props.multiple) {
|
||||
selectedList.value.push(item);
|
||||
} else {
|
||||
jsList.value.map((item: any) => item.selected = false);
|
||||
selectedList.value = [item];
|
||||
}
|
||||
item.selected = true;
|
||||
}
|
||||
|
||||
const handleCancel = () => {
|
||||
showPopup.value = false;
|
||||
}
|
||||
|
||||
const handleOk = () => {
|
||||
showPopup.value = false;
|
||||
if (props.multiple) {
|
||||
emit("change", selectedList.value, props.parentData);
|
||||
} else {
|
||||
emit("change", selectedList.value[0], props.parentData);
|
||||
}
|
||||
}
|
||||
|
||||
const showPicker = () => {
|
||||
showPopup.value = true;
|
||||
};
|
||||
|
||||
const rebuildJsList = () => {
|
||||
jsList.value = [];
|
||||
jsListAll.value.map((item: any) => {
|
||||
if (props.excludeIds && props.excludeIds.length && props.excludeIds.includes(item.id)) {
|
||||
return;
|
||||
}
|
||||
// 判断教师姓名jsxm包含搜索关键词
|
||||
if ( !searchKey || item.jsxm.includes(searchKey)) {
|
||||
jsList.value.push({
|
||||
label: item.jsxm,
|
||||
value: item.id,
|
||||
headPic: item.headPic
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 暴露方法给父组件
|
||||
defineExpose({
|
||||
showPicker
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
const res = await getAllJs()
|
||||
jsListAll.value = res.result || [];
|
||||
rebuildJsList();
|
||||
if (!props.defualtValue) {
|
||||
return;
|
||||
}
|
||||
if (props.multiple) {
|
||||
if (!props.defualtValue.length) {
|
||||
return;
|
||||
}
|
||||
selectedList.value = [];
|
||||
for (let i = 0; i < jsListAll.value.length; i++) {
|
||||
const item = jsListAll.value[i];
|
||||
if (props.defualtValue.indexOf(item.value) > -1) {
|
||||
item.selected = true;
|
||||
selectedList.value.push(item);
|
||||
} else {
|
||||
item.selected = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < jsList.value.length; i++) {
|
||||
const item = jsList.value[i];
|
||||
if (item.value === props.defualtValue) {
|
||||
item.selected = true;
|
||||
selectedList.value.push(item);
|
||||
} else {
|
||||
item.selected = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.js-picker {
|
||||
flex: 1;
|
||||
.js-selected {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.js-selected-name {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
.js-picker-popup {
|
||||
min-height: 50vh;
|
||||
max-height: 90vh;
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.js-picker-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: bold;
|
||||
.js-cancel-btn {
|
||||
flex: 0 0 50;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
.js-ok-btn {
|
||||
flex: 0 0 50;
|
||||
padding: 5px 10px;
|
||||
color: #3c9cff;
|
||||
}
|
||||
.js-picker-title {
|
||||
flex: 1 0 1px;
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
.js-picker-search {
|
||||
margin: 10px 0;
|
||||
}
|
||||
.js-list {
|
||||
flex: 1 0 1px;
|
||||
.js-item {
|
||||
display: flex;
|
||||
padding: 10px 15px;
|
||||
border-bottom: 1px solid #eee;
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
&.selected {
|
||||
background-color: beige;
|
||||
}
|
||||
.js-name {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -81,7 +81,7 @@
|
||||
<view class="dk-title pl-15 pr-15">
|
||||
<BasicTitle line title="请假流程" :isBorder="false" />
|
||||
</view>
|
||||
<ProgressList :procInstId="qjData.procInstId" />
|
||||
<ProgressList :qjId="qjData.id" />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@ -1,348 +1,143 @@
|
||||
<template>
|
||||
<view class="back-f8f8f8">
|
||||
<view class="flex-row items-center justify-between py-15 global-bg-color">
|
||||
<view class="dk-title">
|
||||
<BasicTitle line title="代课明细" :isBorder="false" />
|
||||
</view>
|
||||
<view @click="getPkkbList">
|
||||
<BasicIcon type="refreshempty" size="25" />
|
||||
</view>
|
||||
<view class="js-qj-dk-edit">
|
||||
<view class="dk-header">
|
||||
<text class="dk-title">代课教师</text>
|
||||
<BasicJsPicker
|
||||
:customTrigger="true"
|
||||
:defualtValue="[]"
|
||||
@change="handleDkJsChange"
|
||||
>
|
||||
<template #trigger>
|
||||
<button class="add-btn" type="primary" size="mini">添加代课教师</button>
|
||||
</template>
|
||||
</BasicJsPicker>
|
||||
</view>
|
||||
<view class="dk-tabs mb-10" v-if="dkList && dkList.length">
|
||||
<BasicTabs
|
||||
class="type-tabs"
|
||||
ref="tabsRef"
|
||||
:list="tabList"
|
||||
bar-width="60px"
|
||||
scroll-count="4"
|
||||
:current="curTabIndex"
|
||||
@change="switchTab"
|
||||
/>
|
||||
<view class="dk-card" v-if="curTabIndex === 0">
|
||||
<view class="card-body">
|
||||
<view class="info-row">
|
||||
<text class="label">代课老师:</text>
|
||||
<view class="value">
|
||||
<JsPicker
|
||||
@change="changeJsByTy"
|
||||
:parent-data="tyDk"
|
||||
:defualtValue="tyDk.dkJsId"
|
||||
:multiple="false"
|
||||
:excludeIds="excludeIds"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="dk-list">
|
||||
<view
|
||||
v-for="(item, index) in dkList"
|
||||
:key="item.id"
|
||||
class="dk-item"
|
||||
>
|
||||
<view class="dk-info">
|
||||
<text class="js-name">{{ item.jsxm }}</text>
|
||||
<text class="course-info">{{ item.kcmc }} - {{ item.kcsj }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="curTabIndex === 1">
|
||||
<view v-for="(item, index) in kmDkList" :key="index">
|
||||
<view class="dk-card" style="margin: 0">
|
||||
<view class="card-body">
|
||||
<view class="info-row">
|
||||
<text class="label">排课名称:</text>
|
||||
<text class="value">{{ item.pkMc }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="label">代课老师:</text>
|
||||
<view class="value">
|
||||
<JsPicker
|
||||
@change="changeJsByKm"
|
||||
:parent-data="item"
|
||||
:defualtValue="item.dkJsId"
|
||||
:multiple="false"
|
||||
:excludeIds="excludeIds"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="dk-actions">
|
||||
<button
|
||||
class="remove-btn"
|
||||
size="mini"
|
||||
@click="removeDkJs(index)"
|
||||
>
|
||||
删除
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="dkList.length > 0">
|
||||
<view v-for="(item, index) in dkList" :key="index">
|
||||
<view class="dk-card">
|
||||
<view class="card-header">
|
||||
<text class="applicant-name"
|
||||
>{{ item.dktime }}({{ wdNameList[item.xq - 1] }})的{{
|
||||
item.jcmc
|
||||
}}</text
|
||||
>
|
||||
</view>
|
||||
<view class="card-body">
|
||||
<view class="info-row">
|
||||
<text class="label">排课名称:</text>
|
||||
<text class="value">{{ item.pkMc }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="label">上课时间:</text>
|
||||
<text class="value">{{ item.startTime }}-{{ item.endTime }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="label">代课老师:</text>
|
||||
<view class="value" v-if="curTabIndex === 2">
|
||||
<JsPicker
|
||||
@change="changeJs"
|
||||
:parent-data="item"
|
||||
:defualtValue="item.dkJsId"
|
||||
:multiple="false"
|
||||
:excludeIds="excludeIds"
|
||||
/>
|
||||
</view>
|
||||
<view v-else>{{ item.dkJsName }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else class="p-15 flex-row-center color-9 font-13 white-bg-color"
|
||||
>暂无数据</view
|
||||
>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getPkkbByJsRangeTimeApi } from "@/api/base/jsQjApi";
|
||||
import JsPicker from "@/pages/components/JsPicker/index.vue";
|
||||
import { useUserStore } from "@/store/modules/user";
|
||||
const { getJs } = useUserStore();
|
||||
import { ref, watch } from 'vue'
|
||||
import BasicJsPicker from '@/components/BasicJsPicker/Picker.vue'
|
||||
|
||||
// 接收外部传入属性
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
data: any;
|
||||
}>(),
|
||||
{
|
||||
data: () => ({
|
||||
jsId: "",
|
||||
qjkstime: "", // 请假开始时间
|
||||
qjjstime: "", // 请假结束时间
|
||||
}),
|
||||
// Props
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
);
|
||||
})
|
||||
|
||||
const wdNameList = ref<string[]>([
|
||||
"周一",
|
||||
"周二",
|
||||
"周三",
|
||||
"周四",
|
||||
"周五",
|
||||
"周六",
|
||||
"周日",
|
||||
]);
|
||||
// Emits
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
const jsTypeMc: any = {
|
||||
ZAM: "早自习",
|
||||
AM: "上午",
|
||||
PM: "下午",
|
||||
};
|
||||
// 响应式数据
|
||||
const dkList = ref<any[]>([])
|
||||
|
||||
// 代课教师要排除自己
|
||||
const excludeIds = ref<any>([]);
|
||||
|
||||
if (props.data && props.data.jsId && props.data.jsId.length) {
|
||||
excludeIds.value.push(props.data.jsId);
|
||||
} else {
|
||||
excludeIds.value.push(getJs.id);
|
||||
// 处理代课教师变化
|
||||
const handleDkJsChange = (selectedItems: any[]) => {
|
||||
console.log(selectedItems)
|
||||
if (Array.isArray(selectedItems)) {
|
||||
dkList.value = selectedItems
|
||||
emit('update:modelValue', selectedItems)
|
||||
}
|
||||
}
|
||||
|
||||
const tyDk = ref<any>({});
|
||||
// 移除代课教师
|
||||
const removeDkJs = (index: number) => {
|
||||
dkList.value.splice(index, 1)
|
||||
emit('update:modelValue', dkList.value)
|
||||
}
|
||||
|
||||
const dkList = ref<any>([]);
|
||||
|
||||
const kmDkList = ref<any>([]);
|
||||
|
||||
const tabList = ref([
|
||||
{ name: "统一设置", id: "tab-ty" },
|
||||
{ name: "按排课设置", id: "tab-pk" },
|
||||
{ name: "按节次设置", id: "tab-jc" },
|
||||
]);
|
||||
|
||||
const curTabIndex = ref(0);
|
||||
|
||||
const switchTab = (index: number) => {
|
||||
curTabIndex.value = index;
|
||||
};
|
||||
|
||||
const getPkkbList = async () => {
|
||||
const res = await getPkkbByJsRangeTimeApi({
|
||||
jsId: props.data.jsId,
|
||||
startTime: props.data.qjkstime,
|
||||
endTime: props.data.qjjstime,
|
||||
});
|
||||
// 记录原始选课的数据
|
||||
const srcData: any = {};
|
||||
dkList.value.map((item: any) => {
|
||||
const key = item.dktime + item.jcType + item.jc;
|
||||
srcData[key] = {
|
||||
dkJsId: item.dkJsId,
|
||||
dkJsName: item.dkJsName,
|
||||
};
|
||||
});
|
||||
const kmMap: any = {};
|
||||
dkList.value = res.result.map((item: any) => {
|
||||
item.dktime = item.kbtime.split(" ")[0];
|
||||
item.njbjmx = item.bc + item.bjmc;
|
||||
item.jcmc = jsTypeMc[item.jcType] + "第" + item.jc + "节";
|
||||
const key = item.dktime + item.jcType + item.jc;
|
||||
const src = srcData[key];
|
||||
if (src) {
|
||||
item.dkJsId = src.dkJsId;
|
||||
item.dkJsName = src.dkJsName;
|
||||
} else {
|
||||
item.dkJsId = "";
|
||||
item.dkJsName = "";
|
||||
}
|
||||
kmMap[item.pkId] = kmMap[item.pkId] || {
|
||||
pkMc: item.pkMc,
|
||||
pkId: item.pkId,
|
||||
};
|
||||
return item;
|
||||
});
|
||||
// 将kmMap转换成value对应的数组
|
||||
kmDkList.value = Object.values(kmMap);
|
||||
};
|
||||
|
||||
const changeJsByTy = (selected: any, item: any) => {
|
||||
item.dkJsId = selected.value;
|
||||
item.dkJsName = selected.label;
|
||||
const newList = dkList.value.map((dk: any) => {
|
||||
return {
|
||||
...dk,
|
||||
dkJsId: item.dkJsId,
|
||||
dkJsName: item.dkJsName,
|
||||
};
|
||||
});
|
||||
dkList.value = newList;
|
||||
const newKmList = kmDkList.value.map((km: any) => {
|
||||
return {
|
||||
...km,
|
||||
dkJsId: item.dkJsId,
|
||||
dkJsName: item.dkJsName,
|
||||
};
|
||||
});
|
||||
kmDkList.value = newKmList;
|
||||
};
|
||||
|
||||
const changeJsByKm = (selected: any, item: any) => {
|
||||
item.dkJsId = selected.value;
|
||||
item.dkJsName = selected.label;
|
||||
// 同时更新主列表中的数据
|
||||
const newList = dkList.value.map((dk: any) => {
|
||||
if (dk.pkId === item.pkId) {
|
||||
return { ...dk, dkJsId: item.dkJsId, dkJsName: item.dkJsName };
|
||||
}
|
||||
return dk;
|
||||
});
|
||||
dkList.value = newList;
|
||||
};
|
||||
|
||||
const changeJs = (selected: any, item: any) => {
|
||||
item.dkJsId = selected.value;
|
||||
item.dkJsName = selected.label;
|
||||
};
|
||||
|
||||
const validate = async () => {
|
||||
// 如果没有查询到代课信息,重新查询一遍,避免遗漏
|
||||
if (!dkList.value || !dkList.value.length) {
|
||||
await getPkkbList();
|
||||
// 监听modelValue变化
|
||||
watch(() => props.modelValue, (newVal) => {
|
||||
if (Array.isArray(newVal)) {
|
||||
dkList.value = newVal
|
||||
}
|
||||
const list = dkList.value;
|
||||
// 如果还是没有查询需要代课的数据,则返回true
|
||||
if (!list || !list.length) {
|
||||
return true;
|
||||
}
|
||||
// 遍历列表,判断是否有未选择的教师
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
const item = list[i];
|
||||
if (!item.dkJsId) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
// 暴露接口给ref调用
|
||||
const getDkList = () => {
|
||||
return dkList.value;
|
||||
};
|
||||
|
||||
// 暴露接口给外部调用
|
||||
defineExpose({
|
||||
getPkkbList,
|
||||
validate,
|
||||
getDkList,
|
||||
});
|
||||
}, { deep: true, immediate: true })
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.dk-tabs {
|
||||
flex: 1 0 1px;
|
||||
}
|
||||
|
||||
.dk-card {
|
||||
background-color: #ffffff;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 15px;
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
padding: 12px 15px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
|
||||
.applicant-name {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.card-body {
|
||||
padding: 15px;
|
||||
|
||||
.info-row {
|
||||
.js-qj-dk-edit {
|
||||
.dk-header {
|
||||
display: flex;
|
||||
margin-bottom: 10px;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
width: 70px;
|
||||
flex-shrink: 0;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 14px;
|
||||
.dk-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.add-btn {
|
||||
background: #007aff;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 6rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.dk-list {
|
||||
.dk-item {
|
||||
display: flex;
|
||||
.data {
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20rpx;
|
||||
background: #f8f9fa;
|
||||
border-radius: 8rpx;
|
||||
margin-bottom: 16rpx;
|
||||
|
||||
.dk-info {
|
||||
flex: 1;
|
||||
|
||||
.js-name {
|
||||
display: block;
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.course-info {
|
||||
display: block;
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
.dk-actions {
|
||||
.remove-btn {
|
||||
background: #ff3b30;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 6rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card-footer {
|
||||
padding: 12px 15px;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
color: #888;
|
||||
cursor: pointer;
|
||||
|
||||
.arrow {
|
||||
font-size: 16px;
|
||||
color: #ccc;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -1,251 +1,233 @@
|
||||
<template>
|
||||
<BasicLayout :fixed="false">
|
||||
<view class="p-15">
|
||||
<BasicForm @register="register">
|
||||
<template #dkmx>
|
||||
<JsQjDkEdit :data="formData" ref="dkRef" v-if="formData.dkfs === 0" />
|
||||
</template>
|
||||
</BasicForm>
|
||||
</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="navigateBack" />
|
||||
<u-button text="提交" class="mr-15 mr-7" type="primary" @click="submit" />
|
||||
<view class="js-qj-edit">
|
||||
<uni-forms ref="formRef" :model="formData" :rules="formRules">
|
||||
<!-- 基本信息 -->
|
||||
<view class="form-section">
|
||||
<view class="section-title">基本信息</view>
|
||||
<uni-forms-item label="请假类型" name="qjlx">
|
||||
<uni-data-select
|
||||
v-model="formData.qjlx"
|
||||
:localdata="qjlxOptions"
|
||||
placeholder="请选择请假类型"
|
||||
/>
|
||||
</uni-forms-item>
|
||||
|
||||
<uni-forms-item label="请假开始时间" name="qjkstime">
|
||||
<uni-datetime-picker
|
||||
v-model="formData.qjkstime"
|
||||
type="datetime"
|
||||
placeholder="请选择开始时间"
|
||||
/>
|
||||
</uni-forms-item>
|
||||
|
||||
<uni-forms-item label="请假结束时间" name="qjjstime">
|
||||
<uni-datetime-picker
|
||||
v-model="formData.qjjstime"
|
||||
type="datetime"
|
||||
placeholder="请选择结束时间"
|
||||
/>
|
||||
</uni-forms-item>
|
||||
|
||||
<uni-forms-item label="请假原因" name="qjyy">
|
||||
<uni-easyinput
|
||||
v-model="formData.qjyy"
|
||||
type="textarea"
|
||||
placeholder="请输入请假原因"
|
||||
/>
|
||||
</uni-forms-item>
|
||||
</view>
|
||||
|
||||
<!-- 代课信息 -->
|
||||
<view class="form-section">
|
||||
<view class="section-title">代课信息</view>
|
||||
<uni-forms-item label="代课方式" name="dkfs">
|
||||
<uni-data-select
|
||||
v-model="formData.dkfs"
|
||||
:localdata="dkfsOptions"
|
||||
placeholder="请选择代课方式"
|
||||
@change="handleDkfsChange"
|
||||
/>
|
||||
</uni-forms-item>
|
||||
|
||||
<view v-if="formData.dkfs === '0'">
|
||||
<JsQjDkEdit v-model="formData.dkList" />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</BasicLayout>
|
||||
|
||||
<!-- 审批信息 -->
|
||||
<view class="form-section">
|
||||
<view class="section-title">审批信息</view>
|
||||
<BasicSpCsMgr
|
||||
:qjId="qjId"
|
||||
v-model:approvers="formData.sprList"
|
||||
v-model:ccPersons="formData.csrList"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<view class="form-actions">
|
||||
<button type="primary" @click="handleSubmit">提交申请</button>
|
||||
<button @click="handleSave">保存草稿</button>
|
||||
</view>
|
||||
</uni-forms>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import JsQjDkEdit from "./jsQjDkEdit.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";
|
||||
import dayjs from "dayjs";
|
||||
import { useUserStore } from "@/store/modules/user";
|
||||
import { useDicStore } from "@/store/modules/dic";
|
||||
const { getJs, getUser } = useUserStore();
|
||||
const { findByPid } = useDicStore();
|
||||
import { ref, reactive, onMounted, watch } from 'vue'
|
||||
import { useCommonStore } from '@/store/modules/common'
|
||||
import BasicSpCsMgr from '@/components/BasicSpCsMgr/index.vue'
|
||||
import JsQjDkEdit from './jsQjDkEdit.vue'
|
||||
import { findQjByIdApi, jsQjSqApi } from '@/api/base/jsQjApi'
|
||||
|
||||
// 接收外部传入属性
|
||||
const props = withDefaults(defineProps<{
|
||||
data?: any
|
||||
}>(), {
|
||||
data: () => ({
|
||||
id: "",
|
||||
qjlx: "",
|
||||
qjkstime: "",
|
||||
qjjstime: "",
|
||||
qjsc: "",
|
||||
qjsy: "",
|
||||
dkfs: 0,
|
||||
})
|
||||
});
|
||||
// Props
|
||||
const props = defineProps({
|
||||
qjId: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
|
||||
const dkRef = ref<any>(null);
|
||||
// Emits
|
||||
const emit = defineEmits(['submitSuccess'])
|
||||
|
||||
let formData = ref<any>({
|
||||
...props.data,
|
||||
jsId: getJs.id,
|
||||
});
|
||||
// Store
|
||||
const commonStore = useCommonStore()
|
||||
|
||||
if (typeof props.data.dkfs === "string") {
|
||||
nextTick(() => {
|
||||
formData.value.dkfs = parseInt(props.data.dkfs);
|
||||
})
|
||||
// 响应式数据
|
||||
const formRef = ref(null)
|
||||
const formData = reactive({
|
||||
qjlx: '',
|
||||
qjkstime: '',
|
||||
qjjstime: '',
|
||||
qjyy: '',
|
||||
dkfs: '',
|
||||
dkList: [],
|
||||
sprList: [],
|
||||
csrList: []
|
||||
})
|
||||
|
||||
// 表单验证规则
|
||||
const formRules = {
|
||||
qjlx: {
|
||||
rules: [{ required: true, errorMessage: '请选择请假类型' }]
|
||||
},
|
||||
qjkstime: {
|
||||
rules: [{ required: true, errorMessage: '请选择开始时间' }]
|
||||
},
|
||||
qjjstime: {
|
||||
rules: [{ required: true, errorMessage: '请选择结束时间' }]
|
||||
},
|
||||
qjyy: {
|
||||
rules: [{ required: true, errorMessage: '请输入请假原因' }]
|
||||
},
|
||||
dkfs: {
|
||||
rules: [{ required: true, errorMessage: '请选择代课方式' }]
|
||||
}
|
||||
}
|
||||
|
||||
const [register, { setValue, getValue }] = useForm({
|
||||
schema: [
|
||||
{
|
||||
field: "qjlx",
|
||||
label: "请假类型",
|
||||
required: true,
|
||||
component: "BasicPicker",
|
||||
componentProps: {
|
||||
api: findByPid,
|
||||
param: { pid: 1007011432 },
|
||||
rangeKey: "dictionaryValue",
|
||||
savaKey: "dictionaryCode",
|
||||
},
|
||||
},
|
||||
{
|
||||
field: "qjkstime",
|
||||
label: "开始时间",
|
||||
component: "BasicDateTimes",
|
||||
required: true,
|
||||
componentProps: {
|
||||
type: 'datetime',
|
||||
change: (e: string) => changeKsTime(e)
|
||||
},
|
||||
},
|
||||
{
|
||||
field: "qjjstime",
|
||||
label: "结束时间",
|
||||
component: "BasicDateTimes",
|
||||
required: true,
|
||||
componentProps: {
|
||||
type: 'datetime',
|
||||
change: (e: string) => changeJsTime(e)
|
||||
},
|
||||
},
|
||||
{
|
||||
field: "qjsc",
|
||||
label: "请假时长",
|
||||
component: "BasicInput",
|
||||
componentProps: {
|
||||
disabled: true,
|
||||
placeholder: "请输入选择开始时间和结束时间"
|
||||
},
|
||||
},
|
||||
{
|
||||
field: "qjsy",
|
||||
label: "请假事由",
|
||||
component: "BasicInput",
|
||||
required: true,
|
||||
itemProps: {
|
||||
labelPosition: "top",
|
||||
},
|
||||
componentProps: {
|
||||
type: "textarea",
|
||||
},
|
||||
},
|
||||
// {
|
||||
// field: "qjtp",
|
||||
// label: "请假图片",
|
||||
// component: "BasicUpload",
|
||||
// required: true,
|
||||
// itemProps: {
|
||||
// labelPosition: "top",
|
||||
// },
|
||||
// componentProps: {},
|
||||
// },
|
||||
{ interval: true },
|
||||
{
|
||||
field: "dkfs",
|
||||
label: "代课方式",
|
||||
component: "BasicCheckbox",
|
||||
required: true,
|
||||
itemProps: {
|
||||
labelPosition: "top",
|
||||
},
|
||||
componentProps: {
|
||||
data: [
|
||||
{ value: 0, text: "自行协调" },
|
||||
{ value: 1, text: "教科处协调" },
|
||||
{ value: 2, text: "无须代课" },
|
||||
],
|
||||
change: (value: any) => {
|
||||
formData.value.dkfs = value;
|
||||
updateDk();
|
||||
},
|
||||
},
|
||||
},
|
||||
{ colSlot: "dkmx" },
|
||||
],
|
||||
});
|
||||
// 选项数据
|
||||
const qjlxOptions = [
|
||||
{ value: '病假', text: '病假' },
|
||||
{ value: '事假', text: '事假' },
|
||||
{ value: '其他', text: '其他' }
|
||||
]
|
||||
|
||||
const changeKsTime = (selectedTime?: string) => {
|
||||
if (!selectedTime) {
|
||||
return;
|
||||
}
|
||||
formData.value.qjkstime = selectedTime;
|
||||
validateTime();
|
||||
};
|
||||
const dkfsOptions = [
|
||||
{ value: '0', text: '自行协调' },
|
||||
{ value: '1', text: '教务处协调' },
|
||||
{ value: '2', text: '无需代课' }
|
||||
]
|
||||
|
||||
const changeJsTime = (selectedTime?: string) => {
|
||||
if (!selectedTime) {
|
||||
return;
|
||||
// 处理代课方式变化
|
||||
const handleDkfsChange = (value: string) => {
|
||||
if (value !== '0') {
|
||||
formData.dkList = []
|
||||
}
|
||||
formData.value.qjjstime = selectedTime;
|
||||
validateTime();
|
||||
};
|
||||
|
||||
const validateTime = () => {
|
||||
const data = formData.value;
|
||||
if (!data.qjkstime || !data.qjjstime) {
|
||||
return false;
|
||||
}
|
||||
// 使用dayjs库进行时间比较
|
||||
const ksTime = dayjs(data.qjkstime).valueOf();
|
||||
const jsTime = dayjs(data.qjjstime).valueOf();
|
||||
if (ksTime > jsTime) {
|
||||
uni.showToast({
|
||||
title: "请假开始时间不能大于请假结束时间!",
|
||||
icon: "none",
|
||||
});
|
||||
return false;
|
||||
}
|
||||
// 计算请假时长(小时)
|
||||
data.qjsc = Math.round((jsTime - ksTime) / (1000 * 60 * 60)) + "小时";
|
||||
setValue({ qjsc: data.qjsc });
|
||||
updateDk();
|
||||
return true;
|
||||
}
|
||||
|
||||
const updateDk = () => {
|
||||
if (formData.value.dkfs === 0) {
|
||||
nextTick(() => {
|
||||
dkRef.value.getPkkbList();
|
||||
})
|
||||
}
|
||||
};
|
||||
// 加载请假详情
|
||||
const loadQjDetail = async () => {
|
||||
if (!props.qjId) return
|
||||
|
||||
// 初始化
|
||||
setValue(props.data)
|
||||
updateDk();
|
||||
|
||||
const submit = async () => {
|
||||
const fd = await getValue();
|
||||
if (!validateTime()) {
|
||||
return;
|
||||
}
|
||||
const params = { ...fd };
|
||||
if (fd.dkfs === 0) {
|
||||
const flag = await dkRef.value.validate();
|
||||
if (!flag) {
|
||||
uni.showToast({
|
||||
title: "请选择代课教师",
|
||||
icon: "none",
|
||||
});
|
||||
return;
|
||||
}
|
||||
let dkList = dkRef.value.getDkList() || [];
|
||||
if (dkList.length) {
|
||||
params.dkList = dkList.map((item: any) => {
|
||||
const newItem = {...item};
|
||||
newItem.jsId = item.dkJsId;
|
||||
newItem.jsName = item.dkJsName;
|
||||
newItem.pkkbId = item.id;
|
||||
newItem.dktime = item.dktime + " 00:00:00";
|
||||
newItem.id = "";
|
||||
newItem.qjId = "";
|
||||
return newItem;
|
||||
});
|
||||
} else {
|
||||
params.dkList = [];
|
||||
try {
|
||||
const res = await findQjByIdApi(props.qjId)
|
||||
if (res && res.result) {
|
||||
Object.assign(formData, res.result)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载请假详情失败:', error)
|
||||
commonStore.showToast('加载详情失败')
|
||||
}
|
||||
let submitApi = jsQjSqApi;
|
||||
if (props.data && props.data.id) {
|
||||
params.id = props.data.id;
|
||||
submitApi = jsQjCxtjApi
|
||||
} else {
|
||||
params.id = null;
|
||||
params.jsId = getJs.id;
|
||||
params.jsName = getJs.jsxm;
|
||||
}
|
||||
uni.showLoading({ title: "提交中..." });
|
||||
await submitApi(params).then(() => {
|
||||
showToast({ title: "提交成功", icon: "success" });
|
||||
uni.reLaunch({
|
||||
url: "/pages/base/service/index"
|
||||
});
|
||||
});
|
||||
uni.hideLoading();
|
||||
};
|
||||
}
|
||||
|
||||
// 处理提交
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
await formRef.value.validate()
|
||||
|
||||
const submitData = {
|
||||
...formData,
|
||||
jsId: commonStore.userInfo.id
|
||||
}
|
||||
|
||||
await jsQjSqApi(submitData)
|
||||
commonStore.showToast('提交成功')
|
||||
emit('submitSuccess')
|
||||
} catch (error) {
|
||||
console.error('提交失败:', error)
|
||||
commonStore.showToast('提交失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 处理保存
|
||||
const handleSave = async () => {
|
||||
try {
|
||||
// 保存草稿逻辑
|
||||
commonStore.showToast('保存成功')
|
||||
} catch (error) {
|
||||
console.error('保存失败:', error)
|
||||
commonStore.showToast('保存失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 监听qjId变化
|
||||
watch(() => props.qjId, (newVal) => {
|
||||
if (newVal) {
|
||||
loadQjDetail()
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
if (props.qjId) {
|
||||
loadQjDetail()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.js-qj-edit {
|
||||
padding: 20rpx;
|
||||
|
||||
.form-section {
|
||||
margin-bottom: 30rpx;
|
||||
|
||||
.section-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
margin-bottom: 20rpx;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.form-actions {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
margin-top: 40rpx;
|
||||
|
||||
button {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -1,63 +1,163 @@
|
||||
<template>
|
||||
<view class="approval-progress">
|
||||
<view class="progress-title">
|
||||
<!-- <image src="/static/icons/approval.png" class="title-icon"></image> -->
|
||||
<text class="applicant-name">请假流程进度</text>
|
||||
<text class="applicant-name">审批进度</text>
|
||||
</view>
|
||||
<view class="divider"></view>
|
||||
<view class="progress-list">
|
||||
<view class="progress-item" v-for="(task, index) in taskList" :key="index">
|
||||
<view class="progress-item" v-for="(approver, index) in approvalList" :key="index">
|
||||
<view class="progress-item-row">
|
||||
<view class="item-avatar">
|
||||
<image
|
||||
:src="task.avatar || '/static/base/home/user-5-line.png'"
|
||||
:src="approver.avatar || '/static/base/home/user-5-line.png'"
|
||||
class="w-full h-full"
|
||||
></image>
|
||||
</view>
|
||||
<view class="item-middle">
|
||||
<text class="item-name">{{ task.name }}</text>
|
||||
<text class="item-detail">{{ task.assignee || '' }}</text>
|
||||
<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="task.endTime">{{ task.endTime }}</text>
|
||||
<text class="item-status" v-else>待处理</text>
|
||||
<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 < taskList.length - 1"></view>
|
||||
<view class="progress-item-line" v-if="index < approvalList.length - 1"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { getQjActivitiHistoryApi } from "@/api/base/jsQjApi";
|
||||
import dayjs from "dayjs";
|
||||
import { getJsQjApprovalProcessApi } from "@/api/base/jsQjApi";
|
||||
|
||||
// 接收外部传入属性
|
||||
const props = withDefaults(defineProps<{
|
||||
procInstId: string
|
||||
qjId: string
|
||||
}>(), {
|
||||
procInstId: ''
|
||||
qjId: ''
|
||||
});
|
||||
|
||||
// 工作流
|
||||
const taskList = ref<any[]>([]);
|
||||
// 审批流程数据
|
||||
const approvalList = ref<any[]>([]);
|
||||
|
||||
const loadList = async (procInstId: string) => {
|
||||
const res = await getQjActivitiHistoryApi({ processInstanceId: procInstId });
|
||||
const list = res.result || [];
|
||||
// list反向
|
||||
taskList.value = list.reverse();
|
||||
}
|
||||
// 获取审批流程
|
||||
const loadApprovalProcess = async () => {
|
||||
if (!props.qjId) return;
|
||||
|
||||
watch(() => props.procInstId, (newVal, oldVal) => {
|
||||
// 添加回调函数处理逻辑
|
||||
console.log('procInstId changed:', newVal, oldVal);
|
||||
try {
|
||||
// 调用后端API获取审批流程数据
|
||||
const res = await getJsQjApprovalProcessApi(props.qjId, 'JS_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/user-5-line.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 '抄送人';
|
||||
case 'DK': return '代课老师';
|
||||
case 'JWC': return '教科处';
|
||||
default: return '未知';
|
||||
}
|
||||
};
|
||||
|
||||
// 加载模拟数据(备用方案)
|
||||
const loadMockData = () => {
|
||||
const mockData = [
|
||||
{
|
||||
userName: '教师',
|
||||
spType: 'SQ',
|
||||
approveStatus: 'approved',
|
||||
approveTime: new Date(),
|
||||
approveRemark: '申请人提交',
|
||||
avatar: '/static/base/home/user-5-line.png'
|
||||
},
|
||||
{
|
||||
userName: '审批人',
|
||||
spType: 'SP',
|
||||
approveStatus: 'pending',
|
||||
approveTime: null,
|
||||
approveRemark: '待审批',
|
||||
avatar: '/static/base/home/user-5-line.png'
|
||||
}
|
||||
];
|
||||
approvalList.value = mockData;
|
||||
};
|
||||
|
||||
// 获取审批类型文本
|
||||
const getSpTypeText = (spType: string) => {
|
||||
switch (spType) {
|
||||
case 'SQ': return '申请人';
|
||||
case 'SP': return '审批人';
|
||||
case 'CC': return '抄送人';
|
||||
case 'DK': return '代课老师';
|
||||
case 'JWC': 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) {
|
||||
loadList(newVal);
|
||||
loadApprovalProcess();
|
||||
}
|
||||
});
|
||||
|
||||
if (props.procInstId) {
|
||||
loadList(props.procInstId);
|
||||
// 初始化
|
||||
if (props.qjId) {
|
||||
loadApprovalProcess();
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -75,12 +175,6 @@ if (props.procInstId) {
|
||||
color: #333;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.title-icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.applicant-name {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
@ -99,51 +193,88 @@ if (props.procInstId) {
|
||||
flex-direction: column;
|
||||
|
||||
.progress-item {
|
||||
position: relative;
|
||||
|
||||
.progress-item-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px 0;
|
||||
|
||||
.item-avatar {
|
||||
flex: 0 0 40px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
margin-right: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.item-middle {
|
||||
margin-left: 16px;
|
||||
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 {
|
||||
flex: 1 0 1px;
|
||||
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 {
|
||||
width: 1px;
|
||||
height: 30px;
|
||||
background-color: #999;
|
||||
margin: 10px 20px;
|
||||
height: 20px;
|
||||
width: 2px;
|
||||
background-color: #e8e8e8;
|
||||
margin-left: 19px;
|
||||
margin-top: -10px;
|
||||
margin-bottom: -10px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.item-name {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.item-detail {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.item-time,
|
||||
.item-status {
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -1,81 +1,76 @@
|
||||
<template>
|
||||
<view class="leave-page">
|
||||
<JsQjEdit :data="qjData" v-if="qjData.id" />
|
||||
</view>
|
||||
<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>
|
||||
|
||||
<!-- 使用请假申请组件 -->
|
||||
<JsQjEdit :data="qjData" />
|
||||
</view>
|
||||
</BasicLayout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { findQjById } from "@/api/base/jsQjApi";
|
||||
import { xxtsFindByIdApi } from "@/api/base/server";
|
||||
import { useDataStore } from "@/store/modules/data";
|
||||
import { useUserStore } from "@/store/modules/user";
|
||||
import { onLoad } from "@dcloudio/uni-app";
|
||||
import { ref, computed, nextTick } from "vue";
|
||||
import { ref } from "vue";
|
||||
import JsQjEdit from "./components/jsQjEdit.vue";
|
||||
|
||||
const { getJs, loginByOpenId } = useUserStore();
|
||||
const { setData, getData, setXxts, getXxts } = useDataStore();
|
||||
const qjId = ref<string>();
|
||||
const qjData = ref<any>({});
|
||||
|
||||
const dbFlag = ref(false);
|
||||
|
||||
const qjData = computed(() => getData);
|
||||
|
||||
onLoad(async (data: any) => {
|
||||
// 从待办过来的,需要从后端获取数据
|
||||
if (data && data.from && data.from == "db") {
|
||||
dbFlag.value = true;
|
||||
|
||||
// 检查登录状态
|
||||
const isLoggedIn = await loginByOpenId(data.openId);
|
||||
if (!isLoggedIn) {
|
||||
console.log("用户未登录,跳过处理");
|
||||
return;
|
||||
// 加载请假信息
|
||||
const loadQjData = async () => {
|
||||
try {
|
||||
const result = await findQjById({ id: qjId.value });
|
||||
if (result.code === 1) {
|
||||
qjData.value = result.data;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取请假信息失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
// 优先从后端根据url中的id去查询Xxts
|
||||
const xxtsRes = await xxtsFindByIdApi({ id: data.id });
|
||||
if (xxtsRes && xxtsRes.result) {
|
||||
const xxts = xxtsRes.result;
|
||||
|
||||
// 检查待办状态
|
||||
if (xxts.dbZt === "B") {
|
||||
setData({ id: xxts.xxzbId }); // 使用消息推送中的主表ID
|
||||
let url = "/pages/view/hr/jsQj/detail";
|
||||
uni.navigateTo({ url });
|
||||
return;
|
||||
}
|
||||
setXxts(xxts);
|
||||
|
||||
// 根据主表ID去查询请假信息
|
||||
const res = await findQjById({ id: xxts.xxzbId });
|
||||
nextTick(() => {
|
||||
setData(res.result);
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("获取待办信息失败", error);
|
||||
// 如果获取Xxts失败,回退到原来的逻辑
|
||||
const xxtsData = getXxts();
|
||||
if (xxtsData && xxtsData.dbZt === "B") {
|
||||
setXxts(xxtsData);
|
||||
let url = "/pages/view/hr/jsQj/detail";
|
||||
uni.navigateTo({ url });
|
||||
return;
|
||||
}
|
||||
const res = await findQjById({ id: data.id });
|
||||
nextTick(() => {
|
||||
setData(res.result);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
dbFlag.value = false;
|
||||
onLoad((options) => {
|
||||
if (options.qjId) {
|
||||
qjId.value = options.qjId;
|
||||
loadQjData();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.leave-page {
|
||||
height: 100vh;
|
||||
.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;
|
||||
|
||||
.card-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.card-content {
|
||||
padding: 15px;
|
||||
|
||||
.info-text {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
line-height: 1.5;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,321 +1,250 @@
|
||||
<template>
|
||||
<BasicLayout>
|
||||
<!-- 选项卡 -->
|
||||
<BasicTabs
|
||||
class="leave-tabs"
|
||||
ref="tabsRef"
|
||||
:list="tabList"
|
||||
bar-width="60px"
|
||||
scroll-count="4"
|
||||
:current="curTabIndex"
|
||||
@change="switchTab"
|
||||
/>
|
||||
<view v-if="curTabIndex === 0">
|
||||
<view class="p-15">
|
||||
<!-- 请假信息卡片 -->
|
||||
<view class="info-card" v-for="(item, index) in dkList" :key="index">
|
||||
<view class="info-card">
|
||||
<view class="card-header">
|
||||
<text class="applicant-name"
|
||||
>{{ item.dktime }}({{ item.xqLabel }})的{{ item.jcmc }}</text
|
||||
>
|
||||
<text class="applicant-name">教师{{ qjData.jsName }}的请假申请</text>
|
||||
</view>
|
||||
<view class="divider"></view>
|
||||
<view class="card-body">
|
||||
<view class="info-row">
|
||||
<text class="label">排课名称:</text>
|
||||
<text class="value">{{ item.pkName }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="label">上课时间:</text>
|
||||
<text class="value">{{ item.startTime }}-{{ item.endTime }}</text>
|
||||
</view>
|
||||
<view class="card-content">
|
||||
<view class="info-row">
|
||||
<text class="label">请假老师:</text>
|
||||
<view class="value">{{ qjData.jsName }}</view>
|
||||
<text class="value">{{ qjData.jsName }}</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 }} 至 {{ 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.qjsy }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 代课信息 -->
|
||||
<view class="dk-info-card">
|
||||
<view class="card-header">
|
||||
<text class="card-title">代课信息</text>
|
||||
</view>
|
||||
<view class="card-content">
|
||||
<view class="info-row">
|
||||
<text class="label">代课时间:</text>
|
||||
<text class="value">{{ dkData.dktime }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="label">课程名称:</text>
|
||||
<text class="value">{{ dkData.kcmc }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="label">班级:</text>
|
||||
<text class="value">{{ dkData.bjmc }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 确认操作 -->
|
||||
<view class="action-section">
|
||||
<view class="action-title">确认代课</view>
|
||||
<view class="action-buttons">
|
||||
<u-button
|
||||
text="拒绝代课"
|
||||
type="error"
|
||||
@click="handleReject"
|
||||
class="action-btn"
|
||||
/>
|
||||
<u-button
|
||||
text="确认代课"
|
||||
type="primary"
|
||||
@click="handleConfirm"
|
||||
class="action-btn"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<JsQjDetail
|
||||
v-show="curTabIndex === 1"
|
||||
:qjId="qjId"
|
||||
:dbFlag="dbFlag"
|
||||
v-if="qjId && qjId.length"
|
||||
@loadQjData="loadQjData"
|
||||
@loadDkList="loadDkList"
|
||||
/>
|
||||
<!-- 驳回弹窗 -->
|
||||
<u-popup
|
||||
:show="dlgFlag"
|
||||
mode="center"
|
||||
:closeOnClickOverlay="false"
|
||||
@close="closeDlg"
|
||||
>
|
||||
<view class="popup-content">
|
||||
<view class="popup-title">驳回原因</view>
|
||||
<u-input
|
||||
v-model="rejectReason"
|
||||
type="textarea"
|
||||
placeholder="请填写驳回原因"
|
||||
:autoHeight="true"
|
||||
maxlength="200"
|
||||
/>
|
||||
<view class="popup-actions flex-row justify-end mt-4">
|
||||
<u-button class="mr-2" @click="closeDlg">取消</u-button>
|
||||
<u-button type="primary" @click="handleReject">确定</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
<template #bottom>
|
||||
<view class="white-bg-color py-5">
|
||||
<view class="divider"></view>
|
||||
<view class="flex-row items-center pb-10 pt-5">
|
||||
<u-button
|
||||
text="驳回"
|
||||
class="ml-15 mr-7"
|
||||
:plain="true"
|
||||
@click="showDlg"
|
||||
/>
|
||||
<u-button
|
||||
text="同意"
|
||||
class="mr-15 mr-7"
|
||||
type="primary"
|
||||
@click="submit"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</BasicLayout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { jsQjDkQrApi } from "@/api/base/jsQjApi";
|
||||
import { xxtsFindByIdApi } from "@/api/base/server";
|
||||
import { useDataStore } from "@/store/modules/data";
|
||||
import { findQjById, findDkByIdApi } from "@/api/base/jsQjApi";
|
||||
import { useUserStore } from "@/store/modules/user";
|
||||
import { navigateBack } from "@/utils/uniapp";
|
||||
import { onLoad } from "@dcloudio/uni-app";
|
||||
import { ref } from "vue";
|
||||
import JsQjDetail from "./components/jsQjDetail.vue";
|
||||
const { getJs, loginByOpenId } = useUserStore();
|
||||
const { setData, getData, setXxts, getXxts } = useDataStore();
|
||||
|
||||
const dkList = ref<any>([]);
|
||||
const qjData = ref<any>({});
|
||||
|
||||
const dbFlag = ref(false);
|
||||
const { getJs } = useUserStore();
|
||||
|
||||
const qjId = ref<string>();
|
||||
const dkId = ref<string>();
|
||||
const qjData = ref<any>({});
|
||||
const dkData = ref<any>({});
|
||||
|
||||
const dlgFlag = ref(false);
|
||||
const rejectReason = ref("");
|
||||
|
||||
const tabList = ref([
|
||||
{ name: "当前代课", id: "dk-info" },
|
||||
{ name: "请假信息", id: "qj-info" },
|
||||
]);
|
||||
|
||||
const curTabIndex = ref(1);
|
||||
|
||||
const switchTab = (index: number) => {
|
||||
curTabIndex.value = index;
|
||||
};
|
||||
|
||||
const loadQjData = (data: any) => {
|
||||
qjData.value = data;
|
||||
};
|
||||
const loadDkList = (data: any) => {
|
||||
// dbGlId是逗号分隔的字符串,需拆分为数组后判断
|
||||
if (!getXxts.xxglId) {
|
||||
getXxts.xxglId = "";
|
||||
}
|
||||
const idArr = getXxts.xxglId.split(",");
|
||||
dkList.value = [];
|
||||
data.map((dk: any) => {
|
||||
if (idArr.includes(String(dk.id))) {
|
||||
dkList.value.push(dk);
|
||||
// 加载请假信息
|
||||
const loadQjData = async () => {
|
||||
try {
|
||||
const result = await findQjById({ id: qjId.value });
|
||||
if (result.code === 1) {
|
||||
qjData.value = result.data;
|
||||
}
|
||||
});
|
||||
switchTab(0);
|
||||
} catch (error) {
|
||||
console.error('获取请假信息失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const submit = async () => {
|
||||
// 加载代课信息
|
||||
const loadDkData = async () => {
|
||||
try {
|
||||
const result = await findDkByIdApi({ id: dkId.value });
|
||||
if (result.code === 1) {
|
||||
dkData.value = result.data;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取代课信息失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 确认代课
|
||||
const handleConfirm = async () => {
|
||||
const params = {
|
||||
qjId: qjId.value,
|
||||
jsId: getJs.id,
|
||||
qrStatus: 2,
|
||||
qrYj: "同意",
|
||||
qrStatus: "1", // 1表示确认
|
||||
remark: "确认代课"
|
||||
};
|
||||
uni.showLoading({ title: "确认代课中..." });
|
||||
await jsQjDkQrApi(params);
|
||||
uni.hideLoading();
|
||||
navigateBack();
|
||||
};
|
||||
|
||||
const showDlg = () => {
|
||||
dlgFlag.value = true;
|
||||
};
|
||||
|
||||
const closeDlg = () => {
|
||||
dlgFlag.value = false;
|
||||
};
|
||||
|
||||
// 驳回处理
|
||||
const handleReject = async () => {
|
||||
if (!rejectReason.value.trim()) {
|
||||
uni.showToast({ title: "请填写驳回意见", icon: "none" });
|
||||
return;
|
||||
}
|
||||
const params: any = {
|
||||
qjId: qjId.value,
|
||||
jsId: getJs.id,
|
||||
qrStatus: 1, // 1为拒绝
|
||||
qrYj: rejectReason.value,
|
||||
};
|
||||
uni.showLoading({ title: "正在驳回..." });
|
||||
uni.showLoading({ title: "确认中..." });
|
||||
try {
|
||||
await jsQjDkQrApi(params);
|
||||
uni.hideLoading();
|
||||
uni.showToast({ title: "已驳回", icon: "success" });
|
||||
closeDlg();
|
||||
setTimeout(() => {
|
||||
navigateBack();
|
||||
}, 500);
|
||||
} catch (e) {
|
||||
uni.hideLoading();
|
||||
uni.showToast({ title: "确认成功", icon: "success" });
|
||||
navigateBack();
|
||||
} catch (error) {
|
||||
uni.showToast({ title: "确认失败", icon: "error" });
|
||||
}
|
||||
uni.hideLoading();
|
||||
};
|
||||
|
||||
onLoad(async (data: any) => {
|
||||
// 从待办过来的,需要从后端获取数据
|
||||
if (data && data.from && data.from == "db") {
|
||||
dbFlag.value = true;
|
||||
// 拒绝代课
|
||||
const handleReject = async () => {
|
||||
uni.showModal({
|
||||
title: "确认拒绝",
|
||||
content: "确定要拒绝代课吗?",
|
||||
success: async (res) => {
|
||||
if (res.confirm) {
|
||||
const params = {
|
||||
qjId: qjId.value,
|
||||
jsId: getJs.id,
|
||||
qrStatus: "2", // 2表示拒绝
|
||||
remark: "拒绝代课"
|
||||
};
|
||||
|
||||
// 检查登录状态
|
||||
const isLoggedIn = await loginByOpenId(data.openId);
|
||||
if (!isLoggedIn) {
|
||||
console.log("用户未登录,跳过处理");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 优先从后端根据url中的id去查询Xxts
|
||||
const xxtsRes = await xxtsFindByIdApi({ id: data.id });
|
||||
if (xxtsRes && xxtsRes.result) {
|
||||
const xxts = xxtsRes.result;
|
||||
|
||||
// 检查待办状态
|
||||
if (xxts.dbZt === "B") {
|
||||
setData({ id: xxts.xxzbId });
|
||||
let url = "/pages/view/hr/jsQj/detail";
|
||||
uni.navigateTo({ url });
|
||||
return;
|
||||
uni.showLoading({ title: "处理中..." });
|
||||
try {
|
||||
await jsQjDkQrApi(params);
|
||||
uni.showToast({ title: "已拒绝", icon: "success" });
|
||||
navigateBack();
|
||||
} catch (error) {
|
||||
uni.showToast({ title: "操作失败", icon: "error" });
|
||||
}
|
||||
setXxts(xxts);
|
||||
|
||||
// 使用消息推送中的主表ID和关联表ID
|
||||
qjId.value = xxts.xxzbId;
|
||||
dkId.value = xxts.xxglId;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("获取待办信息失败", error);
|
||||
// 如果获取Xxts失败,回退到原来的逻辑
|
||||
qjId.value = data.id;
|
||||
dkId.value = data.dkId;
|
||||
const xxtsData = getXxts();
|
||||
if (xxtsData && xxtsData.dbZt === "B") {
|
||||
setData({ id: data.id });
|
||||
let url = "/pages/view/hr/jsQj/detail";
|
||||
uni.navigateTo({ url });
|
||||
return;
|
||||
uni.hideLoading();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
qjId.value = getData.id;
|
||||
dbFlag.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
onLoad((options) => {
|
||||
if (options.qjId) {
|
||||
qjId.value = options.qjId;
|
||||
}
|
||||
if (options.dkId) {
|
||||
dkId.value = options.dkId;
|
||||
}
|
||||
|
||||
// 加载数据
|
||||
if (qjId.value) {
|
||||
loadQjData();
|
||||
}
|
||||
if (dkId.value) {
|
||||
loadDkData();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.popup-content {
|
||||
width: 80vw;
|
||||
background: #fff;
|
||||
border-radius: 12px;
|
||||
padding: 24px 16px 16px 16px;
|
||||
}
|
||||
.popup-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.popup-actions {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.info-card {
|
||||
margin: 15px;
|
||||
background-color: #fff;
|
||||
<style lang="scss" scoped>
|
||||
.info-card,
|
||||
.dk-info-card {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
|
||||
margin-bottom: 15px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
.card-header {
|
||||
padding: 15px;
|
||||
border-bottom: 1px solid #eee;
|
||||
|
||||
.applicant-name,
|
||||
.card-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
padding: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.applicant-name {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
.card-content {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.info-row {
|
||||
display: flex;
|
||||
margin-bottom: 10px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.divider {
|
||||
height: 1px;
|
||||
background-color: #eee;
|
||||
.label {
|
||||
width: 80px;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
padding: 15px;
|
||||
.value {
|
||||
flex: 1;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.info-row {
|
||||
display: flex;
|
||||
margin-bottom: 10px;
|
||||
.action-section {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 15px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
|
||||
.label {
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
width: 70px;
|
||||
flex-shrink: 0;
|
||||
margin-right: 8px;
|
||||
}
|
||||
.action-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
.info-column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
|
||||
.label {
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
flex-shrink: 0;
|
||||
margin-right: 8px;
|
||||
width: 100%;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.value {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
flex: 1;
|
||||
}
|
||||
.action-btn {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,51 +1,79 @@
|
||||
<template>
|
||||
<view class="leave-page">
|
||||
<!-- 选项卡 -->
|
||||
<BasicTabs class="leave-tabs"
|
||||
ref="tabsRef" :list="tabList" bar-width="60px" scroll-count="4"
|
||||
:current="curTabIndex" @change="switchTab"
|
||||
/>
|
||||
<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 />
|
||||
</view>
|
||||
|
||||
<view class="leave-list" v-else>
|
||||
<JsQjList />
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import JsQjEdit from "./components/jsQjEdit.vue"
|
||||
import JsQjList from "./components/jsQjList.vue"
|
||||
|
||||
// 响应式数据
|
||||
const tabList = ref([
|
||||
{ name: "请假申请", id: "leave-edit" },
|
||||
{ name: "请假记录", id: "leave-list" },
|
||||
]);
|
||||
])
|
||||
|
||||
const curTabIndex = ref(0);
|
||||
const curTabIndex = ref(0)
|
||||
|
||||
const switchTab = (index : number) => {
|
||||
curTabIndex.value = index;
|
||||
// 切换标签页
|
||||
const switchTab = (index: number) => {
|
||||
curTabIndex.value = index
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
.leave-page {
|
||||
flex: 1 0 1px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
||||
|
||||
.leave-tabs {
|
||||
flex: 0 0 45px;
|
||||
background: #fff;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.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>
|
||||
@ -3,10 +3,10 @@
|
||||
<JsQjDetail :qjId="qjId" :dbFlag="dbFlag" v-if="qjId && qjId.length" />
|
||||
<!-- 驳回弹窗 -->
|
||||
<u-popup
|
||||
:show="bhDlgFlag"
|
||||
:show="dlgFlag"
|
||||
mode="center"
|
||||
:closeOnClickOverlay="false"
|
||||
@close="closeBhDlg"
|
||||
@close="closeDlg"
|
||||
>
|
||||
<view class="popup-content">
|
||||
<view class="popup-title">驳回原因</view>
|
||||
@ -18,32 +18,11 @@
|
||||
maxlength="200"
|
||||
/>
|
||||
<view class="popup-actions flex-row justify-end mt-4">
|
||||
<u-button class="mr-2" @click="closeBhDlg">取消</u-button>
|
||||
<u-button class="mr-2" @click="closeDlg">取消</u-button>
|
||||
<u-button type="primary" @click="handleReject">确定</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
<!-- 转办弹窗 -->
|
||||
<u-popup
|
||||
:show="zbDlgFlag"
|
||||
mode="center"
|
||||
:closeOnClickOverlay="false"
|
||||
@close="closeZbDlg"
|
||||
>
|
||||
<view class="popup-content">
|
||||
<view class="popup-title">转到教师</view>
|
||||
<JsPicker
|
||||
@change="changeZbJs"
|
||||
:multiple="false"
|
||||
:excludeIds="excludeIds"
|
||||
/>
|
||||
<!-- 这里可以扩展选择转办对象等内容 -->
|
||||
<view class="popup-actions flex-row justify-end mt-4">
|
||||
<u-button class="mr-2" @click="closeZbDlg">取消</u-button>
|
||||
<u-button type="primary" @click="handleTransfer">确定</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
<template #bottom>
|
||||
<view class="white-bg-color py-5">
|
||||
<view class="divider"></view>
|
||||
@ -52,9 +31,8 @@
|
||||
text="驳回"
|
||||
class="ml-15 mr-7"
|
||||
:plain="true"
|
||||
@click="showBhDlg"
|
||||
@click="showDlg"
|
||||
/>
|
||||
<u-button text="转办" class="mr-7" :plain="true" @click="showZbDlg" />
|
||||
<u-button
|
||||
text="同意"
|
||||
class="mr-15 mr-7"
|
||||
@ -68,63 +46,39 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { jsQjJwcQrApi, jsQjZbApi } from "@/api/base/jsQjApi";
|
||||
import { xxtsFindByIdApi } from "@/api/base/server";
|
||||
import JsPicker from "@/pages/components/JsPicker/index.vue";
|
||||
import { useDataStore } from "@/store/modules/data";
|
||||
import { jsQjJwcQrApi } from "@/api/base/jsQjApi";
|
||||
import { useUserStore } from "@/store/modules/user";
|
||||
import { navigateBack } from "@/utils/uniapp";
|
||||
import { onLoad } from "@dcloudio/uni-app";
|
||||
import { ref } from "vue";
|
||||
import JsQjDetail from "./components/jsQjDetail.vue";
|
||||
const { getJs, loginByOpenId } = useUserStore();
|
||||
const { setData, getData, setXxts, getXxts } = useDataStore();
|
||||
|
||||
const { getJs } = useUserStore();
|
||||
|
||||
const dbFlag = ref(false);
|
||||
|
||||
const qjId = ref<string>();
|
||||
|
||||
// 驳回弹窗
|
||||
const bhDlgFlag = ref(false);
|
||||
const dlgFlag = ref(false);
|
||||
const rejectReason = ref("");
|
||||
|
||||
// 转办弹窗
|
||||
const zbDlgFlag = ref(false);
|
||||
const excludeIds = ref<string[]>([getJs.id]);
|
||||
const zbJs = ref<any>({});
|
||||
|
||||
// 同意处理
|
||||
const submit = async () => {
|
||||
const params = {
|
||||
qjId: qjId.value,
|
||||
jsId: getJs.id,
|
||||
qrStatus: 2,
|
||||
qrYj: "同意",
|
||||
spStatus: "approved",
|
||||
spRemark: "同意",
|
||||
};
|
||||
uni.showLoading({ title: "审批中..." });
|
||||
uni.showLoading({ title: "确认中..." });
|
||||
await jsQjJwcQrApi(params);
|
||||
uni.hideLoading();
|
||||
navigateBack();
|
||||
};
|
||||
|
||||
// 显示/关闭驳回弹窗
|
||||
const showBhDlg = () => {
|
||||
bhDlgFlag.value = true;
|
||||
};
|
||||
const closeBhDlg = () => {
|
||||
bhDlgFlag.value = false;
|
||||
const showDlg = () => {
|
||||
dlgFlag.value = true;
|
||||
};
|
||||
|
||||
// 显示/关闭转办弹窗
|
||||
const showZbDlg = () => {
|
||||
zbDlgFlag.value = true;
|
||||
};
|
||||
const closeZbDlg = () => {
|
||||
zbDlgFlag.value = false;
|
||||
};
|
||||
|
||||
const changeZbJs = (selected: any) => {
|
||||
zbJs.value = selected;
|
||||
const closeDlg = () => {
|
||||
dlgFlag.value = false;
|
||||
};
|
||||
|
||||
// 驳回处理
|
||||
@ -133,115 +87,51 @@ const handleReject = async () => {
|
||||
uni.showToast({ title: "请填写驳回意见", icon: "none" });
|
||||
return;
|
||||
}
|
||||
const params: any = {
|
||||
const params = {
|
||||
qjId: qjId.value,
|
||||
jsId: getJs.id,
|
||||
spStatus: 1, // 1为拒绝
|
||||
spYj: rejectReason.value,
|
||||
spStatus: "rejected",
|
||||
spRemark: rejectReason.value,
|
||||
};
|
||||
uni.showLoading({ title: "正在驳回..." });
|
||||
try {
|
||||
await jsQjJwcQrApi(params);
|
||||
uni.hideLoading();
|
||||
uni.showToast({ title: "已驳回", icon: "success" });
|
||||
closeBhDlg();
|
||||
setTimeout(() => {
|
||||
navigateBack();
|
||||
}, 500);
|
||||
} catch (e) {
|
||||
uni.hideLoading();
|
||||
}
|
||||
await jsQjJwcQrApi(params);
|
||||
uni.hideLoading();
|
||||
closeDlg();
|
||||
navigateBack();
|
||||
};
|
||||
|
||||
// 转办处理
|
||||
const handleTransfer = async () => {
|
||||
if (!zbJs.value || !zbJs.value.value) {
|
||||
uni.showToast({ title: "请填写转办教师", icon: "none" });
|
||||
return;
|
||||
onLoad((options) => {
|
||||
if (options.qjId) {
|
||||
qjId.value = options.qjId;
|
||||
}
|
||||
const params: any = {
|
||||
qjId: qjId.value,
|
||||
jsId: getJs.id,
|
||||
dbId: getXxts.id,
|
||||
zbJsId: zbJs.value.value,
|
||||
zbJsxm: zbJs.value.label,
|
||||
};
|
||||
uni.showLoading({ title: "正在转办..." });
|
||||
try {
|
||||
await jsQjZbApi(params);
|
||||
uni.hideLoading();
|
||||
uni.showToast({ title: "已转办", icon: "success" });
|
||||
closeBhDlg();
|
||||
setTimeout(() => {
|
||||
navigateBack();
|
||||
}, 500);
|
||||
} catch (e) {
|
||||
uni.hideLoading();
|
||||
}
|
||||
};
|
||||
|
||||
onLoad(async (data: any) => {
|
||||
// 从待办过来的,需要从后端获取数据
|
||||
if (data && data.from && data.from == "db") {
|
||||
dbFlag.value = true;
|
||||
|
||||
// 检查登录状态
|
||||
const isLoggedIn = await loginByOpenId(data.openId);
|
||||
if (!isLoggedIn) {
|
||||
console.log("用户未登录,跳过处理");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 优先从后端根据url中的id去查询Xxts
|
||||
const xxtsRes = await xxtsFindByIdApi({ id: data.id });
|
||||
if (xxtsRes && xxtsRes.result) {
|
||||
const xxts = xxtsRes.result;
|
||||
|
||||
// 检查待办状态
|
||||
if (xxts.dbZt === "B") {
|
||||
setData({ id: xxts.xxzbId });
|
||||
let url = "/pages/view/hr/jsQj/detail";
|
||||
uni.navigateTo({ url });
|
||||
return;
|
||||
}
|
||||
setXxts(xxts);
|
||||
|
||||
// 使用消息推送中的主表ID
|
||||
qjId.value = xxts.xxzbId;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("获取待办信息失败", error);
|
||||
// 如果获取Xxts失败,回退到原来的逻辑
|
||||
qjId.value = data.id;
|
||||
const xxtsData = getXxts();
|
||||
if (xxtsData && xxtsData.dbZt === "B") {
|
||||
setData({ id: data.id });
|
||||
let url = "/pages/view/hr/jsQj/detail";
|
||||
uni.navigateTo({ url });
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
qjId.value = getData.id;
|
||||
dbFlag.value = false;
|
||||
if (options.dbFlag) {
|
||||
dbFlag.value = options.dbFlag === "true";
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
<style lang="scss" scoped>
|
||||
.popup-content {
|
||||
width: 80vw;
|
||||
background: #fff;
|
||||
border-radius: 12px;
|
||||
padding: 24px 16px 16px 16px;
|
||||
background-color: white;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.popup-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 12px;
|
||||
margin-bottom: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.popup-actions {
|
||||
margin-top: 16px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.divider {
|
||||
height: 1px;
|
||||
background-color: #eee;
|
||||
margin: 0 15px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,194 +1,366 @@
|
||||
<template>
|
||||
<BasicLayout>
|
||||
<!-- 选项卡 -->
|
||||
<BasicTabs
|
||||
class="leave-tabs"
|
||||
ref="tabsRef"
|
||||
:list="tabList"
|
||||
bar-width="60px"
|
||||
scroll-count="4"
|
||||
:current="curTabIndex"
|
||||
@change="switchTab"
|
||||
/>
|
||||
<view class="pl-15 pr-15" v-show="curTabIndex === 0">
|
||||
<JsQjDkEdit
|
||||
:data="formData"
|
||||
ref="dkRef"
|
||||
v-if="formData && formData.dkfs === '1'"
|
||||
/>
|
||||
</view>
|
||||
<JsQjDetail
|
||||
v-show="curTabIndex === 1"
|
||||
:qjId="qjId"
|
||||
:dbFlag="dbFlag"
|
||||
v-if="qjId && qjId.length"
|
||||
@loadQjData="loadQjData"
|
||||
/>
|
||||
<template #bottom>
|
||||
<view class="white-bg-color py-5">
|
||||
<view class="divider"></view>
|
||||
<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="mr-15 mr-7"
|
||||
type="primary"
|
||||
@click="submit"
|
||||
/>
|
||||
<view class="p-15">
|
||||
<!-- 请假信息卡片 -->
|
||||
<view class="info-card">
|
||||
<view class="card-header">
|
||||
<text class="applicant-name">教师{{ qjData.jsName }}的请假申请</text>
|
||||
</view>
|
||||
<view class="card-content">
|
||||
<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 }} 至 {{ 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.qjsy }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<!-- 代课安排 -->
|
||||
<view class="dk-arrange-card">
|
||||
<view class="card-header">
|
||||
<text class="card-title">代课安排</text>
|
||||
</view>
|
||||
<view class="card-content">
|
||||
<view class="dk-list">
|
||||
<view
|
||||
v-for="(dk, index) in dkList"
|
||||
:key="index"
|
||||
class="dk-item"
|
||||
>
|
||||
<view class="dk-header">
|
||||
<text class="dk-title">代课{{ index + 1 }}</text>
|
||||
<u-button
|
||||
text="删除"
|
||||
size="mini"
|
||||
type="error"
|
||||
@click="removeDk(index)"
|
||||
/>
|
||||
</view>
|
||||
<view class="dk-content">
|
||||
<view class="info-row">
|
||||
<text class="label">代课教师:</text>
|
||||
<text class="value">{{ dk.jsName }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="label">代课时间:</text>
|
||||
<text class="value">{{ dk.dktime }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="label">课程名称:</text>
|
||||
<text class="value">{{ dk.kcmc }}</text>
|
||||
</view>
|
||||
<view class="info-row">
|
||||
<text class="label">班级:</text>
|
||||
<text class="value">{{ dk.bjmc }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="add-dk-section">
|
||||
<u-button
|
||||
text="添加代课安排"
|
||||
type="primary"
|
||||
@click="showDkSelector"
|
||||
class="add-btn"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 提交按钮 -->
|
||||
<view class="submit-section">
|
||||
<u-button
|
||||
text="提交协调结果"
|
||||
type="primary"
|
||||
@click="handleSubmit"
|
||||
class="submit-btn"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 代课教师选择弹窗 -->
|
||||
<u-popup
|
||||
:show="showSelector"
|
||||
mode="bottom"
|
||||
height="70%"
|
||||
@close="closeSelector"
|
||||
>
|
||||
<view class="selector-container">
|
||||
<view class="selector-header">
|
||||
<text class="selector-title">选择代课教师</text>
|
||||
<u-button text="确定" type="primary" @click="confirmSelection" />
|
||||
</view>
|
||||
<view class="selector-content">
|
||||
<u-checkbox-group v-model="selectedDkIds">
|
||||
<view
|
||||
v-for="dk in availableDkList"
|
||||
:key="dk.id"
|
||||
class="dk-option"
|
||||
>
|
||||
<u-checkbox
|
||||
:value="dk.id"
|
||||
:label="`${dk.jsName} - ${dk.kcmc} - ${dk.bjmc}`"
|
||||
/>
|
||||
</view>
|
||||
</u-checkbox-group>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</BasicLayout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { jsQjJwcXtApi } from "@/api/base/jsQjApi";
|
||||
import { xxtsFindByIdApi } from "@/api/base/server";
|
||||
import { useDataStore } from "@/store/modules/data";
|
||||
import { findQjById, getPkkbByJsRangeTimeApi } from "@/api/base/jsQjApi";
|
||||
import { useUserStore } from "@/store/modules/user";
|
||||
import { navigateBack, showToast } from "@/utils/uniapp";
|
||||
import { navigateBack } from "@/utils/uniapp";
|
||||
import { onLoad } from "@dcloudio/uni-app";
|
||||
import { ref, nextTick } from "vue";
|
||||
import JsQjDetail from "./components/jsQjDetail.vue";
|
||||
import JsQjDkEdit from "./components/jsQjDkEdit.vue";
|
||||
const { getJs, loginByOpenId } = useUserStore();
|
||||
const { setData, getData, setXxts, getXxts } = useDataStore();
|
||||
import { ref } from "vue";
|
||||
|
||||
const dkRef = ref<any>(null);
|
||||
|
||||
const formData = ref<any>({});
|
||||
|
||||
const dbFlag = ref(false);
|
||||
const { getJs } = useUserStore();
|
||||
|
||||
const qjId = ref<string>();
|
||||
const qjData = ref<any>({});
|
||||
const dkList = ref<any[]>([]);
|
||||
const availableDkList = ref<any[]>([]);
|
||||
const selectedDkIds = ref<string[]>([]);
|
||||
const showSelector = ref(false);
|
||||
|
||||
const tabList = ref([
|
||||
{ name: "代课协调", id: "dk-edit" },
|
||||
{ name: "请假信息", id: "qj-info" },
|
||||
]);
|
||||
|
||||
const curTabIndex = ref(1);
|
||||
let initDkTabFlag = false;
|
||||
|
||||
const switchTab = (index: number) => {
|
||||
curTabIndex.value = index;
|
||||
if (index === 0 && !initDkTabFlag) {
|
||||
nextTick(() => {
|
||||
dkRef.value.getPkkbList();
|
||||
initDkTabFlag = true;
|
||||
});
|
||||
// 加载请假信息
|
||||
const loadQjData = async () => {
|
||||
try {
|
||||
const result = await findQjById({ id: qjId.value });
|
||||
if (result.code === 1) {
|
||||
qjData.value = result.data;
|
||||
// 加载可用的代课安排
|
||||
await loadAvailableDkList();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取请假信息失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const loadQjData = (data: any) => {
|
||||
formData.value = data;
|
||||
switchTab(0);
|
||||
// 加载可用的代课安排
|
||||
const loadAvailableDkList = async () => {
|
||||
try {
|
||||
const params = {
|
||||
jsId: qjData.value.jsId,
|
||||
startTime: qjData.value.qjkstime,
|
||||
endTime: qjData.value.qjjstime
|
||||
};
|
||||
const result = await getPkkbByJsRangeTimeApi(params);
|
||||
if (result.code === 1) {
|
||||
availableDkList.value = result.data || [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取可用代课安排失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const submit = async () => {
|
||||
const params = {
|
||||
qjId: qjId.value,
|
||||
jsId: getJs.id,
|
||||
dkList: [],
|
||||
};
|
||||
const flag = await dkRef.value.validate();
|
||||
if (!flag) {
|
||||
// 显示代课选择器
|
||||
const showDkSelector = () => {
|
||||
selectedDkIds.value = [];
|
||||
showSelector.value = true;
|
||||
};
|
||||
|
||||
// 关闭选择器
|
||||
const closeSelector = () => {
|
||||
showSelector.value = false;
|
||||
selectedDkIds.value = [];
|
||||
};
|
||||
|
||||
// 确认选择
|
||||
const confirmSelection = () => {
|
||||
const selectedDks = availableDkList.value.filter(dk =>
|
||||
selectedDkIds.value.includes(dk.id)
|
||||
);
|
||||
|
||||
// 添加到代课列表
|
||||
selectedDks.forEach(dk => {
|
||||
const dkItem = {
|
||||
jsId: dk.jsId,
|
||||
jsName: dk.jsName,
|
||||
dktime: dk.dktime,
|
||||
kcmc: dk.kcmc,
|
||||
bjmc: dk.bjmc,
|
||||
pkkbId: dk.id
|
||||
};
|
||||
dkList.value.push(dkItem);
|
||||
});
|
||||
|
||||
closeSelector();
|
||||
};
|
||||
|
||||
// 删除代课安排
|
||||
const removeDk = (index: number) => {
|
||||
dkList.value.splice(index, 1);
|
||||
};
|
||||
|
||||
// 提交协调结果
|
||||
const handleSubmit = async () => {
|
||||
if (dkList.value.length === 0) {
|
||||
uni.showToast({
|
||||
title: "请选择代课教师",
|
||||
icon: "none",
|
||||
title: "请至少添加一个代课安排",
|
||||
icon: "none"
|
||||
});
|
||||
return;
|
||||
}
|
||||
let dkList = dkRef.value.getDkList() || [];
|
||||
if (dkList.length) {
|
||||
params.dkList = dkList.map((item: any) => {
|
||||
const newItem = { ...item };
|
||||
newItem.jsId = item.dkJsId;
|
||||
newItem.jsName = item.dkJsName;
|
||||
newItem.pkkbId = item.id;
|
||||
newItem.dktime = item.dktime + " 00:00:00";
|
||||
newItem.id = "";
|
||||
newItem.qjId = "";
|
||||
return newItem;
|
||||
});
|
||||
}
|
||||
|
||||
const params = {
|
||||
qjId: qjId.value,
|
||||
jsId: getJs.id,
|
||||
dkList: dkList.value
|
||||
};
|
||||
|
||||
uni.showLoading({ title: "提交中..." });
|
||||
await jsQjJwcXtApi(params).then(() => {
|
||||
showToast({ title: "提交成功", icon: "success" });
|
||||
uni.reLaunch({
|
||||
url: "/pages/base/service/index",
|
||||
});
|
||||
});
|
||||
try {
|
||||
await jsQjJwcXtApi(params);
|
||||
uni.showToast({ title: "协调成功", icon: "success" });
|
||||
navigateBack();
|
||||
} catch (error) {
|
||||
uni.showToast({ title: "协调失败", icon: "error" });
|
||||
}
|
||||
uni.hideLoading();
|
||||
};
|
||||
|
||||
onLoad(async (data: any) => {
|
||||
// 从待办过来的,需要从后端获取数据
|
||||
if (data && data.from && data.from == "db") {
|
||||
dbFlag.value = true;
|
||||
|
||||
// 检查登录状态
|
||||
const isLoggedIn = await loginByOpenId(data.openId);
|
||||
if (!isLoggedIn) {
|
||||
console.log("用户未登录,跳过处理");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 优先从后端根据url中的id去查询Xxts
|
||||
const xxtsRes = await xxtsFindByIdApi({ id: data.id });
|
||||
if (xxtsRes && xxtsRes.result) {
|
||||
const xxts = xxtsRes.result;
|
||||
|
||||
// 检查待办状态
|
||||
if (xxts.dbZt === "B") {
|
||||
setData({ id: xxts.xxzbId });
|
||||
let url = "/pages/view/hr/jsQj/detail";
|
||||
uni.navigateTo({ url });
|
||||
return;
|
||||
}
|
||||
setXxts(xxts);
|
||||
|
||||
// 使用消息推送中的主表ID
|
||||
qjId.value = xxts.xxzbId;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("获取待办信息失败", error);
|
||||
// 如果获取Xxts失败,回退到原来的逻辑
|
||||
qjId.value = data.id;
|
||||
const xxtsData = getXxts();
|
||||
if (xxtsData && xxtsData.dbZt === "B") {
|
||||
setData({ id: data.id });
|
||||
let url = "/pages/view/hr/jsQj/detail";
|
||||
uni.navigateTo({ url });
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
qjId.value = getData.id;
|
||||
dbFlag.value = false;
|
||||
onLoad((options) => {
|
||||
if (options.qjId) {
|
||||
qjId.value = options.qjId;
|
||||
loadQjData();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.popup-content {
|
||||
width: 80vw;
|
||||
background: #fff;
|
||||
border-radius: 12px;
|
||||
padding: 24px 16px 16px 16px;
|
||||
<style lang="scss" scoped>
|
||||
.info-card,
|
||||
.dk-arrange-card {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 15px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.popup-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 12px;
|
||||
|
||||
.card-header {
|
||||
padding: 15px;
|
||||
border-bottom: 1px solid #eee;
|
||||
|
||||
.applicant-name,
|
||||
.card-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
.popup-actions {
|
||||
margin-top: 16px;
|
||||
|
||||
.card-content {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.info-row {
|
||||
display: flex;
|
||||
margin-bottom: 10px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.label {
|
||||
width: 80px;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.value {
|
||||
flex: 1;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.dk-list {
|
||||
.dk-item {
|
||||
border: 1px solid #eee;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.dk-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
background-color: #f5f5f5;
|
||||
border-bottom: 1px solid #eee;
|
||||
|
||||
.dk-title {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.dk-content {
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.add-dk-section {
|
||||
margin-top: 15px;
|
||||
text-align: center;
|
||||
|
||||
.add-btn {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.submit-section {
|
||||
margin-top: 20px;
|
||||
|
||||
.submit-btn {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.selector-container {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.selector-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 15px;
|
||||
border-bottom: 1px solid #eee;
|
||||
|
||||
.selector-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.selector-content {
|
||||
flex: 1;
|
||||
padding: 15px;
|
||||
overflow-y: auto;
|
||||
|
||||
.dk-option {
|
||||
padding: 10px 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,40 +1,36 @@
|
||||
<template>
|
||||
<BasicLayout>
|
||||
<JsQjDetail :qjId="qjId" :dbFlag="dbFlag" v-if="qjId && qjId.length" />
|
||||
<!-- 驳回弹窗 -->
|
||||
<u-popup
|
||||
:show="dlgFlag"
|
||||
mode="center"
|
||||
:closeOnClickOverlay="false"
|
||||
@close="closeDlg"
|
||||
>
|
||||
<view class="popup-content">
|
||||
<view class="popup-title">驳回原因</view>
|
||||
<u-input
|
||||
v-model="rejectReason"
|
||||
type="textarea"
|
||||
placeholder="请填写驳回原因"
|
||||
:autoHeight="true"
|
||||
maxlength="200"
|
||||
/>
|
||||
<view class="popup-actions flex-row justify-end mt-4">
|
||||
<u-button class="mr-2" @click="closeDlg">取消</u-button>
|
||||
<u-button type="primary" @click="handleReject">确定</u-button>
|
||||
<view class="qj-detail">
|
||||
<!-- 使用公共组件展示请假详情 -->
|
||||
<JsQjDetail
|
||||
:qjId="qjId"
|
||||
:dbFlag="dbFlag"
|
||||
@loadQjData="handleQjDataLoaded"
|
||||
@loadDkList="handleDkListLoaded"
|
||||
/>
|
||||
|
||||
<!-- 审批意见卡片 -->
|
||||
<view class="info-card">
|
||||
<view class="card-header">
|
||||
<text class="applicant-name">审批意见</text>
|
||||
</view>
|
||||
<view class="divider"></view>
|
||||
<view class="card-body">
|
||||
<BasicForm @register="register" />
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</view>
|
||||
<template #bottom>
|
||||
<view class="white-bg-color py-5">
|
||||
<view class="divider"></view>
|
||||
<view class="flex-row items-center pb-10 pt-5">
|
||||
<u-button
|
||||
text="驳回"
|
||||
text="取消"
|
||||
class="ml-15 mr-7"
|
||||
:plain="true"
|
||||
@click="showDlg"
|
||||
@click="navigateBack"
|
||||
/>
|
||||
<u-button
|
||||
text="同意"
|
||||
text="提交"
|
||||
class="mr-15 mr-7"
|
||||
type="primary"
|
||||
@click="submit"
|
||||
@ -46,72 +42,114 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { jsQjSpApi } from "@/api/base/jsQjApi";
|
||||
import { xxtsFindByIdApi } from "@/api/base/server";
|
||||
import { useDataStore } from "@/store/modules/data";
|
||||
import { useUserStore } from "@/store/modules/user";
|
||||
import { navigateBack } from "@/utils/uniapp";
|
||||
import { onLoad } from "@dcloudio/uni-app";
|
||||
import { ref } from "vue";
|
||||
import { useForm } from "@/components/BasicForm/hooks/useForm";
|
||||
import { navigateBack } from "@/utils/uniapp";
|
||||
import { jsQjSpApi } from "@/api/base/jsQjApi";
|
||||
import { useUserStore } from "@/store/modules/user";
|
||||
import { useDataStore } from "@/store/modules/data";
|
||||
import { ref, computed } from "vue";
|
||||
import { xxtsFindByIdApi } from "@/api/base/server";
|
||||
import JsQjDetail from "./components/jsQjDetail.vue";
|
||||
|
||||
const { getJs, loginByOpenId } = useUserStore();
|
||||
const { setData, getData, setXxts, getXxts } = useDataStore();
|
||||
const { getData, setXxts, setData, getXxts } = useDataStore();
|
||||
|
||||
const dbFlag = ref(false);
|
||||
const qjId = ref('');
|
||||
|
||||
const qjId = ref<string>();
|
||||
// 请假基础数据
|
||||
const qjData = computed(() => getData || {});
|
||||
|
||||
const dlgFlag = ref(false);
|
||||
const rejectReason = ref("");
|
||||
const [register, { getValue }] = useForm({
|
||||
schema: [
|
||||
{
|
||||
field: "spStatus",
|
||||
label: "审批意见",
|
||||
component: "BasicCheckbox",
|
||||
required: true,
|
||||
itemProps: {
|
||||
labelPosition: "top",
|
||||
},
|
||||
componentProps: {
|
||||
data: [
|
||||
{ value: 'approved', text: "同意" },
|
||||
{ value: 'rejected', text: "拒绝" },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
field: "spYj",
|
||||
label: "审批说明",
|
||||
component: "BasicInput",
|
||||
required: true,
|
||||
itemProps: {
|
||||
labelPosition: "top",
|
||||
},
|
||||
componentProps: {
|
||||
type: "textarea",
|
||||
placeholder: "请输入审批说明",
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// 请假基础数据
|
||||
const qjData = computed(() => getData || {});
|
||||
|
||||
const handleQjDataLoaded = (data: any) => {
|
||||
setData(data);
|
||||
};
|
||||
|
||||
const handleDkListLoaded = (list: any[]) => {
|
||||
// 代课明细数据已由JsQjDetail组件处理
|
||||
};
|
||||
|
||||
const submit = async () => {
|
||||
const params = {
|
||||
qjId: qjId.value,
|
||||
jsId: getJs.id,
|
||||
spStatus: 2,
|
||||
spYj: "同意",
|
||||
};
|
||||
uni.showLoading({ title: "审批中..." });
|
||||
await jsQjSpApi(params);
|
||||
uni.hideLoading();
|
||||
navigateBack();
|
||||
};
|
||||
|
||||
const showDlg = () => {
|
||||
dlgFlag.value = true;
|
||||
};
|
||||
|
||||
const closeDlg = () => {
|
||||
dlgFlag.value = false;
|
||||
};
|
||||
|
||||
// 驳回处理
|
||||
const handleReject = async () => {
|
||||
if (!rejectReason.value.trim()) {
|
||||
uni.showToast({ title: "请填写驳回意见", icon: "none" });
|
||||
return;
|
||||
}
|
||||
const params: any = {
|
||||
qjId: qjId.value,
|
||||
jsId: getJs.id,
|
||||
spStatus: 1, // 1为拒绝
|
||||
spYj: rejectReason.value,
|
||||
};
|
||||
uni.showLoading({ title: "正在驳回..." });
|
||||
try {
|
||||
await jsQjSpApi(params);
|
||||
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 jsQjSpApi(params);
|
||||
|
||||
uni.hideLoading();
|
||||
uni.showToast({ title: "已驳回", icon: "success" });
|
||||
closeDlg();
|
||||
uni.showToast({
|
||||
title: '审批提交成功',
|
||||
icon: 'success'
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
navigateBack();
|
||||
}, 500);
|
||||
} catch (e) {
|
||||
}, 1500);
|
||||
|
||||
} catch (error) {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: '提交失败,请重试',
|
||||
icon: 'none'
|
||||
});
|
||||
console.error('审批提交失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
onLoad(async (data: any) => {
|
||||
onLoad(async (data?: any) => {
|
||||
// 从待办过来的,需要从后端获取数据
|
||||
if (data && data.from && data.from == "db") {
|
||||
dbFlag.value = true;
|
||||
@ -128,7 +166,6 @@ onLoad(async (data: any) => {
|
||||
const xxtsRes = await xxtsFindByIdApi({ id: data.id });
|
||||
if (xxtsRes && xxtsRes.result) {
|
||||
const xxts = xxtsRes.result;
|
||||
|
||||
// 检查待办状态
|
||||
if (xxts.dbZt === "B") {
|
||||
setData({ id: xxts.xxzbId });
|
||||
@ -137,42 +174,61 @@ onLoad(async (data: any) => {
|
||||
return;
|
||||
}
|
||||
setXxts(xxts);
|
||||
|
||||
// 使用消息推送中的主表ID
|
||||
qjId.value = xxts.xxzbId;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("获取待办信息失败", error);
|
||||
// 如果获取Xxts失败,回退到原来的逻辑
|
||||
qjId.value = data.id;
|
||||
const xxtsData = getXxts();
|
||||
const xxtsData = getXxts;
|
||||
if (xxtsData && xxtsData.dbZt === "B") {
|
||||
setData({ id: data.id });
|
||||
let url = "/pages/view/hr/jsQj/detail";
|
||||
uni.navigateTo({ url });
|
||||
return;
|
||||
}
|
||||
qjId.value = data.id;
|
||||
}
|
||||
} else {
|
||||
qjId.value = getData.id;
|
||||
dbFlag.value = false;
|
||||
// 直接加载请假详情
|
||||
qjId.value = getData.id || '';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.popup-content {
|
||||
width: 80vw;
|
||||
background: #fff;
|
||||
border-radius: 12px;
|
||||
padding: 24px 16px 16px 16px;
|
||||
<style lang="scss" scoped>
|
||||
.qj-detail {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
.popup-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.popup-actions {
|
||||
margin-top: 16px;
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
<!-- 陪餐教师选择 -->
|
||||
<view class="section" v-if="curBj">
|
||||
<view class="section-title">陪餐教师</view>
|
||||
<JsPicker
|
||||
<BasicJsPicker
|
||||
:multiple="true"
|
||||
@change="jsXz"
|
||||
placeholder="请选择陪餐教师"
|
||||
@ -252,7 +252,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import NjBjPicker from '@/pages/components/NjBjPicker/index.vue'
|
||||
import JsPicker from '@/pages/components/JsPicker/index.vue'
|
||||
import BasicJsPicker from '@/components/BasicJsPicker/Picker.vue'
|
||||
import DmPsComponent from '@/pages/components/dmPs/index.vue'
|
||||
import { getClassStudentDmDataApi, submitJcDmDataApi } from '@/api/base/jcApi'
|
||||
import { imagUrl } from "@/utils";
|
||||
|
||||
@ -987,12 +987,12 @@ const ensureTeacherDataCached = async () => {
|
||||
if (!hasTeacherData) {
|
||||
console.log('localStorage中没有教师数据,开始获取并缓存');
|
||||
|
||||
// 导入并调用getAllJs方法
|
||||
// 导入并调用getAllJsBasicInfoVo方法
|
||||
const { useCommonStore } = await import('@/store/modules/common');
|
||||
const commonStore = useCommonStore();
|
||||
|
||||
try {
|
||||
const result = await commonStore.getAllJs();
|
||||
const result = await commonStore.getAllJsBasicInfoVo();
|
||||
console.log('成功获取教师数据并写入缓存:', result);
|
||||
} catch (error) {
|
||||
console.error('获取教师数据失败:', error);
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import {
|
||||
bjFindByNjId,
|
||||
jsFindAll,
|
||||
jsFindAllBasicInfoVo,
|
||||
njFindAll,
|
||||
zwFindAllApi,
|
||||
zwGetListByLxApi,
|
||||
@ -50,6 +51,20 @@ export const useCommonStore = defineStore({
|
||||
}
|
||||
return Promise.resolve(this.data.allJs);
|
||||
},
|
||||
// 所有教师基础信息(Vo版本)
|
||||
async getAllJsBasicInfoVo(): Promise<any> {
|
||||
// if (!this.data.allJsBasicInfoVo) {
|
||||
this.data.allJsBasicInfoVo = await jsFindAllBasicInfoVo();
|
||||
// }
|
||||
return Promise.resolve(this.data.allJsBasicInfoVo);
|
||||
},
|
||||
// 获取教师列表(Vo版本,用于审批人/抄送人选择)
|
||||
async getJsList(): Promise<any> {
|
||||
if (!this.data.jsList) {
|
||||
this.data.jsList = await jsFindAllBasicInfoVo();
|
||||
}
|
||||
return Promise.resolve(this.data.jsList);
|
||||
},
|
||||
// 所有职务
|
||||
async getAllZw(): Promise<any> {
|
||||
if (!this.data.allZw) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user