调整完善教师端关于选课点名相关界面

This commit is contained in:
ywyonui 2025-08-19 01:48:06 +08:00
parent 44e789f595
commit 526cdc1075
11 changed files with 1151 additions and 551 deletions

View File

@ -35,11 +35,16 @@ export const getXkDmXsPageApi = async (params: any) => {
return await get('/api/xkDmXs/findPage', params) return await get('/api/xkDmXs/findPage', params)
} }
// 选课学生列表 // 选课待点名学生列表
export const getWaitDmXsListApi = async (params: any) => { export const getWaitDmXsListApi = async (params: any) => {
return await get("/api/xkDmXs/getWaitDmXsList", params); return await get("/api/xkDmXs/getWaitDmXsList", params);
}; };
// 选课点名学生列表
export const getDmXsListApi = async (params: any) => {
return await get("/api/xkDmXs/getDmXsList", params);
};
/** /**
* *
*/ */

View File

@ -527,16 +527,16 @@
} }
}, },
{ {
"path": "pages/view/routine/xk/dmRecord", "path": "pages/view/routine/xk/dmList",
"style": { "style": {
"navigationBarTitleText": "点名记录", "navigationBarTitleText": "点名列表",
"enablePullDownRefresh": false "enablePullDownRefresh": false
} }
}, },
{ {
"path": "pages/view/routine/xk/dmXsRecord", "path": "pages/view/routine/xk/dmXsList",
"style": { "style": {
"navigationBarTitleText": "点名学生记录", "navigationBarTitleText": "点名学生列表",
"enablePullDownRefresh": false "enablePullDownRefresh": false
} }
}, },

View File

@ -54,7 +54,7 @@
</view> </view>
</view> </view>
<view class="video-upload" @click="recordVideo"> <view class="video-upload" @click="recordVideo">
<u-icon name="videocam" size="32" color="#999"></u-icon> <u-icon name="camera" size="32" color="#999"></u-icon>
<text class="upload-text">{{ videoUploadText || '点击录制' }}</text> <text class="upload-text">{{ videoUploadText || '点击录制' }}</text>
</view> </view>
</view> </view>

View File

@ -0,0 +1,281 @@
<template>
<view class="media-preview">
<!-- 照片预览 -->
<view v-if="photoList.length > 0" class="media-section">
<view class="section-title">
<u-icon name="photo" color="#4080ff" size="16"></u-icon>
<text class="title-text">现场照片 ({{ photoList.length }})</text>
</view>
<view class="photo-grid">
<view
v-for="(photo, index) in photoList"
:key="index"
class="photo-item"
@click="previewPhoto(index)"
>
<image
:src="getImageUrl(photo)"
mode="aspectFill"
class="photo-img"
/>
<view class="photo-overlay">
<u-icon name="eye" color="white" size="16"></u-icon>
</view>
</view>
</view>
</view>
<!-- 视频预览 -->
<view v-if="videoList.length > 0" class="media-section">
<view class="section-title">
<u-icon name="camera" color="#4080ff" size="16"></u-icon>
<text class="title-text">现场视频 ({{ videoList.length }})</text>
</view>
<view class="video-list">
<view
v-for="(video, index) in videoList"
:key="index"
class="video-item"
@click="playVideo(video)"
>
<view class="video-thumbnail">
<image
:src="getVideoThumbnail(video)"
mode="aspectFill"
class="video-img"
/>
<view class="video-play-btn">
<u-icon name="play-right" color="white" size="20"></u-icon>
</view>
</view>
<view class="video-info">
<text class="video-title">视频 {{ index + 1 }}</text>
<text class="video-duration">{{ getVideoDuration(video) }}</text>
</view>
</view>
</view>
</view>
<!-- 无媒体文件提示 -->
<view v-if="photoList.length === 0 && videoList.length === 0" class="empty-tip">
<u-icon name="photo" color="#ccc" size="32"></u-icon>
<text class="empty-text">暂无媒体文件</text>
</view>
</view>
</template>
<script setup lang="ts">
import { computed } from "vue";
// BASE_IMAGE_URL使
const BASE_IMAGE_URL = import.meta.env.VITE_BASE_IMAGE_URL || '';
//
interface Props {
zp?: string; //
sp?: string; //
}
const props = withDefaults(defineProps<Props>(), {
zp: '',
sp: ''
});
//
const photoList = computed(() => {
if (!props.zp) return [];
return props.zp.split(',').filter(path => path.trim());
});
//
const videoList = computed(() => {
if (!props.sp) return [];
return props.sp.split(',').filter(path => path.trim());
});
// URL
const getImageUrl = (path: string) => {
if (!path) return '';
if (path.startsWith('http')) return path;
return BASE_IMAGE_URL + path;
};
//
const getVideoThumbnail = (videoPath: string) => {
//
//
return '/static/images/video-thumbnail.png';
};
//
const getVideoDuration = (videoPath: string) => {
//
return '00:00';
};
//
const previewPhoto = (index: number) => {
const urls = photoList.value.map(photo => getImageUrl(photo));
// @ts-ignore
uni.previewImage({
current: index,
urls: urls
});
};
//
const playVideo = (videoPath: string) => {
const videoUrl = getImageUrl(videoPath);
// 使uni-app
// @ts-ignore
uni.navigateTo({
url: `/pages/components/videoPlayer/index?url=${encodeURIComponent(videoUrl)}`
});
};
</script>
<style scoped lang="scss">
.media-preview {
padding: 15px;
}
.media-section {
margin-bottom: 20px;
&:last-child {
margin-bottom: 0;
}
}
.section-title {
display: flex;
align-items: center;
margin-bottom: 12px;
padding: 8px 0;
.title-text {
margin-left: 8px;
font-size: 16px;
font-weight: 500;
color: #333;
}
}
.photo-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 8px;
}
.photo-item {
position: relative;
aspect-ratio: 1;
border-radius: 8px;
overflow: hidden;
cursor: pointer;
&:hover .photo-overlay {
opacity: 1;
}
}
.photo-img {
width: 100%;
height: 100%;
object-fit: cover;
}
.photo-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.3);
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transition: opacity 0.3s ease;
}
.video-list {
display: flex;
flex-direction: column;
gap: 12px;
}
.video-item {
display: flex;
align-items: center;
padding: 12px;
background: #f8f9fa;
border-radius: 8px;
cursor: pointer;
transition: background-color 0.2s ease;
&:hover {
background: #e9ecef;
}
}
.video-thumbnail {
position: relative;
width: 80px;
height: 60px;
border-radius: 6px;
overflow: hidden;
margin-right: 12px;
}
.video-img {
width: 100%;
height: 100%;
object-fit: cover;
}
.video-play-btn {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 32px;
height: 32px;
background: rgba(0, 0, 0, 0.6);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.video-info {
flex: 1;
display: flex;
flex-direction: column;
gap: 4px;
}
.video-title {
font-size: 14px;
font-weight: 500;
color: #333;
}
.video-duration {
font-size: 12px;
color: #666;
}
.empty-tip {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 40px 20px;
color: #999;
.empty-text {
margin-top: 12px;
font-size: 14px;
}
}
</style>

View File

@ -94,10 +94,10 @@ import { imagUrl } from "@/utils";
import { onLoad } from "@dcloudio/uni-app"; import { onLoad } from "@dcloudio/uni-app";
import { xsJzListByXsIdApi } from "@/api/base/server"; import { xsJzListByXsIdApi } from "@/api/base/server";
import { useDataStore } from "@/store/modules/data"; import { useDataStore } from "@/store/modules/data";
const { getData } = useDataStore(); const { getXs } = useDataStore();
const xsInfo = computed(() => { const xsInfo = computed(() => {
const xs = getData || {}; const xs = getXs || {};
// //
const studentInfo = { const studentInfo = {
id: xs.xsId || xs.id, // id: xs.xsId || xs.id, //
@ -159,7 +159,7 @@ onLoad(async (options) => {
if (xsInfo.value && xsInfo.value.id) { if (xsInfo.value && xsInfo.value.id) {
try { try {
uni.showLoading({ title: "加载中..." }); uni.showLoading({ title: "加载中..." });
const res = await xsJzListByXsIdApi({ xsId: xsInfo.value.id }); const res:any = await xsJzListByXsIdApi({ xsId: xsInfo.value.id });
if (res && res.resultCode === 1) { if (res && res.resultCode === 1) {
jzList.value = res.result || []; jzList.value = res.result || [];
} else { } else {

View File

@ -56,7 +56,7 @@ import NjBjPicker from "@/pages/components/NjBjPicker/index.vue";
import { imagUrl } from "@/utils"; import { imagUrl } from "@/utils";
import { xsFindList } from "@/api/base/server"; import { xsFindList } from "@/api/base/server";
import { useDataStore } from "@/store/modules/data"; import { useDataStore } from "@/store/modules/data";
const { setData } = useDataStore(); const { setXs } = useDataStore();
const xsList = ref<any>([]); const xsList = ref<any>([]);
const xsTotal = ref(0); const xsTotal = ref(0);
@ -114,7 +114,7 @@ const getXsList = async () => {
// //
const goToDetail = (xs: any) => { const goToDetail = (xs: any) => {
setData(xs); setXs(xs);
uni.navigateTo({ uni.navigateTo({
url: `/pages/view/homeSchool/parentAddressBook/detail` url: `/pages/view/homeSchool/parentAddressBook/detail`
}); });

View File

@ -32,35 +32,40 @@
</view> </view>
<!-- 学生列表 --> <!-- 学生列表 -->
<view class="student-list mb-30 white-bg-color"> <view class="student-list mb-15 white-bg-color">
<view class="student-grid"> <view class="student-grid">
<view <view
v-for="(xs, index) in xsList" v-for="(xs, index) in xsList"
:key="index" :key="index"
class="student-item bg-white r-md p-12" class="student-item bg-white r-md p-12"
> >
<view class="flex-row items-center"> <view class="student-content">
<view class="avatar-container mr-8"> <!-- 第一行头像和学生信息 -->
<view class="top-row">
<view class="avatar-container">
<image <image
class="student-avatar" class="student-avatar"
:src="getImageUrl(xs.tx || xs.xstx)" :src="getImageUrl(xs.tx || xs.xstx)"
mode="aspectFill" mode="aspectFill"
></image> ></image>
</view> </view>
<view class="flex-1 overflow-hidden"> <view class="student-info">
<view class="flex-row items-center mb-3"> <text class="student-name">{{ xs.xsXm || xs.xsxm }}</text>
<text class="font-14 font-bold mr-5 text-ellipsis">{{ xs.xsXm || xs.xsxm }}</text> <text class="student-class">{{ xs.bjmc }}</text>
</view>
</view>
<!-- 第二行状态和联系家长 -->
<view class="bottom-row">
<view <view
class="status-tag" class="status-tag"
:class="getStatusClass(xs.xsZt || xs.xszt)" :class="getStatusClass(xs.xsZt || xs.xszt)"
@click="openStatusPicker(xs)" @click="openStatusPicker(xs)"
> >
{{ getStatusText(xs.xsZt || xs.xszt) }} {{ getStatusText(xs.xsZt || xs.xszt) }}
<u-icon name="arrow-down" size="10"></u-icon> <u-icon name="arrow-down" size="12"></u-icon>
</view> </view>
</view> <view class="contact-parent" @click="contactParent(xs)">
<text class="font-12 cor-666">{{ xs.bjmc }}</text>
<view class="contact-parent mt-8 flex-center" @click="contactParent(xs)">
<text class="font-12 cor-primary">联系家长</text> <text class="font-12 cor-primary">联系家长</text>
<u-icon <u-icon
name="phone" name="phone"
@ -75,6 +80,7 @@
</view> </view>
</view> </view>
<view class="mx-15">
<DmPsComponent <DmPsComponent
v-model="mediaData" v-model="mediaData"
:photo-title="'现场拍照'" :photo-title="'现场拍照'"
@ -84,6 +90,7 @@
:max-video-duration="60" :max-video-duration="60"
ref="dmPsRef" ref="dmPsRef"
/> />
</view>
<!-- 状态选择弹窗 --> <!-- 状态选择弹窗 -->
<u-picker <u-picker
@ -131,7 +138,7 @@ const { findByPid } = useDicStore();
import dayjs from "dayjs"; import dayjs from "dayjs";
const { getJs } = useUserStore(); const { getJs } = useUserStore();
const { getData, setData } = useDataStore(); const { getData, setData, setXs } = useDataStore();
const js = computed(() => getJs) const js = computed(() => getJs)
const xkkc = computed(() => getData) const xkkc = computed(() => getData)
@ -355,7 +362,7 @@ const contactParent = (dmXs: any) => {
}; };
// store使 // store使
setData(completeStudent); setXs(completeStudent);
// //
uni.navigateTo({ uni.navigateTo({
@ -538,13 +545,25 @@ onMounted(async () => {
.student-grid { .student-grid {
display: grid; display: grid;
grid-template-columns: repeat(2, 1fr); grid-template-columns: repeat(2, 1fr);
gap: 20rpx; gap: 10px;
} }
.student-item { .student-item {
position: relative; position: relative;
} }
.student-content {
display: flex;
flex-direction: column;
gap: 12px;
}
.top-row {
display: flex;
align-items: center;
gap: 12px;
}
.avatar-container { .avatar-container {
width: 46px; width: 46px;
height: 46px; height: 46px;
@ -555,6 +574,7 @@ onMounted(async () => {
align-items: center; align-items: center;
justify-content: center; justify-content: center;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05); box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
flex-shrink: 0;
} }
.student-avatar { .student-avatar {
@ -564,13 +584,51 @@ onMounted(async () => {
background-color: #f5f5f5; background-color: #f5f5f5;
} }
.student-info {
flex: 1;
display: flex;
flex-direction: column;
gap: 4px;
}
.student-name {
font-size: 14px;
font-weight: bold;
color: #333;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.student-class {
font-size: 12px;
color: #666;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.bottom-row {
display: flex;
justify-content: space-between;
align-items: center;
gap: 5px;
}
.status-tag { .status-tag {
font-size: 10px; font-size: 12px;
padding: 1px 5px; padding: 3px 8px;
border-radius: 4px; border-radius: 4px;
display: flex; display: flex;
align-items: center; align-items: center;
cursor: pointer; cursor: pointer;
background-color: rgba(64, 128, 255, 0.1);
border: 1px solid transparent;
transition: all 0.2s ease;
&:active {
transform: scale(0.95);
}
} }
.status-normal { .status-normal {
@ -589,7 +647,7 @@ onMounted(async () => {
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
max-width: 60px; max-width: 100%;
} }
.contact-parent { .contact-parent {
@ -597,11 +655,14 @@ onMounted(async () => {
border-radius: 4px; border-radius: 4px;
border: 1px solid #4080ff; border: 1px solid #4080ff;
display: inline-flex; display: inline-flex;
align-items: center;
cursor: pointer; cursor: pointer;
transition: all 0.2s ease; transition: all 0.2s ease;
white-space: nowrap;
background-color: rgba(64, 128, 255, 0.05);
&:active { &:active {
background-color: rgba(64, 128, 255, 0.1); background-color: rgba(64, 128, 255, 0.15);
transform: scale(0.95); transform: scale(0.95);
} }
} }

View File

@ -240,6 +240,7 @@ const goDm = (xkkc: any) => {
} else { } else {
msg = "上课时间未到,无法点名"; msg = "上课时间未到,无法点名";
} }
dmFlag = true;
if (dmFlag) { if (dmFlag) {
setData(xkkc); setData(xkkc);
uni.navigateTo({ uni.navigateTo({
@ -259,57 +260,10 @@ const goDm = (xkkc: any) => {
// //
const goRecord = (xkkc: any) => { const goRecord = (xkkc: any) => {
const now = dayjs();
let wDay = now.day();
if (wDay === 0) {
wDay = 7;
}
let mDay = now.date();
const strDate = now.format('YYYY-MM-DD') + ' ';
let recordFlag = false;
let msg = "";
//
switch (xkkc.skzqlx) {
case '每天':
recordFlag = true;
break;
case '每周':
const daysOfWeek = xkkc.skzq.split(',').map(Number);
recordFlag = daysOfWeek.includes(wDay);
// wdNameListdaysOfWeek
xkkc.skzqmc = daysOfWeek.map((day: number) => wdNameList[day - 1]).join(',');
break;
case '每月':
const daysOfMonth = xkkc.skzq.split(',').map(Number);
recordFlag = daysOfMonth.includes(mDay);
//
xkkc.skzqmc = daysOfMonth.map((day: number) => day + "号").join(',');
break;
}
//
if (recordFlag) {
// xkkc.skkstimedmBeforeMinute
const startTime = dayjs(strDate + xkkc.skkstime).subtract(dmBeforeMinute.value, 'minute').format('YYYY-MM-DD HH:mm:ss');
const endTime = dayjs(strDate + xkkc.skjstime, 'YYYY-MM-DD HH:mm:ss');
recordFlag = now.isBefore(endTime) && now.isAfter(startTime)
} else {
msg = "上课时间未到,无法查看点名记录";
}
if (recordFlag) {
setData(xkkc); setData(xkkc);
uni.navigateTo({ uni.navigateTo({
url: `/pages/view/routine/xk/dmXkkcRecord`, url: `/pages/view/routine/xk/dmList`,
}); });
} else {
if (msg === "") {
msg = "上课时间未到,无法查看点名记录";
}
uni.showToast({
title: msg,
icon: 'none',
duration: 2000
});
}
}; };
// //

View File

@ -1,7 +1,9 @@
<template> <template>
<BasicLayout> <!-- 使用 BasicListLayout 包裹记录列表 -->
<BasicListLayout @register="register" :fixed="false" class="flex-1">
<template #top>
<!-- 课程信息卡片 --> <!-- 课程信息卡片 -->
<view class="course-card mx-15 my-15 bg-white white-bg-color r-md p-15"> <view class="course-card">
<view class="flex-row items-center mb-15"> <view class="flex-row items-center mb-15">
<view class="course-icon flex-center mr-10"> <view class="course-icon flex-center mr-10">
<u-icon name="calendar" color="#4080ff" size="20"></u-icon> <u-icon name="calendar" color="#4080ff" size="20"></u-icon>
@ -10,80 +12,96 @@
<text class="font-14 cor-999 ml-10">{{ todayInfo.date }} ({{ todayInfo.weekName }})</text> <text class="font-14 cor-999 ml-10">{{ todayInfo.date }} ({{ todayInfo.weekName }})</text>
</view> </view>
</view> </view>
<!-- 搜索筛选区域 -->
<!-- 点名记录统计 --> <view class="search-section">
<view class="record-stats mx-15 mb-15"> <view class="search-row">
<view class="section-title">点名记录</view> <view class="search-item">
<view class="records-grid"> <text class="label">时间范围</text>
<view <uni-datetime-picker
v-for="(record, index) in dmRecords" type="daterange"
:key="record.id" :value="[startTime, endTime]"
class="record-card" @change="onTimeRangeChange"
@click="viewRecordDetail(record)" 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="!hasSearched" class="initial-tip">
<view class="tip-icon">🔍</view>
<text class="tip-text">请选择时间范围开始搜索</text>
</view>
</template>
<template #default="{ data }">
<view class="record-card" @click="viewRecordDetail(data)">
<view class="record-header"> <view class="record-header">
<view class="record-time"> <view class="record-time">
<u-icon name="clock" color="#666" size="14"></u-icon> <u-icon name="clock" color="#666" size="14"></u-icon>
<text class="time-text">{{ formatDateTime(record.dmTime) }}</text> <text class="time-text">{{ formatDateTime(data.dmTime) }}</text>
</view>
<view class="record-status" :class="getRecordStatusClass(record.status)">
{{ getRecordStatusText(record.status) }}
</view> </view>
</view> </view>
<view class="record-stats"> <view class="record-stats">
<view class="stat-item"> <view class="stat-item">
<text class="stat-number">{{ record.zrs || 0 }}</text> <text class="stat-number">{{ data.zrs || 0 }}</text>
<text class="stat-label">总人数</text> <text class="stat-label">总人数</text>
</view> </view>
<view class="stat-item present"> <view class="stat-item present">
<text class="stat-number">{{ record.sdRs || 0 }}</text> <text class="stat-number">{{ data.sdRs || 0 }}</text>
<text class="stat-label">实到</text> <text class="stat-label">实到</text>
</view> </view>
<view class="stat-item leave"> <view class="stat-item leave">
<text class="stat-number">{{ record.qjRs || 0 }}</text> <text class="stat-number">{{ data.qjRs || 0 }}</text>
<text class="stat-label">请假</text> <text class="stat-label">请假</text>
</view> </view>
<view class="stat-item absent"> <view class="stat-item absent">
<text class="stat-number">{{ record.qqRs || 0 }}</text> <text class="stat-number">{{ data.qqRs || 0 }}</text>
<text class="stat-label">缺勤</text> <text class="stat-label">缺勤</text>
</view> </view>
</view> </view>
<view class="record-footer"> <view class="record-footer">
<text class="teacher-name">教师{{ record.jsMc || '未知' }}</text> <text class="teacher-name">教师{{ data.createdUserName || '未知' }}</text>
<view class="view-detail"> <view class="view-detail">
<text class="detail-text">查看详情</text> <text class="detail-text">查看详情</text>
<u-icon name="arrow-right" color="#4080ff" size="12"></u-icon> <u-icon name="arrow-right" color="#4080ff" size="12"></u-icon>
</view> </view>
</view> </view>
</view> </view>
</view> </template>
<!-- 空状态 -->
<view v-if="dmRecords.length === 0" class="empty-state">
<u-icon name="info-circle" color="#ccc" size="48"></u-icon>
<text class="empty-text">暂无点名记录</text>
</view>
</view>
<template #bottom>
<!-- 返回按钮 --> <!-- 返回按钮 -->
<view class="bottom-actions mx-15 mb-30"> <view class="bottom-actions mx-15 mb-15">
<button class="back-btn" @click="goBack">返回</button> <button class="back-btn" @click="goBack">返回</button>
</view> </view>
</BasicLayout> </template>
</BasicListLayout>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, onMounted } from "vue"; import { ref, computed, onMounted } from "vue";
import { useUserStore } from "@/store/modules/user"; import { useUserStore } from "@/store/modules/user";
import { useDataStore } from "@/store/modules/data"; import { useDataStore } from "@/store/modules/data";
import BasicLayout from "@/components/BasicLayout/Layout.vue"; import { useLayout } from "@/components/BasicListLayout/hooks/useLayout";
import { getXkDmPageApi } from "@/api/base/xkApi"; import { getXkDmPageApi } from "@/api/base/xkApi";
import dayjs from "dayjs"; import dayjs from "dayjs";
const { getJs } = useUserStore(); const { getJs } = useUserStore();
const { getData } = useDataStore(); const { getData, setData } = useDataStore();
const js = computed(() => getJs); const js = computed(() => getJs);
const xkkc = computed(() => getData); const xkkc = computed(() => getData);
@ -100,8 +118,110 @@ const todayInfo = ref({
weekName: wdNameList[wDay - 1], weekName: wdNameList[wDay - 1],
}); });
// //
const dmRecords = ref<any[]>([]); const loading = ref(false);
const startTime = ref("");
const endTime = ref("");
const dmRecords = ref<any>([]);
const totalCount = ref<any>(0);
const hasSearched = ref(false);
// 使 BasicListLayout
const [register, { reload, setParam }] = useLayout({
api: async (params: any) => {
try {
const res = await getXkDmPageApi({
...params,
xkkcId: xkkc.value.id,
jsId: js.value.id,
sidx: 'dmTime',
sord: 'desc'
});
console.log("API返回数据:", res, xkkc.value);
if (res) {
dmRecords.value = res?.rows || [];
totalCount.value = res?.total || 0;
return res;
} else {
dmRecords.value = [];
totalCount.value = 0;
return { rows: [], total: 0, page: 1, pageSize: 10 };
}
} catch (error) {
console.error("获取数据失败:", error);
dmRecords.value = [];
totalCount.value = 0;
return { rows: [], total: 0, page: 1, pageSize: 10 };
}
},
componentProps: {
auto: false,
},
});
//
const onTimeRangeChange = (e: any) => {
if (e && Array.isArray(e)) {
const [start, end] = e;
startTime.value = start;
endTime.value = end;
} else if (e && typeof e === "string") {
startTime.value = e;
endTime.value = e;
}
};
const getTimeRangeText = () => {
if (!startTime.value || !endTime.value) return "选择时间范围";
if (startTime.value === endTime.value) {
return formatDate(startTime.value);
}
return `${formatDate(startTime.value)} - ${formatDate(endTime.value)}`;
};
const searchRecords = async () => {
if (!startTime.value || !endTime.value) {
uni.showToast({
title: "请选择时间范围",
icon: "none",
});
return;
}
//
if (startTime.value > endTime.value) {
uni.showToast({
title: "开始时间不能大于结束时间",
icon: "none",
});
return;
}
hasSearched.value = true;
console.log("搜索参数:", {
startTime: startTime.value + " 00:00:00",
endTime: endTime.value + " 23:59:59",
pageNo: 1,
});
//
setParam({
startTime: startTime.value + " 00:00:00",
endTime: endTime.value + " 23:59:59",
pageNo: 1,
});
reload();
};
const resetSearch = () => {
startTime.value = "";
endTime.value = "";
dmRecords.value = [];
totalCount.value = 0;
hasSearched.value = false;
};
// //
const formatDateTime = (dateTime: string | Date) => { const formatDateTime = (dateTime: string | Date) => {
@ -109,6 +229,15 @@ const formatDateTime = (dateTime: string | Date) => {
return dayjs(dateTime).format('MM-DD HH:mm'); return dayjs(dateTime).format('MM-DD HH:mm');
}; };
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")}`;
};
// //
const getRecordStatusClass = (status: string) => { const getRecordStatusClass = (status: string) => {
switch (status) { switch (status) {
@ -132,7 +261,6 @@ const getRecordStatusText = (status: string) => {
// //
const viewRecordDetail = (record: any) => { const viewRecordDetail = (record: any) => {
// store // store
const { setData } = useDataStore();
setData({ setData({
...xkkc.value, ...xkkc.value,
dmRecord: record dmRecord: record
@ -149,50 +277,33 @@ const goBack = () => {
uni.navigateBack(); uni.navigateBack();
}; };
// //
const loadStudentList = async () => {
try {
uni.showLoading({ title: '加载中...' });
const res = await getXkDmPageApi({
xkkcId: xkkc.value.id,
jsId: js.value.id,
pageNum: 1,
pageSize: 50,
sidx: 'dmTime',
sord: 'desc'
});
if (res && res.resultCode === 1) {
dmRecords.value = res.result?.rows || [];
} else {
dmRecords.value = [];
uni.showToast({
title: (res as any)?.resultMessage || '获取点名记录失败',
icon: 'none'
});
}
} catch (error) {
console.error('加载点名记录失败:', error);
dmRecords.value = [];
uni.showToast({
title: '加载点名记录失败',
icon: 'none'
});
} finally {
uni.hideLoading();
}
};
onMounted(() => { onMounted(() => {
loadStudentList(); //
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,
});
//
}); });
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.course-card { .course-card {
border-radius: 8px; background-color: #fff;
border-radius: 16rpx;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05); box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
margin: 30rpx;
padding: 30rpx;
} }
.course-icon { .course-icon {
@ -202,26 +313,89 @@ onMounted(() => {
background-color: rgba(64, 128, 255, 0.1); background-color: rgba(64, 128, 255, 0.1);
} }
.record-stats { .search-section {
.section-title { background-color: #fff;
font-size: 16px; border-radius: 16rpx;
font-weight: bold; padding: 30rpx;
color: #333; margin-left: 30rpx;
margin-bottom: 15px; margin-right: 30rpx;
padding-left: 10px; margin-bottom: 20rpx;
border-left: 4px solid #4080ff; box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
} }
.records-grid { .search-row {
display: flex; display: flex;
flex-direction: column; gap: 20rpx;
gap: 15px; margin-bottom: 20rpx;
&: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 {
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;
margin-bottom: 20rpx;
.section-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
display: flex;
justify-content: space-between;
align-items: center;
}
} }
.record-card { .record-card {
background: white; background: white;
border-radius: 12px; border-radius: 12px;
padding: 20px; padding: 20px;
margin-bottom: 30rpx;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
transition: all 0.3s ease; transition: all 0.3s ease;
cursor: pointer; cursor: pointer;
@ -340,15 +514,43 @@ onMounted(() => {
.empty-state { .empty-state {
text-align: center; text-align: center;
padding: 60px 20px; padding: 100rpx 0;
color: #ccc; }
.empty-icon {
font-size: 80rpx;
margin-bottom: 20rpx;
}
.empty-text { .empty-text {
display: block; display: block;
margin-top: 15px; font-size: 32rpx;
font-size: 16px; 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;
} }
.bottom-actions { .bottom-actions {
@ -382,3 +584,4 @@ onMounted(() => {
.font-bold { font-weight: bold; } .font-bold { font-weight: bold; }
.cor-999 { color: #999; } .cor-999 { color: #999; }
</style> </style>

View File

@ -11,27 +11,43 @@
</view> </view>
<view class="dm-time"> <view class="dm-time">
<u-icon name="clock" color="#666" size="14"></u-icon> <u-icon name="clock" color="#666" size="14"></u-icon>
<text class="time-text">{{ formatDateTime(dmRecord.dmTime) }}</text> <text class="time-text">{{ formatDateTime(safeDmRecord.dmTime) }}</text>
</view> </view>
</view> </view>
<!-- 考勤统计 --> <!-- 考勤统计 -->
<view class="attendance-stats"> <view class="attendance-stats">
<view class="stats-grid"> <view class="stats-grid">
<view class="stat-item total"> <view
<view class="stat-number">{{ dmRecord.zrs || 0 }}</view> class="stat-item total"
:class="{ active: currentFilter === 'all' }"
@click="setFilter('all')"
>
<view class="stat-number">{{ safeDmRecord.zrs || 0 }}</view>
<view class="stat-label">总人数</view> <view class="stat-label">总人数</view>
</view> </view>
<view class="stat-item present"> <view
<view class="stat-number">{{ dmRecord.sdRs || 0 }}</view> class="stat-item present"
:class="{ active: currentFilter === 'A' }"
@click="setFilter('A')"
>
<view class="stat-number">{{ safeDmRecord.sdRs || 0 }}</view>
<view class="stat-label">实到</view> <view class="stat-label">实到</view>
</view> </view>
<view class="stat-item leave"> <view
<view class="stat-number">{{ dmRecord.qjRs || 0 }}</view> class="stat-item leave"
:class="{ active: currentFilter === 'B' }"
@click="setFilter('B')"
>
<view class="stat-number">{{ safeDmRecord.qjRs || 0 }}</view>
<view class="stat-label">请假</view> <view class="stat-label">请假</view>
</view> </view>
<view class="stat-item absent"> <view
<view class="stat-number">{{ dmRecord.qqRs || 0 }}</view> class="stat-item absent"
:class="{ active: currentFilter === 'C' }"
@click="setFilter('C')"
>
<view class="stat-number">{{ safeDmRecord.qqRs || 0 }}</view>
<view class="stat-label">缺勤</view> <view class="stat-label">缺勤</view>
</view> </view>
</view> </view>
@ -39,97 +55,95 @@
</view> </view>
<!-- 学生列表 --> <!-- 学生列表 -->
<view class="student-section mx-15 mb-15"> <view class="student-list mb-15 white-bg-color">
<view class="section-header"> <view class="student-grid">
<text class="section-title">学生列表</text>
<view class="filter-tabs">
<view <view
v-for="tab in filterTabs" v-for="(xs, index) in filteredStudentList"
:key="tab.value" :key="index"
class="filter-tab" class="student-item bg-white r-md p-12"
:class="{ active: currentFilter === tab.value }"
@click="setFilter(tab.value)"
> >
{{ tab.label }} <view class="student-content">
</view> <!-- 第一行头像和学生信息 -->
</view> <view class="top-row">
</view> <view class="avatar-container">
<view class="student-list">
<view
v-for="(student, index) in filteredStudents"
:key="student.id"
class="student-item"
>
<view class="student-avatar">
<image <image
v-if="student.tx || student.xstx" class="student-avatar"
:src="getImageUrl(student.tx || student.xstx)" :src="getImageUrl(xs.tx || xs.xstx)"
mode="aspectFill" mode="aspectFill"
class="avatar-img" ></image>
/>
<view v-else class="avatar-text">{{ (student.xsXm || student.xsxm)?.charAt(0) || '学' }}</view>
</view> </view>
<view class="student-info"> <view class="student-info">
<view class="student-name">{{ student.xsXm || student.xsxm }}</view> <text class="student-name">{{ xs.xsXm || xs.xsxm }}</text>
<view class="student-class">{{ student.njmc }} {{ student.bjmc }}</view> <text class="student-class">{{ xs.bjmc }}</text>
</view>
</view>
<!-- 第二行状态和联系家长 -->
<view class="bottom-row">
<view class="status-tag" :class="getStatusClass(xs.xsZt || xs.xszt)">
{{ getStatusText(xs.xsZt || xs.xszt) }}
</view>
<view class="contact-parent" @click="contactParent(xs)">
<text class="font-12 cor-primary">联系家长</text>
<u-icon
name="phone"
color="#4080ff"
size="14"
class="ml-2"
></u-icon>
</view>
</view>
</view> </view>
<view class="student-status">
<text :class="getStatusClass(student.xsZt || student.xszt)">
{{ getStatusText(student.xsZt || student.xszt) }}
</text>
</view> </view>
</view> </view>
</view> </view>
<!-- 空状态 --> <!-- 媒体预览 -->
<view v-if="filteredStudents.length === 0" class="empty-state"> <view v-if="safeDmRecord.zp || safeDmRecord.sp" class="media-section mx-15 mb-15">
<u-icon name="info-circle" color="#ccc" size="48"></u-icon> <DmPsPreview
<text class="empty-text">暂无学生数据</text> :zp="safeDmRecord.zp"
</view> :sp="safeDmRecord.sp"
/>
</view> </view>
<template #bottom>
<!-- 返回按钮 --> <!-- 返回按钮 -->
<view class="bottom-actions mx-15 mb-30"> <view class="bottom-actions mx-15 mb-15">
<button class="back-btn" @click="goBack">返回</button> <button class="back-btn" @click="goBack">返回</button>
</view> </view>
</template>
</BasicLayout> </BasicLayout>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, onMounted } from "vue"; import { ref, computed, onMounted } from "vue";
import { useUserStore } from "@/store/modules/user";
import { useDataStore } from "@/store/modules/data";
import BasicLayout from "@/components/BasicLayout/Layout.vue"; import BasicLayout from "@/components/BasicLayout/Layout.vue";
import { getXkDmXsPageApi } from "@/api/base/xkApi"; import DmPsPreview from "@/pages/components/dmPs/preview.vue";
import { useDataStore } from "@/store/modules/data";
import { getDmXsListApi } from "@/api/base/xkApi";
import { BASE_IMAGE_URL } from "@/config"; import { BASE_IMAGE_URL } from "@/config";
import dayjs from "dayjs"; import dayjs from "dayjs";
const { getJs } = useUserStore(); const { getData, setXs } = useDataStore();
const { getData } = useDataStore();
const js = computed(() => getJs);
const xkkc = computed(() => getData); const xkkc = computed(() => getData);
const dmRecord = computed(() => getData?.dmRecord || {}); const dmRecord = computed(() => getData?.dmRecord || {});
// // dmRecord undefined
const studentList = ref<any[]>([]); const safeDmRecord = computed(() => {
const currentFilter = ref<string>('all'); return dmRecord.value || {};
});
// //
const filterTabs = ref([ const allStudentList = ref<any[]>([]); //
{ label: '全部', value: 'all' }, const currentFilter = ref<string>('all'); //
{ label: '正常', value: 'A' },
{ label: '请假', value: 'B' },
{ label: '缺勤', value: 'C' }
]);
// //
const filteredStudents = computed(() => { const filteredStudentList = computed(() => {
if (currentFilter.value === 'all') { if (currentFilter.value === 'all') {
return studentList.value; return allStudentList.value;
} }
return studentList.value.filter(student => return allStudentList.value.filter(student =>
(student.xsZt || student.xszt) === currentFilter.value (student.xsZt || student.xszt) === currentFilter.value
); );
}); });
@ -137,14 +151,24 @@ const filteredStudents = computed(() => {
// //
const formatDateTime = (dateTime: string | Date) => { const formatDateTime = (dateTime: string | Date) => {
if (!dateTime) return ''; if (!dateTime) return '';
try {
return dayjs(dateTime).format('MM-DD HH:mm'); return dayjs(dateTime).format('MM-DD HH:mm');
} catch (error) {
console.error('日期格式化错误:', error);
return '';
}
}; };
// URL // URL
const getImageUrl = (path: string) => { const getImageUrl = (path: string) => {
if (!path) return ''; if (!path) return '';
try {
if (path.startsWith('http')) return path; if (path.startsWith('http')) return path;
return BASE_IMAGE_URL + path; return BASE_IMAGE_URL + path;
} catch (error) {
console.error('图片URL处理错误:', error);
return '';
}
}; };
// //
@ -175,46 +199,65 @@ const setFilter = (filter: string) => {
currentFilter.value = filter; currentFilter.value = filter;
}; };
//
const contactParent = (dmXs: any) => {
//
const completeStudent = {
...dmXs,
//
id: dmXs.xsId || dmXs.id,
xsxm: dmXs.xsXm || dmXs.xsxm || dmXs.xm,
xstx: dmXs.tx || dmXs.xstx || dmXs.avatar,
xb: dmXs.xb || dmXs.gender,
sfzh: dmXs.sfzh,
cstime: dmXs.cstime,
njmc: dmXs.njmcName || dmXs.njmc,
bjmc: dmXs.bjmc,
// njIdbjId
njId: dmXs.njId,
bjId: dmXs.bjId
};
// store使
setXs(completeStudent);
//
// @ts-ignore
uni.navigateTo({
url: "/pages/view/homeSchool/parentAddressBook/detail"
});
};
// //
const goBack = () => { const goBack = () => {
// @ts-ignore
uni.navigateBack(); uni.navigateBack();
}; };
// //
const loadStudentList = async () => { const loadStudentList = async () => {
try { try {
uni.showLoading({ title: '加载中...' }); const res = await getDmXsListApi({
dmId: safeDmRecord.value.id
const res = await getXkDmXsPageApi({
dmId: dmRecord.value.id,
pageNum: 1,
pageSize: 100,
sidx: 'xsXm',
sord: 'asc'
});
if (res && res.resultCode === 1) {
studentList.value = res.result?.rows || [];
} else {
studentList.value = [];
uni.showToast({
title: (res as any)?.resultMessage || '获取学生列表失败',
icon: 'none'
}); });
if (res) {
allStudentList.value = res?.result || [];
} }
} catch (error) { } catch (error) {
console.error('加载学生列表失败:', error); console.error("获取数据失败:", error);
studentList.value = []; allStudentList.value = [];
uni.showToast({
title: '加载学生列表失败',
icon: 'none'
});
} finally {
uni.hideLoading();
} }
}; };
//
onMounted(() => { onMounted(() => {
console.log("dmXsList 组件初始化:", {
xkkc: xkkc.value,
dmRecord: safeDmRecord.value,
hasData: !!getData
});
//
loadStudentList(); loadStudentList();
}); });
</script> </script>
@ -250,34 +293,80 @@ onMounted(() => {
.stats-grid { .stats-grid {
display: grid; display: grid;
grid-template-columns: repeat(4, 1fr); grid-template-columns: repeat(4, 1fr);
gap: 15px; gap: 20rpx;
} }
.stat-item { .stat-item {
background: white; background: white;
border-radius: 12px; border-radius: 12px;
padding: 20px 15px; padding: 20rpx 0;
text-align: center; text-align: center;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
cursor: pointer;
transition: all 0.3s ease;
position: relative;
&:hover {
transform: translateY(-2px);
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
}
&.active {
transform: translateY(-2px);
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.15);
&::after {
content: '';
position: absolute;
bottom: -2px;
left: 50%;
transform: translateX(-50%);
width: 50rpx;
height: 20rpx;
background: #4080ff;
border-top-left-radius: 50rpx;
border-top-right-radius: 50rpx;
}
}
&.total { &.total {
border: 2px solid #666; border: 2px solid #666;
.stat-number { color: #333; } .stat-number { color: #333; }
&.active {
border-color: #4080ff;
background: rgba(64, 128, 255, 0.05);
}
} }
&.present { &.present {
border: 2px solid #2879ff; border: 2px solid #52c41a;
.stat-number { color: #2879ff; } .stat-number { color: #52c41a; }
&.active {
border-color: #4080ff;
background: rgba(64, 128, 255, 0.05);
}
} }
&.leave { &.leave {
border: 2px solid #ff9900; border: 2px solid #ff9900;
.stat-number { color: #ff9900; } .stat-number { color: #ff9900; }
&.active {
border-color: #4080ff;
background: rgba(64, 128, 255, 0.05);
}
} }
&.absent { &.absent {
border: 2px solid #ff4d4f; border: 2px solid #ff4d4f;
.stat-number { color: #ff4d4f; } .stat-number { color: #ff4d4f; }
&.active {
border-color: #4080ff;
background: rgba(64, 128, 255, 0.05);
}
} }
.stat-number { .stat-number {
@ -293,135 +382,127 @@ onMounted(() => {
} }
} }
.student-section { .media-section {
.section-header { background: white;
display: flex; border-radius: 8px;
justify-content: space-between; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
align-items: center; overflow: hidden;
margin-bottom: 15px;
.section-title {
font-size: 16px;
font-weight: bold;
color: #333;
padding-left: 10px;
border-left: 4px solid #4080ff;
}
.filter-tabs {
display: flex;
gap: 10px;
.filter-tab {
padding: 6px 12px;
border-radius: 16px;
font-size: 12px;
background: #f5f5f5;
color: #666;
cursor: pointer;
transition: all 0.3s ease;
&.active {
background: #4080ff;
color: white;
}
}
}
} }
.student-list { .student-list {
display: flex; padding: 0 15px;
flex-direction: column; }
.student-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 10px; gap: 10px;
}
.student-item { .student-item {
position: relative;
}
.student-content {
display: flex;
flex-direction: column;
gap: 12px;
}
.top-row {
display: flex; display: flex;
align-items: center; align-items: center;
padding: 15px; gap: 12px;
background: white; }
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); .avatar-container {
width: 46px;
height: 46px;
border-radius: 50%;
padding: 3px;
background-color: #fff;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
flex-shrink: 0;
}
.student-avatar { .student-avatar {
width: 40px; width: 40px;
height: 40px; height: 40px;
border-radius: 50%; border-radius: 50%;
overflow: hidden; background-color: #f5f5f5;
margin-right: 15px;
.avatar-img {
width: 100%;
height: 100%;
object-fit: cover;
}
.avatar-text {
width: 100%;
height: 100%;
background: #4080ff;
color: white;
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
font-weight: bold;
}
} }
.student-info { .student-info {
flex: 1; flex: 1;
display: flex;
flex-direction: column;
gap: 4px;
}
.student-name { .student-name {
font-size: 16px; font-size: 14px;
font-weight: 500; font-weight: bold;
color: #333; color: #333;
margin-bottom: 4px; white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
} }
.student-class { .student-class {
font-size: 14px; font-size: 12px;
color: #666; color: #666;
} white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
} }
.student-status { .bottom-row {
.status-normal { display: flex;
color: #2879ff; justify-content: space-between;
background: rgba(40, 121, 255, 0.1); align-items: center;
padding: 4px 8px; gap: 5px;
border-radius: 4px; }
.status-tag {
font-size: 12px; font-size: 12px;
padding: 3px 12px;
border-radius: 4px;
display: flex;
align-items: center;
background-color: rgba(64, 128, 255, 0.1);
border: 1px solid transparent;
transition: all 0.2s ease;
}
.status-normal {
color: #4080ff;
} }
.status-leave { .status-leave {
color: #ff9900; color: #ff9900;
background: rgba(255, 153, 0, 0.1);
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
} }
.status-absent { .status-absent {
color: #ff4d4f; color: #ff4d4f;
background: rgba(255, 77, 79, 0.1); }
padding: 4px 8px;
.contact-parent {
padding: 3px 8px;
border-radius: 4px; border-radius: 4px;
font-size: 12px; border: 1px solid #4080ff;
} display: inline-flex;
} align-items: center;
} cursor: pointer;
} transition: all 0.2s ease;
white-space: nowrap;
background-color: rgba(64, 128, 255, 0.05);
.empty-state { &:active {
text-align: center; background-color: rgba(64, 128, 255, 0.15);
padding: 60px 20px; transform: scale(0.95);
color: #ccc;
.empty-text {
display: block;
margin-top: 15px;
font-size: 16px;
}
} }
} }
@ -449,10 +530,18 @@ onMounted(() => {
.white-bg-color { background-color: white; } .white-bg-color { background-color: white; }
.r-md { border-radius: 8px; } .r-md { border-radius: 8px; }
.p-15 { padding: 15px; } .p-15 { padding: 15px; }
.p-12 { padding: 12px; }
.flex-row { display: flex; flex-direction: row; } .flex-row { display: flex; flex-direction: row; }
.flex-center { display: flex; align-items: center; justify-content: center; }
.items-center { align-items: center; } .items-center { align-items: center; }
.font-16 { font-size: 16px; } .font-16 { font-size: 16px; }
.font-14 { font-size: 14px; } .font-14 { font-size: 14px; }
.font-12 { font-size: 12px; }
.font-bold { font-weight: bold; } .font-bold { font-weight: bold; }
.cor-999 { color: #999; } .cor-999 { color: #999; }
.cor-primary { color: #4080ff; }
.cor-warning { color: #ff9900; }
.cor-danger { color: #ff4d4f; }
.cor-666 { color: #666; }
.ml-2 { margin-left: 2px; }
</style> </style>

View File

@ -8,6 +8,7 @@ export const useDataStore = defineStore({
xxts: {}, // 添加xxts字段 xxts: {}, // 添加xxts字段
global: {}, global: {},
file: {}, file: {},
xs: {}, // 学生专用
}), }),
getters: { getters: {
getData(): any { getData(): any {
@ -25,6 +26,9 @@ export const useDataStore = defineStore({
getFile(): any { getFile(): any {
return this.file; return this.file;
}, },
getXs(): any {
return this.xs;
}
}, },
actions: { actions: {
setData(data: any) { setData(data: any) {
@ -42,6 +46,9 @@ export const useDataStore = defineStore({
setFile(data: any) { setFile(data: any) {
this.file = data; this.file = data;
}, },
setXs(data: any) {
this.xs = data;
}
}, },
persist: { persist: {
enabled: true, enabled: true,