821 lines
19 KiB
Vue
Raw Normal View History

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