feat: 提交家校活动前端工程源码(Vue3 + Vite + view-ui-plus)
build / build (push) Successful in 5m24s

This commit is contained in:
lijing
2026-09-20 18:00:18 +08:00
parent 267ba04af2
commit 66ad7fc6c3
333 changed files with 33141 additions and 2 deletions
+170
View File
@@ -0,0 +1,170 @@
<script setup lang="ts">
import { ref, reactive, computed, onMounted, watch, provide } from "vue";
import layout from "./components/layout.vue";
import { useRoute, useRouter } from "vue-router";
import config from "@/assets/config/host.config";
import { baseApi } from "@/api/module/activity";
import { resolveMenuPage } from "@/utils/menuRoute";
import { Message } from "view-ui-plus";
const router = useRouter();
const route = useRoute();
const isLogin = ref(false);
/**
* 视图重挂载信号(配合下方 router-view 的 :key)。
*
* 解决的问题:路由表里有多组**共用同一组件**的路由 ——
* /list ↔ /activitylist 都是 ActivityList.vue
* /par/activities ↔ /par/mine 都是 ParentActivities.vue
* Vue 遇到「同一组件」会复用实例:onMounted 不再执行,按 meta 只算一次的状态
* (家长端「我的报名」默认落「已报名」标签)也不会重算,
* 于是表现为「点左边栏右边没反应,多点几下/刷新才变」——家长端尤其明显。
* 加 key 后每个路由 path 各持有一份实例,切换必然重建。
*
* 顺带把「点击当前菜单项」从 router.go(0)(整页 reload、重新拉身份与菜单、白屏一下)
* 换成自增 tick,只软重挂载内容区。
*/
const viewTick = ref(0);
provide("refreshView", () => {
viewTick.value += 1;
});
const viewKey = computed(() => `${route.fullPath}#${viewTick.value}`);
const layoutData = reactive({
appName: "家校活动",
baseHost: config.baseHost,
ssoHost: config.ssoHost,
appList: [] as any[],
userInfo: null as any,
modList: [] as any[],
isAlone: false,
});
function roleOfPath(p: string): "teacher" | "parent" {
if (p.startsWith("/par")) return "parent";
return "teacher";
}
async function initShell() {
const role = roleOfPath(router.currentRoute.value.path);
try {
// 端分离:先切换 mock 当前视角,再拉身份与菜单
await baseApi.setRole(role).send();
} catch {
/* 忽略:非 mock 环境可能无此接口 */
}
try {
// 1. 获取当前用户信息
const user: any = await baseApi.getUserInfo().send();
const { id, name, profilePicture } = user || {};
// 2. 获取当前用户已授权的模块列表
const modList: any[] = await baseApi.getUserMod().send();
layoutData.userInfo = { id, name: name || "管理员", avatar: profilePicture || "" };
layoutData.modList = modList || [];
// 开发环境打印平台原始模块配置,便于排查菜单路径解析问题
if (import.meta.env.DEV) {
console.table(
(modList || []).map((m: any) => ({
name: m.name,
id: m.id,
modId: m.modId,
path: m.page,
}))
);
}
// 有子菜单时不算「单模块独占」模式
const hasChildren = (modList || []).some((m: any) => m.children && m.children.length > 0);
layoutData.isAlone = (modList || []).length <= 1 && !hasChildren;
isLogin.value = true;
// 登录后跳转第一个可用模块("/" 本身即我发布的页,无需跳转)
if (router.currentRoute.value.path === "/") {
const firstModule = (modList || [])[0];
const firstRaw = firstModule?.children?.length
? firstModule.children[0]
: firstModule;
const first = resolveMenuPage(firstRaw?.page, firstRaw?.modId ?? firstRaw?.id, firstRaw?.name);
if (!first.external && first.path && first.path !== "/") {
router.replace(first.path);
}
}
} catch (error: any) {
console.error("加载用户信息失败", error);
if (error?.message !== "redirect") {
Message.error("载入用户信息失败,请检查后端服务是否正常!");
}
// 降级:使用默认值("/" 即我发布的页,无需跳转)
layoutData.userInfo = { name: "管理员", avatar: "" };
layoutData.modList = [];
layoutData.isAlone = true;
isLogin.value = true;
}
}
onMounted(initShell);
// 端分离:路由跨角色前缀(/par 家长端、其它发布端)切换时,重新设定视角并初始化壳层
watch(
() => router.currentRoute.value.path,
(p, prev) => {
if (roleOfPath(p) !== roleOfPath(prev || "")) initShell();
}
);
</script>
<template>
<layout v-if="isLogin" :layoutData="layoutData" />
<div v-if="isLogin" class="base-wrap-main" :class="{ 'alone-page': layoutData.isAlone }">
<div class="main-wrapper">
<router-view v-slot="{ Component }">
<component :is="Component" :key="viewKey" />
</router-view>
</div>
</div>
</template>
<style lang="scss">
html,
body {
margin: 0;
padding: 0;
height: 100%;
overflow: hidden;
}
#app {
height: 100%;
}
/* 页面级滚动容器:main-wrapper 只有 min-height 时内容超一屏会被 body 的
overflow:hidden 直接裁掉(如发布活动页),无任何滚动条。定高 + 纵向滚动,
放得下的列表页(表格高度由 Utils.useTableHeight 算好)不受影响 */
.base-wrap-main .main-wrapper {
height: 100%;
overflow-y: auto;
}
/* 表格操作列(operatingHandle 渲染的 Tooltip 包裹层)横排且随单元格居中 */
.ivu-table-cell > .ivu-tooltip {
display: inline-block;
vertical-align: middle;
margin: 0 2px;
}
/* 表格横向滚动条:列多时必然横向滚动(不要靠合并列收进一屏,会把表头挤成两行),
把浏览器默认的粗灰条收成 8px 浅灰圆角,避免压住下方的分页条 */
.ivu-table-overflowX::-webkit-scrollbar {
height: 8px;
}
.ivu-table-overflowX::-webkit-scrollbar-thumb {
background: #dcdee2;
border-radius: 4px;
}
.ivu-table-overflowX::-webkit-scrollbar-thumb:hover {
background: #c5c8ce;
}
.ivu-table-overflowX::-webkit-scrollbar-track {
background: transparent;
}
</style>