1、增加了一些查询年级班级选择器的参数

2、调整标准列表查询
This commit is contained in:
ywyonui 2025-09-13 21:48:00 +08:00
parent 563c107d00
commit 80f126c25a
5 changed files with 432 additions and 122 deletions

View File

@ -199,6 +199,9 @@ const props = withDefaults(defineProps<{
title?: string title?: string
useKzTree?: boolean // 使 useKzTree?: boolean // 使
multiple?: boolean // multiple?: boolean //
onlyConfirmChangeFlag: boolean // change
autoSelectedFirst: boolean //
bjRequired: boolean //
}>(), { }>(), {
modelValue: null, modelValue: null,
defaultValue: null, defaultValue: null,
@ -209,7 +212,10 @@ const props = withDefaults(defineProps<{
disabled: false, disabled: false,
title: '选择班级', title: '选择班级',
useKzTree: false, useKzTree: false,
multiple: false multiple: false,
onlyConfirmChangeFlag: false,
autoSelectedFirst: false,
bjRequired: false
}) })
// Emits // Emits
@ -307,6 +313,9 @@ const onGradeRadioChange = (event: any) => {
if (selectedGrade) { if (selectedGrade) {
selectedGrades.value = [selectedGrade] selectedGrades.value = [selectedGrade]
selectedClasses.value = [] selectedClasses.value = []
if (props.autoSelectedFirst) {
selectedClasses.value = [selectedGrade.children[0]]
}
} else { } else {
selectedGrades.value = [] selectedGrades.value = []
selectedClasses.value = [] selectedClasses.value = []
@ -365,7 +374,16 @@ const onClassRadioChange = (event: any) => {
} }
// //
const updateModelValue = () => { const updateModelValue = (confirmFlag: boolean = false) => {
// change
if (!confirmFlag && props.onlyConfirmChangeFlag) {
return;
}
if (props.bjRequired && selectedClasses.value.length === 0) {
uni.showToast({ title: '请选择班级', icon: 'error' });
return;
}
const result: TreeSelectResult = { const result: TreeSelectResult = {
selectedGrades: [...selectedGrades.value], selectedGrades: [...selectedGrades.value],
selectedClasses: [...selectedClasses.value], selectedClasses: [...selectedClasses.value],
@ -390,7 +408,7 @@ const closeSelector = () => {
// //
const confirmSelection = () => { const confirmSelection = () => {
updateModelValue() updateModelValue(true)
closeSelector() closeSelector()
} }
@ -414,6 +432,11 @@ const loadData = async () => {
if (props.defaultValue) { if (props.defaultValue) {
selectedGrades.value = props.defaultValue.selectedGrades || [] selectedGrades.value = props.defaultValue.selectedGrades || []
selectedClasses.value = props.defaultValue.selectedClasses || [] selectedClasses.value = props.defaultValue.selectedClasses || []
} else if (props.autoSelectedFirst) {
//
selectedGrades.value = [njList.value[0]]
selectedClasses.value = [njList.value[0].children[0]]
updateModelValue(true)
} }
} catch (error) { } catch (error) {
console.error('加载年级班级数据失败:', error) console.error('加载年级班级数据失败:', error)

View File

@ -1,149 +1,400 @@
<template> <template>
<view class="bz-list"> <view class="jc-list-container">
<!-- 使用 BasicListLayout 包裹记录列表 --> <!-- 页面标题 -->
<BasicListLayout @register="register" :fixed="false" class="flex-1"> <view class="page-header">
<template #default="{ data }"> <text class="page-title">就餐标准列表</text>
<view class="bz-card" @click="goToBz(data)"> </view>
<view class="card-header">
<view class="card-title">{{ data.bzMc }}</view> <!-- 就餐列表 -->
</view> <view class="section" v-if="jcBzList.length > 0">
<view class="divider"></view> <!-- 就餐列表 -->
<view class="card-content"> <view class="jc-list">
<view class="info-row"> <view
<text class="info-label">年级</text> v-for="bz in jcBzList"
<text class="info-value">{{ data.njmc }}</text> :key="bz.id"
</view> class="jc-item bg-white r-md p-12"
<view class="info-row"> @click="goToBz(bz)"
<text class="info-label">说明</text> >
<text class="info-value">{{ data.bzSm }}</text> <view class="jc-header">
</view> <view class="jc-title">{{ bz.bzMc }}</view>
</view>
<view class="card-footer">
<text class="view-detail">前往点名 </text>
</view> </view>
<view class="jc-info">
<view class="info-row">
<text class="info-label">学期名称</text>
<text class="info-value">{{ bz.xqMc || '未知' }}</text>
</view>
<view class="info-row">
<text class="info-label">年级范围</text>
<text class="info-value">{{ bz.njmc || '全部' }}</text>
</view>
<view class="info-row">
<text class="info-label">说明</text>
<text class="info-value">{{ bz.bzSm }}</text>
</view>
</view>
</view> </view>
</template> </view>
</BasicListLayout> </view>
<!-- 空状态 -->
<view class="empty-state" v-if="!loading && jcBzList.length === 0 && hasSearched">
<view class="empty-icon">📚</view>
<view class="empty-text">暂无就餐数据</view>
<view class="empty-tip">请联系管理员添加就餐信息</view>
</view>
<!-- 加载状态 -->
<view class="loading-state" v-if="loading">
<view class="loading-text">正在加载...</view>
</view>
<!-- 滚动加载更多 -->
<view class="load-more" v-if="hasMore && !loading">
<view class="load-more-text">上拉加载更多</view>
</view>
<!-- 没有更多数据 -->
<view class="no-more" v-if="!hasMore && hasSearched && jcBzList.length > 0">
<view class="no-more-text">没有更多数据了</view>
</view>
</view> </view>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { useLayout } from "@/components/BasicListLayout/hooks/useLayout"; import { ref } from 'vue'
import { jcBzFindPageApi } from "@/api/base/jcApi"; import { onLoad } from '@dcloudio/uni-app'
import { jcBzFindPageApi } from '@/api/base/jcApi'
import { useDataStore } from "@/store/modules/data"; import { useDataStore } from "@/store/modules/data";
const { setJcBz } = useDataStore(); const { setJcBz } = useDataStore();
// //
const loading = ref(false); const jcBzList = ref<any>([])
const bzList = ref<any>([]); const loading = ref(false)
const total = ref<any>(0); const hasSearched = ref(false)
const hasMore = ref(true)
const currentPage = ref(1)
const pageSize = ref(10)
// 使 BasicListLayout //
const [register, { reload, setParam }] = useLayout({ onLoad(() => {
api: async (params: any) => { console.log('就餐列表页面加载')
try { loadJcBzList()
const res = await jcBzFindPageApi(params); })
console.log("API返回数据:", res); //
// //
if (res && res.rows) { const loadJcBzList = async (isLoadMore = false) => {
bzList.value = res.rows; if (loading.value) return
total.value = res.records || res.total || 0;
loading.value = true
hasSearched.value = true
try {
//
const response = await jcBzFindPageApi({
pageNo: currentPage.value,
rows: 10,
});
console.log('API返回结果:', response)
if (response && response.rows) {
const newData = response.rows || []
if (isLoadMore) {
//
jcBzList.value = [...jcBzList.value, ...newData]
} else { } else {
bzList.value = []; //
total.value = 0; jcBzList.value = newData
} }
return res;
} catch (error) {
console.error("获取数据失败:", error);
bzList.value = [];
total.value = 0;
return { rows: [], total: 0 };
}
}
});
//
hasMore.value = newData.length >= pageSize.value
} else {
throw new Error('查询失败')
}
} catch (error) {
console.error('查询就餐列表失败:', error)
uni.showToast({
title: '查询失败,请重试',
icon: 'none'
})
if (!isLoadMore) {
jcBzList.value = []
}
} finally {
loading.value = false
}
}
//
const loadMore = () => {
if (loading.value || !hasMore.value) {
return
}
currentPage.value++
loadJcBzList(true)
}
//
const onReachBottom = () => {
loadMore()
}
//
const goToBz = (bz: any) => { const goToBz = (bz: any) => {
setJcBz(bz); setJcBz(bz);
uni.navigateTo({ url: "/pages/view/routine/jc/index"}); uni.navigateTo({ url: "/pages/view/routine/jc/index"});
}; };
// //
onMounted(() => { const getStatusClass = (status: string) => {
// switch (status) {
reload(); case '未开始':
}); return 'status-pending'
case '进行中':
return 'status-active'
case '已结束':
return 'status-ended'
default:
return 'status-unknown'
}
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.bz-list { .jc-list-container {
min-height: 100vh; min-height: 100vh;
background-color: #f5f5f5; background-color: #f5f5f5;
padding: 20rpx;
}
.page-header {
background-color: #fff;
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
}
.page-title {
font-size: 36rpx;
font-weight: bold;
color: #333;
}
.section {
background-color: #fff;
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
}
.section-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 20rpx;
}
.jc-list {
margin-bottom: 30rpx;
}
.jc-item {
margin-bottom: 20rpx;
border-radius: 16rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
transition: all 0.2s;
position: relative;
cursor: pointer;
&:hover {
transform: translateY(-2rpx);
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.15);
}
&:active {
transform: translateY(0);
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
}
}
.jc-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
padding-bottom: 15rpx;
border-bottom: 1rpx solid #f0f0f0;
}
.jc-actions {
display: flex;
align-items: center;
}
.jc-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
flex: 1;
}
.jc-status {
font-size: 24rpx;
padding: 8rpx 16rpx;
border-radius: 20rpx;
font-weight: bold;
&.status-pending {
background-color: #fff7e6;
color: #fa8c16;
border: 1rpx solid #ffd591;
}
&.status-active {
background-color: #f6ffed;
color: #52c41a;
border: 1rpx solid #b7eb8f;
}
&.status-ended {
background-color: #fff2f0;
color: #ff4d4f;
border: 1rpx solid #ffccc7;
}
&.status-unknown {
background-color: #f5f5f5;
color: #999;
border: 1rpx solid #d9d9d9;
}
}
.more-btn {
display: flex;
align-items: center;
justify-content: center;
width: 60rpx;
height: 60rpx;
border-radius: 50%;
background-color: #f5f5f5;
transition: all 0.2s ease;
cursor: pointer;
&:hover {
background-color: #e6f7ff;
transform: scale(1.1);
}
&:active {
background-color: #d9d9d9;
transform: scale(0.95);
}
}
.more-icon {
width: 32rpx;
height: 32rpx;
}
.jc-info {
margin-bottom: 20rpx;
}
.info-row {
display: flex;
margin-bottom: 12rpx;
font-size: 28rpx;
}
.info-label {
color: #666;
margin-right: 10rpx;
min-width: 140rpx;
}
.info-value {
color: #333;
flex: 1;
}
.empty-state {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 40rpx;
.empty-icon {
font-size: 120rpx;
margin-bottom: 20rpx;
}
.empty-text {
font-size: 32rpx;
color: #666;
margin-bottom: 10rpx;
}
.empty-tip {
font-size: 28rpx;
color: #999;
}
} }
.bz-card { .loading-state {
border: 1px solid #f0f0f0; display: flex;
border-radius: 16rpx; justify-content: center;
padding: 24rpx; align-items: center;
margin-bottom: 20rpx; padding: 100rpx;
background-color: #fff;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
transition: all 0.3s ease;
.card-header { .loading-text {
font-size: 16px; font-size: 32rpx;
font-weight: bold;
color: #333;
margin-bottom: 10px;
.card-title {
font-size: 16px;
font-weight: bold;
color: #333;
}
}
.divider {
height: 1px;
background-color: #eee;
margin-bottom: 15px;
}
.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; color: #666;
font-weight: normal;
flex: 0 0 160rpx;
} }
}
.info-value { .bg-white {
background-color: #fff;
}
.r-md {
border-radius: 16rpx;
}
.p-12 {
padding: 24rpx;
}
/* 滚动加载样式 */
.load-more {
display: flex;
justify-content: center;
align-items: center;
padding: 30rpx;
.load-more-text {
font-size: 28rpx; font-size: 28rpx;
color: #333; color: #999;
font-weight: bold;
flex: 1;
} }
}
.card-footer { .no-more {
text-align: right; display: flex;
justify-content: center;
align-items: center;
padding: 30rpx;
.no-more-text {
font-size: 28rpx;
color: #ccc;
} }
} }
</style> </style>

View File

@ -2,9 +2,23 @@
<view class="start-dm-content"> <view class="start-dm-content">
<!-- 班级选择器 --> <!-- 班级选择器 -->
<view class="section"> <view class="section">
<view class="section-title">选择班级</view> <text class="section-title">选择班级</text>
<NjBjPicker @change="changeNjBj" icon-arrow="right" :customStyle="{ backgroundColor: '#fff', borderRadius: '0', padding: '12px 15px' }" /> <BasicNjBjSelect
:custom-style="{
borderRadius: '12rpx',
background: 'background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%)',
padding: '0rpx 30rpx',
border: '1rpx solid #e9ecef',
transition: 'all 0.3s ease',
boxShadow: '0 2px 4px rgba(0, 0, 0, 0.1)',
}"
v-model="selectedNjBj"
placeholder="请选择年级班级"
:only-confirm-change-flag="true"
:auto-selected-first="false"
:bj-required="true"
@change="changeNjBj"
/>
<!-- 班级选择提示 --> <!-- 班级选择提示 -->
<view v-if="!curBj" class="class-tip"> <view v-if="!curBj" class="class-tip">
<text class="tip-icon"></text> <text class="tip-icon"></text>
@ -59,7 +73,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed } from 'vue' import { ref, computed } from 'vue'
import NjBjPicker from '@/pages/components/NjBjPicker/index.vue' import BasicNjBjSelect from '@/components/BasicNjBjSelect/index.vue'
import DmJs from './dmJs.vue' import DmJs from './dmJs.vue'
import DmXs from './dmXs.vue' import DmXs from './dmXs.vue'
import DmPsComponent from '@/pages/components/dmPs/index.vue' import DmPsComponent from '@/pages/components/dmPs/index.vue'
@ -73,6 +87,7 @@ const { getJcBz } = useDataStore();
// isSubmitting useDebounce // isSubmitting useDebounce
const { isProcessing: isSubmitting, debounce } = useDebounce(2000); const { isProcessing: isSubmitting, debounce } = useDebounce(2000);
const selectedNjBj = ref<any>(null);
/** /**
* 就餐点名组件 * 就餐点名组件
* 功能 * 功能
@ -106,7 +121,11 @@ const mediaData = ref({
const dmPsRef = ref<any>(null); const dmPsRef = ref<any>(null);
// //
const changeNjBj = async (nj: any, bj: any) => { const changeNjBj = (val: any) => {
const njList = val.selectedGrades || [];
const nj = njList[0] || {};
const bjList = val.selectedClasses || [];
const bj = bjList[0] || {};
curNj.value = nj curNj.value = nj
curBj.value = bj curBj.value = bj
}; };

View File

@ -9,10 +9,21 @@
<view class="search-item"> <view class="search-item">
<text class="label">班级</text> <text class="label">班级</text>
<view class="flex-1"> <view class="flex-1">
<NjBjPicker @change="changeNjBj" icon-arrow="right" :customStyle="{ <BasicNjBjSelect
borderRadius: '0', :custom-style="{
padding: '0.5rem 0.625rem', borderRadius: '12rpx',
}" /> background: 'background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%)',
padding: '0rpx 30rpx',
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>
@ -83,7 +94,7 @@
<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 BasicNjBjSelect from '@/components/BasicNjBjSelect/index.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";
@ -108,6 +119,8 @@ 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);
// 使 BasicListLayout // 使 BasicListLayout
const [register, { reload, setParam }] = useLayout({ const [register, { reload, setParam }] = useLayout({
api: async (params: any) => { api: async (params: any) => {
@ -138,7 +151,11 @@ const [register, { reload, setParam }] = useLayout({
}); });
// //
const changeNjBj = async (nj: any, bj: any) => { const changeNjBj = (val: any) => {
const njList = val.selectedGrades || [];
const nj = njList[0] || {};
const bjList = val.selectedClasses || [];
const bj = bjList[0] || {};
selectedClass.value = { selectedClass.value = {
njId: nj.key, njId: nj.key,
bjId: bj.key, bjId: bj.key,

View File

@ -39,7 +39,7 @@ import DmComponent from './components/dm.vue'
import DmListComponent from './components/dmList.vue' import DmListComponent from './components/dmList.vue'
// //
const activeTab = ref('record') const activeTab = ref('start')
const loading = ref(false) const loading = ref(false)
// //