286 lines
6.7 KiB
Vue
286 lines
6.7 KiB
Vue
<template>
|
||
<view class="start-dm-content">
|
||
<!-- 班级选择器 -->
|
||
<view class="section">
|
||
<view class="section-title">选择班级</view>
|
||
<NjBjPicker @change="changeNjBj" icon-arrow="right" :customStyle="{ backgroundColor: '#fff', borderRadius: '0', padding: '12px 15px' }" />
|
||
|
||
<!-- 班级选择提示 -->
|
||
<view v-if="!curBj" class="class-tip">
|
||
<text class="tip-icon">ℹ️</text>
|
||
<text class="tip-text">请先选择班级</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 陪餐教师选择 -->
|
||
<dm-js v-if="curBj" :bj-id="curBj?.key" ref="dmJsRef" />
|
||
|
||
<!-- 学生状态列表 -->
|
||
<dm-xs
|
||
v-if="curBj"
|
||
:bz-id="getJcBz.id"
|
||
:nj-id="curNj?.key"
|
||
:bj-id="curBj?.key"
|
||
ref="dmXsRef"
|
||
/>
|
||
|
||
<!-- 拍照视频组件 -->
|
||
<view class="section" v-if="curBj">
|
||
<DmPsComponent
|
||
v-model="mediaData"
|
||
:photo-title="'就餐现场拍照'"
|
||
:video-title="'就餐现场视频'"
|
||
:max-photo-count="9"
|
||
:max-video-count="3"
|
||
:max-video-duration="60"
|
||
ref="dmPsRef"
|
||
/>
|
||
</view>
|
||
|
||
<!-- 加载提示 -->
|
||
<view v-if="isLoading" class="loading-overlay">
|
||
<view class="loading-content">
|
||
<text>加载中...</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 提交按钮 - 固定在底部 -->
|
||
<view class="fixed-bottom" v-if="curBj">
|
||
<button
|
||
class="submit-btn"
|
||
:disabled="isSubmitting"
|
||
@click="tjDm"
|
||
>
|
||
提交点名
|
||
</button>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, computed } from 'vue'
|
||
import NjBjPicker from '@/pages/components/NjBjPicker/index.vue'
|
||
import DmJs from './dmJs.vue'
|
||
import DmXs from './dmXs.vue'
|
||
import DmPsComponent from '@/pages/components/dmPs/index.vue'
|
||
import { submitJcDmDataApi } from '@/api/base/jcApi'
|
||
import { useUserStore } from '@/store/modules/user'
|
||
import { useDataStore } from '@/store/modules/data'
|
||
import { useDebounce } from "@/utils/debounce";
|
||
const { getJs } = useUserStore()
|
||
const { getJcBz } = useDataStore();
|
||
|
||
// 替换 isSubmitting 状态为 useDebounce
|
||
const { isProcessing: isSubmitting, debounce } = useDebounce(2000);
|
||
|
||
/**
|
||
* 就餐点名组件
|
||
* 功能:
|
||
* 1. 选择班级,获取学生列表
|
||
* 2. 将学生分为两类:已缴费(可切换正常/请假/缺勤)和未缴费/未报名(状态为未缴费/未报名)
|
||
* 3. 选择陪餐教师
|
||
* 4. 批量提交点名记录,包含jsId和dmPcId字段
|
||
*/
|
||
|
||
// 接收外部传入属性
|
||
const props = withDefaults(defineProps<{
|
||
title?: string
|
||
}>(), {
|
||
title: '点名'
|
||
});
|
||
|
||
const isLoading = ref(false);
|
||
|
||
// 响应式数据
|
||
const curNj = ref<any>(null);
|
||
const curBj = ref<any>(null);
|
||
|
||
const dmXsRef = ref<any>(null);
|
||
const dmJsRef = ref<any>(null);
|
||
|
||
// 媒体数据
|
||
const mediaData = ref({
|
||
photoList: [],
|
||
videoList: []
|
||
});
|
||
const dmPsRef = ref<any>(null);
|
||
|
||
// 改变了年级班级
|
||
const changeNjBj = async (nj: any, bj: any) => {
|
||
curNj.value = nj
|
||
curBj.value = bj
|
||
};
|
||
|
||
const tjDm = debounce(async () => {
|
||
if (!curBj.value) { // 改为检查已缴费学生数量
|
||
uni.showToast({
|
||
title: '请先选择班级',
|
||
icon: 'none'
|
||
})
|
||
return;
|
||
}
|
||
try {
|
||
const dmXsList = dmXsRef.value.getDmXsList();
|
||
const dmJsList = dmJsRef.value.getDmJsList();
|
||
// 先上传媒体文件
|
||
let photoUrls = '';
|
||
let videoUrls = '';
|
||
|
||
if (mediaData.value.photoList.length > 0 || mediaData.value.videoList.length > 0) {
|
||
try {
|
||
if (dmPsRef.value) {
|
||
const uploadResult = await dmPsRef.value.uploadMedia();
|
||
photoUrls = uploadResult.photoUrls;
|
||
videoUrls = uploadResult.videoUrls;
|
||
}
|
||
} catch (uploadError) {
|
||
console.error('媒体文件上传失败:', uploadError);
|
||
uni.showToast({
|
||
title: '媒体文件上传失败,请重试',
|
||
icon: 'none'
|
||
});
|
||
return;
|
||
}
|
||
}
|
||
let dmData: any = {
|
||
jcTime: new Date(),
|
||
bjId: curBj.value.key,
|
||
njId: curNj.value.key,
|
||
bjmc: curBj.value.title,
|
||
njmc: curNj.value.title,
|
||
dmJsId: getJs.id || '', // 点名教师ID
|
||
pcRs: dmJsList.length,
|
||
zrs: dmXsList.length,
|
||
sdRs: dmXsList.filter((s: any) => s.jcZt === 'A').length,
|
||
qjRs: dmXsList.filter((s: any) => s.jcZt === 'B').length,
|
||
qqRs: dmXsList.filter((s: any) => s.jcZt === 'C').length,
|
||
// 媒体文件地址
|
||
zp: photoUrls, // 照片字段,逗号分隔的字符串
|
||
sp: videoUrls, // 视频字段,逗号分隔的字符串
|
||
xsList: dmXsList,
|
||
ptJsList: dmJsList
|
||
};
|
||
console.log('提交数据:', dmData);
|
||
// return;
|
||
// 提交点名数据
|
||
const response = await submitJcDmDataApi(dmData);
|
||
if (response.result) {
|
||
uni.showToast({
|
||
title: '提交成功',
|
||
icon: 'success'
|
||
})
|
||
|
||
// 重置表单
|
||
curNj.value = null
|
||
curBj.value = null
|
||
mediaData.value = {
|
||
photoList: [],
|
||
videoList: []
|
||
};
|
||
|
||
// 返回上一页
|
||
uni.navigateBack()
|
||
} else {
|
||
throw new Error(response.message || '提交失败')
|
||
}
|
||
} catch (error) {
|
||
console.error('提交失败:', error)
|
||
uni.showToast({
|
||
title: error instanceof Error ? error.message : '提交失败',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
});
|
||
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.start-dm-content {
|
||
padding: 20rpx;
|
||
padding-bottom: 120rpx; /* 为固定底部按钮留出空间 */
|
||
}
|
||
|
||
.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;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.class-tip {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-top: 20rpx;
|
||
padding: 15rpx 20rpx;
|
||
background-color: #fffbe6;
|
||
border: 1rpx solid #ffe58f;
|
||
border-radius: 12rpx;
|
||
color: #faad14;
|
||
font-size: 28rpx;
|
||
font-weight: bold;
|
||
|
||
.tip-icon {
|
||
margin-right: 10rpx;
|
||
font-size: 32rpx;
|
||
}
|
||
}
|
||
|
||
.fixed-bottom {
|
||
position: fixed;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
padding: 20rpx;
|
||
background-color: #fff;
|
||
box-shadow: 0 -2rpx 8rpx rgba(0, 0, 0, 0.1);
|
||
z-index: 10;
|
||
}
|
||
|
||
.submit-btn {
|
||
width: 100%;
|
||
height: 80rpx;
|
||
background-color: #007aff;
|
||
color: #fff;
|
||
border: none;
|
||
border-radius: 40rpx;
|
||
font-size: 32rpx;
|
||
font-weight: bold;
|
||
|
||
&:disabled {
|
||
background-color: #d9d9d9;
|
||
color: #999;
|
||
}
|
||
}
|
||
|
||
.loading-overlay {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background-color: rgba(0, 0, 0, 0.5);
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
z-index: 1000;
|
||
}
|
||
|
||
.loading-content {
|
||
background-color: #fff;
|
||
padding: 40rpx;
|
||
border-radius: 16rpx;
|
||
color: #333;
|
||
font-size: 28rpx;
|
||
}
|
||
</style> |