zhxy-jsd/src/pages/base/mine/index.vue

787 lines
19 KiB
Vue
Raw Normal View History

2025-04-22 10:22:33 +08:00
<template>
<view class="mine-page">
2025-06-14 13:09:26 +08:00
<!-- 1. 顶部 Header - 复制自service页面 -->
<view class="header-section">
<view class="header-gradient"></view>
<!-- 退出按钮 -->
<view class="logout-btn" @click="handleLogout">
<text class="logout-text">退出</text>
</view>
<!-- 老师信息 -->
<view class="teacher-info">
<view class="teacher-avatar">
<image
class="avatar-image"
:src="teacherData.avatar || '/static/base/default-avatar.png'"
mode="aspectFill"
></image>
</view>
<view class="teacher-details">
<view class="teacher-name">{{ teacherData.name }}</view>
2025-08-27 22:25:20 +08:00
<view class="teacher-position">{{ teacherPosition }}</view>
2025-06-14 13:09:26 +08:00
<view class="teacher-class">{{ teacherData.className }}</view>
</view>
</view>
2025-08-27 22:25:20 +08:00
<!-- 统计信息 - 暂时隐藏 -->
<!-- <view class="stats-info">
2025-06-14 13:09:26 +08:00
<view class="stat-item">
<text class="stat-label">积分</text>
<text class="stat-value">{{ teacherData.score }}</text>
</view>
<view class="stat-divider">|</view>
<view class="stat-item">
<text class="stat-label">工作量</text>
<text class="stat-value">{{ teacherData.workload }}课时</text>
</view>
2025-08-27 22:25:20 +08:00
</view> -->
2025-06-14 13:09:26 +08:00
<!-- 介绍文字 -->
<view class="teacher-intro">
{{ teacherData.introduction }}
</view>
</view>
<!-- 2. 日程管理区域 -->
<view class="main-content">
<!-- 近期日程标题 -->
<view class="schedule-header">
<text class="schedule-title">近期日程</text>
<view class="schedule-link" @click="goToSchedule">
<text class="link-text">查看日程</text>
<text class="link-arrow">></text>
</view>
</view>
<!-- 日期选择器 -->
<view class="date-selector">
<view class="date-container">
<view
class="date-item"
v-for="(date, index) in weekDates"
:key="index"
:class="{ active: selectedDateIndex === index }"
@click="selectDate(index)"
>
<text class="date-day">{{ date.day }}</text>
<text class="date-number">{{ date.number }}</text>
<view v-if="date.hasEvent" class="date-dot"></view>
</view>
</view>
</view>
<!-- 日程列表 -->
<scroll-view class="schedule-list" scroll-y="true" show-scrollbar="false" enhanced="true">
<view class="schedule-content">
<view
class="schedule-item"
v-for="(item, index) in getCurrentSchedule"
:key="index"
>
<view class="time-column">
<text class="start-time">{{ item.startTime }}</text>
<text class="end-time" v-if="item.endTime">{{ item.endTime }}</text>
</view>
<view class="content-column">
<view class="event-header">
<view
class="event-tag"
:class="item.type"
v-if="item.tag"
>
<text class="tag-text">{{ item.tag }}</text>
</view>
<text class="event-title">{{ item.title }}</text>
</view>
<view class="event-details" v-if="item.details">
<view class="event-location" v-if="item.location">
<text class="location-icon">📍</text>
<text class="location-text">{{ item.location }}</text>
</view>
<view class="event-description" v-if="item.description">
<text class="description-icon">📝</text>
<text class="description-text">{{ item.description }}</text>
</view>
</view>
</view>
</view>
<!-- 无日程时的提示 -->
<view class="no-schedule" v-if="getCurrentSchedule.length === 0">
<text class="no-schedule-text">暂无日程安排</text>
</view>
</view>
</scroll-view>
</view>
2025-04-22 10:22:33 +08:00
</view>
</template>
<script lang="ts" setup>
2025-06-14 13:09:26 +08:00
import { useUserStore } from "@/store/modules/user";
2025-08-27 22:25:20 +08:00
import { useCommonStore } from "@/store/modules/common";
2025-06-14 13:09:26 +08:00
import { imagUrl } from "@/utils";
import { reactive, ref, computed, onMounted } from "vue";
// 定义老师数据接口
interface TeacherData {
name: string;
position: string;
className: string;
score: number;
workload: number;
introduction: string;
avatar?: string;
}
// 日程数据接口
interface ScheduleItem {
startTime: string;
endTime?: string;
title: string;
type: string;
tag?: string;
location?: string;
description?: string;
details?: boolean;
}
// 日期数据接口
interface DateItem {
day: string;
number: string;
hasEvent: boolean;
date: string;
}
2025-08-27 22:25:20 +08:00
const { logout, getUser, getJs } = useUserStore();
const { getZwListByLx } = useCommonStore();
// 职务标签
const dzZwLabel = ref<string>("");
const qtZwLabel = ref<string>("");
2025-06-14 13:09:26 +08:00
// 老师数据
const teacherData = reactive<TeacherData>({
2025-08-27 22:25:20 +08:00
name: getJs.jsxm || getUser.loginName,
position: "", // 将在初始化时设置
className: getJs.njz || "",
score: 88, // 默认值,后续可以从接口获取
workload: 40, // 默认值,后续可以从接口获取
introduction: getJs.introduction || "北冥有鱼,其名为鲲。鲲之大,不知其几千里也。",
2025-06-14 13:09:26 +08:00
avatar: imagUrl(getUser.profilePhoto),
});
2025-08-27 22:25:20 +08:00
// 计算属性:动态获取职务信息
const teacherPosition = computed(() => {
const positions = [];
if (dzZwLabel.value) positions.push(dzZwLabel.value);
if (qtZwLabel.value) positions.push(qtZwLabel.value);
return positions.join('、') || '暂无职务信息';
});
2025-06-14 13:09:26 +08:00
// 退出登录
const handleLogout = () => {
uni.showModal({
title: "确认退出",
content: "确定要退出登录吗?",
success: (res) => {
if (res.confirm) {
logout();
uni.reLaunch({
url: "/pages/system/login/login",
});
}
},
});
};
// 日程相关数据
const selectedDateIndex = ref(0); // 默认选中第一天
// 生成一周的日期
const weekDates = ref<DateItem[]>([]);
2025-08-27 22:25:20 +08:00
// 生成空数据的函数
2025-06-14 13:09:26 +08:00
const generateScheduleData = () => {
const today = new Date();
const currentDay = today.getDay();
const mondayOffset = currentDay === 0 ? -6 : 1 - currentDay;
const startOfWeek = new Date(today);
startOfWeek.setDate(today.getDate() + mondayOffset);
const data: Record<string, ScheduleItem[]> = {};
2025-08-27 22:25:20 +08:00
// 生成一周的日期,但不包含任何日程数据
2025-06-14 13:09:26 +08:00
for (let i = 0; i < 7; i++) {
const date = new Date(startOfWeek);
date.setDate(startOfWeek.getDate() + i);
const dateStr = date.getDate().toString();
2025-08-27 22:25:20 +08:00
// 所有日期都没有日程安排
data[dateStr] = [];
2025-06-14 13:09:26 +08:00
}
return data;
};
// 日程数据
const scheduleData = reactive<Record<string, ScheduleItem[]>>(generateScheduleData());
2025-04-22 10:22:33 +08:00
2025-06-14 13:09:26 +08:00
// 获取当前选中日期的日程
const getCurrentSchedule = computed(() => {
const currentDate = weekDates.value[selectedDateIndex.value];
if (!currentDate) return [];
return scheduleData[currentDate.number] || [];
});
2025-08-27 22:25:20 +08:00
// 初始化职务信息
const initPositionInfo = async () => {
try {
let dzZw: string[] = [];
let qtZw: string[] = [];
// 解析党政职务
if (getJs.dzzw && typeof getJs.dzzw === "string") {
dzZw = getJs.dzzw.split(",");
}
// 解析其他职务
if (getJs.qtzw && typeof getJs.qtzw === "string") {
qtZw = getJs.qtzw.split(",");
}
// 获取党政职务名称
if (dzZw && dzZw.length > 0) {
const res = await getZwListByLx({ zwlx: "党政职务" });
dzZwLabel.value = dzZw
.map((zwId: string) => {
const zw = res.result.find((zw: any) => zwId === zw.id);
return zw ? zw.zwmc : "";
})
.filter(Boolean)
.join(", ");
}
// 获取其他职务名称
if (qtZw && qtZw.length > 0) {
const res = await getZwListByLx({ zwlx: "其他职务" });
qtZwLabel.value = qtZw
.map((zwId: string) => {
const zw = res.result.find((zw: any) => zwId === zw.id);
return zw ? zw.zwmc : "";
})
.filter(Boolean)
.join(", ");
}
console.log("职务信息初始化完成:", { dzZwLabel: dzZwLabel.value, qtZwLabel: qtZwLabel.value });
} catch (error) {
console.error("初始化职务信息失败:", error);
}
};
2025-06-14 13:09:26 +08:00
// 初始化日期
const initDates = () => {
const days = ['一', '二', '三', '四', '五', '六', '日'];
const today = new Date();
const currentDay = today.getDay(); // 0-60是周日
// 计算本周的开始日期(周一)
const startOfWeek = new Date(today);
const mondayOffset = currentDay === 0 ? -6 : 1 - currentDay; // 如果是周日往前推6天否则推到周一
startOfWeek.setDate(today.getDate() + mondayOffset);
weekDates.value = [];
for (let i = 0; i < 7; i++) {
const date = new Date(startOfWeek);
date.setDate(startOfWeek.getDate() + i);
const dateStr = date.getDate().toString();
const hasEvent = scheduleData[dateStr] && scheduleData[dateStr].length > 0;
weekDates.value.push({
day: days[i],
number: dateStr,
hasEvent,
date: date.toISOString().split('T')[0]
});
}
};
// 选择日期
const selectDate = (index: number) => {
selectedDateIndex.value = index;
};
// 跳转到日程页面
const goToSchedule = () => {
uni.navigateTo({
url: '/pages/view/schedule/index'
});
};
2025-08-27 22:25:20 +08:00
onMounted(async () => {
// 初始化职务信息
await initPositionInfo();
// 初始化日期
2025-06-14 13:09:26 +08:00
initDates();
2025-08-27 22:25:20 +08:00
2025-06-14 13:09:26 +08:00
// 默认选中今天
const today = new Date();
const currentDay = today.getDay();
const todayIndex = currentDay === 0 ? 6 : currentDay - 1; // 周日是6其他是day-1
selectedDateIndex.value = todayIndex;
});
2025-04-22 10:22:33 +08:00
</script>
<style scoped lang="scss">
2025-06-14 13:09:26 +08:00
.mine-page {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
position: relative;
}
// 顶部 Header - 复制自service页面
.header-section {
position: relative;
background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 50%, #ec4899 100%);
color: #ffffff;
overflow: hidden;
padding: 40rpx 30rpx 30rpx;
height: auto;
2025-08-27 22:25:20 +08:00
min-height: 270rpx;
2025-06-14 13:09:26 +08:00
.header-gradient {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(
45deg,
rgba(255, 255, 255, 0.1) 0%,
rgba(255, 255, 255, 0.05) 100%
);
z-index: 1;
}
.logout-btn {
position: absolute;
top: 40rpx;
right: 30rpx;
z-index: 3;
background: rgba(255, 255, 255, 0.2);
border-radius: 20rpx;
padding: 10rpx 20rpx;
border: 1px solid rgba(255, 255, 255, 0.3);
backdrop-filter: blur(10px);
.logout-text {
color: #ffffff;
font-size: 14px;
font-weight: 500;
}
}
.teacher-info {
display: flex;
align-items: center;
margin-top: 20rpx;
z-index: 2;
position: relative;
.teacher-avatar {
width: 120rpx;
height: 120rpx;
border-radius: 50%;
overflow: hidden;
border: 3px solid rgba(255, 255, 255, 0.3);
margin-right: 20rpx;
.avatar-image {
width: 100%;
height: 100%;
}
}
.teacher-details {
flex: 1;
.teacher-name {
font-size: 20px;
font-weight: bold;
color: #ffffff;
margin-bottom: 5rpx;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
}
.teacher-position {
font-size: 14px;
color: rgba(255, 255, 255, 0.9);
margin-bottom: 5rpx;
}
.teacher-class {
font-size: 12px;
color: rgba(255, 255, 255, 0.8);
}
}
}
.stats-info {
display: flex;
align-items: center;
justify-content: center;
margin-top: 20rpx;
z-index: 2;
position: relative;
.stat-item {
display: flex;
align-items: center;
.stat-label {
font-size: 14px;
color: rgba(255, 255, 255, 0.9);
}
.stat-value {
font-size: 14px;
color: #ffffff;
font-weight: 600;
}
}
.stat-divider {
margin: 0 30rpx;
color: rgba(255, 255, 255, 0.6);
font-size: 14px;
}
}
.teacher-intro {
text-align: center;
font-size: 12px;
color: rgba(255, 255, 255, 0.8);
margin-top: 20rpx;
line-height: 1.4;
z-index: 2;
position: relative;
}
}
// 主要内容区域
.main-content {
box-sizing: border-box;
padding: 20px 12px 0px 12px; // 减少左右padding
position: relative;
background: linear-gradient(
to bottom,
rgba(255, 255, 255, 0.95) 0%,
rgba(248, 250, 252, 0.98) 100%
);
border-radius: 20px 20px 0 0;
margin-top: -20px;
z-index: 3;
box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.1);
height: calc(100vh - 340rpx); // 固定高度
display: flex;
flex-direction: column;
overflow: hidden; // 防止内容溢出
}
// 日程标题
.schedule-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
flex-shrink: 0; // 不压缩
.schedule-title {
font-size: 17px;
font-weight: 600;
color: #1f2937;
}
.schedule-link {
display: flex;
align-items: center;
cursor: pointer;
.link-text {
font-size: 13px;
color: #6b7280;
margin-right: 4px;
}
.link-arrow {
font-size: 13px;
color: #6b7280;
}
}
}
// 日期选择器
.date-selector {
margin-bottom: 15px;
background: #ffffff;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
flex-shrink: 0; // 不压缩
margin-left: 0;
margin-right: 0;
.date-container {
display: flex;
padding: 8px 4px;
justify-content: space-around; // 使用space-around确保更好的分布
width: 100%; // 占满宽度
box-sizing: border-box;
}
.date-item {
display: flex;
flex-direction: column;
align-items: center;
padding: 6px 2px;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
position: relative;
flex: 1; // 平均分配空间
max-width: calc((100% - 32px) / 7); // 减去padding后平均分配
min-width: 0; // 允许压缩
.date-day {
font-size: 10px;
color: #6b7280;
margin-bottom: 2px;
white-space: nowrap; // 防止换行
text-align: center;
}
.date-number {
font-size: 14px;
font-weight: 600;
color: #374151;
white-space: nowrap; // 防止换行
text-align: center;
}
.date-dot {
position: absolute;
bottom: 1px;
width: 3px;
height: 3px;
background: #10b981;
border-radius: 50%;
}
&.active {
background: #10b981;
color: #ffffff;
transform: scale(1.02); // 减少放大倍数,避免超出
z-index: 1; // 确保选中项在最上层
margin: 0 -1px; // 轻微负边距补偿放大效果
.date-day,
.date-number {
color: #ffffff;
}
.date-dot {
background: #ffffff;
}
}
}
}
// 日程列表
.schedule-list {
flex: 1; // 占据剩余空间
height: 0; // 配合flex使用确保滚动正常
padding-bottom: 20px;
.schedule-content {
padding-bottom: 35px; // 底部留白
}
.schedule-item {
display: flex;
padding: 14px 0;
border-bottom: 1px solid #f3f4f6;
&:last-child {
border-bottom: none;
}
.time-column {
width: 60px;
flex-shrink: 0;
margin-right: 15px;
.start-time {
display: block;
font-size: 15px;
font-weight: 600;
color: #374151;
line-height: 1.2;
}
.end-time {
display: block;
font-size: 13px;
color: #6b7280;
margin-top: 2px;
line-height: 1.2;
}
}
.content-column {
flex: 1;
min-width: 0; // 允许内容压缩
.event-header {
display: flex;
align-items: center;
margin-bottom: 8px;
.event-tag {
display: inline-flex;
align-items: center;
padding: 3px 8px;
border-radius: 12px;
margin-right: 8px;
flex-shrink: 0; // 标签不压缩
&.meeting {
background: #10b981;
}
&.class {
background: #3b82f6;
}
&.tutoring {
background: #f59e0b;
}
&.preparation {
background: #8b5cf6;
}
&.counseling {
background: #ef4444;
}
.tag-text {
font-size: 11px;
color: #ffffff;
font-weight: 500;
white-space: nowrap;
}
}
.event-title {
font-size: 15px;
font-weight: 500;
color: #1f2937;
flex: 1;
line-height: 1.3;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap; // 单行显示,超出省略
}
}
.event-details {
.event-location,
.event-description {
display: flex;
align-items: center;
margin-bottom: 4px;
.location-icon,
.description-icon {
font-size: 13px;
margin-right: 4px;
flex-shrink: 0;
}
.location-text,
.description-text {
font-size: 13px;
color: #6b7280;
line-height: 1.3;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap; // 单行显示,超出省略
}
}
}
}
}
.no-schedule {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
min-height: 200px;
.no-schedule-text {
font-size: 15px;
color: #9ca3af;
}
}
}
2025-04-22 10:22:33 +08:00
2025-06-14 13:09:26 +08:00
// 动画定义
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
2025-04-22 10:22:33 +08:00
2025-06-14 13:09:26 +08:00
// 小屏幕适配
@media (max-width: 375px) {
.main-content {
padding: 20px 8px 0px 8px; // 进一步减少padding
}
.date-selector {
.date-container {
padding: 8px 2px; // 减少内边距
}
.date-item {
padding: 5px 1px; // 减少项目内边距
.date-day {
font-size: 9px; // 稍微减小字体
}
.date-number {
font-size: 13px; // 稍微减小字体
}
&.active {
transform: scale(1.01); // 进一步减少放大倍数
margin: 0; // 移除负边距
}
}
}
}
2025-04-22 10:22:33 +08:00
</style>