ywyonui dee7d67bcc 1、调整就餐点名
2、增加中文姓名排序依赖库
2025-09-11 18:59:56 +08:00

821 lines
19 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="service-page">
<!-- 1. 顶部 Header -->
<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="jsTx || '/static/base/default-avatar.png'"
mode="aspectFill"
></image>
</view>
<view class="teacher-details">
<view class="teacher-name">{{ js.jsxm }}</view>
<view class="teacher-position">{{ dzZwLabel }}</view>
<view class="teacher-position">{{ qtZwLabel }}</view>
<view class="teacher-class">{{ js.njz }}</view>
</view>
</view>
<!-- &lt;!&ndash; 统计信息 &ndash;&gt;
<view class="stats-info">
<view class="stat-item">
<text class="stat-label">积分</text>
<text class="stat-value">{{ jsWork.jf }}</text>
</view>
<view class="stat-divider">|</view>
<view class="stat-item">
<text class="stat-label">工作量</text>
<text class="stat-value">{{ jsWork.ks }}课时</text>
</view>
</view>-->
<!-- 介绍文字 -->
<view class="teacher-intro">
{{ js.introduction || "北冥有鱼,其名为鲲。鲲之大,不知其几千里也。" }}
</view>
</view>
<!-- 2. 主要内容区域 -->
<view class="main-content">
<!-- 循环渲染功能分区 -->
<view
class="section-block"
v-for="(section, index) in sections"
:key="section.id"
v-show="hasSectionPermission(section)"
>
<view class="section-title">
<view
class="title-decorator"
:style="{ backgroundColor: getSectionColor(index) }"
></view>
<text>{{ section.title }}</text>
<view class="title-line"></view>
</view>
<view class="section-grid-card">
<template v-for="(item, itemIdx) in section.items" :key="item.id">
<view
v-if="item.show && hasPermissionDirect(item.permissionKey)"
class="grid-item"
@click="handleGridItemClick(item)"
>
<view
class="grid-icon-container"
:style="{ backgroundColor: getIconBgColor(index) }"
>
<image
class="grid-icon"
:src="`/static/base/home/${item.icon}.png`"
mode="aspectFit"
></image>
</view>
<text class="grid-text">{{ item.text }}</text>
</view>
</template>
</view>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import { jsdfindJsByPhoneApi, clearUserOpenIdApi } from "@/api/base/server";
import { useCommonStore } from "@/store/modules/common";
import { useUserStore } from "@/store/modules/user";
import { imagUrl } from "@/utils";
import { hideLoading, showLoading } from "@/utils/uniapp";
import { hasPermission } from "@/utils/permission";
import { set } from "lodash";
import { reactive, ref, computed, watch, onMounted } from "vue";
const { logout, getUser, getJs, setJs } = useUserStore();
const { getZwListByLx } = useCommonStore();
const jsTx = computed(() => imagUrl(getUser.profilePhoto));
const js = computed(() => getJs);
const dzZwLabel = ref<any>("");
const qtZwLabel = ref<any>("");
// 加载状态
const isLoading = ref(true);
// 教师工作信息
const jsWork = ref<any>({
jf: 88,
ks: 40,
});
interface GridItem {
id: number | string;
icon: string; // 图标文件名 (不含扩展名)
text: string;
show: boolean; // 是否显示
permissionKey: string; // 权限键
path?: string; // 页面路径
}
interface Section {
id: number | string;
title: string;
permissionKey?: string; // 整个区域的权限编码
items: GridItem[];
}
// 退出登录
const handleLogout = () => {
uni.showModal({
title: "确认退出",
content: "确定要退出登录吗?",
success: async (res) => {
if (res.confirm) {
try {
// 从userdata中获取用户ID
const userDataStr = uni.getStorageSync("app-user");
let userData = null;
if (userDataStr) {
try {
userData =
typeof userDataStr === "string"
? JSON.parse(userDataStr)
: userDataStr;
} catch (e) {
// 静默处理错误
}
}
// 清空用户open_id
const userId = userData?.userdata?.id || userData?.id;
if (userId) {
await clearUserOpenIdApi({ userId: userId });
}
} catch (error) {
// 静默处理错误
}
// 清除用户数据
logout();
// 跳转到登录页面
uni.reLaunch({
url: "/pages/system/login/login",
});
}
},
});
};
// 定义功能分区数据
const sections = reactive<Section[]>([
{
id: "routine",
title: "教学常规",
permissionKey: "routine", // 整个常规区域的权限编码
items: [
{
id: "r2",
icon: "jfpj",
text: "积分评价",
show: true,
permissionKey: "routine-jfpj", // 积分评价权限编码
path: "/pages/view/routine/JiFenPingJia/JiFenPingJia",
},
{
id: "r3",
icon: "gzltj",
text: "工作量",
show: true,
permissionKey: "routine-gzl", // 工作量权限编码
path: "/pages/view/routine/GongZuoLiang/index",
},
{
id: "hs4",
icon: "cjfx",
text: "成绩分析",
show: true,
permissionKey: "home-cjfx", // 成绩分析权限编码
path: "/pages/view/homeSchool/ChengJiFenXi",
},
{
id: "r1",
icon: "stack-fill",
text: "教学资源",
show: true,
permissionKey: "routine-jszr", // 教学资源权限编码
path: "/pages/view/routine/JiaoXueZiYuan/index",
},
{
id: "r4",
icon: "rzrj",
text: "任教任职",
show: true,
permissionKey: "routine-rzrj", // 任教任职权限编码
path: "/pages/view/routine/RengJiaoRengZhi/index",
},
{
id: "r10",
icon: "qdfb",
text: "签到发布",
show: true,
permissionKey: "routine-qdfb", // 签到发布权限编码
path: "/pages/view/routine/qd/index",
},
{
id: "r7",
icon: "kctb",
text: "课程填报",
show: true,
permissionKey: "routine-kcjs", // 课程介绍权限编码
path: "/pages/view/routine/xk/xkList",
},
{
id: "r6",
icon: "kfxc",
text: "课服巡查",
show: true,
permissionKey: "routine-kfxc", // 课服巡查权限编码
path: "/pages/view/routine/kefuxuncha/xcXkList",
},
{
id: "r5",
icon: "hc-fill",
text: "食堂巡查",
show: true,
permissionKey: "routine-stxc", // 食堂巡查权限编码
path: "/pages/view/routine/ShiTangXunCha/index",
},
],
},
{
id: "home-school",
title: "家校沟通",
permissionKey: "home", // 整个家校区域的权限编码
items: [
{
id: "hs1",
icon: "jskb",
text: "教师课表",
show: true,
permissionKey: "home-jskb", // 教师课表权限编码
path: "/pages/view/homeSchool/JiaoShiKeBiao",
},
{
id: "hs2",
icon: "bjkb",
text: "班级课表",
show: true,
permissionKey: "home-bjkb", // 班级课表权限编码
path: "/pages/view/homeSchool/BanJiKeBiao",
},
{
id: "hs3",
icon: "txl",
text: "家长通讯录",
show: true,
permissionKey: "home-jztxl", // 家长通讯录权限编码
path: "/pages/view/homeSchool/parentAddressBook/index",
},
{
id: "r8",
icon: "xkdm",
text: "选课点名",
show: true,
permissionKey: "routine-kcdm", // 选课点名权限编码
path: "/pages/view/routine/xk/dmIndex",
},
{
id: "r11",
icon: "jcdm",
text: "就餐点名",
show: true,
permissionKey: "routine-jcdm", // 就餐点名权限编码
path: "/pages/view/routine/jc/bzList",
},
{
id: "r9",
icon: "jlfb",
text: "发布接龙",
show: true,
permissionKey: "routine-bjjl", // 发布接龙权限编码
path: "/pages/view/notice/index",
},
],
},
{
id: "hr",
title: "行政办公",
permissionKey: "personnel", // 整个人事区域的权限编码
items: [
{
id: "hr1",
icon: "qjsq",
text: "请假申请",
show: true,
permissionKey: "personnel-qjsq", // 请假申请权限编码
path: "/pages/view/hr/jsQj/index",
},
{
id: "hr2",
icon: "jsda",
text: "教师档案",
show: true,
permissionKey: "personnel-jsda", // 教师档案权限编码
path: "/pages/view/hr/teacherProfile/index",
},
{
id: "r12",
icon: "gw",
text: "公文流转",
show: true,
permissionKey: "routine-gwlz", // 公文流转权限编码
path: "/pages/view/routine/gwlz/index",
},
{
id: "hr3",
icon: "gz",
text: "工资条",
show: true,
permissionKey: "personnel-gzt", // 工资条权限编码
path: "/pages/view/hr/salarySlip/index",
},
],
},
]);
// 获取分区颜色
const getSectionColor = (index: number) => {
const colors = ["#4F46E5", "#059669", "#DC2626"];
return colors[index % colors.length];
};
// 获取图标背景颜色
const getIconBgColor = (index: number) => {
const colors = [
"rgba(79, 70, 229, 0.1)",
"rgba(5, 150, 105, 0.1)",
"rgba(220, 38, 38, 0.1)",
];
return colors[index % colors.length];
};
// 处理网格项点击事件
const handleGridItemClick = async (item: GridItem) => {
if (item.text == "教师档案") {
showLoading("加载中...");
const res = await jsdfindJsByPhoneApi({
phone: getJs.lxdh,
});
if (res.result.headPic && res.result.headPic.length) {
res.result.headPic = imagUrl(res.result.headPic);
}
setJs(res.result);
hideLoading();
}
setTimeout(() => {
if (item.path) {
uni.navigateTo({ url: item.path });
} else {
uni.showToast({ title: `功能 ${item.text} 暂未开放`, icon: "none" });
}
}, 200);
};
// 初始化
onMounted(async () => {
// 初始化职务信息
await initPositionInfo();
// 完成加载
isLoading.value = false;
// 强制清除权限缓存,确保数据一致性
const { clearPermissionCachePublic } = await import("@/utils/permission");
clearPermissionCachePublic();
});
// 初始化职务信息
async function initPositionInfo() {
let dzZw: any = [];
let qtZw: any = [];
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) {
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 : "";
})
.join(", ");
}
if (qtZw && qtZw.length) {
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 : "";
})
.join(", ");
}
}
// 检查区域权限
const hasSectionPermission = (section: Section) => {
if (!section.permissionKey) {
return true; // 没有权限编码的区域默认显示
}
return hasPermissionDirect(section.permissionKey);
};
// 直接权限检查函数,避免缓存问题
const hasPermissionDirect = (permissionKey: string) => {
if (!permissionKey) return true;
const userStore = useUserStore();
const permissions = userStore.getAuth;
if (!permissions || permissions.length === 0) return false;
const uniquePermissions = [...new Set(permissions)];
return uniquePermissions.includes(permissionKey);
};
</script>
<style scoped lang="scss">
.service-page {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
position: relative;
}
// 加载状态样式
.loading-container {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
display: flex;
align-items: center;
justify-content: center;
z-index: 9999;
}
.loading-content {
display: flex;
flex-direction: column;
align-items: center;
color: #ffffff;
}
.loading-spinner {
width: 40px;
height: 40px;
border: 3px solid rgba(255, 255, 255, 0.3);
border-top: 3px solid #ffffff;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-bottom: 20px;
}
.loading-text {
font-size: 16px;
font-weight: 500;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
// 顶部 Header
.header-section {
position: relative;
background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 50%, #ec4899 100%);
color: #ffffff;
overflow: hidden;
padding: 40rpx 30rpx 30rpx;
height: auto;
min-height: 270rpx;
.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 {
padding: 20px 15px 40px 15px;
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);
}
// 功能分区块
.section-block {
margin-bottom: 25px;
animation: fadeInUp 0.6s ease-out;
&:nth-child(1) {
animation-delay: 0.1s;
}
&:nth-child(2) {
animation-delay: 0.2s;
}
&:nth-child(3) {
animation-delay: 0.3s;
}
.section-title {
display: flex;
align-items: center;
margin-bottom: 15px;
padding-left: 5px;
position: relative;
.title-decorator {
width: 8px;
height: 8px;
border-radius: 50%;
margin-right: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
}
text {
font-size: 20px;
font-weight: 600;
color: #1f2937;
letter-spacing: 0.5px;
}
.title-line {
flex: 1;
height: 1px;
background: linear-gradient(
to right,
rgba(156, 163, 175, 0.3),
transparent
);
margin-left: 15px;
}
}
.section-grid-card {
background: #ffffff;
border-radius: 16px;
padding: 20px 15px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
border: 1px solid rgba(255, 255, 255, 0.8);
backdrop-filter: blur(10px);
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px 15px;
transition: all 0.3s ease;
&:hover {
transform: translateY(-2px);
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);
}
}
}
// 网格项
.grid-item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
cursor: pointer;
padding: 10px 5px;
border-radius: 12px;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
&:hover {
transform: translateY(-3px) scale(1.05);
background: rgba(79, 70, 229, 0.05);
}
&:active {
transform: translateY(-1px) scale(1.02);
}
.grid-icon-container {
width: 48px;
height: 48px;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 8px;
transition: all 0.3s ease;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
.grid-icon {
width: 24px;
height: 24px;
transition: all 0.3s ease;
}
}
.grid-text {
font-size: 14px;
color: #4b5563;
font-weight: 500;
line-height: 1.3;
transition: color 0.3s ease;
}
&:hover .grid-text {
color: #1f2937;
}
&:hover .grid-icon-container {
transform: scale(1.1);
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.15);
}
}
// 动画定义
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
// 响应式优化
@media (max-width: 375px) {
.section-grid-card {
gap: 15px 8px;
padding: 15px 10px;
}
.grid-item {
padding: 8px 3px;
}
.grid-icon-container {
width: 42px;
height: 42px;
.grid-icon {
width: 20px;
height: 20px;
}
}
}
</style>