This commit is contained in:
itcsx committed 2026-09-18 15:43:17 +08:00
1 parent 08a11abb30
commit 61869f1af0
3 files changed
+174 -53

No files matched your search

@@ -10,7 +10,7 @@
"DbContexts": {
"MasterDbContext": {
"DbContextTypeName": "MasterDbContext",
"ConnectionString": "server=nas.youjiala.com;port=3307;database=Furion;uid=root;pwd=Root@1237890;CharSet=utf8mb4;",
"ConnectionString": "server=nas.youjiala.com;port=3307;database=builder;uid=root;pwd=Root@1237890;CharSet=utf8mb4;",
"DatabaseType": "MySql",
"LazyLoadingProxiesEnabled": true,
"AuditEntityEnabled": true,
+14 -3
View File
@@ -39,17 +39,28 @@ export async function setupRouter(app: App) {
meta: {
title: "首页",
},
children: [...chs],
});
// 欢迎页使用独立外壳,不套用后台的侧边菜单 / 顶栏 / 面包屑
router.addRoute({
path: "/dashboard",
name: "dashboard",
component: () => import("../layout/home.vue"),
meta: {
title: "首页",
role: "*",
description: "欢迎页",
},
children: [
{
path: "/dashboard",
name: "dashboard",
path: "",
name: "dashboardWelcome",
meta: {
role: "*",
description: "欢迎页",
},
component: () => import("../views/dashboard.vue"),
},
...chs,
],
});
} else {
+159 -49
View File
@@ -1,64 +1,174 @@
<template>
<div class="page" :style="{
height: bsInfo.height - 120 + 'px',
}">
<div v-for="(item, index) in fd?.SecurityFunctionDisplayDatas" :key="index">
<a-row style="margin-bottom: 10px;">
<a-col :span="12">
<a-button type="default"
style="max-width: 200px;text-align: left;opacity:.6; align-items: center;justify-content: flex-start;display: flex;"
size="large" :style="'width:' + (bsInfo.width - 100) + 'px;'"
@click.prevent="onpush(item.ApiRoute)">
<cust-icon-font size="26" :type="'icon-' + item.Icon" />
<span style="font-weight: 600;color:black;"> {{ item.FunctionLocalName }}</span>
</a-button>
</a-col>
</a-row>
<a-spin :spinning="loading" :delay="200">
<div class="page-container dash-page">
<!-- 顶部欢迎区:通栏,滚动时随内容一起上移 -->
<DashboardHero />
<!-- 功能分组:卡片外壳 / 标题字号沿用 ft-page 体系 -->
<div class="page-main ft-page">
<DashboardGroupCard v-for="grp in groups" :id="'grp-' + grp.key" :key="grp.key" :name="grp.name"
:color="grp.color" :soft="grp.soft" :icon="grp.icon" :layout="grp.layout" :items="grp.items"
@navigate="onpush" />
<a-empty v-if="!groups.length && !loading" description="没有匹配的功能" />
</div>
<!-- 底部快捷栏:与其它页面一致,固定在底部 -->
<div class="page-footer dash-footer">
<DashboardBottomNav :items="navItems" @jump="onNavClick" />
</div>
</div>
</div>
</a-spin>
</template>
<script setup lang="ts">
import { reactive, inject, defineComponent, onMounted, onUnmounted, ref, unref, provide, h, computed } from "vue";
import {
DeleteOutlined, EditOutlined, LoginOutlined, CloseCircleOutlined, LaptopOutlined, ShareAltOutlined, ScissorOutlined, UploadOutlined, FolderAddOutlined, DownloadOutlined,
FolderOutlined, FileImageOutlined, NotificationOutlined, CrownOutlined, ArrowUpOutlined, PictureOutlined, UnorderedListOutlined
} from "@ant-design/icons-vue";
import { useRoute, useRouter } from "vue-router";
import securityUserService from "@/service/security/securityUserService";
import { SecurityFunctionResultData } from "@/data/auto/securityFunctionResultData";
import { bsInfo } from "@/utils/browser"
const route = useRoute();
<script lang="ts" setup>
import { ref, computed, onMounted } from "vue";
import { useRouter } from "vue-router";
import securityUserService from "@/service/security/securityUserService";
import type { SecurityFunctionDisplayData } from "@/data/auto/securityFunctionDisplayData";
import DashboardHero from "@/components/dashboard/DashboardHero.vue";
import DashboardGroupCard from "@/components/dashboard/DashboardGroupCard.vue";
import DashboardBottomNav from "@/components/dashboard/DashboardBottomNav.vue";
interface NavItem {
key: string;
label: string;
icon: string;
}
const router = useRouter();
const fd = ref<SecurityFunctionResultData>();
const loading = ref(false);
const allItems = ref<SecurityFunctionDisplayData[]>([]);
onMounted(async () => {
/* ==================== 分组定义 ==================== */
type GroupKey = "disk" | "share" | "photo" | "audio" | "video" | "family" | "other";
fd.value = await securityUserService.FindMenuByUsername();
const groupDefs: {
key: GroupKey;
name: string;
color: string;
soft: string;
icon: string;
layout: 2 | 3;
}[] = [
{ key: "disk", name: "网盘", color: "#1677ff", soft: "#e6f4ff", icon: "dbank", layout: 2 },
{ key: "share", name: "共享中心", color: "#fa8c16", soft: "#fff7e6", icon: "dgroup", layout: 2 },
{ key: "photo", name: "相册", color: "#722ed1", soft: "#f9f0ff", icon: "picture", layout: 3 },
{ key: "audio", name: "音乐", color: "#eb2f96", soft: "#fff0f6", icon: "foldermp3", layout: 3 },
{ key: "video", name: "视频", color: "#13c2c2", soft: "#e6fffb", icon: "foldervideo", layout: 3 },
{ key: "family", name: "族谱管理", color: "#52c41a", soft: "#f6ffed", icon: "family", layout: 2 },
{ key: "other", name: "更多", color: "#8a919f", soft: "#f5f6f8", icon: "gailan", layout: 3 },
];
if (fd.value && fd.value.SecurityFunctionDisplayDatas && fd.value.SecurityFunctionDisplayDatas.length > 0) {
fd.value.SecurityFunctionDisplayDatas = fd.value.SecurityFunctionDisplayDatas.filter(item => item.FunctionIndc !== "N" && item.PageUrl?.indexOf('master') > 0);
}
});
const onpush = (apiRoute: string) => {
router.push(apiRoute);
const GROUP_OF: Record<string, GroupKey> = {
DmsFileEnquiry: "disk",
DmsFileDeleteEnquiry: "disk",
FamilyGroupShare: "share",
FamilyShareOperation: "share",
FamilyGroupOperation: "share",
FamilyGroupEnquiry: "share",
Preview: "photo",
PublishPhotoOperation: "photo",
PublishPhotoEnquiry: "photo",
PreviewAudio: "audio",
PublishAudioOperation: "audio",
PublishAudioEnquiry: "audio",
PreviewVideo: "video",
PublishVideoOperation: "video",
PublishVideoEnquiry: "video",
FamilyPersonEnquiry: "family",
FamilyPersonOperation: "family",
FamilyTreeEnquiry: "family",
FamilyTreeOperation: "family",
};
const resolveGroup = (it: SecurityFunctionDisplayData): GroupKey =>
GROUP_OF[it.FunctionName] ?? "other";
/* ==================== 分组结果 ==================== */
const groups = computed(() => {
const taken = new Set<string>();
const result: { key: GroupKey; name: string; color: string; soft: string; icon: string; layout: 2 | 3; items: SecurityFunctionDisplayData[] }[] = [];
for (const g of groupDefs) {
const items: SecurityFunctionDisplayData[] = [];
for (const it of allItems.value) {
if (resolveGroup(it) !== g.key) continue;
const label = it.FunctionLocalName || it.FunctionName;
if (taken.has(label)) continue;
taken.add(label);
items.push(it);
}
if (items.length) result.push({ ...g, items });
}
return result;
});
/** 底部快捷栏只保留五大高频域 */
const navItems = computed<NavItem[]>(() =>
groupDefs
.filter(g => ["disk", "photo", "audio", "video", "family"].includes(g.key))
.map(g => {
let label = g.name;
if (label === "共享中心") label = "共享";
if (label === "族谱管理") label = "族谱";
return { key: g.key, label, icon: g.icon };
})
);
/* ==================== 交互 ==================== */
const onpush = (apiRoute: string) => {
if (apiRoute) router.push(apiRoute);
};
const onNavClick = (key: string) => {
document.getElementById("grp-" + key)?.scrollIntoView({ behavior: "smooth", block: "start" });
};
onMounted(async () => {
loading.value = true;
try {
const fd = await securityUserService.FindMenuByUsername();
const raw = fd?.SecurityFunctionDisplayDatas ?? [];
allItems.value = raw.filter(
it => it.FunctionIndc !== "N" && (it.PageUrl?.indexOf("master") ?? -1) > 0
);
} finally {
loading.value = false;
}
});
</script>
<style lang="less" scoped>
.page {
overflow: auto;
background-color: bisque;
padding-left: 16px;
padding-right: 16px;
padding-top: 8px;
margin-right: 8px;
background-image: url('/images/main/p3.png');
background-size: cover;
width: 100%;
z-index: -999;
/* a-spin 会插入两层容器,把高度透传下去,height: 100% 才会生效 */
.ant-spin-nested-loading {
height: 100%;
}
:deep(.ant-spin-container) {
height: 100%;
}
/* 独立外壳(layout/home.vue)下没有侧边菜单与面包屑,页面自己撑满整屏 */
.dash-page {
display: flex;
flex-direction: column;
height: 100%;
}
/* 卡片区独占剩余高度并内部滚动,底栏因此始终贴底 */
.dash-page .page-main {
flex: 1 1 auto;
min-height: 0;
/* 底栏已是列内固定项,不再需要为 fixed 预留的 96px */
padding-bottom: 12px;
}
/* 底部快捷栏:沿用 .page-footer,仅取消 fixed,改由 flex 列天然贴底 */
.dash-footer {
position: relative;
bottom: auto;
flex: none;
padding: 0;
}
</style>