重新封装模块
This commit is contained in:
parent
ee2e2fc16c
commit
c12aa5c773
@ -62,6 +62,13 @@ export const xsXkListApi = async (params: any) => {
|
||||
return await get("/mobile/jz/xsxk/list", params);
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询学生选课列表
|
||||
*/
|
||||
export const xsXkkcListApi = async (params: any) => {
|
||||
return await get("/mobile/jz/xkkc/list", params);
|
||||
};
|
||||
|
||||
/**
|
||||
* 查询学生考试场次
|
||||
*/
|
||||
|
||||
208
src/pages/base/components/XkCountdown/index.vue
Normal file
208
src/pages/base/components/XkCountdown/index.vue
Normal file
@ -0,0 +1,208 @@
|
||||
<template>
|
||||
<view class="countdown-section">
|
||||
<template v-if="!isEnrollmentEnded">
|
||||
<view class="countdown-title">{{ countdownTitle }}</view>
|
||||
<view class="countdown-timer">
|
||||
<view class="time-block">
|
||||
<text class="time-value">{{ countdownTime.hours }}</text>
|
||||
<text class="time-unit">时</text>
|
||||
</view>
|
||||
<text class="time-separator">:</text>
|
||||
<view class="time-block">
|
||||
<text class="time-value">{{ countdownTime.minutes }}</text>
|
||||
<text class="time-unit">分</text>
|
||||
</view>
|
||||
<text class="time-separator">:</text>
|
||||
<view class="time-block">
|
||||
<text class="time-value">{{ countdownTime.seconds }}</text>
|
||||
<text class="time-unit">秒</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<template v-else>
|
||||
<view class="enrollment-ended">
|
||||
<u-icon name="info-circle" color="#FF9900" size="20"></u-icon>
|
||||
<text>选课已经结束</text>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import dayjs from "dayjs";
|
||||
|
||||
// 接收外部传入属性
|
||||
const props = defineProps<{
|
||||
xk: any // 选课信息
|
||||
}>();
|
||||
|
||||
// 定义一个上级传入的emit响应事件用于接收数据变更
|
||||
const emit = defineEmits(['over'])
|
||||
|
||||
// 倒计时相关
|
||||
const countdownTime = reactive({
|
||||
hours: "00",
|
||||
minutes: "00",
|
||||
seconds: "00",
|
||||
});
|
||||
|
||||
const kcStatus = ref(false);
|
||||
|
||||
// 倒计时标题文本
|
||||
const countdownTitle = computed(() => {
|
||||
return kcStatus.value ? "距离选课结束还剩" : "距离选课开始还剩";
|
||||
});
|
||||
|
||||
// 当前选中的学生
|
||||
let countdownTimer: number | null = null;
|
||||
const remainTime = ref("00:00:00");
|
||||
|
||||
// 添加选课是否已结束的标记
|
||||
const isEnrollmentEnded = ref(false);
|
||||
|
||||
// 启动倒计时
|
||||
const startCountdown = (endTimeStamp: number) => {
|
||||
if (countdownTimer) {
|
||||
clearInterval(countdownTimer);
|
||||
}
|
||||
|
||||
const updateCountdown = () => {
|
||||
const now = new Date().getTime();
|
||||
const timeLeft = endTimeStamp - now;
|
||||
|
||||
if (timeLeft <= 0) {
|
||||
// 倒计时结束
|
||||
countdownTime.hours = "00";
|
||||
countdownTime.minutes = "00";
|
||||
countdownTime.seconds = "00";
|
||||
remainTime.value = "00:00:00";
|
||||
|
||||
if (countdownTimer) {
|
||||
clearInterval(countdownTimer);
|
||||
countdownTimer = null;
|
||||
}
|
||||
|
||||
// 判断当前倒计时是选课开始还是选课结束
|
||||
if (!kcStatus.value && props.xk) {
|
||||
// 如果是选课开始倒计时结束,则切换到选课结束倒计时
|
||||
kcStatus.value = true; // 更新状态为选课已开始
|
||||
uni.showToast({
|
||||
title: "选课已开始",
|
||||
icon: "none",
|
||||
});
|
||||
|
||||
// 开始选课结束倒计时
|
||||
const xkjstime = dayjs(props.xk.xkjstime).valueOf();
|
||||
startCountdown(xkjstime);
|
||||
} else {
|
||||
// 如果是选课结束倒计时结束
|
||||
isEnrollmentEnded.value = true; // 标记选课已结束
|
||||
emit("over");
|
||||
uni.showToast({
|
||||
title: "选课已结束",
|
||||
icon: "none",
|
||||
});
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// 计算小时、分钟和秒
|
||||
const hours = Math.floor(timeLeft / (1000 * 60 * 60));
|
||||
const minutes = Math.floor((timeLeft % (1000 * 60 * 60)) / (1000 * 60));
|
||||
const seconds = Math.floor((timeLeft % (1000 * 60)) / 1000);
|
||||
|
||||
// 格式化显示
|
||||
countdownTime.hours = hours < 10 ? `0${hours}` : `${hours}`;
|
||||
countdownTime.minutes = minutes < 10 ? `0${minutes}` : `${minutes}`;
|
||||
countdownTime.seconds = seconds < 10 ? `0${seconds}` : `${seconds}`;
|
||||
|
||||
// 更新remainTime(兼容原有逻辑)
|
||||
remainTime.value = `${countdownTime.hours}:${countdownTime.minutes}:${countdownTime.seconds}`;
|
||||
};
|
||||
|
||||
// 立即执行一次
|
||||
updateCountdown();
|
||||
|
||||
// 每秒更新一次
|
||||
countdownTimer = setInterval(updateCountdown, 1000) as unknown as number;
|
||||
};
|
||||
|
||||
const changeXk = (xk: any) => {
|
||||
//获取当前时间
|
||||
const now = dayjs().valueOf();
|
||||
//获取选课开始时间
|
||||
const xkkstime = dayjs(xk.xkkstime).valueOf();
|
||||
//获取选课结束时间
|
||||
const xkjstime = dayjs(xk.xkjstime).valueOf();
|
||||
|
||||
// 检查是否已经超过选课结束时间
|
||||
if (now > xkjstime) {
|
||||
kcStatus.value = true;
|
||||
isEnrollmentEnded.value = true;
|
||||
countdownTime.hours = "00";
|
||||
countdownTime.minutes = "00";
|
||||
countdownTime.seconds = "00";
|
||||
}
|
||||
//判断选课是否开始
|
||||
else if (now < xkkstime) {
|
||||
kcStatus.value = false;
|
||||
startCountdown(xkkstime);
|
||||
} else {
|
||||
kcStatus.value = true;
|
||||
startCountdown(xkjstime);
|
||||
}
|
||||
}
|
||||
|
||||
// 页面卸载前清除定时器
|
||||
onBeforeUnmount(() => {
|
||||
if (countdownTimer) {
|
||||
clearInterval(countdownTimer);
|
||||
countdownTimer = null;
|
||||
}
|
||||
});
|
||||
|
||||
watch(() => props.xk, (newVal) => {
|
||||
changeXk(newVal);
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.countdown-section {
|
||||
.countdown-title {
|
||||
font-size: 14px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.countdown-timer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.time-block {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 6px;
|
||||
min-width: 40px;
|
||||
padding: 5px 8px;
|
||||
text-align: center;
|
||||
|
||||
.time-value {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.time-unit {
|
||||
font-size: 12px;
|
||||
opacity: 0.9;
|
||||
}
|
||||
}
|
||||
|
||||
.time-separator {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
margin: 0 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
196
src/pages/base/components/XkPicker/index.vue
Normal file
196
src/pages/base/components/XkPicker/index.vue
Normal file
@ -0,0 +1,196 @@
|
||||
<template>
|
||||
<view>
|
||||
<view class="title-section" @click="showPicker">
|
||||
<view class="title">
|
||||
<text v-if="curXk && curXk.xkmc">{{ curXk.xkmc }}</text>
|
||||
<text v-else>选课信息</text>
|
||||
</view>
|
||||
<view class="switch-btn" v-if="xkList.length > 1">切换</view>
|
||||
</view>
|
||||
<!-- 俱乐部选择弹窗 -->
|
||||
<u-popup :show="showFlag" @close="showFlag = false" mode="bottom" round="10">
|
||||
<view class="xk-picker">
|
||||
<view class="xk-header">
|
||||
<text class="xk-title">选择俱乐部</text>
|
||||
<u-icon name="close" size="20" @click="showFlag = false"></u-icon>
|
||||
</view>
|
||||
<view class="xk-list">
|
||||
<view v-for="(xk, index) in xkList" :key="index" class="xk-item" :class="{
|
||||
'xk-item-active': curXk.id === xk.id
|
||||
}" @click="switchXk(xk)">
|
||||
<view class="xk-info">
|
||||
<text class="xk-name">{{ xk.xkmc }}</text>
|
||||
</view>
|
||||
<u-icon v-if="curXk.id === xk.id" name="checkmark" color="#409EFF" size="20"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from "vue";
|
||||
import { xsXkkcListApi } from "@/api/base/server";
|
||||
import { useUserStore } from "@/store/modules/user";
|
||||
const { getCurXs } = useUserStore();
|
||||
|
||||
// 接收外部传入属性
|
||||
const props = defineProps<{
|
||||
xsId: string,
|
||||
njId: string,
|
||||
xklxId: string; // 选课类型Id(962488654:兴趣课 / 816059832:俱乐部)
|
||||
}>();
|
||||
|
||||
// 定义一个上级传入的emit响应事件用于接收数据变更
|
||||
const emit = defineEmits(['change'])
|
||||
|
||||
const curXs = computed(() => getCurXs);
|
||||
|
||||
// 学生列表数据
|
||||
const xkList = ref<any>([]);
|
||||
// 当前学生数据
|
||||
const curXk = ref<any>({});
|
||||
// 控制选择器显示状态
|
||||
const showFlag = ref(false);
|
||||
// 显示选择器
|
||||
const showPicker = () => {
|
||||
showFlag.value = true;
|
||||
}
|
||||
|
||||
// 加载选课列表
|
||||
const loadXkList = async () => {
|
||||
uni.showLoading({
|
||||
title: "加载中...",
|
||||
});
|
||||
xsXkkcListApi(props)
|
||||
.then((res) => {
|
||||
if (res.resultCode == 1) {
|
||||
if (res.result && res.result.length) {
|
||||
xkList.value = res.result;
|
||||
curXk.value = res.result[0];
|
||||
} else {
|
||||
xkList.value = [];
|
||||
curXk.value = {};
|
||||
}
|
||||
switchXk(curXk.value);
|
||||
uni.hideLoading();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
uni.hideLoading();
|
||||
});
|
||||
}
|
||||
|
||||
// 切换选课
|
||||
function switchXk(xk: any) {
|
||||
curXk.value = xk;
|
||||
showFlag.value = false;
|
||||
// 显示切换成功提示
|
||||
uni.showToast({
|
||||
title: `已切换到${xk.xkmc}`,
|
||||
icon: "none",
|
||||
});
|
||||
emit("change", xk);
|
||||
}
|
||||
|
||||
// 监听当前学生信息变更
|
||||
watch(() => props.xsId, (newVal) => {
|
||||
console.log("当前学生信息变更", newVal);
|
||||
loadXkList();
|
||||
});
|
||||
|
||||
// 初始化加载数据
|
||||
if (props.xsId && props.njId) {
|
||||
loadXkList();
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/* 全局图片样式 */
|
||||
.w-full {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.h-full {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.title-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.title {
|
||||
flex: 1 0 1px;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 14px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.switch-btn {
|
||||
padding: 5px 15px;
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
color: #fff;
|
||||
border-radius: 15px;
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 选择器弹窗样式 */
|
||||
.xk-picker {
|
||||
background-color: #ffffff;
|
||||
border-top-left-radius: 12px;
|
||||
border-top-right-radius: 12px;
|
||||
padding-bottom: 20px;
|
||||
|
||||
.xk-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 15px;
|
||||
border-bottom: 1px solid #f2f2f2;
|
||||
|
||||
.xk-title {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
|
||||
.xk-list {
|
||||
padding: 0 15px;
|
||||
|
||||
.xk-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 15px 0;
|
||||
border-bottom: 1px solid #f2f2f2;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&-active {
|
||||
background-color: rgba(64, 158, 255, 0.05);
|
||||
}
|
||||
|
||||
.xk-info {
|
||||
flex: 1;
|
||||
margin-left: 12px;
|
||||
|
||||
.xk-name {
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
353
src/pages/base/components/XsPicker/index.vue
Normal file
353
src/pages/base/components/XsPicker/index.vue
Normal file
@ -0,0 +1,353 @@
|
||||
<template>
|
||||
<view>
|
||||
<view class="user-content" v-if="!isBar">
|
||||
<view class="user-avatar">
|
||||
<image :src="curXs.xstxUrl || '/static/base/home/11222.png'" class="avatar-img"></image>
|
||||
<view class="avatar-ring"></view>
|
||||
</view>
|
||||
<view class="user-details">
|
||||
<text class="user-name">{{ curXs.xm }}</text>
|
||||
<view class="user-class-container">
|
||||
<view class="class-tag">
|
||||
<text class="user-class">{{ curXs.njmc }} {{ curXs.bjmc }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="btn-group">
|
||||
<view class="switch-btn" @click="showPicker" v-if="xsList && xsList.length > 1">
|
||||
<u-icon name="arrow-down" size="12" color="#fff"></u-icon>
|
||||
<text>切换</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="xs-bar" @click="showPicker" v-else>
|
||||
<view class="user-avatar">
|
||||
<image :src="curXs.xstxUrl || '/static/base/home/11222.png'" class="w-full h-full"></image>
|
||||
</view>
|
||||
<view class="xs-info">
|
||||
<text class="xs-name">{{ curXs.xm }}</text>
|
||||
<text class="xs-bj">{{ curXs.njmc }} {{ curXs.bjmc }}</text>
|
||||
</view>
|
||||
<view class="switch-btn" v-if="xsList.length > 1">切换</view>
|
||||
</view>
|
||||
<u-popup :show="showFlag" @close="showFlag = false" mode="bottom" round="20">
|
||||
<view class="xs-picker">
|
||||
<view class="xs-header">
|
||||
<text class="xs-title">选择学生</text>
|
||||
<view class="close-btn" @click="showFlag = false">
|
||||
<u-icon name="close" size="18" color="#909399"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
<view class="xs-list">
|
||||
<view v-for="(xs, index) in xsList" :key="index" class="xs-item"
|
||||
:class="{ 'xs-item-active': curXs.id === xs.id }" @click="switchXs(xs)">
|
||||
<view class="xs-tx">
|
||||
<image :src="xs.xstxUrl || '/static/base/home/11222.png'" class="tx-img"></image>
|
||||
</view>
|
||||
<view class="xs-info">
|
||||
<text class="xs-name">{{ xs.xm }}</text>
|
||||
<text class="xs-class">{{ xs.njmc }} {{ xs.bjmc }}</text>
|
||||
</view>
|
||||
<view class="check-icon" v-if="curXs.id === xs.id">
|
||||
<u-icon name="checkmark" color="#4A90E2" size="18"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useUserStore } from "@/store/modules/user";
|
||||
const { getUser, setCurXs, getCurXs } = useUserStore();
|
||||
|
||||
// 接收外部传入属性
|
||||
const props = defineProps<{
|
||||
isBar: boolean;
|
||||
}>();
|
||||
|
||||
// 定义一个上级传入的emit响应事件用于接收数据变更
|
||||
const emit = defineEmits(['change'])
|
||||
|
||||
// 学生列表数据
|
||||
const xsList = computed(() => getUser.xsList)
|
||||
// 当前学生数据
|
||||
const curXs = computed(() => getCurXs);
|
||||
// 控制选择器显示状态
|
||||
const showFlag = ref(false);
|
||||
// 显示选择器
|
||||
const showPicker = () => {
|
||||
showFlag.value = true;
|
||||
}
|
||||
|
||||
// 切换当前学生
|
||||
const switchXs = (xs: any) => {
|
||||
setCurXs(xs);
|
||||
showFlag.value = false;
|
||||
emit('change', xs);
|
||||
// 这里可以添加切换学生后的其他操作,如刷新页面数据等
|
||||
uni.showToast({
|
||||
title: `已切换到${xs.xm}`,
|
||||
icon: "none",
|
||||
});
|
||||
}
|
||||
|
||||
// 如果是bar形式,则默认打开选择器
|
||||
if (props.isBar && getUser.xsList.length > 1) {
|
||||
showPicker();
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/** 当前学生信息 */
|
||||
.user-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
|
||||
.user-avatar {
|
||||
position: relative;
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 16px rgba(74, 144, 226, 0.3);
|
||||
border: 3px solid #ffffff;
|
||||
flex-shrink: 0;
|
||||
|
||||
.avatar-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.avatar-ring {
|
||||
position: absolute;
|
||||
top: -3px;
|
||||
left: -3px;
|
||||
width: calc(100% + 6px);
|
||||
height: calc(100% + 6px);
|
||||
border-radius: 50%;
|
||||
border: 2px solid rgba(74, 144, 226, 0.3);
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
}
|
||||
|
||||
.user-details {
|
||||
flex: 1;
|
||||
margin-left: 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
.user-name {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: white;
|
||||
margin-bottom: 8px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.user-class-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.class-tag {
|
||||
background: linear-gradient(135deg, #4A90E2 0%, #357ABD 100%);
|
||||
color: #ffffff;
|
||||
padding: 4px 12px;
|
||||
border-radius: 12px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
box-shadow: 0 2px 8px rgba(74, 144, 226, 0.3);
|
||||
|
||||
.user-class {
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.switch-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
padding: 8px 16px;
|
||||
background: linear-gradient(135deg, #4A90E2 0%, #357ABD 100%);
|
||||
color: #ffffff;
|
||||
border-radius: 20px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
box-shadow: 0 4px 12px rgba(74, 144, 226, 0.3);
|
||||
transition: all 0.3s ease;
|
||||
flex-shrink: 0;
|
||||
min-width: 60px;
|
||||
height: 16px;
|
||||
|
||||
&:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
text {
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 学生选择栏样式
|
||||
.xs-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 10px;
|
||||
padding: 10px;
|
||||
|
||||
.user-avatar {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
background-color: #fff;
|
||||
overflow: hidden;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.xs-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.xs-name {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.xs-bj {
|
||||
font-size: 12px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.switch-btn {
|
||||
padding: 4px 12px;
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
color: #fff;
|
||||
border-radius: 20px;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 学生选择器弹窗样式 */
|
||||
.xs-picker {
|
||||
background-color: #ffffff;
|
||||
border-top-left-radius: 12px;
|
||||
border-top-right-radius: 12px;
|
||||
padding-bottom: 20px;
|
||||
|
||||
.xs-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 15px;
|
||||
border-bottom: 1px solid #f2f2f2;
|
||||
|
||||
.xs-title {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
|
||||
.xs-list {
|
||||
padding: 0 15px;
|
||||
|
||||
.xs-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 15px 0;
|
||||
border-bottom: 1px solid #f2f2f2;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&-active {
|
||||
background-color: rgba(64, 158, 255, 0.05);
|
||||
}
|
||||
|
||||
.xs-tx {
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
border-radius: 50%;
|
||||
background-color: #f0f0f0;
|
||||
overflow: hidden;
|
||||
flex-shrink: 0;
|
||||
|
||||
.tx-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
.xs-info {
|
||||
flex: 1;
|
||||
margin-left: 12px;
|
||||
|
||||
.xs-name {
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.xs-class {
|
||||
font-size: 13px;
|
||||
color: #606266;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 动画效果 */
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: scale(1.05);
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 响应式优化 */
|
||||
@media (max-width: 375px) {
|
||||
.user-content .user-avatar {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -14,7 +14,7 @@
|
||||
<view class="student-selector-bar" @click="showStudentSelector">
|
||||
<view class="user-avatar">
|
||||
<image
|
||||
:src="currentStudent.avatar || '/static/base/home/11222.png'"
|
||||
:src="currentStudent.xstxUrl || '/static/base/home/11222.png'"
|
||||
class="w-full h-full"
|
||||
></image>
|
||||
</view>
|
||||
@ -132,7 +132,7 @@
|
||||
>
|
||||
<view class="student-avatar">
|
||||
<image
|
||||
:src="student.avatar || '/static/base/home/11222.png'"
|
||||
:src="student.xstxUrl || '/static/base/home/11222.png'"
|
||||
class="w-full h-full"
|
||||
></image>
|
||||
</view>
|
||||
|
||||
@ -3,75 +3,29 @@
|
||||
<!-- 选课信息头部 - 固定部分 -->
|
||||
<view class="selection-header">
|
||||
<view class="header-content">
|
||||
<view class="title-section">
|
||||
<view class="title">
|
||||
<text v-if="kcData && kcData.xkmc">{{ kcData.xkmc }}</text>
|
||||
<text v-else>选课信息</text>
|
||||
</view>
|
||||
<!-- <view class="subtitle">互动兴趣课程</view> -->
|
||||
</view>
|
||||
<!-- 选课类型选择部分 -->
|
||||
<XkPicker xklx-id="816059832" :xs-id="curXs.id" :nj-id="curXs.njId" @change="switchXk" />
|
||||
<!-- 学生选择部分 -->
|
||||
<view class="student-selector-bar" @click="showStudentSelector">
|
||||
<view class="user-avatar">
|
||||
<image
|
||||
:src="curXs.xstxUrl || '/static/base/home/11222.png'"
|
||||
class="w-full h-full"
|
||||
></image>
|
||||
</view>
|
||||
<view class="student-info">
|
||||
<text class="student-name">{{ curXs.xm }}</text>
|
||||
<text class="student-class"
|
||||
>{{ curXs.njmc }} {{ curXs.bjmc }}</text
|
||||
>
|
||||
</view>
|
||||
<view class="switch-btn" v-if="xsList.length > 1">切换</view>
|
||||
</view>
|
||||
<view class="countdown-section">
|
||||
<template v-if="!isEnrollmentEnded">
|
||||
<view class="countdown-title">{{ countdownTitle }}</view>
|
||||
<view class="countdown-timer">
|
||||
<view class="time-block">
|
||||
<text class="time-value">{{ countdownTime.hours }}</text>
|
||||
<text class="time-unit">时</text>
|
||||
</view>
|
||||
<text class="time-separator">:</text>
|
||||
<view class="time-block">
|
||||
<text class="time-value">{{ countdownTime.minutes }}</text>
|
||||
<text class="time-unit">分</text>
|
||||
</view>
|
||||
<text class="time-separator">:</text>
|
||||
<view class="time-block">
|
||||
<text class="time-value">{{ countdownTime.seconds }}</text>
|
||||
<text class="time-unit">秒</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<template v-else>
|
||||
<view class="enrollment-ended">
|
||||
<u-icon name="info-circle" color="#FF9900" size="20"></u-icon>
|
||||
<text>选课已经结束</text>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
<XsPicker :is-bar="true" />
|
||||
<!-- 倒计时-->
|
||||
<XkCountdown :xk="curXk" @over="xkTimeOver" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 可滚动的内容区域 -->
|
||||
<view class="scrollable-content">
|
||||
<!-- 课程网格列表 -->
|
||||
<view class="course-grid" v-if="xkkcList.length > 0">
|
||||
<view class="course-grid" v-if="courseListData.length > 0">
|
||||
<view
|
||||
v-for="(course, index) in xkkcList"
|
||||
v-for="(course, index) in courseListData"
|
||||
:key="course.id || index"
|
||||
class="course-item"
|
||||
:class="{ selected: course.isSelected }"
|
||||
@click="toggleSelection(course)"
|
||||
>
|
||||
<view class="course-name">{{ course.kcmc }}</view>
|
||||
<view class="register-info">
|
||||
<text>报名情况:</text>
|
||||
<text>上课人数:</text>
|
||||
<text class="register-count">{{ course.ybmr }}</text>
|
||||
<text> | {{ course.maxNum }}</text>
|
||||
</view>
|
||||
<view class="detail-btn" @click.stop="viewCourseDetail(course)"
|
||||
>详情</view
|
||||
@ -94,422 +48,42 @@
|
||||
<view class="empty-text">暂无课程数据</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部报名按钮 - 固定部分 -->
|
||||
<view class="register-btn-container">
|
||||
<view class="register-btn" @click="submitRegistration">
|
||||
<text v-if="selectedCoursesCount > 0">
|
||||
点击报名 (已选{{ selectedCoursesCount }}门)
|
||||
</text>
|
||||
<text v-else>点击报名</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view>
|
||||
<!-- 学生选择弹窗 -->
|
||||
<u-popup
|
||||
:show="showSelector"
|
||||
@close="showSelector = false"
|
||||
mode="bottom"
|
||||
round="10"
|
||||
>
|
||||
<view class="student-selector">
|
||||
<view class="selector-header">
|
||||
<text class="selector-title">选择学生</text>
|
||||
<u-icon
|
||||
name="close"
|
||||
size="20"
|
||||
@click="showSelector = false"
|
||||
></u-icon>
|
||||
</view>
|
||||
<view class="student-list">
|
||||
<view
|
||||
v-for="(xs, index) in xsList"
|
||||
:key="index"
|
||||
class="student-item"
|
||||
:class="{
|
||||
'student-item-active': curXs.id === xs.id,
|
||||
}"
|
||||
@click="switchXs(xs)"
|
||||
>
|
||||
<view class="student-avatar">
|
||||
<image
|
||||
:src="xs.xstxUrl || '/static/base/home/11222.png'"
|
||||
class="w-full h-full"
|
||||
></image>
|
||||
</view>
|
||||
<view class="student-info">
|
||||
<text class="student-name">{{ xs.xm }}</text>
|
||||
<text class="student-class"
|
||||
>{{ xs.njmc }} {{ xs.bjmc }}</text
|
||||
>
|
||||
</view>
|
||||
<u-icon
|
||||
v-if="curXs.id === xs.id"
|
||||
name="checkmark"
|
||||
color="#409EFF"
|
||||
size="20"
|
||||
></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
ref,
|
||||
computed,
|
||||
reactive,
|
||||
onBeforeUnmount,
|
||||
watch,
|
||||
onMounted,
|
||||
} from "vue";
|
||||
import XsPicker from "@/pages/base/components/XsPicker/index.vue"
|
||||
import XkPicker from "@/pages/base/components/XkPicker/index.vue"
|
||||
import XkCountdown from "@/pages/base/components/XkCountdown/index.vue"
|
||||
import { useUserStore } from "@/store/modules/user";
|
||||
import { useDataStore } from "@/store/modules/data";
|
||||
import {
|
||||
xkAddXkqdApi,
|
||||
xkListApi,
|
||||
xkxkbmInfoApi,
|
||||
xkXkqdApi,
|
||||
} from "@/api/base/server";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
const { getUser } = useUserStore();
|
||||
const { getData, setKcData, setData } = useDataStore();
|
||||
const { sign_file } = getData;
|
||||
const { getCurXs } = useUserStore();
|
||||
const { setKcData } = useDataStore();
|
||||
|
||||
// 倒计时相关
|
||||
const countdownTime = reactive({
|
||||
hours: "00",
|
||||
minutes: "00",
|
||||
seconds: "00",
|
||||
});
|
||||
const curXs = computed(() => getCurXs);
|
||||
|
||||
// 倒计时标题文本
|
||||
const countdownTitle = computed(() => {
|
||||
return kcStatus.value ? "距离选课结束还剩" : "距离选课开始还剩";
|
||||
});
|
||||
|
||||
// 当前选中的学生
|
||||
let countdownTimer: number | null = null;
|
||||
const remainTime = ref("00:00:00");
|
||||
|
||||
// 学生列表数据
|
||||
const xsList = computed(() => {
|
||||
return getUser.xsList;
|
||||
});
|
||||
|
||||
const curXs = ref<any>({});
|
||||
|
||||
// 控制选择器显示状态
|
||||
const showSelector = ref(false);
|
||||
|
||||
const kcData = ref();
|
||||
const curXk = ref();
|
||||
const kcStatus = ref(false);
|
||||
|
||||
// 添加选课是否已结束的标记
|
||||
const isEnrollmentEnded = ref(false);
|
||||
|
||||
// 添加轮询定时器变量
|
||||
let pollTimer: number | null = null;
|
||||
|
||||
const curXkkc = ref({});
|
||||
|
||||
// 封装检查学生报名状态的通用方法
|
||||
function checkStudentEnrollmentApi(xs: any): Promise<boolean> {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!xs || !xs.id || !xs.njId) {
|
||||
console.error("学生信息不完整:", xs);
|
||||
resolve(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查kcData是否已加载
|
||||
if (!kcData.value || !kcData.value.id) {
|
||||
console.error("课程数据未加载:", kcData.value);
|
||||
resolve(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// 调用选课签到接口检查学生是否已报名
|
||||
xkXkqdApi({
|
||||
njId: xs.njId,
|
||||
xsId: xs.id,
|
||||
xklxId: "816059832", // 课程类型ID
|
||||
xkId: kcData.value.id,
|
||||
})
|
||||
.then((res) => {
|
||||
console.log(1122, res);
|
||||
|
||||
// 根据接口返回的result判断是否已报名
|
||||
if (res && res.resultCode === 1) {
|
||||
setData({
|
||||
...getData,
|
||||
kcData,
|
||||
studentInfo: xs,
|
||||
enrolledCourse: res.result,
|
||||
});
|
||||
resolve(res.result.length > 0); // 转换为布尔值返回
|
||||
} else {
|
||||
// 接口调用成功但返回错误
|
||||
console.warn("检查报名状态接口返回错误:", res);
|
||||
resolve(false);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
// 接口调用失败
|
||||
console.error("调用检查报名状态接口失败:", error);
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
const courseInfo = ref<any>({});
|
||||
// 切换选课
|
||||
const switchXk = (xk: any) => {
|
||||
curXk.value = xk;
|
||||
}
|
||||
|
||||
// 轮询获取课程报名人数
|
||||
const pollEnrollmentCount = () => {
|
||||
xkxkbmInfoApi({ xkId: kcData.value.id })
|
||||
.then((res) => {
|
||||
if (res && res.resultCode === 1 && Array.isArray(res.result)) {
|
||||
updateEnrollmentCount(res.result);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("获取报名人数失败:", error);
|
||||
});
|
||||
};
|
||||
|
||||
// 更新课程报名人数
|
||||
const updateEnrollmentCount = (
|
||||
data: Array<{ xkkcId: string; bmrs: number }>
|
||||
) => {
|
||||
if (!Array.isArray(xkkcList.value) || xkkcList.value.length === 0)
|
||||
return;
|
||||
|
||||
let hasUpdates = false;
|
||||
|
||||
xkkcList.value.forEach((course) => {
|
||||
const newCount = data.find((item) => item.xkkcId === course.id);
|
||||
if (newCount && course.ybmr !== newCount.bmrs) {
|
||||
course.ybmr = newCount.bmrs;
|
||||
hasUpdates = true;
|
||||
}
|
||||
});
|
||||
|
||||
// 如果有更新,强制重新渲染列表
|
||||
if (hasUpdates) {
|
||||
xkkcList.value = [...xkkcList.value];
|
||||
}
|
||||
};
|
||||
|
||||
// 启动轮询
|
||||
const startPolling = (interval = 5000) => {
|
||||
// 清除可能存在的旧定时器
|
||||
stopPolling();
|
||||
|
||||
// 立即执行一次
|
||||
pollEnrollmentCount();
|
||||
|
||||
// 设置定时器定期执行
|
||||
pollTimer = setInterval(pollEnrollmentCount, interval) as unknown as number;
|
||||
};
|
||||
|
||||
// 停止轮询
|
||||
const stopPolling = () => {
|
||||
if (pollTimer) {
|
||||
clearInterval(pollTimer);
|
||||
pollTimer = null;
|
||||
}
|
||||
};
|
||||
|
||||
// 在组件挂载时开始轮询
|
||||
onMounted(() => {
|
||||
if (xsList.value.length > 0 && xsList.value.length === 1) {
|
||||
showSelector.value = true;
|
||||
}
|
||||
// 当课程列表加载完成后启动轮询
|
||||
watch(xkkcList, (newVal) => {
|
||||
if (newVal.length > 0 && !pollTimer) {
|
||||
startPolling();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 页面初始化时检查当前学生是否已报名
|
||||
const checkInitialEnrollment = (curXs: any) => {
|
||||
if (!curXs) return;
|
||||
|
||||
// 使用封装的API函数检查学生报名状态
|
||||
checkStudentEnrollmentApi(curXs)
|
||||
.then((isEnrolled) => {
|
||||
if (isEnrolled) {
|
||||
uni.hideLoading();
|
||||
uni.reLaunch({
|
||||
url: `/pages/base/course-selection/enrolled`,
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
// 检查失败时不做特殊处理,继续正常流程
|
||||
console.log("检查学生报名状态失败,继续正常流程");
|
||||
});
|
||||
};
|
||||
|
||||
// 加载课程列表
|
||||
const loadXkList = (curXs: any) => {
|
||||
if (!curXs) {
|
||||
uni.hideLoading();
|
||||
return;
|
||||
}
|
||||
|
||||
xkListApi({
|
||||
njId: curXs.njId,
|
||||
xklxId: "816059832",
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.resultCode == 1) {
|
||||
if (res.result) {
|
||||
kcData.value = res.result;
|
||||
//获取当前时间
|
||||
const now = dayjs().valueOf();
|
||||
//获取选课开始时间
|
||||
const xkkstime = dayjs(res.result.xkkstime).valueOf();
|
||||
//获取选课结束时间
|
||||
const xkjstime = dayjs(res.result.xkjstime).valueOf();
|
||||
|
||||
// 检查是否已经超过选课结束时间
|
||||
if (now > xkjstime) {
|
||||
kcStatus.value = true;
|
||||
isEnrollmentEnded.value = true;
|
||||
countdownTime.hours = "00";
|
||||
countdownTime.minutes = "00";
|
||||
countdownTime.seconds = "00";
|
||||
}
|
||||
//判断选课是否开始
|
||||
else if (now < xkkstime) {
|
||||
kcStatus.value = false;
|
||||
startCountdown(xkkstime);
|
||||
} else {
|
||||
kcStatus.value = true;
|
||||
startCountdown(xkjstime);
|
||||
}
|
||||
|
||||
// 课程数据加载完成后,检查学生报名状态
|
||||
checkInitialEnrollment(curXs);
|
||||
} else {
|
||||
uni.reLaunch({
|
||||
url: "/pages/base/course-selection/notopen",
|
||||
});
|
||||
}
|
||||
uni.hideLoading();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
uni.hideLoading();
|
||||
});
|
||||
};
|
||||
|
||||
// 显示学生选择器
|
||||
const showStudentSelector = () => {
|
||||
if (xsList.value.length > 1) {
|
||||
showSelector.value = true;
|
||||
}
|
||||
// 选课时间结束
|
||||
const xkTimeOver = (val: any) => {
|
||||
console.log(val);
|
||||
}
|
||||
|
||||
// 切换学生
|
||||
const switchXs = (xs: any) => {
|
||||
curXs.value = xs;
|
||||
showSelector.value = false;
|
||||
|
||||
// 显示加载中
|
||||
uni.showLoading({
|
||||
title: "加载中...",
|
||||
});
|
||||
|
||||
// 显示切换成功提示
|
||||
uni.showToast({
|
||||
title: `已切换到${xs.xm}`,
|
||||
icon: "none",
|
||||
});
|
||||
|
||||
// 先加载当前学生的课程列表,课程数据加载完成后会自动检查报名状态
|
||||
loadXkList(xs);
|
||||
}
|
||||
|
||||
// 启动倒计时
|
||||
const startCountdown = (endTimeStamp: number) => {
|
||||
if (countdownTimer) {
|
||||
clearInterval(countdownTimer);
|
||||
}
|
||||
|
||||
const updateCountdown = () => {
|
||||
const now = new Date().getTime();
|
||||
const timeLeft = endTimeStamp - now;
|
||||
|
||||
if (timeLeft <= 0) {
|
||||
// 倒计时结束
|
||||
countdownTime.hours = "00";
|
||||
countdownTime.minutes = "00";
|
||||
countdownTime.seconds = "00";
|
||||
remainTime.value = "00:00:00";
|
||||
|
||||
if (countdownTimer) {
|
||||
clearInterval(countdownTimer);
|
||||
countdownTimer = null;
|
||||
}
|
||||
|
||||
// 判断当前倒计时是选课开始还是选课结束
|
||||
if (!kcStatus.value && kcData.value) {
|
||||
// 如果是选课开始倒计时结束,则切换到选课结束倒计时
|
||||
kcStatus.value = true; // 更新状态为选课已开始
|
||||
uni.showToast({
|
||||
title: "选课已开始",
|
||||
icon: "none",
|
||||
});
|
||||
|
||||
// 开始选课结束倒计时
|
||||
const xkjstime = dayjs(kcData.value.xkjstime).valueOf();
|
||||
startCountdown(xkjstime);
|
||||
} else {
|
||||
// 如果是选课结束倒计时结束
|
||||
isEnrollmentEnded.value = true; // 标记选课已结束
|
||||
uni.showToast({
|
||||
title: "选课已结束",
|
||||
icon: "none",
|
||||
});
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// 计算小时、分钟和秒
|
||||
const hours = Math.floor(timeLeft / (1000 * 60 * 60));
|
||||
const minutes = Math.floor((timeLeft % (1000 * 60 * 60)) / (1000 * 60));
|
||||
const seconds = Math.floor((timeLeft % (1000 * 60)) / 1000);
|
||||
|
||||
// 格式化显示
|
||||
countdownTime.hours = hours < 10 ? `0${hours}` : `${hours}`;
|
||||
countdownTime.minutes = minutes < 10 ? `0${minutes}` : `${minutes}`;
|
||||
countdownTime.seconds = seconds < 10 ? `0${seconds}` : `${seconds}`;
|
||||
|
||||
// 更新remainTime(兼容原有逻辑)
|
||||
remainTime.value = `${countdownTime.hours}:${countdownTime.minutes}:${countdownTime.seconds}`;
|
||||
};
|
||||
|
||||
// 立即执行一次
|
||||
updateCountdown();
|
||||
|
||||
// 每秒更新一次
|
||||
countdownTimer = setInterval(updateCountdown, 1000) as unknown as number;
|
||||
};
|
||||
|
||||
// 从课程数据中提取课程列表
|
||||
const displayCourseList = computed(() => {
|
||||
if (
|
||||
!kcData.value ||
|
||||
!kcData.value.xkkcs ||
|
||||
!Array.isArray(kcData.value.xkkcs)
|
||||
!curXk.value ||
|
||||
!curXk.value.xkkcs ||
|
||||
!Array.isArray(curXk.value.xkkcs)
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
@ -518,87 +92,38 @@ const displayCourseList = computed(() => {
|
||||
const selectedCourseIds = uni.getStorageSync("selectedCourseIds") || [];
|
||||
|
||||
// 为课程添加选中状态属性
|
||||
return kcData.value.xkkcs.map((course: any) => ({
|
||||
return curXk.value.xkkcs.map((course: any) => ({
|
||||
...course,
|
||||
isSelected: selectedCourseIds.includes(course.id),
|
||||
}));
|
||||
});
|
||||
|
||||
// 可修改的课程列表数据
|
||||
const xkkcList = ref<any[]>([]);
|
||||
const courseListData = ref<any[]>([]);
|
||||
|
||||
// 计算已选择的课程数量
|
||||
const selectedCoursesCount = computed(() => {
|
||||
return xkkcList.value.filter((course: any) => course.isSelected).length;
|
||||
return courseListData.value.filter((course: any) => course.isSelected).length;
|
||||
});
|
||||
|
||||
// 监听计算属性变化,更新可修改的数据
|
||||
watch(
|
||||
displayCourseList,
|
||||
(newVal) => {
|
||||
xkkcList.value = JSON.parse(JSON.stringify(newVal));
|
||||
courseListData.value = JSON.parse(JSON.stringify(newVal));
|
||||
|
||||
// 初始化时,将已选课程的全部信息存入curXkkc
|
||||
// 初始化时,将已选课程的全部信息存入courseInfo
|
||||
const selectedCourseIds = uni.getStorageSync("selectedCourseIds") || [];
|
||||
if (selectedCourseIds.length > 0) {
|
||||
const selectedCourses = newVal.filter((course: any) =>
|
||||
selectedCourseIds.includes(course.id)
|
||||
);
|
||||
curXkkc.value = selectedCourses;
|
||||
courseInfo.value = selectedCourses;
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
// 选择或取消选择课程
|
||||
const toggleSelection = (course: any) => {
|
||||
// 只检查课程是否已满,不判断选课是否开始
|
||||
if (course.ybmr >= course.maxNum) {
|
||||
uni.showToast({
|
||||
title: "该课程名额已满",
|
||||
icon: "none",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const courseIndex = xkkcList.value.findIndex(
|
||||
(item) => item.id === course.id
|
||||
);
|
||||
if (courseIndex === -1) return;
|
||||
|
||||
// 切换当前课程的选中状态
|
||||
xkkcList.value[courseIndex].isSelected =
|
||||
!xkkcList.value[courseIndex].isSelected;
|
||||
|
||||
// 获取所有选中的课程ID
|
||||
const selectedCourseIds = xkkcList.value
|
||||
.filter((item: any) => item.isSelected)
|
||||
.map((item: any) => item.id);
|
||||
|
||||
// 存储选中的课程ID数组
|
||||
uni.setStorageSync("selectedCourseIds", selectedCourseIds);
|
||||
|
||||
// 更新curXkkc为所有选中的课程
|
||||
curXkkc.value = xkkcList.value.filter(
|
||||
(item: any) => item.isSelected
|
||||
);
|
||||
|
||||
// 显示提示
|
||||
if (xkkcList.value[courseIndex].isSelected) {
|
||||
uni.showToast({
|
||||
title: `已选择 ${course.kcmc}`,
|
||||
icon: "none",
|
||||
duration: 1500,
|
||||
});
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: `已取消选择 ${course.kcmc}`,
|
||||
icon: "none",
|
||||
duration: 1500,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 查看课程详情
|
||||
const viewCourseDetail = (course: any) => {
|
||||
setKcData(course);
|
||||
@ -607,116 +132,8 @@ const viewCourseDetail = (course: any) => {
|
||||
});
|
||||
};
|
||||
|
||||
// 提交报名
|
||||
const submitRegistration = () => {
|
||||
// 直接根据当前时间和选课时间判断是否可以报名
|
||||
if (!kcData.value || !kcData.value.xkkstime || !kcData.value.xkjstime) {
|
||||
uni.showToast({
|
||||
title: "选课信息不完整,请联系校方!",
|
||||
icon: "none",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const now = dayjs().valueOf();
|
||||
const xkkstime = dayjs(kcData.value.xkkstime).valueOf();
|
||||
const xkjstime = dayjs(kcData.value.xkjstime).valueOf();
|
||||
|
||||
// 检查是否在选课时间范围内
|
||||
if (now < xkkstime) {
|
||||
uni.showToast({
|
||||
title: "选课时间未开始,请等待!",
|
||||
icon: "none",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (now > xkjstime) {
|
||||
uni.showToast({
|
||||
title: "选课已结束,请等待下一次选课!",
|
||||
icon: "none",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedCourses = xkkcList.value.filter(
|
||||
(course: any) => course.isSelected
|
||||
);
|
||||
|
||||
if (selectedCourses.length === 0) {
|
||||
uni.showToast({
|
||||
title: "请至少选择一门课程",
|
||||
icon: "none",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查选中的课程是否有已满的
|
||||
const fullCourses = selectedCourses.filter(
|
||||
(course) => course.ybmr >= course.maxNum
|
||||
);
|
||||
if (fullCourses.length > 0) {
|
||||
uni.showToast({
|
||||
title: `课程"${fullCourses[0].kcmc}"名额已满,请重新选择!`,
|
||||
icon: "none",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 构建课程名称列表用于确认弹窗
|
||||
const courseNames = selectedCourses.map((course) => course.kcmc).join("、");
|
||||
|
||||
uni.showModal({
|
||||
title: "确认报名",
|
||||
content: `您确定要为${curXs.value.xm}报名以下课程吗?\n\n${courseNames}`,
|
||||
success: async (res) => {
|
||||
if (res.confirm) {
|
||||
uni.showLoading({
|
||||
title: "报名中...",
|
||||
});
|
||||
|
||||
// 将选中的课程ID用逗号隔开
|
||||
const selectedCourseIds = selectedCourses
|
||||
.map((course) => course.id)
|
||||
.join(",");
|
||||
|
||||
const res = await xkAddXkqdApi({
|
||||
xsId: curXs.value.id,
|
||||
xkkcId: selectedCourseIds,
|
||||
qmFile: sign_file,
|
||||
xklxId: "816059832",
|
||||
});
|
||||
uni.hideLoading();
|
||||
if (res.resultCode == 1) {
|
||||
setData({
|
||||
...getData,
|
||||
kcData,
|
||||
studentInfo: curXs.value,
|
||||
enrolledCourse: res.result,
|
||||
});
|
||||
uni.showToast({
|
||||
title: "报名成功",
|
||||
icon: "none",
|
||||
});
|
||||
setTimeout(() => {
|
||||
uni.reLaunch({
|
||||
url: `/pages/base/course-selection/enrolled`,
|
||||
});
|
||||
}, 1500);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// 页面卸载前清除定时器
|
||||
onBeforeUnmount(() => {
|
||||
if (countdownTimer) {
|
||||
clearInterval(countdownTimer);
|
||||
countdownTimer = null;
|
||||
}
|
||||
|
||||
stopPolling();
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -775,97 +192,6 @@ onBeforeUnmount(() => {
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
|
||||
.title-section {
|
||||
.title {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 14px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
// 学生选择栏样式
|
||||
.student-selector-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 10px;
|
||||
padding: 10px;
|
||||
|
||||
.user-avatar {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
background-color: #fff;
|
||||
overflow: hidden;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.student-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.student-name {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.student-class {
|
||||
font-size: 12px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.switch-btn {
|
||||
padding: 4px 12px;
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
color: #fff;
|
||||
border-radius: 20px;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
.countdown-section {
|
||||
.countdown-title {
|
||||
font-size: 14px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.countdown-timer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.time-block {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 6px;
|
||||
min-width: 40px;
|
||||
padding: 5px 8px;
|
||||
text-align: center;
|
||||
|
||||
.time-value {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.time-unit {
|
||||
font-size: 12px;
|
||||
opacity: 0.9;
|
||||
}
|
||||
}
|
||||
|
||||
.time-separator {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
margin: 0 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -959,82 +285,6 @@ onBeforeUnmount(() => {
|
||||
}
|
||||
}
|
||||
|
||||
/* 学生选择器弹窗样式 */
|
||||
.student-selector {
|
||||
background-color: #ffffff;
|
||||
border-top-left-radius: 12px;
|
||||
border-top-right-radius: 12px;
|
||||
padding-bottom: 20px;
|
||||
|
||||
.selector-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 15px;
|
||||
border-bottom: 1px solid #f2f2f2;
|
||||
|
||||
.selector-title {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
|
||||
.student-list {
|
||||
padding: 0 15px;
|
||||
|
||||
.student-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 15px 0;
|
||||
border-bottom: 1px solid #f2f2f2;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&-active {
|
||||
background-color: rgba(64, 158, 255, 0.05);
|
||||
}
|
||||
|
||||
.student-avatar {
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
border-radius: 50%;
|
||||
background-color: #f0f0f0;
|
||||
overflow: hidden;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.student-info {
|
||||
flex: 1;
|
||||
margin-left: 12px;
|
||||
|
||||
.student-name {
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.student-class {
|
||||
font-size: 13px;
|
||||
color: #606266;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 全局图片样式 */
|
||||
.w-full {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.h-full {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
// 暂无数据样式
|
||||
.empty-course-list {
|
||||
display: flex;
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
<view class="student-info">
|
||||
<view class="student-avatar">
|
||||
<image
|
||||
:src="studentInfo.avatar || '/static/base/home/11222.png'"
|
||||
:src="studentInfo.xstxUrl || '/static/base/home/11222.png'"
|
||||
mode="aspectFill"
|
||||
></image>
|
||||
</view>
|
||||
@ -164,7 +164,7 @@ const studentInfo = ref({
|
||||
xm: getData.value.studentInfo.xm,
|
||||
njmc: getData.value.studentInfo.njmc,
|
||||
bjmc: getData.value.studentInfo.bjmc,
|
||||
avatar: getData.value.studentInfo.avatar,
|
||||
xstxUrl: getData.value.studentInfo.xstxUrl,
|
||||
});
|
||||
|
||||
// 已报名课程信息
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
<view class="student-selector-bar" @click="showStudentSelector">
|
||||
<view class="user-avatar">
|
||||
<image
|
||||
:src="currentStudent.avatar || '/static/base/home/11222.png'"
|
||||
:src="currentStudent.xstxUrl || '/static/base/home/11222.png'"
|
||||
class="w-full h-full"
|
||||
></image>
|
||||
</view>
|
||||
@ -129,7 +129,7 @@
|
||||
>
|
||||
<view class="student-avatar">
|
||||
<image
|
||||
:src="student.avatar || '/static/base/home/11222.png'"
|
||||
:src="student.xstxUrl || '/static/base/home/11222.png'"
|
||||
class="w-full h-full"
|
||||
></image>
|
||||
</view>
|
||||
|
||||
@ -8,35 +8,15 @@
|
||||
<view class="banner-overlay">
|
||||
</view>
|
||||
</view>
|
||||
<view class="user-content">
|
||||
<view class="user-avatar">
|
||||
<image :src="curXs.xstxUrl || '/static/base/home/11222.png'" class="avatar-img"></image>
|
||||
<view class="avatar-ring"></view>
|
||||
</view>
|
||||
<view class="user-details">
|
||||
<text class="user-name">{{ curXs.xm }}</text>
|
||||
<view class="user-class-container">
|
||||
<view class="class-tag">
|
||||
<text class="user-class">{{ curXs.njmc }} {{ curXs.bjmc }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="btn-group">
|
||||
<view class="switch-btn" @click="goToGlxs">
|
||||
<u-icon name="arrow-down" size="12" color="#fff"></u-icon>
|
||||
<text>新增学生</text>
|
||||
</view>
|
||||
<view class="switch-btn" @click="showXsSelector" v-if="xsList && xsList.length > 1">
|
||||
<u-icon name="arrow-down" size="12" color="#fff"></u-icon>
|
||||
<text>切换</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 学生信息 -->
|
||||
<XsPicker :is-bar="false" @change="switchXs" />
|
||||
|
||||
<view class="glxs-btn" @click="goToGlxs">
|
||||
<u-icon name="plus" size="12" color="#fff"></u-icon>
|
||||
<text>新增学生</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 学校横幅 -->
|
||||
<!-- -->
|
||||
|
||||
<!-- 功能菜单 -->
|
||||
<view class="menu-section">
|
||||
<view class="section-title">
|
||||
@ -91,51 +71,16 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 学生选择弹窗 -->
|
||||
<u-popup
|
||||
:show="showSelector"
|
||||
@close="showSelector = false"
|
||||
mode="bottom"
|
||||
round="20"
|
||||
>
|
||||
<view class="student-selector">
|
||||
<view class="selector-header">
|
||||
<text class="selector-title">选择学生</text>
|
||||
<view class="close-btn" @click="showSelector = false">
|
||||
<u-icon name="close" size="18" color="#909399"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
<view class="student-list">
|
||||
<view
|
||||
v-for="(xs, index) in xsList"
|
||||
:key="index"
|
||||
class="student-item"
|
||||
:class="{ 'student-item-active': curXs.id === xs.id }"
|
||||
@click="switchXs(xs)"
|
||||
>
|
||||
<view class="student-avatar">
|
||||
<image :src="xs.xstxUrl || '/static/base/home/11222.png'" class="avatar-img"></image>
|
||||
</view>
|
||||
<view class="student-info">
|
||||
<text class="student-name">{{ xs.xm }}</text>
|
||||
<text class="student-class">{{ xs.njmc }} {{ xs.bjmc }}</text>
|
||||
</view>
|
||||
<view class="check-icon" v-if="curXs.id === xs.id">
|
||||
<u-icon name="checkmark" color="#4A90E2" size="18"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import XsPicker from "@/pages/base/components/XsPicker/index.vue"
|
||||
import { cmsArticlePageApi } from "@/api/base/server";
|
||||
import { useUserStore } from "@/store/modules/user";
|
||||
import { useDataStore } from "@/store/modules/data";
|
||||
const { getUser, setCurXs, getCurXs } = useUserStore();
|
||||
const { getCurXs } = useUserStore();
|
||||
const { setData, getAppCode } = useDataStore();
|
||||
|
||||
// 菜单项数据
|
||||
@ -199,24 +144,8 @@ const announcements = ref<any>([
|
||||
}
|
||||
])
|
||||
|
||||
// 学生列表数据
|
||||
const xsList = ref([
|
||||
{
|
||||
id: "1",
|
||||
xm: "陶亦菲",
|
||||
njmc: "2年级",
|
||||
bjmc: "01班",
|
||||
njmcId: "",
|
||||
xstx: "/static/base/home/11222.png",
|
||||
xstxUrl: ''
|
||||
}
|
||||
]);
|
||||
|
||||
// 当前选中的学生
|
||||
let curXs = ref(xsList.value[0]);
|
||||
|
||||
// 控制选择器显示状态
|
||||
const showSelector = ref(false);
|
||||
let curXs = computed(() => getCurXs)
|
||||
|
||||
let pageParams = ref({
|
||||
page: 1,
|
||||
@ -240,23 +169,10 @@ function handleMenuClick(item: any) {
|
||||
}
|
||||
}
|
||||
|
||||
// 显示学生选择器
|
||||
function showXsSelector() {
|
||||
showSelector.value = true;
|
||||
}
|
||||
|
||||
// 切换学生
|
||||
function switchXs(xs: any) {
|
||||
curXs.value = xs;
|
||||
showSelector.value = false;
|
||||
curXs = xs;
|
||||
getArticleList();
|
||||
// 记录存储切换
|
||||
setCurXs(xs);
|
||||
// 这里可以添加切换学生后的其他操作,如刷新页面数据等
|
||||
uni.showToast({
|
||||
title: `已切换到${xs.xm}`,
|
||||
icon: "none",
|
||||
});
|
||||
}
|
||||
|
||||
// 跳转到详情页面
|
||||
@ -279,8 +195,6 @@ const getArticleList = async () => {
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
xsList.value = getUser.xsList;
|
||||
curXs.value = getCurXs;
|
||||
getArticleList();
|
||||
});
|
||||
</script>
|
||||
@ -337,99 +251,29 @@ onMounted(async () => {
|
||||
}
|
||||
}
|
||||
|
||||
.user-content {
|
||||
.glxs-btn {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
padding: 8px 16px;
|
||||
background: linear-gradient(135deg, #4A90E2 0%, #357ABD 100%);
|
||||
color: #ffffff;
|
||||
border-top-left-radius: 20px;
|
||||
border-bottom-right-radius: 20px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
box-shadow: 0 4px 12px rgba(74, 144, 226, 0.3);
|
||||
transition: all 0.3s ease;
|
||||
flex-shrink: 0;
|
||||
min-width: 60px;
|
||||
height: 16px;
|
||||
|
||||
.user-avatar {
|
||||
position: relative;
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 16px rgba(74, 144, 226, 0.3);
|
||||
border: 3px solid #ffffff;
|
||||
flex-shrink: 0;
|
||||
|
||||
.avatar-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.avatar-ring {
|
||||
position: absolute;
|
||||
top: -3px;
|
||||
left: -3px;
|
||||
width: calc(100% + 6px);
|
||||
height: calc(100% + 6px);
|
||||
border-radius: 50%;
|
||||
border: 2px solid rgba(74, 144, 226, 0.3);
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
}
|
||||
|
||||
.user-details {
|
||||
flex: 1;
|
||||
margin-left: 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
.user-name {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: white;
|
||||
margin-bottom: 8px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.user-class-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.class-tag {
|
||||
background: linear-gradient(135deg, #4A90E2 0%, #357ABD 100%);
|
||||
color: #ffffff;
|
||||
padding: 4px 12px;
|
||||
border-radius: 12px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
box-shadow: 0 2px 8px rgba(74, 144, 226, 0.3);
|
||||
|
||||
.user-class {
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.switch-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
padding: 8px 16px;
|
||||
background: linear-gradient(135deg, #4A90E2 0%, #357ABD 100%);
|
||||
color: #ffffff;
|
||||
border-radius: 20px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
box-shadow: 0 4px 12px rgba(74, 144, 226, 0.3);
|
||||
transition: all 0.3s ease;
|
||||
flex-shrink: 0;
|
||||
min-width: 60px;
|
||||
height: 16px;
|
||||
|
||||
&:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
text {
|
||||
line-height: 1;
|
||||
}
|
||||
text {
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -662,160 +506,12 @@ onMounted(async () => {
|
||||
}
|
||||
}
|
||||
|
||||
/* 学生选择器弹窗样式 */
|
||||
.student-selector {
|
||||
background: linear-gradient(135deg, #ffffff 0%, #f8faff 100%);
|
||||
border-top-left-radius: 20px;
|
||||
border-top-right-radius: 20px;
|
||||
padding-bottom: 30px;
|
||||
|
||||
.selector-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20px;
|
||||
border-bottom: 1px solid #f0f2f5;
|
||||
|
||||
.selector-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
background-color: #f5f7fa;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.3s ease;
|
||||
flex-shrink: 0;
|
||||
|
||||
&:active {
|
||||
transform: scale(0.9);
|
||||
background-color: #e6e8eb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.student-list {
|
||||
padding: 0 20px;
|
||||
|
||||
.student-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 16px 0;
|
||||
border-bottom: 1px solid #f5f7fa;
|
||||
transition: all 0.3s ease;
|
||||
border-radius: 12px;
|
||||
margin-bottom: 8px;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&-active {
|
||||
background: linear-gradient(135deg, rgba(74, 144, 226, 0.08) 0%, rgba(53, 122, 189, 0.05) 100%);
|
||||
padding: 16px 12px;
|
||||
}
|
||||
|
||||
.student-avatar {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
flex-shrink: 0;
|
||||
box-shadow: 0 2px 8px rgba(74, 144, 226, 0.15);
|
||||
border: 2px solid #ffffff;
|
||||
|
||||
.avatar-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
.student-info {
|
||||
flex: 1;
|
||||
margin-left: 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
min-width: 0;
|
||||
|
||||
.student-name {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
margin-bottom: 4px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.student-class {
|
||||
font-size: 13px;
|
||||
color: #606266;
|
||||
font-weight: 400;
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.check-icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, #4A90E2 0%, #357ABD 100%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 2px 8px rgba(74, 144, 226, 0.3);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 动画效果 */
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.05);
|
||||
opacity: 0.7;
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 响应式优化 */
|
||||
@media (max-width: 375px) {
|
||||
.content-container {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.user-info-card .user-content .user-avatar {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
|
||||
.grid-menu .grid-item {
|
||||
padding: 15px 8px;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user