完善就餐点名列表和详情页面
This commit is contained in:
parent
d0859759c1
commit
1a066437dc
@ -1,259 +1,303 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="record-content">
|
<view class="record-content">
|
||||||
<!-- 搜索筛选区域 -->
|
|
||||||
<view class="search-section">
|
|
||||||
<view class="search-row">
|
|
||||||
<view class="search-item">
|
|
||||||
<text class="label">班级:</text>
|
|
||||||
<view class="flex-1">
|
|
||||||
<NjBjPicker @change="changeNjBj" icon-arrow="right"
|
|
||||||
:customStyle="{ borderRadius: '0', padding: '0.5rem 0.625rem' }" />
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<view class="search-row">
|
|
||||||
<view class="search-item">
|
|
||||||
<text class="label">时间范围:</text>
|
|
||||||
<uni-datetime-picker
|
|
||||||
type="daterange"
|
|
||||||
:value="[startTime, endTime]"
|
|
||||||
@change="onTimeRangeChange"
|
|
||||||
class="date-picker"
|
|
||||||
>
|
|
||||||
<view class="picker-text">{{ getTimeRangeText() }}</view>
|
|
||||||
</uni-datetime-picker>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<view class="search-row">
|
|
||||||
<button class="search-btn" @click="searchRecords">搜索</button>
|
|
||||||
<button class="reset-btn" @click="resetSearch">重置</button>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 使用 BasicListLayout 包裹记录列表 -->
|
<!-- 使用 BasicListLayout 包裹记录列表 -->
|
||||||
<BasicListLayout @register="register" :fixed="false">
|
<BasicListLayout @register="register" :fixed="false" class="flex-1">
|
||||||
<template #top>
|
<template #top>
|
||||||
<view class="records-header" v-if="dmRecords.length > 0">
|
<!-- 搜索筛选区域 -->
|
||||||
<view class="section-title">
|
<view class="search-section">
|
||||||
点名记录 ({{ totalCount }}条)
|
<view class="search-row">
|
||||||
<text class="export-btn" @click="exportRecords">导出</text>
|
<view class="search-item">
|
||||||
|
<text class="label">班级:</text>
|
||||||
|
<view class="flex-1">
|
||||||
|
<NjBjPicker
|
||||||
|
@change="changeNjBj"
|
||||||
|
icon-arrow="right"
|
||||||
|
:customStyle="{
|
||||||
|
borderRadius: '0',
|
||||||
|
padding: '0.5rem 0.625rem',
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="search-row">
|
||||||
|
<view class="search-item">
|
||||||
|
<text class="label">时间范围:</text>
|
||||||
|
<uni-datetime-picker
|
||||||
|
type="daterange"
|
||||||
|
:value="[startTime, endTime]"
|
||||||
|
@change="onTimeRangeChange"
|
||||||
|
class="date-picker"
|
||||||
|
>
|
||||||
|
<view class="picker-text">{{ getTimeRangeText() }}</view>
|
||||||
|
</uni-datetime-picker>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="search-row">
|
||||||
|
<button class="search-btn" @click="searchRecords">搜索</button>
|
||||||
|
<button class="reset-btn" @click="resetSearch">重置</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 记录头部信息 -->
|
||||||
|
<view class="records-header" v-if="dmRecords && dmRecords.length > 0">
|
||||||
|
<view class="section-title"> 点名记录 ({{ totalCount }}条) </view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 空状态 -->
|
||||||
|
<view
|
||||||
|
v-if="!loading && dmRecords && dmRecords.length === 0 && hasSearched"
|
||||||
|
class="empty-state"
|
||||||
|
>
|
||||||
|
<view class="empty-icon">📋</view>
|
||||||
|
<text class="empty-text">暂无点名记录</text>
|
||||||
|
<text class="empty-tip">请选择班级和时间范围进行搜索</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 初始提示 -->
|
||||||
|
<view v-if="!hasSearched" class="initial-tip">
|
||||||
|
<view class="tip-icon">🔍</view>
|
||||||
|
<text class="tip-text">请选择班级和时间范围开始搜索</text>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #default="{ data }">
|
<template #default="{ data }">
|
||||||
<view
|
<view class="record-card" @click="goToDetail(data)">
|
||||||
class="record-card"
|
|
||||||
@click="goToDetail(data)"
|
|
||||||
>
|
|
||||||
<view class="card-header">
|
|
||||||
<view class="time-info">
|
|
||||||
<text class="record-date">{{ formatDate(data.dmTime) }}</text>
|
|
||||||
<text class="record-time">{{ formatTime(data.dmTime) }}</text>
|
|
||||||
</view>
|
|
||||||
<view class="status-badge">
|
|
||||||
<text class="status-text">已点名</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="card-content">
|
<view class="card-content">
|
||||||
<view class="class-info">
|
<view class="info-row">
|
||||||
<text class="class-name">{{ data.njmc }} {{ data.bjmc }}</text>
|
<text class="info-label">年级:</text>
|
||||||
|
<text class="info-value">{{ data.njmc }}</text>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="info-row">
|
||||||
<view class="stats-row">
|
<text class="info-label">班级:</text>
|
||||||
<view class="stat-item">
|
<text class="info-value">{{ data.bjmc }}</text>
|
||||||
<text class="stat-label">学生</text>
|
</view>
|
||||||
<text class="stat-value">{{ data.xsCount }}人</text>
|
<view class="info-row">
|
||||||
</view>
|
<text class="info-label">就餐日期:</text>
|
||||||
<view class="stat-item">
|
<text class="info-value">{{ formatDate(data.jcTime) }}</text>
|
||||||
<text class="stat-label">陪餐教师</text>
|
</view>
|
||||||
<text class="stat-value">{{ data.jsCount }}人</text>
|
<view class="info-row">
|
||||||
</view>
|
<text class="info-label">就餐时间:</text>
|
||||||
|
<text class="info-value">{{ formatTime(data.jcTime) }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="card-footer">
|
<view class="card-footer">
|
||||||
<text class="view-detail">点击查看详情 →</text>
|
<text class="view-detail">查看详情 →</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
</BasicListLayout>
|
</BasicListLayout>
|
||||||
|
|
||||||
<!-- 空状态 -->
|
|
||||||
<view v-if="!loading && dmRecords.length === 0 && hasSearched" class="empty-state">
|
|
||||||
<view class="empty-icon">📋</view>
|
|
||||||
<text class="empty-text">暂无点名记录</text>
|
|
||||||
<text class="empty-tip">请选择班级和时间范围进行搜索</text>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 初始提示 -->
|
|
||||||
<view v-if="!hasSearched" class="initial-tip">
|
|
||||||
<view class="tip-icon">🔍</view>
|
|
||||||
<text class="tip-text">请选择班级和时间范围开始搜索</text>
|
|
||||||
</view>
|
|
||||||
</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 NjBjPicker from '@/pages/components/NjBjPicker/index.vue'
|
import NjBjPicker from "@/pages/components/NjBjPicker/index.vue";
|
||||||
import { getJcDmPageApi } from '@/api/base/jcApi'
|
import { getJcDmPageApi } from "@/api/base/jcApi";
|
||||||
import { useDataStore } from '@/store/modules/data'
|
import { useDataStore } from "@/store/modules/data";
|
||||||
|
|
||||||
const { setData } = useDataStore()
|
const { setData } = useDataStore();
|
||||||
|
|
||||||
// 接收外部传入属性
|
// 接收外部传入属性
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(
|
||||||
title?: string
|
defineProps<{
|
||||||
}>(), {
|
title?: string;
|
||||||
title: '点名列表'
|
}>(),
|
||||||
});
|
{
|
||||||
|
title: "点名列表",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// 响应式数据
|
// 响应式数据
|
||||||
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 selectedClass = ref<any>(null);
|
||||||
const dmRecords = ref<any[]>([])
|
const dmRecords = ref<any>([]);
|
||||||
const totalCount = ref(0)
|
const totalCount = ref<any>(0);
|
||||||
const hasSearched = ref(false)
|
const hasSearched = ref(false);
|
||||||
|
|
||||||
// 使用 BasicListLayout
|
// 使用 BasicListLayout
|
||||||
const [register, { reload, setParam }] = useLayout({
|
const [register, { reload, setParam }] = useLayout({
|
||||||
api: getJcDmPageApi,
|
api: async (params: any) => {
|
||||||
|
try {
|
||||||
|
const res = await getJcDmPageApi(params);
|
||||||
|
console.log("API返回数据:", res); // 调试日志
|
||||||
|
|
||||||
|
// 确保数据正确赋值
|
||||||
|
if (res && res.rows) {
|
||||||
|
dmRecords.value = res.rows;
|
||||||
|
totalCount.value = res.total || 0;
|
||||||
|
} else {
|
||||||
|
dmRecords.value = [];
|
||||||
|
totalCount.value = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("获取数据失败:", error);
|
||||||
|
dmRecords.value = [];
|
||||||
|
totalCount.value = 0;
|
||||||
|
return { rows: [], total: 0 };
|
||||||
|
}
|
||||||
|
},
|
||||||
componentProps: {
|
componentProps: {
|
||||||
auto: false
|
auto: false,
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
// 改变了年级班级
|
// 改变了年级班级
|
||||||
const changeNjBj = async (nj: any, bj: any) => {
|
const changeNjBj = async (nj: any, bj: any) => {
|
||||||
selectedClass.value = {
|
selectedClass.value = {
|
||||||
njId: nj.key,
|
njId: nj.key,
|
||||||
bjId: bj.key,
|
bjId: bj.key,
|
||||||
}
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const onTimeRangeChange = (e: any) => {
|
const onTimeRangeChange = (e: any) => {
|
||||||
// uni-datetime-picker 返回的是一个数组,包含开始和结束时间
|
// uni-datetime-picker 返回的是一个数组,包含开始和结束时间
|
||||||
if (e && Array.isArray(e)) {
|
if (e && Array.isArray(e)) {
|
||||||
const [start, end] = e
|
const [start, end] = e;
|
||||||
startTime.value = start
|
startTime.value = start;
|
||||||
endTime.value = end
|
endTime.value = end;
|
||||||
} else if (e && typeof e === 'string') {
|
} else if (e && typeof e === "string") {
|
||||||
// 如果返回的是字符串,可能是单个时间
|
// 如果返回的是字符串,可能是单个时间
|
||||||
startTime.value = e
|
startTime.value = e;
|
||||||
endTime.value = e
|
endTime.value = e;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const getTimeRangeText = () => {
|
const getTimeRangeText = () => {
|
||||||
if (!startTime.value || !endTime.value) return '选择时间范围'
|
if (!startTime.value || !endTime.value) return "选择时间范围";
|
||||||
if (startTime.value === endTime.value) {
|
if (startTime.value === endTime.value) {
|
||||||
return formatDate(startTime.value)
|
return formatDate(startTime.value);
|
||||||
}
|
}
|
||||||
return `${formatDate(startTime.value)} - ${formatDate(endTime.value)}`
|
return `${formatDate(startTime.value)} - ${formatDate(endTime.value)}`;
|
||||||
}
|
};
|
||||||
|
|
||||||
const searchRecords = async () => {
|
const searchRecords = async () => {
|
||||||
if (!selectedClass.value || !startTime.value || !endTime.value) {
|
if (!selectedClass.value || !startTime.value || !endTime.value) {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '请选择班级和时间范围',
|
title: "请选择班级和时间范围",
|
||||||
icon: 'none'
|
icon: "none",
|
||||||
})
|
});
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 验证开始时间不能大于结束时间
|
// 验证开始时间不能大于结束时间
|
||||||
if (startTime.value > endTime.value) {
|
if (startTime.value > endTime.value) {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '开始时间不能大于结束时间',
|
title: "开始时间不能大于结束时间",
|
||||||
icon: 'none'
|
icon: "none",
|
||||||
})
|
});
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
hasSearched.value = true
|
hasSearched.value = true;
|
||||||
|
|
||||||
|
// 调试日志
|
||||||
|
console.log("搜索参数:", {
|
||||||
|
njId: selectedClass.value.njId,
|
||||||
|
bjId: selectedClass.value.bjId,
|
||||||
|
startTime: startTime.value + " 00:00:00",
|
||||||
|
endTime: endTime.value + " 23:59:59",
|
||||||
|
pageNo: 1,
|
||||||
|
});
|
||||||
|
|
||||||
// 设置搜索参数并重新加载
|
// 设置搜索参数并重新加载
|
||||||
setParam({
|
setParam({
|
||||||
njId: selectedClass.value.njId,
|
njId: selectedClass.value.njId,
|
||||||
bjId: selectedClass.value.bjId,
|
bjId: selectedClass.value.bjId,
|
||||||
startTime: startTime.value,
|
startTime: startTime.value + " 00:00:00",
|
||||||
endTime: endTime.value,
|
endTime: endTime.value + " 23:59:59",
|
||||||
pageNo: 1
|
pageNo: 1,
|
||||||
});
|
});
|
||||||
reload()
|
reload();
|
||||||
}
|
};
|
||||||
|
|
||||||
const resetSearch = () => {
|
const resetSearch = () => {
|
||||||
selectedClass.value = null
|
selectedClass.value = null;
|
||||||
startTime.value = ''
|
startTime.value = "";
|
||||||
endTime.value = ''
|
endTime.value = "";
|
||||||
dmRecords.value = []
|
dmRecords.value = [];
|
||||||
totalCount.value = 0
|
totalCount.value = 0;
|
||||||
hasSearched.value = false
|
hasSearched.value = false;
|
||||||
}
|
};
|
||||||
|
|
||||||
const goToDetail = (dm: any) => {
|
const goToDetail = (dm: any) => {
|
||||||
setData(dm)
|
setData(dm);
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: '/pages/view/routine/jc/detail'
|
url: "/pages/view/routine/jc/detail",
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
const exportRecords = () => {
|
const exportRecords = () => {
|
||||||
if (dmRecords.value.length === 0) {
|
if (dmRecords.value.length === 0) {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '暂无数据可导出',
|
title: "暂无数据可导出",
|
||||||
icon: 'none'
|
icon: "none",
|
||||||
})
|
});
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 实现导出逻辑
|
// 实现导出逻辑
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '导出功能开发中',
|
title: "导出功能开发中",
|
||||||
icon: 'none'
|
icon: "none",
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
const formatDate = (dateStr: string) => {
|
const formatDate = (dateStr: string) => {
|
||||||
if (!dateStr) return ''
|
if (!dateStr) return "";
|
||||||
const date = new Date(dateStr)
|
const date = new Date(dateStr);
|
||||||
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`
|
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(
|
||||||
}
|
2,
|
||||||
|
"0"
|
||||||
|
)}-${String(date.getDate()).padStart(2, "0")}`;
|
||||||
|
};
|
||||||
|
|
||||||
const formatTime = (timeStr: string) => {
|
const formatTime = (timeStr: string) => {
|
||||||
if (!timeStr) return ''
|
if (!timeStr) return "";
|
||||||
const date = new Date(timeStr)
|
const date = new Date(timeStr);
|
||||||
return `${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`
|
return `${String(date.getHours()).padStart(2, "0")}:${String(
|
||||||
}
|
date.getMinutes()
|
||||||
|
).padStart(2, "0")}`;
|
||||||
|
};
|
||||||
|
|
||||||
// 生命周期
|
// 生命周期
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 设置默认时间范围为最近一周
|
// 设置默认时间范围为最近一周
|
||||||
const today = new Date()
|
const today = new Date();
|
||||||
const oneWeekAgo = new Date(today.getTime() - 7 * 24 * 60 * 60 * 1000)
|
const oneWeekAgo = new Date(today.getTime() - 7 * 24 * 60 * 60 * 1000);
|
||||||
|
|
||||||
|
startTime.value = oneWeekAgo.toISOString().split("T")[0];
|
||||||
|
endTime.value = today.toISOString().split("T")[0];
|
||||||
|
|
||||||
|
// 调试日志
|
||||||
|
console.log("组件初始化完成:", {
|
||||||
|
startTime: startTime.value,
|
||||||
|
endTime: endTime.value,
|
||||||
|
dmRecords: dmRecords.value,
|
||||||
|
totalCount: totalCount.value,
|
||||||
|
});
|
||||||
|
|
||||||
startTime.value = oneWeekAgo.toISOString().split('T')[0]
|
|
||||||
endTime.value = today.toISOString().split('T')[0]
|
|
||||||
// 不自动加载数据,等待用户搜索
|
// 不自动加载数据,等待用户搜索
|
||||||
})
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.record-content {
|
.record-content {
|
||||||
padding: 20rpx;
|
display: flex;
|
||||||
|
flex: 1 0 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-section {
|
.search-section {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border-radius: 16rpx;
|
border-radius: 16rpx;
|
||||||
padding: 30rpx;
|
padding: 30rpx;
|
||||||
margin-bottom: 20rpx;
|
margin: 20rpx 30rpx;
|
||||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -293,7 +337,8 @@ onMounted(() => {
|
|||||||
border: 1px solid #e5e5e5;
|
border: 1px solid #e5e5e5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-btn, .reset-btn {
|
.search-btn,
|
||||||
|
.reset-btn {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: 70rpx;
|
height: 70rpx;
|
||||||
border-radius: 35rpx;
|
border-radius: 35rpx;
|
||||||
@ -312,18 +357,18 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.records-header {
|
.records-header {
|
||||||
|
padding: 0 30rpx;
|
||||||
margin-bottom: 20rpx;
|
margin-bottom: 20rpx;
|
||||||
|
.section-title {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-title {
|
|
||||||
font-size: 32rpx;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 30rpx;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.export-btn {
|
.export-btn {
|
||||||
font-size: 24rpx;
|
font-size: 24rpx;
|
||||||
@ -339,103 +384,11 @@ onMounted(() => {
|
|||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
|
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
&:active {
|
|
||||||
transform: scale(0.98);
|
|
||||||
box-shadow: 0 1rpx 6rpx rgba(0, 0, 0, 0.12);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 20rpx;
|
|
||||||
padding-bottom: 16rpx;
|
|
||||||
border-bottom: 1px solid #f0f0f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.time-info {
|
|
||||||
display: flex;
|
|
||||||
align-items: baseline;
|
|
||||||
gap: 16rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.record-date {
|
|
||||||
font-size: 28rpx;
|
|
||||||
color: #333;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.record-time {
|
|
||||||
font-size: 24rpx;
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-badge {
|
|
||||||
padding: 8rpx 16rpx;
|
|
||||||
background-color: #e0f7fa;
|
|
||||||
border-radius: 20rpx;
|
|
||||||
border: 1px solid #b2ebf2;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-text {
|
|
||||||
font-size: 24rpx;
|
|
||||||
color: #007bff;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-content {
|
|
||||||
margin-bottom: 20rpx;
|
|
||||||
padding-bottom: 16rpx;
|
|
||||||
border-bottom: 1px solid #f0f0f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.class-info {
|
|
||||||
font-size: 28rpx;
|
|
||||||
color: #333;
|
|
||||||
font-weight: bold;
|
|
||||||
margin-bottom: 16rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.class-name {
|
|
||||||
color: #1890ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stats-row {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
gap: 20rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8rpx;
|
|
||||||
flex: 1;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 12rpx;
|
|
||||||
background-color: #f8f9fa;
|
|
||||||
border-radius: 8rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-label {
|
|
||||||
font-size: 24rpx;
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-value {
|
|
||||||
font-size: 26rpx;
|
|
||||||
color: #333;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-footer {
|
.card-footer {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
@ -488,4 +441,34 @@ onMounted(() => {
|
|||||||
color: #333;
|
color: #333;
|
||||||
margin-bottom: 16rpx;
|
margin-bottom: 16rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card-content {
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
padding-bottom: 16rpx;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-label {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #666;
|
||||||
|
font-weight: normal;
|
||||||
|
flex: 0 0 160rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-value {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333;
|
||||||
|
font-weight: bold;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -91,6 +91,14 @@
|
|||||||
<text class="stat-number absent">{{ getStatusCount('C') }}</text>
|
<text class="stat-number absent">{{ getStatusCount('C') }}</text>
|
||||||
<text class="stat-label">缺勤</text>
|
<text class="stat-label">缺勤</text>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="stat-item">
|
||||||
|
<text class="stat-number unpaid">{{ getStatusCount('D') }}</text>
|
||||||
|
<text class="stat-label">未缴费</text>
|
||||||
|
</view>
|
||||||
|
<view class="stat-item">
|
||||||
|
<text class="stat-number unregistered">{{ getStatusCount('E') }}</text>
|
||||||
|
<text class="stat-label">未报名</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 学生列表 -->
|
<!-- 学生列表 -->
|
||||||
@ -100,6 +108,7 @@
|
|||||||
v-for="student in dmDetail.xsList"
|
v-for="student in dmDetail.xsList"
|
||||||
:key="student.id"
|
:key="student.id"
|
||||||
class="student-item bg-white r-md p-12"
|
class="student-item bg-white r-md p-12"
|
||||||
|
:class="getStudentItemClass(student.jcZt)"
|
||||||
>
|
>
|
||||||
<view class="flex-row items-center">
|
<view class="flex-row items-center">
|
||||||
<view class="avatar-container mr-8">
|
<view class="avatar-container mr-8">
|
||||||
@ -112,6 +121,9 @@
|
|||||||
<view class="flex-1 overflow-hidden">
|
<view class="flex-1 overflow-hidden">
|
||||||
<view class="student-name mb-8">
|
<view class="student-name mb-8">
|
||||||
<text class="font-14 cor-333">{{ student.xsXm }}</text>
|
<text class="font-14 cor-333">{{ student.xsXm }}</text>
|
||||||
|
<text v-if="student.jcZt === 'D' || student.jcZt === 'E'" class="status-indicator">
|
||||||
|
{{ student.jcZt === 'D' ? '未缴费' : '未报名' }}
|
||||||
|
</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="flex-row">
|
<view class="flex-row">
|
||||||
<view
|
<view
|
||||||
@ -228,6 +240,17 @@ const getStatusClass = (status: string) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getStudentItemClass = (status: string) => {
|
||||||
|
switch (status) {
|
||||||
|
case 'D':
|
||||||
|
return 'status-unpaid-item'
|
||||||
|
case 'E':
|
||||||
|
return 'status-unregistered-item'
|
||||||
|
default:
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const getTeacherStatusText = (status: string) => {
|
const getTeacherStatusText = (status: string) => {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'A':
|
case 'A':
|
||||||
@ -328,7 +351,7 @@ onMounted(() => {
|
|||||||
.info-label {
|
.info-label {
|
||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
color: #666;
|
color: #666;
|
||||||
width: 120rpx;
|
width: 160rpx;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -339,21 +362,20 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.stats-container {
|
.stats-container {
|
||||||
display: flex;
|
display: grid;
|
||||||
justify-content: space-around;
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
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;
|
||||||
min-width: 120rpx;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-number {
|
.stat-number {
|
||||||
@ -453,6 +475,27 @@ onMounted(() => {
|
|||||||
color: #eb2f96;
|
color: #eb2f96;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.status-unpaid-item {
|
||||||
|
border-left: 8rpx solid #ff4d4f;
|
||||||
|
background-color: #fff0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-unregistered-item {
|
||||||
|
border-left: 8rpx solid #eb2f96;
|
||||||
|
background-color: #fff0f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-indicator {
|
||||||
|
font-size: 20rpx;
|
||||||
|
margin-left: 16rpx;
|
||||||
|
color: #ff4d4f;
|
||||||
|
background-color: #fff0f0;
|
||||||
|
padding: 4rpx 12rpx;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
border: 1rpx solid #ff4d4f;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
.empty-tip {
|
.empty-tip {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #999;
|
color: #999;
|
||||||
|
|||||||
@ -53,6 +53,8 @@ const switchTab = (tab: string) => {
|
|||||||
.dm-container {
|
.dm-container {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
background-color: #f5f5f5;
|
background-color: #f5f5f5;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-container {
|
.tab-container {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user