调整教师请假逻辑
This commit is contained in:
parent
ef933f954c
commit
68fd30a282
@ -16,6 +16,11 @@ export const jsFindAll = async () => {
|
|||||||
return await get("/api/js/findAllBasicInfo");
|
return await get("/api/js/findAllBasicInfo");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 所有教师基础信息(Vo版本)
|
||||||
|
export const jsFindAllBasicInfoVo = async () => {
|
||||||
|
return await get("/api/js/findAllBasicInfoVo");
|
||||||
|
};
|
||||||
|
|
||||||
// 所有职务
|
// 所有职务
|
||||||
export const zwFindAllApi = async () => {
|
export const zwFindAllApi = async () => {
|
||||||
return await get("/api/zw/findAll");
|
return await get("/api/zw/findAll");
|
||||||
|
|||||||
@ -4,26 +4,26 @@
|
|||||||
import { get, post } from "@/utils/request";
|
import { get, post } from "@/utils/request";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 申请
|
* 教师申请请假
|
||||||
*/
|
*/
|
||||||
export const jsQjSqApi = async (params: any) => {
|
export const jsQjSqApi = async (params: any) => {
|
||||||
return await post("/api/jsQj/sq", params);
|
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) => {
|
export const jsQjCxtjApi = async (params: any) => {
|
||||||
return await post("/api/jsQj/cxtj", params);
|
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) => {
|
export const jsQjDkQrApi = async (params: any) => {
|
||||||
return await post("/api/jsQj/dk/qr", params);
|
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) => {
|
export const getApproversByRuleId = async (ruleId: string) => {
|
||||||
return await get("/activiti/history/historicFlow", params);
|
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>
|
<template>
|
||||||
<view class="basic-js-picker">
|
<view class="js-picker">
|
||||||
<view class="js-selected" @click="showPicker">
|
<!-- 自定义触发器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">
|
<view class="js-selected-name">
|
||||||
<text class="data" v-if="selectedList && selectedList.length">{{ getShowSelectedName() }}</text>
|
<text class="data" v-if="selectedList && selectedList.length">{{ getShowSelectedName() }}</text>
|
||||||
<text class="data" style="color: #999;" v-else>{{ placeholder }}</text>
|
<text class="data" style="color: #999;" v-else>{{ placeholder }}</text>
|
||||||
</view>
|
</view>
|
||||||
<uni-icons type="arrowright" size="16" color="#999"></uni-icons>
|
<uni-icons type="arrowright" size="16" color="#999"></uni-icons>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<u-popup :show="showPopup" @close="showPopup=false">
|
<u-popup :show="showPopup" @close="showPopup=false">
|
||||||
<view class="js-picker-popup">
|
<view class="js-picker-popup">
|
||||||
<view class="js-picker-header">
|
<view class="js-picker-header">
|
||||||
@ -14,7 +20,7 @@
|
|||||||
<view class="js-picker-title">{{ title }}</view>
|
<view class="js-picker-title">{{ title }}</view>
|
||||||
<view class="js-ok-btn" @click="handleOk">确定</view>
|
<view class="js-ok-btn" @click="handleOk">确定</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="js-picker-search" v-if="showSearch">
|
<view class="js-picker-search">
|
||||||
<BasicSearch @change="handleSearch" :showAction="false" height="36" :placeholder="searchPlaceholder"/>
|
<BasicSearch @change="handleSearch" :showAction="false" height="36" :placeholder="searchPlaceholder"/>
|
||||||
</view>
|
</view>
|
||||||
<view class="js-list">
|
<view class="js-list">
|
||||||
@ -32,13 +38,13 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref, nextTick } from 'vue';
|
||||||
import { useCommonStore } from "@/store/modules/common";
|
import { useCommonStore } from "@/store/modules/common";
|
||||||
const { getAllJs } = useCommonStore();
|
const { getAllJsBasicInfoVo } = useCommonStore();
|
||||||
|
|
||||||
// 接收外部传入属性
|
// 接收外部传入属性
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
modelValue?: any,
|
defualtValue?: any,
|
||||||
defaultValue?: any,
|
|
||||||
parentData?: any,
|
parentData?: any,
|
||||||
multiple?: boolean,
|
multiple?: boolean,
|
||||||
// 排除id列表
|
// 排除id列表
|
||||||
@ -47,23 +53,23 @@ const props = withDefaults(defineProps<{
|
|||||||
title?: string,
|
title?: string,
|
||||||
placeholder?: string,
|
placeholder?: string,
|
||||||
searchPlaceholder?: string,
|
searchPlaceholder?: string,
|
||||||
showSearch?: boolean
|
// 自定义触发器
|
||||||
|
customTrigger?: boolean
|
||||||
}>(), {
|
}>(), {
|
||||||
modelValue: null,
|
defualtValue: null,
|
||||||
defaultValue: null,
|
|
||||||
parentData: null,
|
parentData: null,
|
||||||
multiple: false,
|
multiple: false,
|
||||||
excludeIds: [],
|
excludeIds: [],
|
||||||
title: '请选择教师',
|
title: '请选择教师',
|
||||||
placeholder: '请选择老师',
|
placeholder: '请选择教师',
|
||||||
searchPlaceholder: '输入教师名称查询',
|
searchPlaceholder: '输入教师名称查询',
|
||||||
showSearch: true
|
customTrigger: false
|
||||||
});
|
});
|
||||||
|
|
||||||
// 定义一个上级传入的emit响应事件用于接收数据变更
|
// 定义一个上级传入的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 showPopup = ref(false);
|
||||||
|
|
||||||
const jsListAll = ref<any>([]);
|
const jsListAll = ref<any>([]);
|
||||||
@ -72,22 +78,8 @@ let searchKey = "";
|
|||||||
|
|
||||||
const selectedList = ref<any>([]);
|
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 getShowSelectedName = () => {
|
||||||
const names = selectedList.value.map((item: any) => item.label).join(",");
|
return selectedList.value.map((item: any) => item.label).join(",");
|
||||||
return names;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSearch = (value: any) => {
|
const handleSearch = (value: any) => {
|
||||||
@ -99,19 +91,24 @@ const handleSearch = (value: any) => {
|
|||||||
const handleSelect = (item: any) => {
|
const handleSelect = (item: any) => {
|
||||||
if (props.multiple) {
|
if (props.multiple) {
|
||||||
// 多选模式:切换选中状态
|
// 多选模式:切换选中状态
|
||||||
const index = selectedList.value.findIndex((selected: any) => selected.value === item.value);
|
const isSelected = selectedList.value.some((selected: any) => selected.value === item.value);
|
||||||
if (index > -1) {
|
if (isSelected) {
|
||||||
// 取消选择:从选中列表中移除
|
// 取消选择
|
||||||
selectedList.value.splice(index, 1);
|
const index = selectedList.value.findIndex((selected: any) => selected.value === item.value);
|
||||||
|
if (index > -1) {
|
||||||
|
selectedList.value.splice(index, 1);
|
||||||
|
}
|
||||||
item.selected = false;
|
item.selected = false;
|
||||||
} else {
|
} else {
|
||||||
// 新增选择:添加到选中列表末尾(保持选择顺序)
|
// 添加选择
|
||||||
selectedList.value.push(item);
|
selectedList.value.push(item);
|
||||||
item.selected = true;
|
item.selected = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 单选模式:清除其他选中状态,设置当前选中
|
// 单选模式,清除其他选中状态
|
||||||
jsList.value.forEach((listItem: any) => listItem.selected = false);
|
for (const listItem of jsList.value) {
|
||||||
|
listItem.selected = false;
|
||||||
|
}
|
||||||
selectedList.value = [item];
|
selectedList.value = [item];
|
||||||
item.selected = true;
|
item.selected = true;
|
||||||
}
|
}
|
||||||
@ -123,217 +120,132 @@ const handleCancel = () => {
|
|||||||
|
|
||||||
const handleOk = () => {
|
const handleOk = () => {
|
||||||
showPopup.value = false;
|
showPopup.value = false;
|
||||||
let result;
|
|
||||||
if (props.multiple) {
|
if (props.multiple) {
|
||||||
result = selectedList.value;
|
emit("change", selectedList.value, props.parentData);
|
||||||
} else {
|
} 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 = () => {
|
const showPicker = () => {
|
||||||
showPopup.value = true;
|
rebuildJsList();
|
||||||
|
nextTick(() => {
|
||||||
|
showPopup.value = true;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const rebuildJsList = () => {
|
const rebuildJsList = () => {
|
||||||
jsList.value = [];
|
jsList.value = [];
|
||||||
jsListAll.value.forEach((item: any) => {
|
if (!jsListAll.value || !Array.isArray(jsListAll.value)) {
|
||||||
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) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 按照selectedList的顺序恢复选中状态,保持选择顺序
|
// 使用for...of确保同步执行
|
||||||
jsList.value.forEach((item: any) => {
|
for (const item of jsListAll.value) {
|
||||||
const isSelected = selectedList.value.some((selected: any) => selected.value === item.value);
|
// 检查item是否有效
|
||||||
item.selected = isSelected;
|
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({
|
defineExpose({
|
||||||
showPicker,
|
showPicker
|
||||||
open: showPicker,
|
|
||||||
close: () => showPopup.value = false
|
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
const res = await getAllJs()
|
try {
|
||||||
jsListAll.value = res.result || [];
|
const res = await getAllJsBasicInfoVo()
|
||||||
|
if (res && res.result && Array.isArray(res.result)) {
|
||||||
rebuildJsList();
|
jsListAll.value = res.result;
|
||||||
|
|
||||||
// 等待下一个tick确保jsList已经构建完成
|
// 确保rebuildJsList完全执行完成
|
||||||
await nextTick();
|
await new Promise<void>((resolve) => {
|
||||||
|
rebuildJsList();
|
||||||
// 设置默认值
|
// 使用nextTick确保DOM更新完成
|
||||||
const initialValue = internalValue.value || props.defaultValue;
|
nextTick(() => {
|
||||||
|
resolve();
|
||||||
if (initialValue) {
|
});
|
||||||
setInitialValue(initialValue);
|
});
|
||||||
}
|
|
||||||
});
|
// 设置默认值
|
||||||
|
if (props.defualtValue) {
|
||||||
// 设置初始值的函数
|
if (props.multiple) {
|
||||||
const setInitialValue = (value: any) => {
|
if (Array.isArray(props.defualtValue) && props.defualtValue.length > 0) {
|
||||||
if (!jsList.value.length) {
|
selectedList.value = [];
|
||||||
return;
|
// 使用for...of确保同步执行
|
||||||
}
|
for (const item of jsList.value) {
|
||||||
|
if (props.defualtValue.includes(item.value)) {
|
||||||
if (props.multiple) {
|
item.selected = true;
|
||||||
if (!Array.isArray(value) || !value.length) {
|
selectedList.value.push(item);
|
||||||
return;
|
} else {
|
||||||
}
|
item.selected = false;
|
||||||
|
}
|
||||||
selectedList.value = [];
|
}
|
||||||
|
}
|
||||||
// 先清空所有选中状态
|
} else {
|
||||||
jsList.value.forEach((item: any) => {
|
// 使用for...of确保同步执行
|
||||||
item.selected = false;
|
for (const item of jsList.value) {
|
||||||
});
|
if (item.value === props.defualtValue) {
|
||||||
|
item.selected = true;
|
||||||
// 处理不同类型的输入值
|
selectedList.value = [item];
|
||||||
let processedValue = value;
|
} else {
|
||||||
|
item.selected = false;
|
||||||
// 如果输入的是对象数组(如{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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
} else {
|
||||||
}
|
console.warn('JsPicker: 获取教师数据失败或数据格式不正确');
|
||||||
};
|
|
||||||
|
|
||||||
// 监听 modelValue 变化,同步到内部状态
|
|
||||||
watch(() => props.modelValue, (newValue) => {
|
|
||||||
if (newValue !== internalValue.value) {
|
|
||||||
internalValue.value = newValue;
|
|
||||||
// 立即尝试设置值,如果jsList还没准备好,会在jsList变化时再次尝试
|
|
||||||
if (jsList.value.length > 0) {
|
|
||||||
setInitialValue(newValue);
|
|
||||||
}
|
}
|
||||||
|
} 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>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.basic-js-picker {
|
.js-picker {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
.js-selected {
|
.js-selected {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
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 {
|
.js-selected-name {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|
||||||
|
.data {
|
||||||
|
font-size: 30rpx;
|
||||||
|
color: #333;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.js-picker-popup {
|
.js-picker-popup {
|
||||||
@ -384,4 +296,4 @@ watch(() => jsList.value, (newJsList) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
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">
|
<view class="dk-title pl-15 pr-15">
|
||||||
<BasicTitle line title="请假流程" :isBorder="false" />
|
<BasicTitle line title="请假流程" :isBorder="false" />
|
||||||
</view>
|
</view>
|
||||||
<ProgressList :procInstId="qjData.procInstId" />
|
<ProgressList :qjId="qjData.id" />
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -1,348 +1,143 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="back-f8f8f8">
|
<view class="js-qj-dk-edit">
|
||||||
<view class="flex-row items-center justify-between py-15 global-bg-color">
|
<view class="dk-header">
|
||||||
<view class="dk-title">
|
<text class="dk-title">代课教师</text>
|
||||||
<BasicTitle line title="代课明细" :isBorder="false" />
|
<BasicJsPicker
|
||||||
</view>
|
:customTrigger="true"
|
||||||
<view @click="getPkkbList">
|
:defualtValue="[]"
|
||||||
<BasicIcon type="refreshempty" size="25" />
|
@change="handleDkJsChange"
|
||||||
</view>
|
>
|
||||||
|
<template #trigger>
|
||||||
|
<button class="add-btn" type="primary" size="mini">添加代课教师</button>
|
||||||
|
</template>
|
||||||
|
</BasicJsPicker>
|
||||||
</view>
|
</view>
|
||||||
<view class="dk-tabs mb-10" v-if="dkList && dkList.length">
|
|
||||||
<BasicTabs
|
<view class="dk-list">
|
||||||
class="type-tabs"
|
<view
|
||||||
ref="tabsRef"
|
v-for="(item, index) in dkList"
|
||||||
:list="tabList"
|
:key="item.id"
|
||||||
bar-width="60px"
|
class="dk-item"
|
||||||
scroll-count="4"
|
>
|
||||||
:current="curTabIndex"
|
<view class="dk-info">
|
||||||
@change="switchTab"
|
<text class="js-name">{{ item.jsxm }}</text>
|
||||||
/>
|
<text class="course-info">{{ item.kcmc }} - {{ item.kcsj }}</text>
|
||||||
<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>
|
</view>
|
||||||
</view>
|
|
||||||
<view v-if="curTabIndex === 1">
|
<view class="dk-actions">
|
||||||
<view v-for="(item, index) in kmDkList" :key="index">
|
<button
|
||||||
<view class="dk-card" style="margin: 0">
|
class="remove-btn"
|
||||||
<view class="card-body">
|
size="mini"
|
||||||
<view class="info-row">
|
@click="removeDkJs(index)"
|
||||||
<text class="label">排课名称:</text>
|
>
|
||||||
<text class="value">{{ item.pkMc }}</text>
|
删除
|
||||||
</view>
|
</button>
|
||||||
<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>
|
</view>
|
||||||
</view>
|
</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>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { getPkkbByJsRangeTimeApi } from "@/api/base/jsQjApi";
|
import { ref, watch } from 'vue'
|
||||||
import JsPicker from "@/pages/components/JsPicker/index.vue";
|
import BasicJsPicker from '@/components/BasicJsPicker/Picker.vue'
|
||||||
import { useUserStore } from "@/store/modules/user";
|
|
||||||
const { getJs } = useUserStore();
|
|
||||||
|
|
||||||
// 接收外部传入属性
|
// Props
|
||||||
const props = withDefaults(
|
const props = defineProps({
|
||||||
defineProps<{
|
modelValue: {
|
||||||
data: any;
|
type: Array,
|
||||||
}>(),
|
default: () => []
|
||||||
{
|
|
||||||
data: () => ({
|
|
||||||
jsId: "",
|
|
||||||
qjkstime: "", // 请假开始时间
|
|
||||||
qjjstime: "", // 请假结束时间
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
);
|
})
|
||||||
|
|
||||||
const wdNameList = ref<string[]>([
|
// Emits
|
||||||
"周一",
|
const emit = defineEmits(['update:modelValue'])
|
||||||
"周二",
|
|
||||||
"周三",
|
|
||||||
"周四",
|
|
||||||
"周五",
|
|
||||||
"周六",
|
|
||||||
"周日",
|
|
||||||
]);
|
|
||||||
|
|
||||||
const jsTypeMc: any = {
|
// 响应式数据
|
||||||
ZAM: "早自习",
|
const dkList = ref<any[]>([])
|
||||||
AM: "上午",
|
|
||||||
PM: "下午",
|
|
||||||
};
|
|
||||||
|
|
||||||
// 代课教师要排除自己
|
// 处理代课教师变化
|
||||||
const excludeIds = ref<any>([]);
|
const handleDkJsChange = (selectedItems: any[]) => {
|
||||||
|
console.log(selectedItems)
|
||||||
if (props.data && props.data.jsId && props.data.jsId.length) {
|
if (Array.isArray(selectedItems)) {
|
||||||
excludeIds.value.push(props.data.jsId);
|
dkList.value = selectedItems
|
||||||
} else {
|
emit('update:modelValue', selectedItems)
|
||||||
excludeIds.value.push(getJs.id);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const tyDk = ref<any>({});
|
// 移除代课教师
|
||||||
|
const removeDkJs = (index: number) => {
|
||||||
|
dkList.value.splice(index, 1)
|
||||||
|
emit('update:modelValue', dkList.value)
|
||||||
|
}
|
||||||
|
|
||||||
const dkList = ref<any>([]);
|
// 监听modelValue变化
|
||||||
|
watch(() => props.modelValue, (newVal) => {
|
||||||
const kmDkList = ref<any>([]);
|
if (Array.isArray(newVal)) {
|
||||||
|
dkList.value = newVal
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
const list = dkList.value;
|
}, { deep: true, immediate: true })
|
||||||
// 如果还是没有查询需要代课的数据,则返回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,
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.dk-tabs {
|
.js-qj-dk-edit {
|
||||||
flex: 1 0 1px;
|
.dk-header {
|
||||||
}
|
|
||||||
|
|
||||||
.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 {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
margin-bottom: 10px;
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
&:last-child {
|
margin-bottom: 20rpx;
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
.dk-title {
|
||||||
|
font-size: 28rpx;
|
||||||
.label {
|
font-weight: bold;
|
||||||
font-size: 14px;
|
|
||||||
color: #666;
|
|
||||||
width: 70px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.value {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #333;
|
color: #333;
|
||||||
flex: 1;
|
}
|
||||||
|
|
||||||
|
.add-btn {
|
||||||
|
background: #007aff;
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 6rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dk-list {
|
||||||
|
.dk-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
.data {
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 20rpx;
|
||||||
|
background: #f8f9fa;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
|
||||||
|
.dk-info {
|
||||||
flex: 1;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
</style>
|
||||||
.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>
|
<template>
|
||||||
<BasicLayout :fixed="false">
|
<view class="js-qj-edit">
|
||||||
<view class="p-15">
|
<uni-forms ref="formRef" :model="formData" :rules="formRules">
|
||||||
<BasicForm @register="register">
|
<!-- 基本信息 -->
|
||||||
<template #dkmx>
|
<view class="form-section">
|
||||||
<JsQjDkEdit :data="formData" ref="dkRef" v-if="formData.dkfs === 0" />
|
<view class="section-title">基本信息</view>
|
||||||
</template>
|
<uni-forms-item label="请假类型" name="qjlx">
|
||||||
</BasicForm>
|
<uni-data-select
|
||||||
</view>
|
v-model="formData.qjlx"
|
||||||
<template #bottom>
|
:localdata="qjlxOptions"
|
||||||
<view class="white-bg-color py-5">
|
placeholder="请选择请假类型"
|
||||||
<view class="flex-row items-center pb-10 pt-5">
|
/>
|
||||||
<u-button text="取消" class="ml-15 mr-7" :plain="true" @click="navigateBack" />
|
</uni-forms-item>
|
||||||
<u-button text="提交" class="mr-15 mr-7" type="primary" @click="submit" />
|
|
||||||
|
<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>
|
||||||
</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>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import JsQjDkEdit from "./jsQjDkEdit.vue"
|
import { ref, reactive, onMounted, watch } from 'vue'
|
||||||
import { navigateBack } from "@/utils/uniapp";
|
import { useCommonStore } from '@/store/modules/common'
|
||||||
import { useForm } from "@/components/BasicForm/hooks/useForm";
|
import BasicSpCsMgr from '@/components/BasicSpCsMgr/index.vue'
|
||||||
import { jsQjSqApi, jsQjCxtjApi } from "@/api/base/jsQjApi";
|
import JsQjDkEdit from './jsQjDkEdit.vue'
|
||||||
import { showToast } from "@/utils/uniapp";
|
import { findQjByIdApi, jsQjSqApi } from '@/api/base/jsQjApi'
|
||||||
import dayjs from "dayjs";
|
|
||||||
import { useUserStore } from "@/store/modules/user";
|
|
||||||
import { useDicStore } from "@/store/modules/dic";
|
|
||||||
const { getJs, getUser } = useUserStore();
|
|
||||||
const { findByPid } = useDicStore();
|
|
||||||
|
|
||||||
// 接收外部传入属性
|
// Props
|
||||||
const props = withDefaults(defineProps<{
|
const props = defineProps({
|
||||||
data?: any
|
qjId: {
|
||||||
}>(), {
|
type: String,
|
||||||
data: () => ({
|
default: ''
|
||||||
id: "",
|
}
|
||||||
qjlx: "",
|
})
|
||||||
qjkstime: "",
|
|
||||||
qjjstime: "",
|
|
||||||
qjsc: "",
|
|
||||||
qjsy: "",
|
|
||||||
dkfs: 0,
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
const dkRef = ref<any>(null);
|
// Emits
|
||||||
|
const emit = defineEmits(['submitSuccess'])
|
||||||
|
|
||||||
let formData = ref<any>({
|
// Store
|
||||||
...props.data,
|
const commonStore = useCommonStore()
|
||||||
jsId: getJs.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (typeof props.data.dkfs === "string") {
|
// 响应式数据
|
||||||
nextTick(() => {
|
const formRef = ref(null)
|
||||||
formData.value.dkfs = parseInt(props.data.dkfs);
|
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: [
|
const qjlxOptions = [
|
||||||
{
|
{ value: '病假', text: '病假' },
|
||||||
field: "qjlx",
|
{ value: '事假', text: '事假' },
|
||||||
label: "请假类型",
|
{ value: '其他', text: '其他' }
|
||||||
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 changeKsTime = (selectedTime?: string) => {
|
const dkfsOptions = [
|
||||||
if (!selectedTime) {
|
{ value: '0', text: '自行协调' },
|
||||||
return;
|
{ value: '1', text: '教务处协调' },
|
||||||
}
|
{ value: '2', text: '无需代课' }
|
||||||
formData.value.qjkstime = selectedTime;
|
]
|
||||||
validateTime();
|
|
||||||
};
|
|
||||||
|
|
||||||
const changeJsTime = (selectedTime?: string) => {
|
// 处理代课方式变化
|
||||||
if (!selectedTime) {
|
const handleDkfsChange = (value: string) => {
|
||||||
return;
|
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) {
|
const loadQjDetail = async () => {
|
||||||
nextTick(() => {
|
if (!props.qjId) return
|
||||||
dkRef.value.getPkkbList();
|
|
||||||
})
|
try {
|
||||||
}
|
const res = await findQjByIdApi(props.qjId)
|
||||||
};
|
if (res && res.result) {
|
||||||
|
Object.assign(formData, res.result)
|
||||||
// 初始化
|
|
||||||
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 = [];
|
|
||||||
}
|
}
|
||||||
|
} 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>
|
</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>
|
<template>
|
||||||
<view class="approval-progress">
|
<view class="approval-progress">
|
||||||
<view class="progress-title">
|
<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>
|
||||||
<view class="divider"></view>
|
<view class="divider"></view>
|
||||||
<view class="progress-list">
|
<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="progress-item-row">
|
||||||
<view class="item-avatar">
|
<view class="item-avatar">
|
||||||
<image
|
<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"
|
class="w-full h-full"
|
||||||
></image>
|
></image>
|
||||||
</view>
|
</view>
|
||||||
<view class="item-middle">
|
<view class="item-middle">
|
||||||
<text class="item-name">{{ task.name }}</text>
|
<text class="item-name">{{ approver.userName }}</text>
|
||||||
<text class="item-detail">{{ task.assignee || '' }}</text>
|
<text class="item-detail">{{ getSpTypeText(approver.spType) }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="item-right">
|
<view class="item-right">
|
||||||
<text class="item-time" v-if="task.endTime">{{ task.endTime }}</text>
|
<text class="item-time" v-if="approver.approveTime">{{ formatTime(approver.approveTime) }}</text>
|
||||||
<text class="item-status" v-else>待处理</text>
|
<text class="item-status" :class="getStatusClass(approver.approveStatus)">
|
||||||
|
{{ getStatusText(approver.approveStatus) }}
|
||||||
|
</text>
|
||||||
</view>
|
</view>
|
||||||
</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>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { getQjActivitiHistoryApi } from "@/api/base/jsQjApi";
|
import dayjs from "dayjs";
|
||||||
|
import { getJsQjApprovalProcessApi } from "@/api/base/jsQjApi";
|
||||||
|
|
||||||
// 接收外部传入属性
|
// 接收外部传入属性
|
||||||
const props = withDefaults(defineProps<{
|
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 loadApprovalProcess = async () => {
|
||||||
const list = res.result || [];
|
if (!props.qjId) return;
|
||||||
// list反向
|
|
||||||
taskList.value = list.reverse();
|
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();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
watch(() => props.procInstId, (newVal, oldVal) => {
|
// 获取默认用户名
|
||||||
// 添加回调函数处理逻辑
|
const getDefaultUserName = (spType: string) => {
|
||||||
console.log('procInstId changed:', newVal, oldVal);
|
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) {
|
if (newVal) {
|
||||||
loadList(newVal);
|
loadApprovalProcess();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (props.procInstId) {
|
// 初始化
|
||||||
loadList(props.procInstId);
|
if (props.qjId) {
|
||||||
|
loadApprovalProcess();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -75,12 +175,6 @@ if (props.procInstId) {
|
|||||||
color: #333;
|
color: #333;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
|
|
||||||
.title-icon {
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.applicant-name {
|
.applicant-name {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
@ -99,51 +193,88 @@ if (props.procInstId) {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
.progress-item {
|
.progress-item {
|
||||||
.progress-item-row {
|
position: relative;
|
||||||
|
|
||||||
|
.progress-item-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
padding: 10px 0;
|
||||||
|
|
||||||
.item-avatar {
|
.item-avatar {
|
||||||
flex: 0 0 40px;
|
width: 40px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
|
border-radius: 50%;
|
||||||
|
overflow: hidden;
|
||||||
|
margin-right: 12px;
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-middle {
|
.item-middle {
|
||||||
margin-left: 16px;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
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 {
|
.item-right {
|
||||||
flex: 1 0 1px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
align-items: flex-end;
|
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 {
|
.progress-item-line {
|
||||||
width: 1px;
|
height: 20px;
|
||||||
height: 30px;
|
width: 2px;
|
||||||
background-color: #999;
|
background-color: #e8e8e8;
|
||||||
margin: 10px 20px;
|
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>
|
</style>
|
||||||
@ -1,81 +1,76 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="leave-page">
|
<BasicLayout>
|
||||||
<JsQjEdit :data="qjData" v-if="qjData.id" />
|
<view class="p-15">
|
||||||
</view>
|
<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>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { findQjById } from "@/api/base/jsQjApi";
|
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 { onLoad } from "@dcloudio/uni-app";
|
||||||
import { ref, computed, nextTick } from "vue";
|
import { ref } from "vue";
|
||||||
import JsQjEdit from "./components/jsQjEdit.vue";
|
import JsQjEdit from "./components/jsQjEdit.vue";
|
||||||
|
|
||||||
const { getJs, loginByOpenId } = useUserStore();
|
const qjId = ref<string>();
|
||||||
const { setData, getData, setXxts, getXxts } = useDataStore();
|
const qjData = ref<any>({});
|
||||||
|
|
||||||
const dbFlag = ref(false);
|
// 加载请假信息
|
||||||
|
const loadQjData = async () => {
|
||||||
const qjData = computed(() => getData);
|
try {
|
||||||
|
const result = await findQjById({ id: qjId.value });
|
||||||
onLoad(async (data: any) => {
|
if (result.code === 1) {
|
||||||
// 从待办过来的,需要从后端获取数据
|
qjData.value = result.data;
|
||||||
if (data && data.from && data.from == "db") {
|
|
||||||
dbFlag.value = true;
|
|
||||||
|
|
||||||
// 检查登录状态
|
|
||||||
const isLoggedIn = await loginByOpenId(data.openId);
|
|
||||||
if (!isLoggedIn) {
|
|
||||||
console.log("用户未登录,跳过处理");
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
try {
|
console.error('获取请假信息失败:', error);
|
||||||
// 优先从后端根据url中的id去查询Xxts
|
}
|
||||||
const xxtsRes = await xxtsFindByIdApi({ id: data.id });
|
};
|
||||||
if (xxtsRes && xxtsRes.result) {
|
|
||||||
const xxts = xxtsRes.result;
|
onLoad((options) => {
|
||||||
|
if (options.qjId) {
|
||||||
// 检查待办状态
|
qjId.value = options.qjId;
|
||||||
if (xxts.dbZt === "B") {
|
loadQjData();
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.leave-page {
|
.info-card {
|
||||||
height: 100vh;
|
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>
|
</style>
|
||||||
|
|||||||
@ -1,321 +1,250 @@
|
|||||||
<template>
|
<template>
|
||||||
<BasicLayout>
|
<BasicLayout>
|
||||||
<!-- 选项卡 -->
|
<view class="p-15">
|
||||||
<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="info-card" v-for="(item, index) in dkList" :key="index">
|
<view class="info-card">
|
||||||
<view class="card-header">
|
<view class="card-header">
|
||||||
<text class="applicant-name"
|
<text class="applicant-name">教师{{ qjData.jsName }}的请假申请</text>
|
||||||
>{{ item.dktime }}({{ item.xqLabel }})的{{ item.jcmc }}</text
|
|
||||||
>
|
|
||||||
</view>
|
</view>
|
||||||
<view class="divider"></view>
|
<view class="card-content">
|
||||||
<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="info-row">
|
<view class="info-row">
|
||||||
<text class="label">请假老师:</text>
|
<text class="label">请假老师:</text>
|
||||||
<view class="value">{{ qjData.jsName }}</view>
|
<text class="value">{{ qjData.jsName }}</text>
|
||||||
</view>
|
</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>
|
</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>
|
</BasicLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { jsQjDkQrApi } from "@/api/base/jsQjApi";
|
import { jsQjDkQrApi } from "@/api/base/jsQjApi";
|
||||||
import { xxtsFindByIdApi } from "@/api/base/server";
|
import { findQjById, findDkByIdApi } from "@/api/base/jsQjApi";
|
||||||
import { useDataStore } from "@/store/modules/data";
|
|
||||||
import { useUserStore } from "@/store/modules/user";
|
import { useUserStore } from "@/store/modules/user";
|
||||||
import { navigateBack } from "@/utils/uniapp";
|
import { navigateBack } from "@/utils/uniapp";
|
||||||
import { onLoad } from "@dcloudio/uni-app";
|
import { onLoad } from "@dcloudio/uni-app";
|
||||||
import { ref } from "vue";
|
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 { getJs } = useUserStore();
|
||||||
const qjData = ref<any>({});
|
|
||||||
|
|
||||||
const dbFlag = ref(false);
|
|
||||||
|
|
||||||
const qjId = ref<string>();
|
const qjId = ref<string>();
|
||||||
const dkId = ref<string>();
|
const dkId = ref<string>();
|
||||||
|
const qjData = ref<any>({});
|
||||||
|
const dkData = ref<any>({});
|
||||||
|
|
||||||
const dlgFlag = ref(false);
|
// 加载请假信息
|
||||||
const rejectReason = ref("");
|
const loadQjData = async () => {
|
||||||
|
try {
|
||||||
const tabList = ref([
|
const result = await findQjById({ id: qjId.value });
|
||||||
{ name: "当前代课", id: "dk-info" },
|
if (result.code === 1) {
|
||||||
{ name: "请假信息", id: "qj-info" },
|
qjData.value = result.data;
|
||||||
]);
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
});
|
} catch (error) {
|
||||||
switchTab(0);
|
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 = {
|
const params = {
|
||||||
qjId: qjId.value,
|
qjId: qjId.value,
|
||||||
jsId: getJs.id,
|
jsId: getJs.id,
|
||||||
qrStatus: 2,
|
qrStatus: "1", // 1表示确认
|
||||||
qrYj: "同意",
|
remark: "确认代课"
|
||||||
};
|
};
|
||||||
uni.showLoading({ title: "确认代课中..." });
|
|
||||||
await jsQjDkQrApi(params);
|
uni.showLoading({ title: "确认中..." });
|
||||||
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: "正在驳回..." });
|
|
||||||
try {
|
try {
|
||||||
await jsQjDkQrApi(params);
|
await jsQjDkQrApi(params);
|
||||||
uni.hideLoading();
|
uni.showToast({ title: "确认成功", icon: "success" });
|
||||||
uni.showToast({ title: "已驳回", icon: "success" });
|
navigateBack();
|
||||||
closeDlg();
|
} catch (error) {
|
||||||
setTimeout(() => {
|
uni.showToast({ title: "确认失败", icon: "error" });
|
||||||
navigateBack();
|
|
||||||
}, 500);
|
|
||||||
} catch (e) {
|
|
||||||
uni.hideLoading();
|
|
||||||
}
|
}
|
||||||
|
uni.hideLoading();
|
||||||
};
|
};
|
||||||
|
|
||||||
onLoad(async (data: any) => {
|
// 拒绝代课
|
||||||
// 从待办过来的,需要从后端获取数据
|
const handleReject = async () => {
|
||||||
if (data && data.from && data.from == "db") {
|
uni.showModal({
|
||||||
dbFlag.value = true;
|
title: "确认拒绝",
|
||||||
|
content: "确定要拒绝代课吗?",
|
||||||
// 检查登录状态
|
success: async (res) => {
|
||||||
const isLoggedIn = await loginByOpenId(data.openId);
|
if (res.confirm) {
|
||||||
if (!isLoggedIn) {
|
const params = {
|
||||||
console.log("用户未登录,跳过处理");
|
qjId: qjId.value,
|
||||||
return;
|
jsId: getJs.id,
|
||||||
}
|
qrStatus: "2", // 2表示拒绝
|
||||||
|
remark: "拒绝代课"
|
||||||
try {
|
};
|
||||||
// 优先从后端根据url中的id去查询Xxts
|
|
||||||
const xxtsRes = await xxtsFindByIdApi({ id: data.id });
|
|
||||||
if (xxtsRes && xxtsRes.result) {
|
|
||||||
const xxts = xxtsRes.result;
|
|
||||||
|
|
||||||
// 检查待办状态
|
uni.showLoading({ title: "处理中..." });
|
||||||
if (xxts.dbZt === "B") {
|
try {
|
||||||
setData({ id: xxts.xxzbId });
|
await jsQjDkQrApi(params);
|
||||||
let url = "/pages/view/hr/jsQj/detail";
|
uni.showToast({ title: "已拒绝", icon: "success" });
|
||||||
uni.navigateTo({ url });
|
navigateBack();
|
||||||
return;
|
} catch (error) {
|
||||||
|
uni.showToast({ title: "操作失败", icon: "error" });
|
||||||
}
|
}
|
||||||
setXxts(xxts);
|
uni.hideLoading();
|
||||||
|
|
||||||
// 使用消息推送中的主表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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} 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>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style lang="scss" scoped>
|
||||||
.popup-content {
|
.info-card,
|
||||||
width: 80vw;
|
.dk-info-card {
|
||||||
background: #fff;
|
background: white;
|
||||||
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;
|
|
||||||
border-radius: 8px;
|
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-size: 16px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: #333;
|
color: #333;
|
||||||
padding: 15px;
|
|
||||||
|
|
||||||
.applicant-name {
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.divider {
|
.card-content {
|
||||||
height: 1px;
|
padding: 15px;
|
||||||
background-color: #eee;
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.card-body {
|
.action-section {
|
||||||
padding: 15px;
|
background: white;
|
||||||
|
border-radius: 8px;
|
||||||
.info-row {
|
padding: 15px;
|
||||||
display: flex;
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
margin-bottom: 10px;
|
|
||||||
|
.action-title {
|
||||||
.label {
|
font-size: 16px;
|
||||||
font-size: 14px;
|
font-weight: bold;
|
||||||
color: #999;
|
color: #333;
|
||||||
width: 70px;
|
margin-bottom: 15px;
|
||||||
flex-shrink: 0;
|
text-align: center;
|
||||||
margin-right: 8px;
|
}
|
||||||
}
|
|
||||||
|
.action-buttons {
|
||||||
.value {
|
display: flex;
|
||||||
font-size: 14px;
|
gap: 15px;
|
||||||
color: #333;
|
|
||||||
flex: 1;
|
.action-btn {
|
||||||
}
|
flex: 1;
|
||||||
}
|
|
||||||
.info-column {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
.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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,51 +1,79 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="leave-page">
|
<view class="leave-page">
|
||||||
<!-- 选项卡 -->
|
<!-- 选项卡 -->
|
||||||
<BasicTabs class="leave-tabs"
|
<BasicTabs
|
||||||
ref="tabsRef" :list="tabList" bar-width="60px" scroll-count="4"
|
class="leave-tabs"
|
||||||
:current="curTabIndex" @change="switchTab"
|
ref="tabsRef"
|
||||||
/>
|
:list="tabList"
|
||||||
|
bar-width="60px"
|
||||||
|
scroll-count="4"
|
||||||
|
:current="curTabIndex"
|
||||||
|
@change="switchTab"
|
||||||
|
/>
|
||||||
|
|
||||||
<view class="leave-edit" v-if="curTabIndex === 0">
|
<view class="leave-edit" v-if="curTabIndex === 0">
|
||||||
<JsQjEdit />
|
<JsQjEdit />
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="leave-list" v-else>
|
<view class="leave-list" v-else>
|
||||||
<JsQjList />
|
<JsQjList />
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
import JsQjEdit from "./components/jsQjEdit.vue"
|
import JsQjEdit from "./components/jsQjEdit.vue"
|
||||||
import JsQjList from "./components/jsQjList.vue"
|
import JsQjList from "./components/jsQjList.vue"
|
||||||
|
|
||||||
|
// 响应式数据
|
||||||
const tabList = ref([
|
const tabList = ref([
|
||||||
{ name: "请假申请", id: "leave-edit" },
|
{ name: "请假申请", id: "leave-edit" },
|
||||||
{ name: "请假记录", id: "leave-list" },
|
{ 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>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
.leave-page {
|
.leave-page {
|
||||||
flex: 1 0 1px;
|
flex: 1 0 1px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
||||||
|
|
||||||
.leave-tabs {
|
.leave-tabs {
|
||||||
flex: 0 0 45px;
|
flex: 0 0 45px;
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
||||||
|
z-index: 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
.leave-edit,
|
.leave-edit,
|
||||||
.leave-list {
|
.leave-list {
|
||||||
flex: 1 0 1px;
|
flex: 1 0 1px;
|
||||||
position: relative;
|
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>
|
||||||
</style>
|
|
||||||
@ -3,10 +3,10 @@
|
|||||||
<JsQjDetail :qjId="qjId" :dbFlag="dbFlag" v-if="qjId && qjId.length" />
|
<JsQjDetail :qjId="qjId" :dbFlag="dbFlag" v-if="qjId && qjId.length" />
|
||||||
<!-- 驳回弹窗 -->
|
<!-- 驳回弹窗 -->
|
||||||
<u-popup
|
<u-popup
|
||||||
:show="bhDlgFlag"
|
:show="dlgFlag"
|
||||||
mode="center"
|
mode="center"
|
||||||
:closeOnClickOverlay="false"
|
:closeOnClickOverlay="false"
|
||||||
@close="closeBhDlg"
|
@close="closeDlg"
|
||||||
>
|
>
|
||||||
<view class="popup-content">
|
<view class="popup-content">
|
||||||
<view class="popup-title">驳回原因</view>
|
<view class="popup-title">驳回原因</view>
|
||||||
@ -18,32 +18,11 @@
|
|||||||
maxlength="200"
|
maxlength="200"
|
||||||
/>
|
/>
|
||||||
<view class="popup-actions flex-row justify-end mt-4">
|
<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>
|
<u-button type="primary" @click="handleReject">确定</u-button>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</u-popup>
|
</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>
|
<template #bottom>
|
||||||
<view class="white-bg-color py-5">
|
<view class="white-bg-color py-5">
|
||||||
<view class="divider"></view>
|
<view class="divider"></view>
|
||||||
@ -52,9 +31,8 @@
|
|||||||
text="驳回"
|
text="驳回"
|
||||||
class="ml-15 mr-7"
|
class="ml-15 mr-7"
|
||||||
:plain="true"
|
:plain="true"
|
||||||
@click="showBhDlg"
|
@click="showDlg"
|
||||||
/>
|
/>
|
||||||
<u-button text="转办" class="mr-7" :plain="true" @click="showZbDlg" />
|
|
||||||
<u-button
|
<u-button
|
||||||
text="同意"
|
text="同意"
|
||||||
class="mr-15 mr-7"
|
class="mr-15 mr-7"
|
||||||
@ -68,63 +46,39 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { jsQjJwcQrApi, jsQjZbApi } from "@/api/base/jsQjApi";
|
import { jsQjJwcQrApi } 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 { useUserStore } from "@/store/modules/user";
|
import { useUserStore } from "@/store/modules/user";
|
||||||
import { navigateBack } from "@/utils/uniapp";
|
import { navigateBack } from "@/utils/uniapp";
|
||||||
import { onLoad } from "@dcloudio/uni-app";
|
import { onLoad } from "@dcloudio/uni-app";
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
import JsQjDetail from "./components/jsQjDetail.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 dbFlag = ref(false);
|
||||||
|
|
||||||
const qjId = ref<string>();
|
const qjId = ref<string>();
|
||||||
|
const dlgFlag = ref(false);
|
||||||
// 驳回弹窗
|
|
||||||
const bhDlgFlag = ref(false);
|
|
||||||
const rejectReason = ref("");
|
const rejectReason = ref("");
|
||||||
|
|
||||||
// 转办弹窗
|
|
||||||
const zbDlgFlag = ref(false);
|
|
||||||
const excludeIds = ref<string[]>([getJs.id]);
|
|
||||||
const zbJs = ref<any>({});
|
|
||||||
|
|
||||||
// 同意处理
|
|
||||||
const submit = async () => {
|
const submit = async () => {
|
||||||
const params = {
|
const params = {
|
||||||
qjId: qjId.value,
|
qjId: qjId.value,
|
||||||
jsId: getJs.id,
|
jsId: getJs.id,
|
||||||
qrStatus: 2,
|
spStatus: "approved",
|
||||||
qrYj: "同意",
|
spRemark: "同意",
|
||||||
};
|
};
|
||||||
uni.showLoading({ title: "审批中..." });
|
uni.showLoading({ title: "确认中..." });
|
||||||
await jsQjJwcQrApi(params);
|
await jsQjJwcQrApi(params);
|
||||||
uni.hideLoading();
|
uni.hideLoading();
|
||||||
navigateBack();
|
navigateBack();
|
||||||
};
|
};
|
||||||
|
|
||||||
// 显示/关闭驳回弹窗
|
const showDlg = () => {
|
||||||
const showBhDlg = () => {
|
dlgFlag.value = true;
|
||||||
bhDlgFlag.value = true;
|
|
||||||
};
|
|
||||||
const closeBhDlg = () => {
|
|
||||||
bhDlgFlag.value = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 显示/关闭转办弹窗
|
const closeDlg = () => {
|
||||||
const showZbDlg = () => {
|
dlgFlag.value = false;
|
||||||
zbDlgFlag.value = true;
|
|
||||||
};
|
|
||||||
const closeZbDlg = () => {
|
|
||||||
zbDlgFlag.value = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
const changeZbJs = (selected: any) => {
|
|
||||||
zbJs.value = selected;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 驳回处理
|
// 驳回处理
|
||||||
@ -133,115 +87,51 @@ const handleReject = async () => {
|
|||||||
uni.showToast({ title: "请填写驳回意见", icon: "none" });
|
uni.showToast({ title: "请填写驳回意见", icon: "none" });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const params: any = {
|
const params = {
|
||||||
qjId: qjId.value,
|
qjId: qjId.value,
|
||||||
jsId: getJs.id,
|
jsId: getJs.id,
|
||||||
spStatus: 1, // 1为拒绝
|
spStatus: "rejected",
|
||||||
spYj: rejectReason.value,
|
spRemark: rejectReason.value,
|
||||||
};
|
};
|
||||||
uni.showLoading({ title: "正在驳回..." });
|
uni.showLoading({ title: "正在驳回..." });
|
||||||
try {
|
await jsQjJwcQrApi(params);
|
||||||
await jsQjJwcQrApi(params);
|
uni.hideLoading();
|
||||||
uni.hideLoading();
|
closeDlg();
|
||||||
uni.showToast({ title: "已驳回", icon: "success" });
|
navigateBack();
|
||||||
closeBhDlg();
|
|
||||||
setTimeout(() => {
|
|
||||||
navigateBack();
|
|
||||||
}, 500);
|
|
||||||
} catch (e) {
|
|
||||||
uni.hideLoading();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 转办处理
|
onLoad((options) => {
|
||||||
const handleTransfer = async () => {
|
if (options.qjId) {
|
||||||
if (!zbJs.value || !zbJs.value.value) {
|
qjId.value = options.qjId;
|
||||||
uni.showToast({ title: "请填写转办教师", icon: "none" });
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
const params: any = {
|
if (options.dbFlag) {
|
||||||
qjId: qjId.value,
|
dbFlag.value = options.dbFlag === "true";
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style lang="scss" scoped>
|
||||||
.popup-content {
|
.popup-content {
|
||||||
width: 80vw;
|
background-color: white;
|
||||||
background: #fff;
|
border-radius: 8px;
|
||||||
border-radius: 12px;
|
padding: 20px;
|
||||||
padding: 24px 16px 16px 16px;
|
width: 300px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.popup-title {
|
.popup-title {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 15px;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.popup-actions {
|
.popup-actions {
|
||||||
margin-top: 16px;
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider {
|
||||||
|
height: 1px;
|
||||||
|
background-color: #eee;
|
||||||
|
margin: 0 15px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,194 +1,366 @@
|
|||||||
<template>
|
<template>
|
||||||
<BasicLayout>
|
<BasicLayout>
|
||||||
<!-- 选项卡 -->
|
<view class="p-15">
|
||||||
<BasicTabs
|
<!-- 请假信息卡片 -->
|
||||||
class="leave-tabs"
|
<view class="info-card">
|
||||||
ref="tabsRef"
|
<view class="card-header">
|
||||||
:list="tabList"
|
<text class="applicant-name">教师{{ qjData.jsName }}的请假申请</text>
|
||||||
bar-width="60px"
|
</view>
|
||||||
scroll-count="4"
|
<view class="card-content">
|
||||||
:current="curTabIndex"
|
<view class="info-row">
|
||||||
@change="switchTab"
|
<text class="label">请假类型:</text>
|
||||||
/>
|
<text class="value">{{ qjData.qjlx }}</text>
|
||||||
<view class="pl-15 pr-15" v-show="curTabIndex === 0">
|
</view>
|
||||||
<JsQjDkEdit
|
<view class="info-row">
|
||||||
:data="formData"
|
<text class="label">请假时间:</text>
|
||||||
ref="dkRef"
|
<text class="value">{{ qjData.qjkstime }} 至 {{ qjData.qjjstime }}</text>
|
||||||
v-if="formData && formData.dkfs === '1'"
|
</view>
|
||||||
/>
|
<view class="info-row">
|
||||||
</view>
|
<text class="label">请假时长:</text>
|
||||||
<JsQjDetail
|
<text class="value">{{ qjData.qjsc }}</text>
|
||||||
v-show="curTabIndex === 1"
|
</view>
|
||||||
:qjId="qjId"
|
<view class="info-row">
|
||||||
:dbFlag="dbFlag"
|
<text class="label">请假事由:</text>
|
||||||
v-if="qjId && qjId.length"
|
<text class="value">{{ qjData.qjsy }}</text>
|
||||||
@loadQjData="loadQjData"
|
</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="取消"
|
|
||||||
class="ml-15 mr-7"
|
|
||||||
:plain="true"
|
|
||||||
@click="navigateBack"
|
|
||||||
/>
|
|
||||||
<u-button
|
|
||||||
text="提交"
|
|
||||||
class="mr-15 mr-7"
|
|
||||||
type="primary"
|
|
||||||
@click="submit"
|
|
||||||
/>
|
|
||||||
</view>
|
</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>
|
</BasicLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { jsQjJwcXtApi } from "@/api/base/jsQjApi";
|
import { jsQjJwcXtApi } from "@/api/base/jsQjApi";
|
||||||
import { xxtsFindByIdApi } from "@/api/base/server";
|
import { findQjById, getPkkbByJsRangeTimeApi } from "@/api/base/jsQjApi";
|
||||||
import { useDataStore } from "@/store/modules/data";
|
|
||||||
import { useUserStore } from "@/store/modules/user";
|
import { useUserStore } from "@/store/modules/user";
|
||||||
import { navigateBack, showToast } from "@/utils/uniapp";
|
import { navigateBack } from "@/utils/uniapp";
|
||||||
import { onLoad } from "@dcloudio/uni-app";
|
import { onLoad } from "@dcloudio/uni-app";
|
||||||
import { ref, nextTick } from "vue";
|
import { ref } from "vue";
|
||||||
import JsQjDetail from "./components/jsQjDetail.vue";
|
|
||||||
import JsQjDkEdit from "./components/jsQjDkEdit.vue";
|
|
||||||
const { getJs, loginByOpenId } = useUserStore();
|
|
||||||
const { setData, getData, setXxts, getXxts } = useDataStore();
|
|
||||||
|
|
||||||
const dkRef = ref<any>(null);
|
const { getJs } = useUserStore();
|
||||||
|
|
||||||
const formData = ref<any>({});
|
|
||||||
|
|
||||||
const dbFlag = ref(false);
|
|
||||||
|
|
||||||
const qjId = ref<string>();
|
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" },
|
const loadQjData = async () => {
|
||||||
{ name: "请假信息", id: "qj-info" },
|
try {
|
||||||
]);
|
const result = await findQjById({ id: qjId.value });
|
||||||
|
if (result.code === 1) {
|
||||||
const curTabIndex = ref(1);
|
qjData.value = result.data;
|
||||||
let initDkTabFlag = false;
|
// 加载可用的代课安排
|
||||||
|
await loadAvailableDkList();
|
||||||
const switchTab = (index: number) => {
|
}
|
||||||
curTabIndex.value = index;
|
} catch (error) {
|
||||||
if (index === 0 && !initDkTabFlag) {
|
console.error('获取请假信息失败:', error);
|
||||||
nextTick(() => {
|
|
||||||
dkRef.value.getPkkbList();
|
|
||||||
initDkTabFlag = true;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const loadQjData = (data: any) => {
|
// 加载可用的代课安排
|
||||||
formData.value = data;
|
const loadAvailableDkList = async () => {
|
||||||
switchTab(0);
|
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 = {
|
const showDkSelector = () => {
|
||||||
qjId: qjId.value,
|
selectedDkIds.value = [];
|
||||||
jsId: getJs.id,
|
showSelector.value = true;
|
||||||
dkList: [],
|
};
|
||||||
};
|
|
||||||
const flag = await dkRef.value.validate();
|
// 关闭选择器
|
||||||
if (!flag) {
|
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({
|
uni.showToast({
|
||||||
title: "请选择代课教师",
|
title: "请至少添加一个代课安排",
|
||||||
icon: "none",
|
icon: "none"
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let dkList = dkRef.value.getDkList() || [];
|
|
||||||
if (dkList.length) {
|
const params = {
|
||||||
params.dkList = dkList.map((item: any) => {
|
qjId: qjId.value,
|
||||||
const newItem = { ...item };
|
jsId: getJs.id,
|
||||||
newItem.jsId = item.dkJsId;
|
dkList: dkList.value
|
||||||
newItem.jsName = item.dkJsName;
|
};
|
||||||
newItem.pkkbId = item.id;
|
|
||||||
newItem.dktime = item.dktime + " 00:00:00";
|
|
||||||
newItem.id = "";
|
|
||||||
newItem.qjId = "";
|
|
||||||
return newItem;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
uni.showLoading({ title: "提交中..." });
|
uni.showLoading({ title: "提交中..." });
|
||||||
await jsQjJwcXtApi(params).then(() => {
|
try {
|
||||||
showToast({ title: "提交成功", icon: "success" });
|
await jsQjJwcXtApi(params);
|
||||||
uni.reLaunch({
|
uni.showToast({ title: "协调成功", icon: "success" });
|
||||||
url: "/pages/base/service/index",
|
navigateBack();
|
||||||
});
|
} catch (error) {
|
||||||
});
|
uni.showToast({ title: "协调失败", icon: "error" });
|
||||||
|
}
|
||||||
uni.hideLoading();
|
uni.hideLoading();
|
||||||
};
|
};
|
||||||
|
|
||||||
onLoad(async (data: any) => {
|
onLoad((options) => {
|
||||||
// 从待办过来的,需要从后端获取数据
|
if (options.qjId) {
|
||||||
if (data && data.from && data.from == "db") {
|
qjId.value = options.qjId;
|
||||||
dbFlag.value = true;
|
loadQjData();
|
||||||
|
|
||||||
// 检查登录状态
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style lang="scss" scoped>
|
||||||
.popup-content {
|
.info-card,
|
||||||
width: 80vw;
|
.dk-arrange-card {
|
||||||
background: #fff;
|
background: white;
|
||||||
border-radius: 12px;
|
border-radius: 8px;
|
||||||
padding: 24px 16px 16px 16px;
|
margin-bottom: 15px;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
.popup-title {
|
|
||||||
font-size: 16px;
|
.card-header {
|
||||||
font-weight: bold;
|
padding: 15px;
|
||||||
margin-bottom: 12px;
|
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>
|
</style>
|
||||||
|
|||||||
@ -1,40 +1,36 @@
|
|||||||
<template>
|
<template>
|
||||||
<BasicLayout>
|
<BasicLayout>
|
||||||
<JsQjDetail :qjId="qjId" :dbFlag="dbFlag" v-if="qjId && qjId.length" />
|
<view class="qj-detail">
|
||||||
<!-- 驳回弹窗 -->
|
<!-- 使用公共组件展示请假详情 -->
|
||||||
<u-popup
|
<JsQjDetail
|
||||||
:show="dlgFlag"
|
:qjId="qjId"
|
||||||
mode="center"
|
:dbFlag="dbFlag"
|
||||||
:closeOnClickOverlay="false"
|
@loadQjData="handleQjDataLoaded"
|
||||||
@close="closeDlg"
|
@loadDkList="handleDkListLoaded"
|
||||||
>
|
/>
|
||||||
<view class="popup-content">
|
|
||||||
<view class="popup-title">驳回原因</view>
|
<!-- 审批意见卡片 -->
|
||||||
<u-input
|
<view class="info-card">
|
||||||
v-model="rejectReason"
|
<view class="card-header">
|
||||||
type="textarea"
|
<text class="applicant-name">审批意见</text>
|
||||||
placeholder="请填写驳回原因"
|
</view>
|
||||||
:autoHeight="true"
|
<view class="divider"></view>
|
||||||
maxlength="200"
|
<view class="card-body">
|
||||||
/>
|
<BasicForm @register="register" />
|
||||||
<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>
|
||||||
</view>
|
</view>
|
||||||
</u-popup>
|
</view>
|
||||||
<template #bottom>
|
<template #bottom>
|
||||||
<view class="white-bg-color py-5">
|
<view class="white-bg-color py-5">
|
||||||
<view class="divider"></view>
|
|
||||||
<view class="flex-row items-center pb-10 pt-5">
|
<view class="flex-row items-center pb-10 pt-5">
|
||||||
<u-button
|
<u-button
|
||||||
text="驳回"
|
text="取消"
|
||||||
class="ml-15 mr-7"
|
class="ml-15 mr-7"
|
||||||
:plain="true"
|
:plain="true"
|
||||||
@click="showDlg"
|
@click="navigateBack"
|
||||||
/>
|
/>
|
||||||
<u-button
|
<u-button
|
||||||
text="同意"
|
text="提交"
|
||||||
class="mr-15 mr-7"
|
class="mr-15 mr-7"
|
||||||
type="primary"
|
type="primary"
|
||||||
@click="submit"
|
@click="submit"
|
||||||
@ -46,72 +42,114 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<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 { 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";
|
import JsQjDetail from "./components/jsQjDetail.vue";
|
||||||
|
|
||||||
const { getJs, loginByOpenId } = useUserStore();
|
const { getJs, loginByOpenId } = useUserStore();
|
||||||
const { setData, getData, setXxts, getXxts } = useDataStore();
|
const { getData, setXxts, setData, getXxts } = useDataStore();
|
||||||
|
|
||||||
const dbFlag = ref(false);
|
const dbFlag = ref(false);
|
||||||
|
const qjId = ref('');
|
||||||
|
|
||||||
const qjId = ref<string>();
|
// 请假基础数据
|
||||||
|
const qjData = computed(() => getData || {});
|
||||||
|
|
||||||
const dlgFlag = ref(false);
|
const [register, { getValue }] = useForm({
|
||||||
const rejectReason = ref("");
|
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 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 {
|
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.hideLoading();
|
||||||
uni.showToast({ title: "已驳回", icon: "success" });
|
uni.showToast({
|
||||||
closeDlg();
|
title: '审批提交成功',
|
||||||
|
icon: 'success'
|
||||||
|
});
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
navigateBack();
|
navigateBack();
|
||||||
}, 500);
|
}, 1500);
|
||||||
} catch (e) {
|
|
||||||
|
} catch (error) {
|
||||||
uni.hideLoading();
|
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") {
|
if (data && data.from && data.from == "db") {
|
||||||
dbFlag.value = true;
|
dbFlag.value = true;
|
||||||
@ -128,7 +166,6 @@ onLoad(async (data: any) => {
|
|||||||
const xxtsRes = await xxtsFindByIdApi({ id: data.id });
|
const xxtsRes = await xxtsFindByIdApi({ id: data.id });
|
||||||
if (xxtsRes && xxtsRes.result) {
|
if (xxtsRes && xxtsRes.result) {
|
||||||
const xxts = xxtsRes.result;
|
const xxts = xxtsRes.result;
|
||||||
|
|
||||||
// 检查待办状态
|
// 检查待办状态
|
||||||
if (xxts.dbZt === "B") {
|
if (xxts.dbZt === "B") {
|
||||||
setData({ id: xxts.xxzbId });
|
setData({ id: xxts.xxzbId });
|
||||||
@ -137,42 +174,61 @@ onLoad(async (data: any) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setXxts(xxts);
|
setXxts(xxts);
|
||||||
|
|
||||||
// 使用消息推送中的主表ID
|
|
||||||
qjId.value = xxts.xxzbId;
|
qjId.value = xxts.xxzbId;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("获取待办信息失败", error);
|
console.error("获取待办信息失败", error);
|
||||||
// 如果获取Xxts失败,回退到原来的逻辑
|
// 如果获取Xxts失败,回退到原来的逻辑
|
||||||
qjId.value = data.id;
|
const xxtsData = getXxts;
|
||||||
const xxtsData = getXxts();
|
|
||||||
if (xxtsData && xxtsData.dbZt === "B") {
|
if (xxtsData && xxtsData.dbZt === "B") {
|
||||||
setData({ id: data.id });
|
setData({ id: data.id });
|
||||||
let url = "/pages/view/hr/jsQj/detail";
|
let url = "/pages/view/hr/jsQj/detail";
|
||||||
uni.navigateTo({ url });
|
uni.navigateTo({ url });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
qjId.value = data.id;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
qjId.value = getData.id;
|
|
||||||
dbFlag.value = false;
|
dbFlag.value = false;
|
||||||
|
// 直接加载请假详情
|
||||||
|
qjId.value = getData.id || '';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style lang="scss" scoped>
|
||||||
.popup-content {
|
.qj-detail {
|
||||||
width: 80vw;
|
background-color: #f5f7fa;
|
||||||
background: #fff;
|
|
||||||
border-radius: 12px;
|
|
||||||
padding: 24px 16px 16px 16px;
|
|
||||||
}
|
}
|
||||||
.popup-title {
|
|
||||||
font-size: 16px;
|
.info-card {
|
||||||
font-weight: bold;
|
margin: 15px;
|
||||||
margin-bottom: 12px;
|
background-color: #fff;
|
||||||
}
|
border-radius: 8px;
|
||||||
.popup-actions {
|
padding: 15px;
|
||||||
margin-top: 16px;
|
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>
|
</style>
|
||||||
|
|||||||
@ -15,7 +15,7 @@
|
|||||||
<!-- 陪餐教师选择 -->
|
<!-- 陪餐教师选择 -->
|
||||||
<view class="section" v-if="curBj">
|
<view class="section" v-if="curBj">
|
||||||
<view class="section-title">陪餐教师</view>
|
<view class="section-title">陪餐教师</view>
|
||||||
<JsPicker
|
<BasicJsPicker
|
||||||
:multiple="true"
|
:multiple="true"
|
||||||
@change="jsXz"
|
@change="jsXz"
|
||||||
placeholder="请选择陪餐教师"
|
placeholder="请选择陪餐教师"
|
||||||
@ -252,7 +252,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
import NjBjPicker from '@/pages/components/NjBjPicker/index.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 DmPsComponent from '@/pages/components/dmPs/index.vue'
|
||||||
import { getClassStudentDmDataApi, submitJcDmDataApi } from '@/api/base/jcApi'
|
import { getClassStudentDmDataApi, submitJcDmDataApi } from '@/api/base/jcApi'
|
||||||
import { imagUrl } from "@/utils";
|
import { imagUrl } from "@/utils";
|
||||||
|
|||||||
@ -987,12 +987,12 @@ const ensureTeacherDataCached = async () => {
|
|||||||
if (!hasTeacherData) {
|
if (!hasTeacherData) {
|
||||||
console.log('localStorage中没有教师数据,开始获取并缓存');
|
console.log('localStorage中没有教师数据,开始获取并缓存');
|
||||||
|
|
||||||
// 导入并调用getAllJs方法
|
// 导入并调用getAllJsBasicInfoVo方法
|
||||||
const { useCommonStore } = await import('@/store/modules/common');
|
const { useCommonStore } = await import('@/store/modules/common');
|
||||||
const commonStore = useCommonStore();
|
const commonStore = useCommonStore();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await commonStore.getAllJs();
|
const result = await commonStore.getAllJsBasicInfoVo();
|
||||||
console.log('成功获取教师数据并写入缓存:', result);
|
console.log('成功获取教师数据并写入缓存:', result);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取教师数据失败:', error);
|
console.error('获取教师数据失败:', error);
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
bjFindByNjId,
|
bjFindByNjId,
|
||||||
jsFindAll,
|
jsFindAll,
|
||||||
|
jsFindAllBasicInfoVo,
|
||||||
njFindAll,
|
njFindAll,
|
||||||
zwFindAllApi,
|
zwFindAllApi,
|
||||||
zwGetListByLxApi,
|
zwGetListByLxApi,
|
||||||
@ -50,6 +51,20 @@ export const useCommonStore = defineStore({
|
|||||||
}
|
}
|
||||||
return Promise.resolve(this.data.allJs);
|
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> {
|
async getAllZw(): Promise<any> {
|
||||||
if (!this.data.allZw) {
|
if (!this.data.allZw) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user