446 lines
10 KiB
Vue
Raw Normal View History

2025-08-11 22:42:29 +08:00
<template>
<view class="record-content">
<!-- 使用 BasicListLayout 包裹记录列表 -->
<BasicListLayout @register="register" :fixed="false" class="flex-1">
<template #top>
<!-- 搜索筛选区域 -->
<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>
2025-08-11 22:42:29 +08:00
</view>
</view>
<!-- 记录头部信息 -->
<view class="records-header" v-if="dmRecords && dmRecords.length > 0">
<view class="section-title"> 点名记录 ({{ totalCount }}) </view>
2025-08-11 22:42:29 +08:00
</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>
2025-08-11 22:42:29 +08:00
</view>
</template>
2025-08-11 22:42:29 +08:00
<template #default="{ data }">
<view class="record-card" @click="goToDetail(data)">
<view class="card-content">
<view class="info-row">
<text class="info-label">年级</text>
<text class="info-value">{{ data.njmc }}</text>
2025-08-11 22:42:29 +08:00
</view>
<view class="info-row">
<text class="info-label">班级</text>
<text class="info-value">{{ data.bjmc }}</text>
2025-08-11 22:42:29 +08:00
</view>
<view class="info-row">
<text class="info-label">就餐日期</text>
<text class="info-value">{{ formatDate(data.jcTime) }}</text>
2025-08-11 22:42:29 +08:00
</view>
<view class="info-row">
<text class="info-label">就餐时间</text>
<text class="info-value">{{ formatTime(data.jcTime) }}</text>
2025-08-11 22:42:29 +08:00
</view>
</view>
2025-08-11 22:42:29 +08:00
<view class="card-footer">
<text class="view-detail">查看详情 </text>
2025-08-11 22:42:29 +08:00
</view>
</view>
</template>
</BasicListLayout>
</view>
</template>
<script setup lang="ts">
import { ref, onMounted, computed } from "vue";
import { useLayout } from "@/components/BasicListLayout/hooks/useLayout";
import NjBjPicker from "@/pages/components/NjBjPicker/index.vue";
import { jcDmFindPageApi } from "@/api/base/jcApi";
import { useDataStore } from "@/store/modules/data";
2025-08-11 22:42:29 +08:00
const { setData, getJcBz } = useDataStore();
2025-08-11 22:42:29 +08:00
// 接收外部传入属性
const props = withDefaults(
defineProps<{
title?: string;
}>(),
{
title: "点名列表",
}
);
2025-08-11 22:42:29 +08:00
// 响应式数据
const loading = ref(false);
const startTime = ref("");
const endTime = ref("");
const selectedClass = ref<any>(null);
const dmRecords = ref<any>([]);
const totalCount = ref<any>(0);
const hasSearched = ref(false);
2025-08-11 22:42:29 +08:00
// 使用 BasicListLayout
const [register, { reload, setParam }] = useLayout({
api: async (params: any) => {
try {
const res = await jcDmFindPageApi(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 };
}
},
2025-08-11 22:42:29 +08:00
componentProps: {
auto: false,
},
});
2025-08-11 22:42:29 +08:00
// 改变了年级班级
const changeNjBj = async (nj: any, bj: any) => {
2025-08-11 22:42:29 +08:00
selectedClass.value = {
njId: nj.key,
bjId: bj.key,
};
2025-08-11 22:42:29 +08:00
};
const onTimeRangeChange = (e: any) => {
// uni-datetime-picker 返回的是一个数组,包含开始和结束时间
if (e && Array.isArray(e)) {
const [start, end] = e;
startTime.value = start;
endTime.value = end;
} else if (e && typeof e === "string") {
2025-08-11 22:42:29 +08:00
// 如果返回的是字符串,可能是单个时间
startTime.value = e;
endTime.value = e;
2025-08-11 22:42:29 +08:00
}
};
2025-08-11 22:42:29 +08:00
const getTimeRangeText = () => {
if (!startTime.value || !endTime.value) return "选择时间范围";
2025-08-11 22:42:29 +08:00
if (startTime.value === endTime.value) {
return formatDate(startTime.value);
2025-08-11 22:42:29 +08:00
}
return `${formatDate(startTime.value)} - ${formatDate(endTime.value)}`;
};
2025-08-11 22:42:29 +08:00
const searchRecords = async () => {
if (!selectedClass.value || !startTime.value || !endTime.value) {
uni.showToast({
title: "请选择班级和时间范围",
icon: "none",
});
return;
2025-08-11 22:42:29 +08:00
}
2025-08-11 22:42:29 +08:00
// 验证开始时间不能大于结束时间
if (startTime.value > endTime.value) {
uni.showToast({
title: "开始时间不能大于结束时间",
icon: "none",
});
return;
2025-08-11 22:42:29 +08:00
}
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,
});
2025-08-11 22:42:29 +08:00
// 设置搜索参数并重新加载
setParam({
bzId: getJcBz.id,
2025-08-11 22:42:29 +08:00
njId: selectedClass.value.njId,
bjId: selectedClass.value.bjId,
startTime: startTime.value + " 00:00:00",
endTime: endTime.value + " 23:59:59",
pageNo: 1,
2025-08-11 22:42:29 +08:00
});
reload();
};
2025-08-11 22:42:29 +08:00
const resetSearch = () => {
selectedClass.value = null;
startTime.value = "";
endTime.value = "";
dmRecords.value = [];
totalCount.value = 0;
hasSearched.value = false;
};
2025-08-11 22:42:29 +08:00
const goToDetail = (dm: any) => {
setData(dm);
2025-08-11 22:42:29 +08:00
uni.navigateTo({
url: "/pages/view/routine/jc/detail",
});
};
2025-08-11 22:42:29 +08:00
const formatDate = (dateStr: string) => {
if (!dateStr) return "";
const date = new Date(dateStr);
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(
2,
"0"
)}-${String(date.getDate()).padStart(2, "0")}`;
};
2025-08-11 22:42:29 +08:00
const formatTime = (timeStr: string) => {
if (!timeStr) return "";
const date = new Date(timeStr);
return `${String(date.getHours()).padStart(2, "0")}:${String(
date.getMinutes()
).padStart(2, "0")}`;
};
2025-08-11 22:42:29 +08:00
// 生命周期
onMounted(() => {
// 设置默认时间范围为最近一周
const today = new Date();
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,
});
2025-08-11 22:42:29 +08:00
// 不自动加载数据,等待用户搜索
});
2025-08-11 22:42:29 +08:00
</script>
<style lang="scss" scoped>
.record-content {
display: flex;
flex: 1 0 1px;
2025-08-11 22:42:29 +08:00
}
.search-section {
background-color: #fff;
border-radius: 16rpx;
padding: 30rpx;
margin: 20rpx 30rpx;
2025-08-11 22:42:29 +08:00
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
}
.search-row {
display: flex;
gap: 20rpx;
margin-bottom: 20rpx;
2025-08-11 22:42:29 +08:00
&:last-child {
margin-bottom: 0;
}
}
.search-item {
flex: 1;
display: flex;
align-items: center;
}
.label {
font-size: 28rpx;
color: #333;
white-space: nowrap;
flex: 0 0 160rpx;
}
.date-picker {
flex: 1;
}
.picker-text {
padding: 16rpx 20rpx;
background-color: #f5f5f5;
border-radius: 8rpx;
font-size: 28rpx;
color: #333;
border: 1px solid #e5e5e5;
}
.search-btn,
.reset-btn {
2025-08-11 22:42:29 +08:00
flex: 1;
height: 70rpx;
border-radius: 35rpx;
font-size: 28rpx;
border: none;
}
.search-btn {
background-color: #007aff;
color: #fff;
}
.reset-btn {
background-color: #f5f5f5;
color: #666;
}
.records-header {
padding: 0 30rpx;
2025-08-11 22:42:29 +08:00
margin-bottom: 20rpx;
.section-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
display: flex;
justify-content: space-between;
align-items: center;
}
2025-08-11 22:42:29 +08:00
}
.export-btn {
font-size: 24rpx;
color: #007aff;
font-weight: normal;
}
.record-card {
border: 1px solid #f0f0f0;
border-radius: 16rpx;
padding: 24rpx;
margin-bottom: 20rpx;
background-color: #fff;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
transition: all 0.3s ease;
}
.card-footer {
text-align: right;
padding-top: 16rpx;
}
.view-detail {
font-size: 26rpx;
color: #007aff;
font-weight: normal;
}
.empty-state {
text-align: center;
padding: 100rpx 0;
}
.empty-icon {
font-size: 80rpx;
margin-bottom: 20rpx;
}
.empty-text {
display: block;
font-size: 32rpx;
color: #333;
margin-bottom: 16rpx;
}
.empty-tip {
font-size: 26rpx;
color: #999;
}
.initial-tip {
text-align: center;
padding: 50rpx 0;
color: #999;
font-size: 28rpx;
}
.tip-icon {
font-size: 60rpx;
margin-bottom: 20rpx;
}
.tip-text {
display: block;
font-size: 32rpx;
color: #333;
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;
}
2025-08-11 22:42:29 +08:00
</style>