调整就餐点名
This commit is contained in:
parent
b578cf8c60
commit
6360186c9d
@ -223,16 +223,38 @@ const tjDm = debounce(async () => {
|
|||||||
jsId: getJs.id || '', // 点名教师ID
|
jsId: getJs.id || '', // 点名教师ID
|
||||||
pcRs: dmJsList.length,
|
pcRs: dmJsList.length,
|
||||||
zrs: dmXsList.length,
|
zrs: dmXsList.length,
|
||||||
sdRs: dmXsList.filter((s: any) => s.jcZt === 'A').length,
|
// 使用一次循环统计所有状态数量,提高效率
|
||||||
qjRs: dmXsList.filter((s: any) => s.jcZt === 'B').length,
|
sdRs: 0,
|
||||||
qqRs: dmXsList.filter((s: any) => s.jcZt === 'C').length,
|
qjRs: 0,
|
||||||
wbmRs: dmXsList.filter((s: any) => s.jcZt === 'E').length,
|
qqRs: 0,
|
||||||
|
wjfRs: 0,
|
||||||
|
wbmRs: 0,
|
||||||
// 媒体文件地址
|
// 媒体文件地址
|
||||||
zp: photoUrls, // 照片字段,逗号分隔的字符串
|
zp: photoUrls, // 照片字段,逗号分隔的字符串
|
||||||
sp: videoUrls, // 视频字段,逗号分隔的字符串
|
sp: videoUrls, // 视频字段,逗号分隔的字符串
|
||||||
xsList: dmXsList,
|
xsList: dmXsList,
|
||||||
ptJsList: dmJsList
|
ptJsList: dmJsList
|
||||||
};
|
};
|
||||||
|
// 通过一次循环统计各状态学生数量,提高效率
|
||||||
|
for (const s of dmXsList) {
|
||||||
|
switch (s.jcZt) {
|
||||||
|
case 'A':
|
||||||
|
dmData.sdRs++;
|
||||||
|
break;
|
||||||
|
case 'B':
|
||||||
|
dmData.qjRs++;
|
||||||
|
break;
|
||||||
|
case 'C':
|
||||||
|
dmData.qqRs++;
|
||||||
|
break;
|
||||||
|
case 'D':
|
||||||
|
dmData.wjfRs++;
|
||||||
|
break;
|
||||||
|
case 'E':
|
||||||
|
dmData.wbmRs++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
uni.showLoading({
|
uni.showLoading({
|
||||||
title: '提交中...',
|
title: '提交中...',
|
||||||
mask: true
|
mask: true
|
||||||
|
|||||||
@ -9,21 +9,11 @@
|
|||||||
<view class="search-item">
|
<view class="search-item">
|
||||||
<text class="label">班级:</text>
|
<text class="label">班级:</text>
|
||||||
<view class="flex-1">
|
<view class="flex-1">
|
||||||
<BasicNjBjSelect
|
<!-- 替换为树形选择器 -->
|
||||||
:custom-style="{
|
<view class="class-selector" @click="showClassTree">
|
||||||
borderRadius: '12rpx',
|
<text :class="{ placeholder: !selectedClassText }">{{ selectedClassText || "请选择班级" }}</text>
|
||||||
background: 'background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%)',
|
<uni-icons type="right" size="16" color="#999"></uni-icons>
|
||||||
padding: '0rpx 30rpx',
|
</view>
|
||||||
border: '1rpx solid #e9ecef',
|
|
||||||
transition: 'all 0.3s ease',
|
|
||||||
}"
|
|
||||||
v-model="selectedNjBj"
|
|
||||||
placeholder="请选择年级班级"
|
|
||||||
:only-confirm-change-flag="true"
|
|
||||||
:auto-selected-first="false"
|
|
||||||
:bj-required="true"
|
|
||||||
@change="changeNjBj"
|
|
||||||
/>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@ -88,15 +78,29 @@
|
|||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
</BasicListLayout>
|
</BasicListLayout>
|
||||||
|
|
||||||
|
<!-- 班级选择树 -->
|
||||||
|
<BasicTree
|
||||||
|
ref="treeRef"
|
||||||
|
:range="treeData"
|
||||||
|
idKey="key"
|
||||||
|
rangeKey="title"
|
||||||
|
title="选择班级"
|
||||||
|
:multiple="false"
|
||||||
|
:selectParent="false"
|
||||||
|
@confirm="onTreeConfirm"
|
||||||
|
@cancel="onTreeCancel"
|
||||||
|
/>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, computed } from "vue";
|
import { ref, onMounted, computed } from "vue";
|
||||||
import { useLayout } from "@/components/BasicListLayout/hooks/useLayout";
|
import { useLayout } from "@/components/BasicListLayout/hooks/useLayout";
|
||||||
import BasicNjBjSelect from '@/components/BasicNjBjSelect/index.vue'
|
import BasicTree from '@/components/BasicTree/Tree.vue'
|
||||||
import { jcDmFindPageApi } from "@/api/base/jcApi";
|
import { jcDmFindPageApi } from "@/api/base/jcApi";
|
||||||
import { useDataStore } from "@/store/modules/data";
|
import { useDataStore } from "@/store/modules/data";
|
||||||
|
import { findAllNjBjTree } from '@/api/base/server'
|
||||||
|
|
||||||
const { setData, getJcBz } = useDataStore();
|
const { setData, getJcBz } = useDataStore();
|
||||||
|
|
||||||
@ -110,16 +114,27 @@ const props = withDefaults(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 树形数据
|
||||||
|
const treeData = ref<any[]>([]);
|
||||||
|
const treeRef = ref();
|
||||||
|
|
||||||
// 响应式数据
|
// 响应式数据
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const startTime = ref("");
|
const startTime = ref("");
|
||||||
const endTime = ref("");
|
const endTime = ref("");
|
||||||
const selectedClass = ref<any>(null);
|
const curNj = ref<any>(null);
|
||||||
const dmRecords = ref<any>([]);
|
const curBj = ref<any>(null);
|
||||||
|
const dmRecords = ref<any[]>([]);
|
||||||
const totalCount = ref<any>(0);
|
const totalCount = ref<any>(0);
|
||||||
const hasSearched = ref(false);
|
const hasSearched = ref(false);
|
||||||
|
|
||||||
const selectedNjBj = ref<any>(null);
|
// 计算属性:显示选中的班级文本
|
||||||
|
const selectedClassText = computed(() => {
|
||||||
|
if (curBj.value && curNj.value) {
|
||||||
|
return `${curNj.value.title} ${curBj.value.title}`;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
});
|
||||||
|
|
||||||
// 使用 BasicListLayout
|
// 使用 BasicListLayout
|
||||||
const [register, { reload, setParam }] = useLayout({
|
const [register, { reload, setParam }] = useLayout({
|
||||||
@ -150,16 +165,58 @@ const [register, { reload, setParam }] = useLayout({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// 改变了年级班级
|
// 加载树形数据
|
||||||
const changeNjBj = (val: any) => {
|
const loadTreeData = async () => {
|
||||||
const njList = val.selectedGrades || [];
|
try {
|
||||||
const nj = njList[0] || {};
|
const res = await findAllNjBjTree();
|
||||||
const bjList = val.selectedClasses || [];
|
if (res.resultCode === 1 && res.result) {
|
||||||
const bj = bjList[0] || {};
|
// 递归转换数据格式以适配 BasicTree 组件
|
||||||
selectedClass.value = {
|
const convertTreeData = (items: any[]): any[] => {
|
||||||
njId: nj.key,
|
return items.map((item: any) => ({
|
||||||
bjId: bj.key,
|
key: item.key,
|
||||||
};
|
title: item.title,
|
||||||
|
njmcId: item.njmcId,
|
||||||
|
children: item.children ? convertTreeData(item.children) : [],
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
treeData.value = convertTreeData(res.result);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
uni.showToast({ title: "加载班级数据失败", icon: "error" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 显示班级选择树
|
||||||
|
const showClassTree = () => {
|
||||||
|
if (treeRef.value) {
|
||||||
|
treeRef.value._show();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 树形选择确认
|
||||||
|
const onTreeConfirm = (selectedItems: any[]) => {
|
||||||
|
if (selectedItems.length > 0) {
|
||||||
|
const selectedItem = selectedItems[0]; // 单选模式,取第一个
|
||||||
|
|
||||||
|
// 如果选择的是班级(有parents表示是班级)
|
||||||
|
if (selectedItem.parents && selectedItem.parents.length > 0) {
|
||||||
|
const parent = selectedItem.parents[0]; // 年级信息
|
||||||
|
const nj = parent; // 年级信息
|
||||||
|
const bj = selectedItem; // 班级信息
|
||||||
|
|
||||||
|
curNj.value = nj;
|
||||||
|
curBj.value = bj;
|
||||||
|
} else {
|
||||||
|
// 如果选择的是年级,清空班级选择
|
||||||
|
curNj.value = selectedItem;
|
||||||
|
curBj.value = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 树形选择取消
|
||||||
|
const onTreeCancel = () => {
|
||||||
|
// 取消选择,不做任何操作
|
||||||
};
|
};
|
||||||
|
|
||||||
const onTimeRangeChange = (e: any) => {
|
const onTimeRangeChange = (e: any) => {
|
||||||
@ -184,7 +241,7 @@ const getTimeRangeText = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const searchRecords = async () => {
|
const searchRecords = async () => {
|
||||||
if (!selectedClass.value || !startTime.value || !endTime.value) {
|
if (!curBj.value || !startTime.value || !endTime.value) {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: "请选择班级和时间范围",
|
title: "请选择班级和时间范围",
|
||||||
icon: "none",
|
icon: "none",
|
||||||
@ -205,8 +262,8 @@ const searchRecords = async () => {
|
|||||||
|
|
||||||
// 调试日志
|
// 调试日志
|
||||||
console.log("搜索参数:", {
|
console.log("搜索参数:", {
|
||||||
njId: selectedClass.value.njId,
|
njId: curNj.value.key,
|
||||||
bjId: selectedClass.value.bjId,
|
bjId: curBj.value.key,
|
||||||
startTime: startTime.value + " 00:00:00",
|
startTime: startTime.value + " 00:00:00",
|
||||||
endTime: endTime.value + " 23:59:59",
|
endTime: endTime.value + " 23:59:59",
|
||||||
pageNo: 1,
|
pageNo: 1,
|
||||||
@ -215,8 +272,8 @@ const searchRecords = async () => {
|
|||||||
// 设置搜索参数并重新加载
|
// 设置搜索参数并重新加载
|
||||||
setParam({
|
setParam({
|
||||||
bzId: getJcBz.id,
|
bzId: getJcBz.id,
|
||||||
njId: selectedClass.value.njId,
|
njId: curNj.value.key,
|
||||||
bjId: selectedClass.value.bjId,
|
bjId: curBj.value.key,
|
||||||
startTime: startTime.value + " 00:00:00",
|
startTime: startTime.value + " 00:00:00",
|
||||||
endTime: endTime.value + " 23:59:59",
|
endTime: endTime.value + " 23:59:59",
|
||||||
pageNo: 1,
|
pageNo: 1,
|
||||||
@ -225,7 +282,8 @@ const searchRecords = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const resetSearch = () => {
|
const resetSearch = () => {
|
||||||
selectedClass.value = null;
|
curNj.value = null;
|
||||||
|
curBj.value = null;
|
||||||
startTime.value = "";
|
startTime.value = "";
|
||||||
endTime.value = "";
|
endTime.value = "";
|
||||||
dmRecords.value = [];
|
dmRecords.value = [];
|
||||||
@ -266,6 +324,9 @@ onMounted(() => {
|
|||||||
startTime.value = oneWeekAgo.toISOString().split("T")[0];
|
startTime.value = oneWeekAgo.toISOString().split("T")[0];
|
||||||
endTime.value = today.toISOString().split("T")[0];
|
endTime.value = today.toISOString().split("T")[0];
|
||||||
|
|
||||||
|
// 加载树形数据
|
||||||
|
loadTreeData();
|
||||||
|
|
||||||
// 调试日志
|
// 调试日志
|
||||||
console.log("组件初始化完成:", {
|
console.log("组件初始化完成:", {
|
||||||
startTime: startTime.value,
|
startTime: startTime.value,
|
||||||
@ -459,4 +520,29 @@ onMounted(() => {
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.class-selector {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 20rpx 30rpx;
|
||||||
|
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
|
||||||
|
border: 1rpx solid #e9ecef;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
|
text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333;
|
||||||
|
|
||||||
|
&.placeholder {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background: linear-gradient(135deg, #e9ecef 0%, #dee2e6 100%);
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -8,16 +8,26 @@
|
|||||||
<!-- 统计信息 -->
|
<!-- 统计信息 -->
|
||||||
<view class="stats-container">
|
<view class="stats-container">
|
||||||
<view class="stat-item clickable" @click="showStudentDrawer('all')">
|
<view class="stat-item clickable" @click="showStudentDrawer('all')">
|
||||||
<text class="stat-number">{{ rsData.zrs }}</text>
|
<text class="stat-number">{{ (bmXsList.length || 0) + (unBmXsList.length || 0) }}</text>
|
||||||
<text class="stat-label">总人数</text>
|
<text class="stat-label">班级人数</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="stat-item clickable" @click="showStudentDrawer('registered')">
|
<view class="stat-item clickable" @click="showStudentDrawer('bm')">
|
||||||
<text class="stat-number unregistered">{{ rsData.bmRs }}</text>
|
<text class="stat-number unregistered">{{ bmXsList.length || 0 }}</text>
|
||||||
<text class="stat-label">报名就餐</text>
|
<text class="stat-label">报名总数</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="stat-item clickable" @click="showStudentDrawer('unregistered')">
|
<view class="stat-item" >
|
||||||
<text class="stat-number unregistered">{{ rsData.unBmRs }}</text>
|
</view>
|
||||||
<text class="stat-label">未报名就餐</text>
|
<view class="stat-item clickable" @click="showStudentDrawer('yjf')">
|
||||||
|
<text class="stat-number unregistered">{{ bmYjfXsList.length || 0 }}</text>
|
||||||
|
<text class="stat-label">报名缴费</text>
|
||||||
|
</view>
|
||||||
|
<view class="stat-item clickable" @click="showStudentDrawer('wjf')">
|
||||||
|
<text class="stat-number unregistered">{{ bmWjfXsList.length || 0 }}</text>
|
||||||
|
<text class="stat-label">报名未缴费</text>
|
||||||
|
</view>
|
||||||
|
<view class="stat-item clickable" @click="showStudentDrawer('unBm')">
|
||||||
|
<text class="stat-number unregistered">{{ unBmXsList.length || 0 }}</text>
|
||||||
|
<text class="stat-label">未报名</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="stat-item clickable" @click="showStudentDrawer('normal')">
|
<view class="stat-item clickable" @click="showStudentDrawer('normal')">
|
||||||
<text class="stat-number normal">{{ hqZtSl('A') }}</text>
|
<text class="stat-number normal">{{ hqZtSl('A') }}</text>
|
||||||
@ -36,11 +46,11 @@
|
|||||||
<!-- 学生列表 - 改为card形式 -->
|
<!-- 学生列表 - 改为card形式 -->
|
||||||
<view class="xs-list">
|
<view class="xs-list">
|
||||||
<!-- 已缴费学生列表 -->
|
<!-- 已缴费学生列表 -->
|
||||||
<view v-if="bmXsList.length > 0" class="xs-section">
|
<view v-if="bmYjfXsList.length > 0" class="xs-section">
|
||||||
<view class="section-subtitle">已报名学生 ({{ bmXsList.length }}人)</view>
|
<view class="section-subtitle">已报名已缴费 ({{ bmYjfXsList.length }}人)</view>
|
||||||
<view class="xs-grid">
|
<view class="xs-grid">
|
||||||
<view
|
<view
|
||||||
v-for="xs in bmXsList"
|
v-for="xs in bmYjfXsList"
|
||||||
:key="xs.id"
|
:key="xs.id"
|
||||||
class="xs-item bg-white r-md p-12"
|
class="xs-item bg-white r-md p-12"
|
||||||
>
|
>
|
||||||
@ -74,8 +84,44 @@
|
|||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 未缴费/未报名学生列表 -->
|
<!-- 未缴费/未报名学生列表 -->
|
||||||
|
<view v-if="bmWjfXsList.length > 0" class="xs-section">
|
||||||
|
<view class="section-subtitle">已报名未缴费 ({{ bmWjfXsList.length }}人)</view>
|
||||||
|
<view class="xs-grid">
|
||||||
|
<view
|
||||||
|
v-for="xs in bmWjfXsList"
|
||||||
|
:key="xs.id"
|
||||||
|
class="xs-item bg-white r-md p-12"
|
||||||
|
>
|
||||||
|
<view class="flex-row items-center">
|
||||||
|
<view class="avatar-container mr-8">
|
||||||
|
<image
|
||||||
|
class="xs-avatar"
|
||||||
|
:src="imagUrl(xs.xstx) || '/static/images/default-avatar.png'"
|
||||||
|
mode="aspectFill"
|
||||||
|
></image>
|
||||||
|
</view>
|
||||||
|
<view class="flex-1 overflow-hidden">
|
||||||
|
<view class="xs-name mb-8">
|
||||||
|
<text class="font-12 cor-333">{{ xs.xm }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="flex-row">
|
||||||
|
<!-- 未缴费学生只显示状态,不能切换 -->
|
||||||
|
<view
|
||||||
|
class="status-tag readonly"
|
||||||
|
:class="getStatusClass('D')"
|
||||||
|
>
|
||||||
|
{{ hqZtWz('D') }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 未报名学生列表 -->
|
||||||
<view v-if="unBmXsList.length > 0" class="xs-section">
|
<view v-if="unBmXsList.length > 0" class="xs-section">
|
||||||
<view class="section-subtitle">未报名学生 ({{ unBmXsList.length }}人)</view>
|
<view class="section-subtitle">未报名 ({{ unBmXsList.length }}人)</view>
|
||||||
<view class="xs-grid">
|
<view class="xs-grid">
|
||||||
<view
|
<view
|
||||||
v-for="xs in unBmXsList"
|
v-for="xs in unBmXsList"
|
||||||
@ -95,7 +141,7 @@
|
|||||||
<text class="font-12 cor-333">{{ xs.xm }}</text>
|
<text class="font-12 cor-333">{{ xs.xm }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="flex-row">
|
<view class="flex-row">
|
||||||
<!-- 未缴费/未报名学生只显示状态,不能切换 -->
|
<!-- 未报名学生只显示状态,不能切换 -->
|
||||||
<view
|
<view
|
||||||
class="status-tag readonly"
|
class="status-tag readonly"
|
||||||
:class="getStatusClass(xs.jcZt)"
|
:class="getStatusClass(xs.jcZt)"
|
||||||
@ -179,15 +225,11 @@ const props = withDefaults(defineProps<{
|
|||||||
bjId: '',
|
bjId: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
// 人数
|
const bmYjfXsList = ref<any>([]); // 报名已缴费学生
|
||||||
const rsData = ref({
|
const bmWjfXsList = ref<any>([]); // 报名未缴费学生
|
||||||
zrs: 0,
|
|
||||||
bmRs: 0,
|
|
||||||
unBmRs: 0
|
|
||||||
});
|
|
||||||
|
|
||||||
const bmXsList = ref<any>([]);
|
const bmXsList = ref<any>([]); // 报名学生
|
||||||
const unBmXsList = ref<any>([]);
|
const unBmXsList = ref<any>([]); // 未报名学生
|
||||||
|
|
||||||
const jzZt = ref(false) // 加载状态
|
const jzZt = ref(false) // 加载状态
|
||||||
|
|
||||||
@ -218,28 +260,38 @@ const showStudentDrawer = (type: string) => {
|
|||||||
title = '全部学生'
|
title = '全部学生'
|
||||||
empty = '暂无学生数据'
|
empty = '暂无学生数据'
|
||||||
break
|
break
|
||||||
case 'registered':
|
case 'bm':
|
||||||
students = bmXsList.value
|
students = bmXsList.value
|
||||||
title = '报名就餐学生'
|
title = '已报名学生'
|
||||||
empty = '暂无报名学生'
|
empty = '暂无报名学生'
|
||||||
break
|
break
|
||||||
case 'unregistered':
|
case 'yjf':
|
||||||
|
students = bmYjfXsList.value
|
||||||
|
title = '已缴费学生'
|
||||||
|
empty = '暂无已缴费学生'
|
||||||
|
break
|
||||||
|
case 'wjf':
|
||||||
|
students = bmWjfXsList.value
|
||||||
|
title = '未缴费学生'
|
||||||
|
empty = '暂无未缴费学生'
|
||||||
|
break
|
||||||
|
case 'unBm':
|
||||||
students = unBmXsList.value
|
students = unBmXsList.value
|
||||||
title = '未报名就餐学生'
|
title = '未报名就餐学生'
|
||||||
empty = '暂无未报名学生'
|
empty = '暂无未报名学生'
|
||||||
break
|
break
|
||||||
case 'normal':
|
case 'normal':
|
||||||
students = bmXsList.value.filter((s: any) => s.jcZt === 'A')
|
students = bmYjfXsList.value.filter((s: any) => s.jcZt === 'A')
|
||||||
title = '正常学生'
|
title = '正常学生'
|
||||||
empty = '暂无正常学生'
|
empty = '暂无正常学生'
|
||||||
break
|
break
|
||||||
case 'leave':
|
case 'leave':
|
||||||
students = bmXsList.value.filter((s: any) => s.jcZt === 'B')
|
students = bmYjfXsList.value.filter((s: any) => s.jcZt === 'B')
|
||||||
title = '请假学生'
|
title = '请假学生'
|
||||||
empty = '暂无请假学生'
|
empty = '暂无请假学生'
|
||||||
break
|
break
|
||||||
case 'absent':
|
case 'absent':
|
||||||
students = bmXsList.value.filter((s: any) => s.jcZt === 'C')
|
students = bmYjfXsList.value.filter((s: any) => s.jcZt === 'C')
|
||||||
title = '缺勤学生'
|
title = '缺勤学生'
|
||||||
empty = '暂无缺勤学生'
|
empty = '暂无缺勤学生'
|
||||||
break
|
break
|
||||||
@ -278,14 +330,21 @@ const loadXsList = async () => {
|
|||||||
rows: 1000
|
rows: 1000
|
||||||
}
|
}
|
||||||
const resQd = await jcQdFindPageApi(params);
|
const resQd = await jcQdFindPageApi(params);
|
||||||
|
// 分离已缴费和未缴费学生
|
||||||
bmXsList.value = (resQd.rows || []).map((item: any) => {
|
bmXsList.value = (resQd.rows || []).map((item: any) => {
|
||||||
return {
|
item.jcZt = 'A';
|
||||||
...item,
|
item.xm = (item.xsxm || item.xm || '').trim();
|
||||||
jcZt: 'A',
|
return item;
|
||||||
xm: (item.xsxm || item.xm || '').trim()
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
bmXsList.value = sortChinese(bmXsList.value, 'xm');
|
bmXsList.value = sortChinese(bmXsList.value, 'xm');
|
||||||
|
// 根据jfZt字段分离已缴费(A)和未缴费(B)的学生
|
||||||
|
bmYjfXsList.value = bmXsList.value.filter((item: any) => item.jfZt === 'B');
|
||||||
|
bmWjfXsList.value = bmXsList.value.filter((item: any) => item.jfZt !== 'B');
|
||||||
|
// 未缴费的状态设置为D
|
||||||
|
bmWjfXsList.value = bmWjfXsList.value.map((item: any) => {
|
||||||
|
item.jcZt = 'D';
|
||||||
|
return item;
|
||||||
|
});
|
||||||
const resUn = await findPageByNoJc(params);
|
const resUn = await findPageByNoJc(params);
|
||||||
unBmXsList.value = (resUn.rows || []).map((item: any) => {
|
unBmXsList.value = (resUn.rows || []).map((item: any) => {
|
||||||
return {
|
return {
|
||||||
@ -295,11 +354,6 @@ const loadXsList = async () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
unBmXsList.value = sortChinese(unBmXsList.value, 'xm');
|
unBmXsList.value = sortChinese(unBmXsList.value, 'xm');
|
||||||
rsData.value = {
|
|
||||||
zrs: (resQd.records || 0) + (resUn.records || 0),
|
|
||||||
bmRs: (resQd.records || 0),
|
|
||||||
unBmRs: (resUn.records || 0),
|
|
||||||
};
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('加载学生列表失败:', error)
|
console.error('加载学生列表失败:', error)
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
@ -336,7 +390,7 @@ const hqZtWz = (status: string) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const hqZtSl = (status: string) => {
|
const hqZtSl = (status: string) => {
|
||||||
return bmXsList.value.filter((s:any) => s.jcZt === status).length
|
return bmYjfXsList.value.filter((s:any) => s.jcZt === status).length
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取状态对应的样式类
|
// 获取状态对应的样式类
|
||||||
@ -382,7 +436,8 @@ const onChangeZt = (e: any) => {
|
|||||||
|
|
||||||
const getDmXsList = () => {
|
const getDmXsList = () => {
|
||||||
let retList: any = [];
|
let retList: any = [];
|
||||||
for(let qd of bmXsList.value) {
|
// 已缴费学生
|
||||||
|
for(let qd of bmYjfXsList.value) {
|
||||||
retList.push({
|
retList.push({
|
||||||
xsId: qd.xsId,
|
xsId: qd.xsId,
|
||||||
xsXm: qd.xm, // 传入学生姓名
|
xsXm: qd.xm, // 传入学生姓名
|
||||||
@ -393,6 +448,19 @@ const getDmXsList = () => {
|
|||||||
tx: qd.xstx // 传入学生头像
|
tx: qd.xstx // 传入学生头像
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// 未缴费学生
|
||||||
|
for(let qd of bmWjfXsList.value) {
|
||||||
|
retList.push({
|
||||||
|
xsId: qd.xsId,
|
||||||
|
xsXm: qd.xm, // 传入学生姓名
|
||||||
|
jcZt: qd.jcZt,
|
||||||
|
jcQdId: qd.id,
|
||||||
|
jcBzId: qd.bzId,
|
||||||
|
jzId: qd.jzId,
|
||||||
|
tx: qd.xstx // 传入学生头像
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 未报名学生
|
||||||
for(let xs of unBmXsList.value) {
|
for(let xs of unBmXsList.value) {
|
||||||
retList.push({
|
retList.push({
|
||||||
xsId: xs.id,
|
xsId: xs.id,
|
||||||
|
|||||||
@ -67,40 +67,51 @@
|
|||||||
|
|
||||||
<!-- 统计信息 -->
|
<!-- 统计信息 -->
|
||||||
<view class="stats-container">
|
<view class="stats-container">
|
||||||
<view class="stat-item">
|
<view class="stat-item clickable" @click="showStudentDrawer('all')">
|
||||||
<text class="stat-number">{{ rsData.zrs }}</text>
|
<text class="stat-number">{{ (bmXsList.length || 0) + (unBmXsList.length || 0) }}</text>
|
||||||
<text class="stat-label">总人数</text>
|
<text class="stat-label">班级人数</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="stat-item">
|
<view class="stat-item clickable" @click="showStudentDrawer('bm')">
|
||||||
<text class="stat-number unregistered">{{ rsData.bmRs }}</text>
|
<text class="stat-number unregistered">{{ bmXsList.length || 0 }}</text>
|
||||||
<text class="stat-label">报名就餐</text>
|
<text class="stat-label">报名总数</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="stat-item">
|
<view class="stat-item" >
|
||||||
<text class="stat-number unregistered">{{ rsData.unBmRs }}</text>
|
|
||||||
<text class="stat-label">未报名就餐</text>
|
|
||||||
</view>
|
</view>
|
||||||
<view class="stat-item">
|
<view class="stat-item clickable" @click="showStudentDrawer('yjf')">
|
||||||
|
<text class="stat-number unregistered">{{ bmYjfXsList.length || 0 }}</text>
|
||||||
|
<text class="stat-label">报名缴费</text>
|
||||||
|
</view>
|
||||||
|
<view class="stat-item clickable" @click="showStudentDrawer('wjf')">
|
||||||
|
<text class="stat-number unregistered">{{ bmWjfXsList.length || 0 }}</text>
|
||||||
|
<text class="stat-label">报名未缴费</text>
|
||||||
|
</view>
|
||||||
|
<view class="stat-item clickable" @click="showStudentDrawer('unBm')">
|
||||||
|
<text class="stat-number unregistered">{{ unBmXsList.length || 0 }}</text>
|
||||||
|
<text class="stat-label">未报名</text>
|
||||||
|
</view>
|
||||||
|
<view class="stat-item clickable" @click="showStudentDrawer('normal')">
|
||||||
<text class="stat-number normal">{{ hqZtSl('A') }}</text>
|
<text class="stat-number normal">{{ hqZtSl('A') }}</text>
|
||||||
<text class="stat-label">正常</text>
|
<text class="stat-label">正常</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="stat-item">
|
<view class="stat-item clickable" @click="showStudentDrawer('leave')">
|
||||||
<text class="stat-number leave">{{ hqZtSl('B') }}</text>
|
<text class="stat-number leave">{{ hqZtSl('B') }}</text>
|
||||||
<text class="stat-label">请假</text>
|
<text class="stat-label">请假</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="stat-item">
|
<view class="stat-item clickable" @click="showStudentDrawer('absent')">
|
||||||
<text class="stat-number absent">{{ hqZtSl('C') }}</text>
|
<text class="stat-number absent">{{ hqZtSl('C') }}</text>
|
||||||
<text class="stat-label">缺勤</text>
|
<text class="stat-label">缺勤</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
|
||||||
<!-- 学生列表 - 分为两个部分 -->
|
<!-- 学生列表 - 分为两个部分 -->
|
||||||
<view class="xs-list">
|
<view class="xs-list">
|
||||||
<!-- 已缴费学生列表 -->
|
<!-- 已缴费学生列表 -->
|
||||||
<view v-if="dmXsList.length > 0" class="xs-section">
|
<view v-if="bmYjfXsList.length > 0" class="xs-section">
|
||||||
<view class="section-subtitle">已报名学生 ({{ dmXsList.length }}人)</view>
|
<view class="section-subtitle">已报名已缴费 ({{ bmYjfXsList.length }}人)</view>
|
||||||
<view class="xs-grid">
|
<view class="xs-grid">
|
||||||
<view
|
<view
|
||||||
v-for="xs in dmXsList"
|
v-for="xs in bmYjfXsList"
|
||||||
:key="xs.id"
|
:key="xs.id"
|
||||||
class="xs-item bg-white r-md p-12"
|
class="xs-item bg-white r-md p-12"
|
||||||
>
|
>
|
||||||
@ -130,9 +141,44 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 未缴费/未报名学生列表 -->
|
<!-- 未缴费学生列表 -->
|
||||||
|
<view v-if="bmWjfXsList.length > 0" class="xs-section">
|
||||||
|
<view class="section-subtitle">已报名未缴费 ({{ bmWjfXsList.length }}人)</view>
|
||||||
|
<view class="xs-grid">
|
||||||
|
<view
|
||||||
|
v-for="xs in bmWjfXsList"
|
||||||
|
:key="xs.id"
|
||||||
|
class="xs-item bg-white r-md p-12"
|
||||||
|
>
|
||||||
|
<view class="flex-row items-center">
|
||||||
|
<view class="avatar-container mr-8">
|
||||||
|
<image
|
||||||
|
class="xs-avatar"
|
||||||
|
:src="imagUrl(xs.tx) || '/static/images/default-avatar.png'"
|
||||||
|
mode="aspectFill"
|
||||||
|
></image>
|
||||||
|
</view>
|
||||||
|
<view class="flex-1 overflow-hidden">
|
||||||
|
<view class="xs-name mb-8">
|
||||||
|
<text class="font-14 cor-333">{{ xs.xsXm }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="flex-row">
|
||||||
|
<view
|
||||||
|
class="status-tag readonly"
|
||||||
|
:class="getStatusClass(xs.jcZt)"
|
||||||
|
>
|
||||||
|
{{ getStatusText(xs.jcZt) }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 未报名学生列表 -->
|
||||||
<view v-if="unBmXsList.length > 0" class="xs-section">
|
<view v-if="unBmXsList.length > 0" class="xs-section">
|
||||||
<view class="section-subtitle">未报名学生 ({{ unBmXsList.length }}人)</view>
|
<view class="section-subtitle">未报名 ({{ unBmXsList.length }}人)</view>
|
||||||
<view class="xs-grid">
|
<view class="xs-grid">
|
||||||
<view
|
<view
|
||||||
v-for="xs in unBmXsList"
|
v-for="xs in unBmXsList"
|
||||||
@ -165,19 +211,54 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view v-if="dmXsList.length === 0" class="empty-tip">
|
<view v-if="bmXsList.length === 0 && unBmXsList.length === 0" class="empty-tip">
|
||||||
暂无学生数据
|
暂无学生数据
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 错误状态 -->
|
<!-- 错误状态 -->
|
||||||
<view v-else-if="error" class="error-state">
|
<view v-else-if="error" class="error-state">
|
||||||
<view class="error-icon">❌</view>
|
<view class="error-icon">❌</view>
|
||||||
<text class="error-text">{{ error }}</text>
|
<text class="error-text">{{ error }}</text>
|
||||||
<button class="retry-btn" @click="loadDetail">重试</button>
|
<button class="retry-btn" @click="loadDetail">重试</button>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<!-- 学生列表抽屉 -->
|
||||||
|
<uni-popup ref="studentPopup" type="bottom" @change="popupChange">
|
||||||
|
<view class="student-drawer">
|
||||||
|
<view class="drawer-header">
|
||||||
|
<text class="drawer-title">{{ drawerTitle }}</text>
|
||||||
|
<text class="drawer-close" @click="closeStudentDrawer">✕</text>
|
||||||
|
</view>
|
||||||
|
<view class="drawer-content">
|
||||||
|
<view v-if="filteredStudentList.length > 0" class="student-list">
|
||||||
|
<view
|
||||||
|
v-for="xs in filteredStudentList"
|
||||||
|
:key="xs.id"
|
||||||
|
class="student-item"
|
||||||
|
>
|
||||||
|
<view class="student-info">
|
||||||
|
<image
|
||||||
|
class="student-avatar"
|
||||||
|
:src="imagUrl(xs.tx) || '/static/images/default-avatar.png'"
|
||||||
|
mode="aspectFill"
|
||||||
|
></image>
|
||||||
|
<view class="student-details">
|
||||||
|
<text class="student-name">{{ xs.xsXm }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="student-tag" :class="getStatusClass(xs.jcZt)">
|
||||||
|
{{ getStatusText(xs.jcZt) }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view v-else class="empty-drawer">
|
||||||
|
<text class="empty-text">{{ emptyText }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</uni-popup>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -189,15 +270,89 @@ import { imagUrl } from "@/utils";
|
|||||||
import { sortChinese } from "@/utils/pinyinUtil"
|
import { sortChinese } from "@/utils/pinyinUtil"
|
||||||
const { getData } = useDataStore()
|
const { getData } = useDataStore()
|
||||||
|
|
||||||
|
// 学生列表抽屉相关
|
||||||
|
const studentPopup = ref<any>(null)
|
||||||
|
const drawerTitle = ref('')
|
||||||
|
const emptyText = ref('')
|
||||||
|
const filteredStudentList = ref<any>([])
|
||||||
|
|
||||||
|
const showStudentDrawer = (type: string) => {
|
||||||
|
let students: any[] = []
|
||||||
|
let title = ''
|
||||||
|
let empty = ''
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case 'all':
|
||||||
|
students = [...bmXsList.value, ...unBmXsList.value]
|
||||||
|
title = '全部学生'
|
||||||
|
empty = '暂无学生数据'
|
||||||
|
break
|
||||||
|
case 'bm':
|
||||||
|
students = bmXsList.value
|
||||||
|
title = '已报名学生'
|
||||||
|
empty = '暂无报名学生'
|
||||||
|
break
|
||||||
|
case 'yjf':
|
||||||
|
students = bmYjfXsList.value
|
||||||
|
title = '已缴费学生'
|
||||||
|
empty = '暂无已缴费学生'
|
||||||
|
break
|
||||||
|
case 'wjf':
|
||||||
|
students = bmWjfXsList.value
|
||||||
|
title = '未缴费学生'
|
||||||
|
empty = '暂无未缴费学生'
|
||||||
|
break
|
||||||
|
case 'unBm':
|
||||||
|
students = unBmXsList.value
|
||||||
|
title = '未报名就餐学生'
|
||||||
|
empty = '暂无未报名学生'
|
||||||
|
break
|
||||||
|
case 'normal':
|
||||||
|
students = bmYjfXsList.value.filter((s: any) => s.jcZt === 'A')
|
||||||
|
title = '正常学生'
|
||||||
|
empty = '暂无正常学生'
|
||||||
|
break
|
||||||
|
case 'leave':
|
||||||
|
students = bmYjfXsList.value.filter((s: any) => s.jcZt === 'B')
|
||||||
|
title = '请假学生'
|
||||||
|
empty = '暂无请假学生'
|
||||||
|
break
|
||||||
|
case 'absent':
|
||||||
|
students = bmYjfXsList.value.filter((s: any) => s.jcZt === 'C')
|
||||||
|
title = '缺勤学生'
|
||||||
|
empty = '暂无缺勤学生'
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
drawerTitle.value = title
|
||||||
|
emptyText.value = empty
|
||||||
|
filteredStudentList.value = students
|
||||||
|
|
||||||
|
if (studentPopup.value) {
|
||||||
|
studentPopup.value.open('bottom')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeStudentDrawer = () => {
|
||||||
|
if (studentPopup.value) {
|
||||||
|
studentPopup.value.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const popupChange = (e: { show: boolean }) => {
|
||||||
|
// 抽屉状态变化处理
|
||||||
|
}
|
||||||
|
|
||||||
// 响应式数据
|
// 响应式数据
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const dmDetail = ref<any>(null)
|
const dmDetail = ref<any>(null)
|
||||||
const error = ref('')
|
const error = ref('')
|
||||||
|
|
||||||
// 点名学生
|
const bmYjfXsList = ref<any>([]); // 报名已缴费学生
|
||||||
const dmXsList = ref<any>([]);
|
const bmWjfXsList = ref<any>([]); // 报名未缴费学生
|
||||||
// 未报名学生
|
|
||||||
const unBmXsList = ref<any>([]);
|
const bmXsList = ref<any>([]); // 已报名学生
|
||||||
|
const unBmXsList = ref<any>([]); // 未报名学生
|
||||||
|
|
||||||
// 人数
|
// 人数
|
||||||
const rsData = ref({
|
const rsData = ref({
|
||||||
@ -220,7 +375,7 @@ const loadDetail = async () => {
|
|||||||
dmDetail.value = response.result;
|
dmDetail.value = response.result;
|
||||||
let srcList = response.result.dmXsList || [];
|
let srcList = response.result.dmXsList || [];
|
||||||
srcList = sortChinese(srcList, 'xsXm');
|
srcList = sortChinese(srcList, 'xsXm');
|
||||||
dmXsList.value = [];
|
bmXsList.value = [];
|
||||||
unBmXsList.value = [];
|
unBmXsList.value = [];
|
||||||
for (let i = 0; i < srcList.length; i++) {
|
for (let i = 0; i < srcList.length; i++) {
|
||||||
const xs = srcList[i];
|
const xs = srcList[i];
|
||||||
@ -229,16 +384,15 @@ const loadDetail = async () => {
|
|||||||
unBmXsList.value.push(xs);
|
unBmXsList.value.push(xs);
|
||||||
} break;
|
} break;
|
||||||
default: {
|
default: {
|
||||||
dmXsList.value.push(xs);
|
if (xs.jcZt === 'D') {
|
||||||
|
bmWjfXsList.value.push(xs);
|
||||||
|
} else {
|
||||||
|
bmYjfXsList.value.push(xs);
|
||||||
|
}
|
||||||
|
bmXsList.value.push(xs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rsData.value = {
|
|
||||||
zrs: srcList.length,
|
|
||||||
bmRs: dmXsList.value.length,
|
|
||||||
unBmRs: unBmXsList.value.length,
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
error.value = response.message || '获取详情失败'
|
error.value = response.message || '获取详情失败'
|
||||||
}
|
}
|
||||||
@ -251,7 +405,7 @@ const loadDetail = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const hqZtSl = (status: string) => {
|
const hqZtSl = (status: string) => {
|
||||||
return dmXsList.value.filter((s:any) => s.jcZt === status).length
|
return bmYjfXsList.value.filter((s:any) => s.jcZt === status).length
|
||||||
}
|
}
|
||||||
|
|
||||||
const formatDateTime = (dateStr: string) => {
|
const formatDateTime = (dateStr: string) => {
|
||||||
@ -380,6 +534,9 @@ onMounted(() => {
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: #333;
|
color: #333;
|
||||||
margin-bottom: 20rpx;
|
margin-bottom: 20rpx;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-subtitle {
|
.section-subtitle {
|
||||||
@ -424,19 +581,59 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.stats-container {
|
.stats-container {
|
||||||
display: grid;
|
display: flex;
|
||||||
grid-template-columns: repeat(3, 1fr);
|
justify-content: space-around;
|
||||||
gap: 20rpx;
|
|
||||||
margin-bottom: 30rpx;
|
margin-bottom: 30rpx;
|
||||||
padding: 20rpx;
|
padding: 20rpx;
|
||||||
background-color: #f8f9fa;
|
background-color: #f8f9fa;
|
||||||
border-radius: 12rpx;
|
border-radius: 12rpx;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 60rpx;
|
||||||
|
|
||||||
.stat-item {
|
.stat-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
text-align: center;
|
min-width: 120rpx;
|
||||||
|
|
||||||
|
&.clickable {
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-2rpx);
|
||||||
|
box-shadow: 0 4rpx 12rpx rgba(235, 47, 150, 0.2);
|
||||||
|
background-color: rgba(235, 47, 150, 0.05);
|
||||||
|
border-radius: 12rpx;
|
||||||
|
padding: 8rpx;
|
||||||
|
margin: -8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
box-shadow: 0 2rpx 8rpx rgba(235, 47, 150, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
right: -10rpx;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
width: 6rpx;
|
||||||
|
height: 6rpx;
|
||||||
|
background-color: #eb2f96;
|
||||||
|
border-radius: 50%;
|
||||||
|
opacity: 0.6;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover::after {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(-50%) scale(1.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.stat-number {
|
.stat-number {
|
||||||
font-size: 36rpx;
|
font-size: 36rpx;
|
||||||
@ -469,6 +666,7 @@ onMounted(() => {
|
|||||||
font-size: 24rpx;
|
font-size: 24rpx;
|
||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -509,6 +707,25 @@ onMounted(() => {
|
|||||||
transform: translateY(-2rpx);
|
transform: translateY(-2rpx);
|
||||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.15);
|
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.avatar-container {
|
||||||
|
width: 92rpx;
|
||||||
|
height: 92rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
padding: 6rpx;
|
||||||
|
background-color: #fff;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-shadow: 0 2rpx 6rpx rgba(0, 0, 0, 0.05);
|
||||||
|
|
||||||
|
.xs-avatar {
|
||||||
|
width: 80rpx;
|
||||||
|
height: 80rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -537,12 +754,29 @@ onMounted(() => {
|
|||||||
font-size: 20rpx;
|
font-size: 20rpx;
|
||||||
padding: 6rpx 16rpx;
|
padding: 6rpx 16rpx;
|
||||||
border-radius: 8rpx;
|
border-radius: 8rpx;
|
||||||
display: inline-block;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4rpx;
|
||||||
|
|
||||||
|
&.clickable {
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 0.8;
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&.readonly {
|
&.readonly {
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.status-arrow {
|
||||||
|
font-size: 16rpx;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-normal {
|
.status-normal {
|
||||||
@ -641,5 +875,128 @@ onMounted(() => {
|
|||||||
.p-12 {
|
.p-12 {
|
||||||
padding: 24rpx;
|
padding: 24rpx;
|
||||||
}
|
}
|
||||||
</style>
|
|
||||||
|
|
||||||
|
/* 学生列表抽屉样式 */
|
||||||
|
.student-drawer {
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 20rpx 20rpx 0 0;
|
||||||
|
max-height: 80vh;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.drawer-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 30rpx 40rpx 20rpx;
|
||||||
|
border-bottom: 1rpx solid #f0f0f0;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
background-color: #fff;
|
||||||
|
z-index: 10;
|
||||||
|
|
||||||
|
.drawer-title {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawer-close {
|
||||||
|
font-size: 36rpx;
|
||||||
|
color: #999;
|
||||||
|
padding: 10rpx;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawer-content {
|
||||||
|
padding: 20rpx 40rpx 40rpx;
|
||||||
|
max-height: calc(80vh - 100rpx);
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.student-list {
|
||||||
|
.student-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 24rpx 0;
|
||||||
|
border-bottom: 1rpx solid #f5f5f5;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.student-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.student-avatar {
|
||||||
|
width: 80rpx;
|
||||||
|
height: 80rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-right: 24rpx;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.student-details {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.student-name {
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.student-tag {
|
||||||
|
font-size: 20rpx;
|
||||||
|
padding: 8rpx 16rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
|
||||||
|
&.status-normal {
|
||||||
|
background-color: #e6f7ff;
|
||||||
|
color: #52c41a;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.status-leave {
|
||||||
|
background-color: #fff7e6;
|
||||||
|
color: #faad14;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.status-absent {
|
||||||
|
background-color: #fff2f0;
|
||||||
|
color: #ff4d4f;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.status-unpaid {
|
||||||
|
background-color: #f9f0ff;
|
||||||
|
color: #722ed1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.status-unregistered {
|
||||||
|
background-color: #fff0f6;
|
||||||
|
color: #eb2f96;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-drawer {
|
||||||
|
text-align: center;
|
||||||
|
padding: 80rpx 0;
|
||||||
|
|
||||||
|
.empty-text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Loading…
x
Reference in New Issue
Block a user