调整请假
This commit is contained in:
parent
7bb1548115
commit
b1cf1b9a6f
@ -121,3 +121,26 @@ export const jzXkTkjApi = async (params: any) => {
|
|||||||
export const jzXkJfCxjApi = async (params: any) => {
|
export const jzXkJfCxjApi = async (params: any) => {
|
||||||
return await post("/mobile/jz/xk/jfcx", params);
|
return await post("/mobile/jz/xk/jfcx", params);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发起学生请假
|
||||||
|
*/
|
||||||
|
export const jzAddXsQjApi = async (params: any) => {
|
||||||
|
return await post("/api/xsQj/save", params);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询学生请假列表
|
||||||
|
*/
|
||||||
|
export const jzXsQjListApi = async (params: any) => {
|
||||||
|
return await get("/api/xsQj/findPage", params);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询学生请假流程处理历史
|
||||||
|
*/
|
||||||
|
export const jzXsQjActivitiHistoryApi = async (params: any) => {
|
||||||
|
return await get("/api/activiti/history/historicFlow", params);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -19,7 +19,7 @@
|
|||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
<view class="p-15">
|
<view class="p-15">
|
||||||
<view v-for="(item,index) in dataList" :key="item.id||index">
|
<view v-for="(item,index) in dataList" :key="item.id || index">
|
||||||
<slot :data="item" :index="index" :list="dataList"/>
|
<slot :data="item" :index="index" :list="dataList"/>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@ -32,7 +32,7 @@
|
|||||||
import {reactive} from "vue";
|
import {reactive} from "vue";
|
||||||
import type {LayoutOptions, PagingRefInterface} from "@/components/BasicListLayout/type/useLayout";
|
import type {LayoutOptions, PagingRefInterface} from "@/components/BasicListLayout/type/useLayout";
|
||||||
|
|
||||||
const dataList = ref([])
|
const dataList = ref<any>([])
|
||||||
const fixed = ref(false)
|
const fixed = ref(false)
|
||||||
const pagingRef = ref<PagingRefInterface | null>(null)
|
const pagingRef = ref<PagingRefInterface | null>(null)
|
||||||
|
|
||||||
|
|||||||
@ -48,36 +48,6 @@ let pageParams = ref({
|
|||||||
xsId: getCurXs.id
|
xsId: getCurXs.id
|
||||||
})
|
})
|
||||||
|
|
||||||
// 模拟请假申请数据
|
|
||||||
const mockLeaveData = [
|
|
||||||
{
|
|
||||||
id: "ks001",
|
|
||||||
applicantName: "施瑞辰",
|
|
||||||
reason: "家里有事要处理",
|
|
||||||
startTime: "2025-02-17 08:00",
|
|
||||||
endTime: "2025-02-17 18:00",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "ks002",
|
|
||||||
applicantName: "李晓明",
|
|
||||||
reason: "身体不适,需要就医",
|
|
||||||
startTime: "2025-02-18 09:00",
|
|
||||||
endTime: "2025-02-18 17:00",
|
|
||||||
},
|
|
||||||
// 可以添加更多模拟数据
|
|
||||||
];
|
|
||||||
|
|
||||||
// 模拟 API,实际应替换为真实 API 调用
|
|
||||||
const testList = async (param?: any): Promise<{ message: string, resultCode: number, rows: any[] }> => {
|
|
||||||
console.log("Simulating API call for ks list with params:", param);
|
|
||||||
// 这里简单返回所有数据,真实场景需要分页
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
setTimeout(() => {
|
|
||||||
resolve({ message: "测试", resultCode: 1, rows: mockLeaveData });
|
|
||||||
}, 500);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const [register, { reload }] = useLayout({
|
const [register, { reload }] = useLayout({
|
||||||
api: xsKsccApi,
|
api: xsKsccApi,
|
||||||
componentProps: {},
|
componentProps: {},
|
||||||
@ -92,9 +62,6 @@ const viewDetails = (item: any | null) => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.ks-application-page {
|
|
||||||
}
|
|
||||||
|
|
||||||
.ks-card {
|
.ks-card {
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
|||||||
200
src/pages/base/leave-request/components/xsQjEdit.vue
Normal file
200
src/pages/base/leave-request/components/xsQjEdit.vue
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
<template>
|
||||||
|
<BasicLayout :fixed="false">
|
||||||
|
<view class="p-15">
|
||||||
|
<BasicForm @register="register" />
|
||||||
|
</view>
|
||||||
|
<template #bottom>
|
||||||
|
<view class="white-bg-color py-5">
|
||||||
|
<view class="flex-row items-center pb-10 pt-5">
|
||||||
|
<u-button
|
||||||
|
text="取消"
|
||||||
|
class="ml-15 mr-7"
|
||||||
|
:plain="true"
|
||||||
|
@click="navigateBack"
|
||||||
|
/>
|
||||||
|
<u-button
|
||||||
|
text="提交"
|
||||||
|
class="mr-15 mr-7"
|
||||||
|
type="primary"
|
||||||
|
@click="submit"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
</BasicLayout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { navigateBack } from "@/utils/uniapp";
|
||||||
|
import { useForm } from "@/components/BasicForm/hooks/useForm";
|
||||||
|
import { dicApi } from "@/api/system/dic";
|
||||||
|
import { jzAddXsQjApi } from "@/api/base/server";
|
||||||
|
import { showToast } from "@/utils/uniapp";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import { useUserStore } from "@/store/modules/user";
|
||||||
|
const { getCurXs, getUser } = useUserStore();
|
||||||
|
|
||||||
|
// 接收外部传入属性
|
||||||
|
const props = withDefaults(defineProps<{
|
||||||
|
data: any
|
||||||
|
}>(), {
|
||||||
|
data: () => ({
|
||||||
|
id: "",
|
||||||
|
qjlx: "事假",
|
||||||
|
qjkssj: "2025-07-07 09:00:00",
|
||||||
|
qjjssj: "2025-07-08 10:00:00",
|
||||||
|
qjsc: "25小时",
|
||||||
|
qjsy: "我有事情",
|
||||||
|
sflx: 1
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
let formData = ref<any>({});
|
||||||
|
|
||||||
|
const [register, { getValue, setValue }] = useForm({
|
||||||
|
schema: [
|
||||||
|
{
|
||||||
|
field: "qjlx",
|
||||||
|
label: "请假类型",
|
||||||
|
required: true,
|
||||||
|
component: "BasicPicker",
|
||||||
|
componentProps: {
|
||||||
|
api: dicApi,
|
||||||
|
param: { pid: 1007011432 },
|
||||||
|
rangeKey: "dictionaryValue",
|
||||||
|
savaKey: "dictionaryCode",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ interval: true },
|
||||||
|
{
|
||||||
|
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: "请输入选择开始时间和结束时间"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ interval: true },
|
||||||
|
{
|
||||||
|
field: "qjsy",
|
||||||
|
label: "请假事由",
|
||||||
|
component: "BasicInput",
|
||||||
|
required: true,
|
||||||
|
itemProps: {
|
||||||
|
labelPosition: "top",
|
||||||
|
},
|
||||||
|
componentProps: {
|
||||||
|
type: "textarea",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: "sflx",
|
||||||
|
label: "是否离校",
|
||||||
|
component: "BasicCheckbox",
|
||||||
|
required: true,
|
||||||
|
itemProps: {
|
||||||
|
labelPosition: "top",
|
||||||
|
},
|
||||||
|
componentProps: {
|
||||||
|
data: [
|
||||||
|
{ value: 0, text: "否" },
|
||||||
|
{ value: 1, text: "是" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// field: "qjtp",
|
||||||
|
// label: "请假图片",
|
||||||
|
// component: "BasicUpload",
|
||||||
|
// required: true,
|
||||||
|
// itemProps: {
|
||||||
|
// labelPosition: "top",
|
||||||
|
// },
|
||||||
|
// componentProps: {},
|
||||||
|
// },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
setValue(props.data)
|
||||||
|
|
||||||
|
const changeKsTime = (selectedTime?: string) => {
|
||||||
|
if (!selectedTime) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
formData.value.qjkstime = selectedTime;
|
||||||
|
validateTime();
|
||||||
|
};
|
||||||
|
|
||||||
|
const changeJsTime = (selectedTime?: string) => {
|
||||||
|
if (!selectedTime) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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 });
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const submit = () => {
|
||||||
|
const fd = getValue();
|
||||||
|
if (!validateTime()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const params = { ...formData, ...fd };
|
||||||
|
if (props.data && props.data.id) {
|
||||||
|
params.id = props.data.id;
|
||||||
|
} else {
|
||||||
|
params.id = null;
|
||||||
|
params.xsId = getCurXs.id; // 学生ID
|
||||||
|
params.jsId = getCurXs.bzrId; // 班主任ID
|
||||||
|
params.xqId = getCurXs.xqId; // 学期ID
|
||||||
|
params.jzId = getUser.jzId; // 家长ID
|
||||||
|
}
|
||||||
|
params.flag = 1;
|
||||||
|
jzAddXsQjApi(params).then(() => {
|
||||||
|
showToast({ title: "提交成功", icon: "success" });
|
||||||
|
navigateBack();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
</script>
|
||||||
147
src/pages/base/leave-request/components/xsQjList.vue
Normal file
147
src/pages/base/leave-request/components/xsQjList.vue
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
<template>
|
||||||
|
<view class="leave-list">
|
||||||
|
<BasicListLayout @register="register" style="position: absolute;">
|
||||||
|
<template v-slot="{ data, index }">
|
||||||
|
<view class="leave-card" @click="goToDetail(data)">
|
||||||
|
<view class="card-header">
|
||||||
|
<text class="applicant-name">{{ data.applicantName }}的请假申请</text>
|
||||||
|
</view>
|
||||||
|
<view class="card-body">
|
||||||
|
<view class="info-row">
|
||||||
|
<text class="label">请假事由:</text>
|
||||||
|
<text class="value">{{ data.reason }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="info-row">
|
||||||
|
<text class="label">开始时间:</text>
|
||||||
|
<text class="value">{{ data.startTime }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="info-row">
|
||||||
|
<text class="label">结束时间:</text>
|
||||||
|
<text class="value">{{ data.endTime }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="card-footer">
|
||||||
|
<text>查看详情</text>
|
||||||
|
<text class="arrow">
|
||||||
|
<uni-icons type="arrowright" size="16" color="#ccc"></uni-icons>
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
</BasicListLayout>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useLayout } from "@/components/BasicListLayout/hooks/useLayout";
|
||||||
|
import { jzXsQjListApi } from "@/api/base/server";
|
||||||
|
import { useUserStore } from "@/store/modules/user";
|
||||||
|
import { useDataStore } from "@/store/modules/data";
|
||||||
|
const { getCurXs } = useUserStore();
|
||||||
|
const { setData } = useDataStore();
|
||||||
|
|
||||||
|
let pageParams = ref({
|
||||||
|
rows: 10,
|
||||||
|
xsId: getCurXs.id
|
||||||
|
})
|
||||||
|
|
||||||
|
const [register, { reload }] = useLayout({
|
||||||
|
api: jzXsQjListApi,
|
||||||
|
componentProps: {},
|
||||||
|
param: pageParams.value
|
||||||
|
});
|
||||||
|
|
||||||
|
// 查看详情或新增处理函数
|
||||||
|
const goToDetail = (item: any | null) => {
|
||||||
|
setData(item);
|
||||||
|
let url = '/pages/base/leave-request/leaveApplication'; // 使用新路径
|
||||||
|
if (item && item.id) {
|
||||||
|
console.log('View details for:', item);
|
||||||
|
url += `?id=${item.id}`; // 查看详情时传递 ID
|
||||||
|
} else {
|
||||||
|
console.log('Navigating to create new leave application.');
|
||||||
|
// 新增时不传递 ID
|
||||||
|
}
|
||||||
|
uni.navigateTo({ url });
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.leave-list {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
flex: 1 0 1px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.leave-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;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
width: 70px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .zp-loading-fixed {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .d-load-main {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -1,166 +1,51 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="leave-application-page">
|
<view class="leave-page">
|
||||||
<BasicListLayout @register="register">
|
<!-- 选项卡 -->
|
||||||
<template v-slot="{ data, index }">
|
<BasicTabs class="leave-tabs"
|
||||||
<view class="leave-card" @click="viewDetails(data)">
|
ref="tabsRef" :list="tabList" bar-width="60px" scroll-count="4"
|
||||||
<view class="card-header">
|
:current="curTabIndex" @change="switchTab"
|
||||||
<text class="applicant-name"
|
/>
|
||||||
>{{ data.applicantName }}的请假申请</text
|
<view class="leave-edit" v-if="curTabIndex === 0">
|
||||||
>
|
<XsQjEdit />
|
||||||
</view>
|
</view>
|
||||||
<view class="card-body">
|
<view class="leave-list" v-else>
|
||||||
<view class="info-row">
|
<XsQjList />
|
||||||
<text class="label">请假事由:</text>
|
</view>
|
||||||
<text class="value">{{ data.reason }}</text>
|
|
||||||
</view>
|
|
||||||
<view class="info-row">
|
|
||||||
<text class="label">开始时间:</text>
|
|
||||||
<text class="value">{{ data.startTime }}</text>
|
|
||||||
</view>
|
|
||||||
<view class="info-row">
|
|
||||||
<text class="label">结束时间:</text>
|
|
||||||
<text class="value">{{ data.endTime }}</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<view class="card-footer">
|
|
||||||
<text>查看详情</text>
|
|
||||||
<text class="arrow">
|
|
||||||
<uni-icons type="arrowright" size="16" color="#ccc"></uni-icons>
|
|
||||||
</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
<template #bottom>
|
|
||||||
<view class="button" @click="viewDetails(null)"> <!-- Pass null for new application -->
|
|
||||||
<text>新增请假</text>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
</BasicListLayout>
|
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from "vue";
|
import XsQjEdit from "./components/xsQjEdit.vue"
|
||||||
import { useLayout } from "@/components/BasicListLayout/hooks/useLayout";
|
import XsQjList from "./components/xsQjList.vue"
|
||||||
|
|
||||||
// 模拟请假申请数据
|
const tabList = ref([
|
||||||
const mockLeaveData = [
|
{ name: "请假申请", id: "leave-edit" },
|
||||||
{
|
{ name: "请假记录", id: "leave-list" },
|
||||||
id: "leave001",
|
]);
|
||||||
applicantName: "施瑞辰",
|
|
||||||
reason: "家里有事要处理",
|
|
||||||
startTime: "2025-02-17 08:00",
|
|
||||||
endTime: "2025-02-17 18:00",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "leave002",
|
|
||||||
applicantName: "李晓明",
|
|
||||||
reason: "身体不适,需要就医",
|
|
||||||
startTime: "2025-02-18 09:00",
|
|
||||||
endTime: "2025-02-18 17:00",
|
|
||||||
},
|
|
||||||
// 可以添加更多模拟数据
|
|
||||||
];
|
|
||||||
|
|
||||||
// 模拟 API,实际应替换为真实 API 调用
|
const curTabIndex = ref(0);
|
||||||
const testList = async (param?: any): Promise<{ message: string, resultCode: number, rows: any[] }> => {
|
|
||||||
console.log("Simulating API call for leave list with params:", param);
|
|
||||||
// 这里简单返回所有数据,真实场景需要分页
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
setTimeout(() => {
|
|
||||||
resolve({ message: "测试", resultCode: 1, rows: mockLeaveData });
|
|
||||||
}, 500);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const [register, { reload }] = useLayout({
|
const switchTab = (index : number) => {
|
||||||
api: testList,
|
curTabIndex.value = index;
|
||||||
componentProps: {},
|
}
|
||||||
});
|
|
||||||
|
|
||||||
// 查看详情或新增处理函数
|
|
||||||
const viewDetails = (item: any | null) => {
|
|
||||||
let url = '/pages/base/leave-request/leaveApplication'; // 使用新路径
|
|
||||||
if (item && item.id) {
|
|
||||||
console.log('View details for:', item);
|
|
||||||
url += `?id=${item.id}`; // 查看详情时传递 ID
|
|
||||||
} else {
|
|
||||||
console.log('Navigating to create new leave application.');
|
|
||||||
// 新增时不传递 ID
|
|
||||||
}
|
|
||||||
uni.navigateTo({ url });
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.leave-application-page {
|
|
||||||
}
|
|
||||||
|
|
||||||
.leave-card {
|
.leave-page {
|
||||||
background-color: #ffffff;
|
flex: 1 0 1px;
|
||||||
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;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
&:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
.label {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #666;
|
|
||||||
width: 70px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
.value {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #333;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-footer {
|
|
||||||
padding: 12px 15px;
|
|
||||||
border-top: 1px solid #f0f0f0;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
flex-direction: column;
|
||||||
align-items: center;
|
height: 100vh;
|
||||||
font-size: 14px;
|
.leave-tabs {
|
||||||
color: #888;
|
flex: 0 0 45px;
|
||||||
cursor: pointer;
|
}
|
||||||
|
.leave-edit,
|
||||||
.arrow {
|
.leave-list {
|
||||||
font-size: 16px;
|
flex: 1 0 1px;
|
||||||
color: #ccc;
|
position: relative;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增按钮样式示例 (可根据 BasicListLayout 的 bottom 插槽调整)
|
|
||||||
.button {
|
|
||||||
padding: 10px 15px;
|
|
||||||
background-color: #447ade;
|
|
||||||
color: white;
|
|
||||||
text-align: center;
|
|
||||||
border-radius: 5px;
|
|
||||||
margin: 10px 15px; // 示例外边距
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
385
src/pages/base/leave-request/xsQjDetail.vue
Normal file
385
src/pages/base/leave-request/xsQjDetail.vue
Normal file
@ -0,0 +1,385 @@
|
|||||||
|
<template>
|
||||||
|
<BasicLayout>
|
||||||
|
<view class="course-detail">
|
||||||
|
<!-- 课程信息卡片 -->
|
||||||
|
<view class="info-card">
|
||||||
|
<view class="card-title">课程信息</view>
|
||||||
|
<view class="divider"></view>
|
||||||
|
|
||||||
|
<view class="course-info">
|
||||||
|
<image
|
||||||
|
class="course-image"
|
||||||
|
:src="courseDetail.xkkcImg"
|
||||||
|
mode="aspectFill"
|
||||||
|
></image>
|
||||||
|
|
||||||
|
<view class="course-content">
|
||||||
|
<view class="course-name">{{ courseDetail.title }}</view>
|
||||||
|
<view class="course-teacher"
|
||||||
|
>开课老师:{{ courseDetail.teacher }}</view
|
||||||
|
>
|
||||||
|
<view class="course-location"
|
||||||
|
>上课地点:{{ courseDetail.location }}</view
|
||||||
|
>
|
||||||
|
<view class="course-time"
|
||||||
|
>上课时间:{{ courseDetail.studyTime }}</view
|
||||||
|
>
|
||||||
|
<view class="course-price">
|
||||||
|
金额:<text class="price-value">¥{{ courseDetail.price }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 老师信息卡片 -->
|
||||||
|
<view class="info-card">
|
||||||
|
<view class="card-title">老师信息</view>
|
||||||
|
<view class="divider"></view>
|
||||||
|
|
||||||
|
<view class="teacher-info">
|
||||||
|
<!-- 教师头部信息区域 -->
|
||||||
|
<view class="teacher-header">
|
||||||
|
<image
|
||||||
|
class="teacher-avatar"
|
||||||
|
:src="teacherInfo.avatar"
|
||||||
|
mode="aspectFill"
|
||||||
|
></image>
|
||||||
|
<view class="teacher-basic-info">
|
||||||
|
<view class="teacher-name">{{ teacherInfo.name }}</view>
|
||||||
|
<view class="teacher-title">课程教师</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 教师简介区域 -->
|
||||||
|
<view class="teacher-intro-section">
|
||||||
|
<view class="intro-title">教师简介</view>
|
||||||
|
<view class="teacher-intro">
|
||||||
|
<text>{{ teacherInfo.introduction }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 教学理念 -->
|
||||||
|
<view class="info-card">
|
||||||
|
<view class="card-title">教学理念</view>
|
||||||
|
<view class="divider"></view>
|
||||||
|
|
||||||
|
<view class="content-section">
|
||||||
|
<text>{{ teachingPhilosophy }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 教学计划 -->
|
||||||
|
<view class="info-card">
|
||||||
|
<view class="card-title">教学计划</view>
|
||||||
|
<view class="divider"></view>
|
||||||
|
|
||||||
|
<view class="content-section">
|
||||||
|
<template v-if="teachingPlan && teachingPlan.length > 0">
|
||||||
|
<view
|
||||||
|
v-for="(phase, index) in teachingPlan"
|
||||||
|
:key="index"
|
||||||
|
class="teaching-phase"
|
||||||
|
>
|
||||||
|
<text>{{ phase }}</text>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<view class="empty-data">
|
||||||
|
<u-icon name="info-circle" color="#C8C9CC" size="18"></u-icon>
|
||||||
|
<text>暂无教学计划</text>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<template #bottom>
|
||||||
|
<view class="white-bg-color py-5">
|
||||||
|
<view class="flex-row items-center pb-10 pt-5">
|
||||||
|
<u-button
|
||||||
|
text="返回"
|
||||||
|
class="ml-15 mr-7"
|
||||||
|
:plain="true"
|
||||||
|
@click="navigateBack"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
</BasicLayout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted, computed } from "vue";
|
||||||
|
import { navigateBack } from "@/utils/uniapp";
|
||||||
|
import { useDataStore } from "@/store/modules/data";
|
||||||
|
import { storeToRefs } from "pinia";
|
||||||
|
import { imagUrl } from "@/utils";
|
||||||
|
import { kcjhFindKcjhByKcIdApi } from "@/api/base/server";
|
||||||
|
|
||||||
|
// 定义课程数据类型
|
||||||
|
interface CourseData {
|
||||||
|
id?: string;
|
||||||
|
kcmc?: string;
|
||||||
|
jsName?: string;
|
||||||
|
kcdd?: string;
|
||||||
|
kcje?: number;
|
||||||
|
studyTime?: string;
|
||||||
|
kcjsms?: string;
|
||||||
|
jxll?: string;
|
||||||
|
remark?: string;
|
||||||
|
xkkcImg?: string;
|
||||||
|
[key: string]: any; // 允许其他字段
|
||||||
|
}
|
||||||
|
|
||||||
|
const useData = useDataStore();
|
||||||
|
const { kcData } = storeToRefs(useData);
|
||||||
|
|
||||||
|
// 教学计划 - 此处假设没有具体教学计划字段,将备注内容拆分为教学计划
|
||||||
|
const teachingPlan = ref<string[]>([]);
|
||||||
|
|
||||||
|
const courseData = kcData.value as CourseData;
|
||||||
|
if (courseData && courseData.id) {
|
||||||
|
kcjhFindKcjhByKcIdApi({
|
||||||
|
xkkcId: courseData.id,
|
||||||
|
}).then((res) => {
|
||||||
|
if (res.resultCode == 1) {
|
||||||
|
teachingPlan.value = res.result.map(
|
||||||
|
(item: any) => item.jhjd + ":" + item.jhms
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 课程详情数据
|
||||||
|
const courseDetail = computed(() => {
|
||||||
|
const data = (kcData.value as CourseData) || {};
|
||||||
|
return {
|
||||||
|
id: data.id || "",
|
||||||
|
title: data.kcmc || "暂无课程名称",
|
||||||
|
teacher: data.jsName || "暂无教师信息",
|
||||||
|
location: data.kcdd || "暂无地点信息",
|
||||||
|
price: data.kcje || 0,
|
||||||
|
studyTime: data.studyTime || "暂无上课时间",
|
||||||
|
xkkcImg: data.xkkcImg || "/static/images/robot-course.jpg", // 默认图片
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// 教师信息
|
||||||
|
const teacherInfo = computed(() => {
|
||||||
|
const data = (kcData.value as CourseData) || {};
|
||||||
|
return {
|
||||||
|
name: data.jsName || "暂无教师信息",
|
||||||
|
avatar: imagUrl(data.jstx), // 默认头像
|
||||||
|
introduction: data.kcjsms || "暂无教师介绍",
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// 教学理念
|
||||||
|
const teachingPhilosophy = computed(() => {
|
||||||
|
const data = (kcData.value as CourseData) || {};
|
||||||
|
return data.jxll || "暂无教学理念信息";
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.course-detail {
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-bar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 15px;
|
||||||
|
height: 44px;
|
||||||
|
background-color: #2879ff;
|
||||||
|
|
||||||
|
.nav-left {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-right {
|
||||||
|
width: 40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-card {
|
||||||
|
margin: 15px;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 15px;
|
||||||
|
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider {
|
||||||
|
height: 1px;
|
||||||
|
background-color: #eee;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-info {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.course-image {
|
||||||
|
width: 120px;
|
||||||
|
height: 138px;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-right: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-content {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.course-name {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-teacher,
|
||||||
|
.course-location {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-time {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.course-price {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
|
||||||
|
.price-value {
|
||||||
|
color: #ff6b00;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.teacher-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.teacher-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
|
||||||
|
.teacher-avatar {
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-right: 15px;
|
||||||
|
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
|
||||||
|
border: 2px solid #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.teacher-basic-info {
|
||||||
|
.teacher-name {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.teacher-title {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
background-color: #f4f7fc;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.teacher-intro-section {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.intro-title {
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #555;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
padding-left: 8px;
|
||||||
|
border-left: 3px solid #2879ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.teacher-intro {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
line-height: 1.6;
|
||||||
|
background-color: #f8f9fc;
|
||||||
|
padding: 12px;
|
||||||
|
border-radius: 8px;
|
||||||
|
text-align: justify;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-section {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
line-height: 1.6;
|
||||||
|
|
||||||
|
.teaching-phase {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-data {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 20px 0;
|
||||||
|
color: #909399;
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
text {
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-action {
|
||||||
|
padding: 15px;
|
||||||
|
margin-top: 20px;
|
||||||
|
|
||||||
|
.back-btn {
|
||||||
|
width: 100%;
|
||||||
|
height: 44px;
|
||||||
|
line-height: 44px;
|
||||||
|
background-color: #fff;
|
||||||
|
color: #333;
|
||||||
|
border-radius: 22px;
|
||||||
|
font-size: 16px;
|
||||||
|
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -220,7 +220,7 @@ async function afterRead(event: any, index: number) {
|
|||||||
const tempFilePath = event.tempFilePaths[0];
|
const tempFilePath = event.tempFilePaths[0];
|
||||||
showLoading({ title: "上传中" });
|
showLoading({ title: "上传中" });
|
||||||
try {
|
try {
|
||||||
const res = await attachmentUpload(tempFilePath);
|
const res = await attachmentUpload(tempFilePath) as { result: Array<{ filePath: string }> };
|
||||||
const result = res.result;
|
const result = res.result;
|
||||||
if (result && result.length > 0 && result[0].filePath) {
|
if (result && result.length > 0 && result[0].filePath) {
|
||||||
students.value[index].xstx = result[0].filePath;
|
students.value[index].xstx = result[0].filePath;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user