Browse Source

监督端首页

dev-V1.0.6
wangmingyong@163.com 2 weeks ago
parent
commit
44dd2e409e
  1. 4
      zdxt-web-client/zdxt-admin-plus-ui/src/assets/styles/sidebar.scss
  2. 11
      zdxt-web-client/zdxt-admin-plus-ui/src/components/Breadcrumb/index.vue
  3. 4
      zdxt-web-client/zdxt-admin-plus-ui/src/components/TopNav/index.vue
  4. 10
      zdxt-web-client/zdxt-admin-plus-ui/src/layout/components/AppMain.vue
  5. 97
      zdxt-web-client/zdxt-admin-plus-ui/src/layout/components/Navbar.vue
  6. 2
      zdxt-web-client/zdxt-admin-plus-ui/src/layout/components/Sidebar/index.vue
  7. 4
      zdxt-web-client/zdxt-admin-plus-ui/src/layout/components/TagsView/index.vue
  8. 78
      zdxt-web-client/zdxt-admin-plus-ui/src/layout/index.vue
  9. 13
      zdxt-web-client/zdxt-admin-plus-ui/src/router/index.ts
  10. 20
      zdxt-web-client/zdxt-admin-plus-ui/src/store/modules/permission.ts
  11. 2
      zdxt-web-client/zdxt-admin-plus-ui/src/views/error/404.vue
  12. 1499
      zdxt-web-client/zdxt-admin-plus-ui/src/views/home/index.vue
  13. 9
      zdxt-web-client/zdxt-admin-plus-ui/src/views/supervision/statistical/utils/chartOptions.ts

4
zdxt-web-client/zdxt-admin-plus-ui/src/assets/styles/sidebar.scss

@ -17,10 +17,10 @@
transition: width 0.28s;
width: $base-sidebar-width !important;
background-color: $base-menu-background;
height: 100%;
height: calc(100% - 50px);
position: fixed;
font-size: 0;
top: 0;
top: 50px;
bottom: 0;
left: 0;
z-index: 1001;

11
zdxt-web-client/zdxt-admin-plus-ui/src/components/Breadcrumb/index.vue

@ -33,10 +33,6 @@ const getBreadcrumb = () => {
} else {
matched = route.matched.filter((item) => item.meta && item.meta.title);
}
//
if (!isDashboard(matched[0])) {
matched = [{ path: '/index', meta: { title: '首页' } }].concat(matched);
}
levelList.value = matched.filter((item) => item.meta && item.meta.title && item.meta.breadcrumb !== false);
};
const findPathNum = (str, char = '/') => {
@ -53,13 +49,6 @@ const getMatched = (pathList, routeList, matched) => {
}
}
};
const isDashboard = (route: RouteLocationMatched) => {
const name = route && (route.name as string);
if (!name) {
return false;
}
return name.trim() === 'Index';
};
const handleLink = (item) => {
const { redirect, path } = item;
redirect ? router.push(redirect) : router.push(path);

4
zdxt-web-client/zdxt-admin-plus-ui/src/components/TopNav/index.vue

@ -32,7 +32,7 @@ const visibleNumber = ref<number>(-1);
// index
const currentIndex = ref<string>();
//
const hideList = ['/index', '/user/profile'];
const hideList = ['/home', '/index', '/user/profile'];
const appStore = useAppStore();
const settingsStore = useSettingsStore();
@ -85,7 +85,7 @@ const childrenMenus = computed(() => {
//
const activeMenu = computed(() => {
let path = route.path;
if (path === '/index') {
if (path === '/home' || path === '/index') {
path = '/system/user';
}
let activePath = path;

10
zdxt-web-client/zdxt-admin-plus-ui/src/layout/components/AppMain.vue

@ -53,7 +53,7 @@ function addIframe() {
<style lang="scss" scoped>
.app-main {
/* 50= navbar 50 */
/* navbar 50px 已由 main-container 的 padding-top 处理 */
min-height: calc(100vh - 50px);
width: 100%;
position: relative;
@ -61,17 +61,19 @@ function addIframe() {
}
.fixed-header + .app-main {
padding-top: 50px;
/* fixed-header 现仅含 tags-view,无 tags-view 时为空,无需额外 padding */
padding-top: 0;
}
.hasTagsView {
.app-main {
/* 84 = navbar + tags-view = 50 + 34 */
/* 84 = navbar 50 + tags-view 34 */
min-height: calc(100vh - 84px);
}
.fixed-header + .app-main {
padding-top: 84px;
/* tags-view 34px 固定在顶部,需让出空间 */
padding-top: 34px;
}
}
</style>

97
zdxt-web-client/zdxt-admin-plus-ui/src/layout/components/Navbar.vue

@ -1,23 +1,29 @@
<template>
<div class="navbar">
<hamburger id="hamburger-container" :is-active="appStore.sidebar.opened" class="hamburger-container"
<div class="flex align-center">
<div class="logo">执法监督码 执法监督</div>
<hamburger v-if="!permissionStore.iframeMode" id="hamburger-container" :is-active="appStore.sidebar.opened" class="hamburger-container"
@toggle-click="toggleSideBar" />
<breadcrumb v-if="!settingsStore.topNav" id="breadcrumb-container" class="breadcrumb-container" />
</div>
<!-- <breadcrumb v-if="!settingsStore.topNav && !permissionStore.iframeMode" id="breadcrumb-container" class="breadcrumb-container" /> -->
<top-nav v-if="settingsStore.topNav" id="topmenu-container" class="topmenu-container" />
<div class="right-menu flex align-center">
<template v-if="appStore.device !== 'mobile'">
<el-select v-model="currentSystemCode" class="min-w-160px mr-8px" placeholder="切换菜单组"
@change="handleSystemCodeChange">
<el-option v-for="item in permissionStore.visibleSystemCodeOptions" :key="item.value" :label="item.label"
:value="item.value">
<el-icon style="vertical-align: middle; margin-right: 4px;">
<component :is="item.icon" />
</el-icon>
<div class="system-tabs">
<div
v-for="item in permissionStore.visibleSystemCodeOptions"
:key="item.value"
class="system-tab"
:class="{ 'is-active': item.value === currentSystemCode }"
@click="handleSystemCodeChange(item.value)"
>
<el-icon><component :is="item.icon" /></el-icon>
<span>{{ item.label }}</span>
</el-option>
<template #prefix><svg-icon icon-class="tree-table" class="el-input__icon input-icon" /></template>
</el-select>
</div>
</div>
<!-- <el-select
v-if="userId === 1 && tenantEnabled"
@ -136,17 +142,14 @@ const searchMenuRef = ref<InstanceType<typeof SearchMenu>>();
//
const currentSystemCode = ref(permissionStore.currentSystemCode);
watch(() => permissionStore.currentSystemCode, (val) => {
currentSystemCode.value = val;
});
/** 切换菜单组 */
const handleSystemCodeChange = async (systemCode: string) => {
currentSystemCode.value = systemCode;
await permissionStore.switchSystemCode(systemCode);
// iframe
if (!permissionStore.iframeMode) {
console.log('切换系统编码', systemCode);
const targetPath = systemCode === 'supervisionCode'
? '/statistics/administrative/enterprise-enforcement'
: '/';
await proxy?.$router.replace(targetPath);
}
};
const openSearchMenu = () => {
@ -231,14 +234,51 @@ watch(
</script>
<style lang="scss" scoped>
:deep(.el-select .el-input__wrapper) {
height: 30px;
}
:deep(.el-badge__content.is-fixed) {
top: 12px;
}
.system-tabs {
display: flex;
align-items: center;
gap: 4px;
margin-right: 12px;
}
.system-tab {
display: flex;
align-items: center;
gap: 5px;
height: 32px;
padding: 0 14px;
border-radius: 4px;
font-size: 13px;
color: #5a5e66;
cursor: pointer;
transition: all 0.2s;
white-space: nowrap;
&:hover {
color: #1e6bb8;
background: rgba(58, 174, 251, 0.08);
}
&.is-active {
background: rgba(58, 174, 251, 0.12);
color: #1e6bb8;
.el-icon {
color: #1e6bb8;
}
}
.el-icon {
font-size: 16px;
color: #5a5e66;
transition: color 0.2s;
}
}
.flex {
display: flex;
}
@ -253,7 +293,14 @@ watch(
position: relative;
//background: #fff;
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
display: flex;
justify-content: space-between;
.logo {
font-size: 18px;
color: #5a5e66;
font-weight: bold;
margin-left: 20px;
}
.hamburger-container {
line-height: 46px;
height: 100%;

2
zdxt-web-client/zdxt-admin-plus-ui/src/layout/components/Sidebar/index.vue

@ -1,6 +1,6 @@
<template>
<div :class="{ 'has-logo': showLogo }" :style="{ backgroundColor: bgColor }">
<logo v-if="showLogo" :collapse="isCollapse" />
<!-- <logo v-if="showLogo" :collapse="isCollapse" /> -->
<el-scrollbar :class="sideTheme" wrap-class="scrollbar-wrapper">
<transition :enter-active-class="proxy?.animate.menuSearchAnimate.enter" mode="out-in">
<el-menu

4
zdxt-web-client/zdxt-admin-plus-ui/src/layout/components/TagsView/index.vue

@ -49,7 +49,7 @@ const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const route = useRoute();
const router = useRouter();
const visitedViews = computed(() => useTagsViewStore().getVisitedViews());
const visitedViews = computed(() => useTagsViewStore().getVisitedViews().filter((v: RouteLocationNormalized) => v.path !== '/home'));
const routes = computed(() => usePermissionStore().getRoutes());
const theme = computed(() => useSettingsStore().theme);
const tagsIcon = computed(() => useSettingsStore().tagsIcon)
@ -81,7 +81,7 @@ const isAffix = (tag: RouteLocationNormalized) => {
};
const isFirstView = () => {
try {
return selectedTag.value.fullPath === '/index' || selectedTag.value.fullPath === visitedViews.value[1].fullPath;
return selectedTag.value.fullPath === '/home' || selectedTag.value.fullPath === visitedViews.value[1].fullPath;
} catch (err) {
return false;
}

78
zdxt-web-client/zdxt-admin-plus-ui/src/layout/index.vue

@ -3,7 +3,7 @@
<div v-if="device === 'mobile' && sidebar.opened" class="drawer-bg" @click="handleClickOutside" />
<!-- iframe 全屏模式隐藏侧边栏标签栏路由内容只保留导航栏 + iframe -->
<template v-if="permissionStore.iframeMode">
<div class="main-container" style="margin-left: 0;">
<div class="main-container iframe-mode" style="margin-left: 0;">
<div :class="{ 'fixed-header': fixedHeader }" style="width: 100%;">
<navbar ref="navbarRef" @set-layout="setLayout" />
</div>
@ -16,10 +16,14 @@
</template>
<!-- 正常菜单模式 -->
<template v-else>
<!-- 顶部头部整行 -->
<div class="top-header">
<navbar ref="navbarRef" @set-layout="setLayout" />
</div>
<!-- 下方左侧边栏 + 右内容 -->
<side-bar v-if="!sidebar.hide" class="sidebar-container" />
<div :class="{ hasTagsView: needTagsView, sidebarHide: sidebar.hide }" class="main-container">
<div :class="{ 'fixed-header': fixedHeader }">
<navbar ref="navbarRef" @set-layout="setLayout" />
<tags-view v-if="needTagsView" />
</div>
<app-main />
@ -125,9 +129,25 @@ const setLayout = () => {
z-index: 999;
}
.fixed-header {
.top-header {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 50px;
z-index: 1002;
background: $fixed-header-bg;
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
}
.main-container {
padding-top: 50px;
box-sizing: border-box;
}
.fixed-header {
position: fixed;
top: 50px;
right: 0;
z-index: 9;
width: calc(100% - #{$base-sidebar-width});
@ -147,6 +167,14 @@ const setLayout = () => {
width: 100%;
}
.iframe-mode {
padding-top: 0;
.fixed-header {
top: 0;
}
}
.iframe-main {
width: 100%;
position: relative;
@ -169,4 +197,48 @@ const setLayout = () => {
min-height: calc(100vh - 50px);
overflow: auto;
}
.iframe-mode .navbar {
background: linear-gradient(180deg, #0d1d3a 0%, #0a1628 100%);
border-bottom: 1px solid rgba(58, 174, 251, 0.2);
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
}
.iframe-mode .navbar .right-menu-item,
.iframe-mode .navbar .breadcrumb-container,
.iframe-mode .navbar .topmenu-container {
color: rgba(255, 255, 255, 0.85);
}
.iframe-mode .navbar .right-menu-item:hover,
.iframe-mode .navbar .avatar-wrapper:hover {
background: rgba(58, 174, 251, 0.15);
}
.iframe-mode .navbar .user-avatar {
border: 2px solid rgba(58, 174, 251, 0.4);
}
.iframe-mode .navbar .system-tab {
color: rgba(255, 255, 255, 0.75);
.el-icon {
color: rgba(255, 255, 255, 0.6);
}
&:hover {
color: #fff;
background: rgba(255, 255, 255, 0.08);
}
&.is-active {
background: rgba(58, 174, 251, 0.25);
color: #fff;
box-shadow: 0 0 12px rgba(58, 174, 251, 0.3);
.el-icon {
color: #3aaefb;
}
}
}
</style>

13
zdxt-web-client/zdxt-admin-plus-ui/src/router/index.ts

@ -57,16 +57,21 @@ export const constantRoutes: RouteRecordRaw[] = [
component: () => import('@/views/error/401.vue'),
hidden: true
},
{
path: '/index',
redirect: '/home',
hidden: true
},
{
path: '',
component: Layout,
redirect: '/index',
redirect: '/home',
hidden: true,
children: [
{
path: '/index',
component: () => import('@/views/index.vue'),
name: 'Index',
path: '/home',
component: () => import('@/views/home/index.vue'),
name: 'Home',
meta: { title: '首页', icon: 'dashboard', affix: true }
}
]

20
zdxt-web-client/zdxt-admin-plus-ui/src/store/modules/permission.ts

@ -23,14 +23,16 @@ export interface SystemCodeOption {
iframeUrl?: string;
/** 当设置了 fullscreenRoute 时,切换到该选项会以全屏 Vue 路由展示 */
fullscreenRoute?: string;
/** 切换到该系统后默认跳转的路由路径 */
defaultRoute?: string;
}
/** 404 兜底路由名称,方便动态移除和重新添加 */
export const CATCH_ALL_ROUTE_NAME = 'CatchAll404';
//http://10.132.108.3:85/loginByUserId
export const systemCodeOptions: SystemCodeOption[] = [
{ label: '首页', value: 'planindex', icon: 'HomeFilled', iframeUrl: 'Same-origin/loginByUserId' },
{ label: '执法监督码', value: 'supervisionCode', icon: 'Document' },
{ label: '后台管理', value: 'authService', icon: 'Setting' }
{ label: '首页', value: 'planindex', icon: 'HomeFilled', fullscreenRoute: '/home' },
{ label: '执法监督码', value: 'supervisionCode', icon: 'Document', defaultRoute: '/statistics/administrative/enterprise-enforcement' },
{ label: '后台管理', value: 'authService', icon: 'Setting', defaultRoute: '/system-setting/menu' }
];
export const usePermissionStore = defineStore('permission', () => {
@ -220,6 +222,11 @@ export const usePermissionStore = defineStore('permission', () => {
// 重新添加404兜底路由,确保放在所有动态路由之后
router.addRoute({ name: CATCH_ALL_ROUTE_NAME, path: '/:pathMatch(.*)*', component: () => import('@/views/error/404.vue') });
console.log('[路由调试] switchSystemCode 完成, 当前路由列表 =', router.getRoutes().map((r) => ({ name: r.name, path: r.path })));
// 跳转到默认路由
if (option?.defaultRoute) {
await router.push(option.defaultRoute);
}
};
/**
@ -236,15 +243,16 @@ export const usePermissionStore = defineStore('permission', () => {
currentSystemCode.value = 'planindex';
sessionStorage.setItem(SYSTEM_CODE_KEY, 'planindex');
const defaultOption = systemCodeOptions.find((o) => o.value === 'planindex');
iframeMode.value = defaultOption?.iframeUrl ? true : false;
iframeMode.value = !!(defaultOption?.iframeUrl || defaultOption?.fullscreenRoute);
iframeUrl.value = defaultOption?.iframeUrl ? buildIframeUrl(defaultOption.iframeUrl) : '';
fullscreenRoute.value = defaultOption?.fullscreenRoute || '';
};
/** 根据后端返回的路由数据,动态过滤可见的系统分类选项(首页始终显示) */
const visibleSystemCodeOptions = computed(() => {
return systemCodeOptions.filter((option) => {
// 首页(有 iframeUrl 的)始终显示
if (option.iframeUrl) return true;
// 首页(有 iframeUrl 或 fullscreenRoute 的)始终显示
if (option.iframeUrl || option.fullscreenRoute) return true;
// 其他选项:只有当后端路由中存在匹配的 systemCode 时才显示
return allRouteDataCache.value.some((route: any) => route.systemCode === option.value);
});

2
zdxt-web-client/zdxt-admin-plus-ui/src/views/error/404.vue

@ -15,7 +15,7 @@
<div class="bullshit__info">
对不起您正在寻找的页面不存在尝试检查URL的错误然后按浏览器上的刷新按钮或尝试在我们的应用程序中找到其他内容
</div>
<router-link to="/index" class="bullshit__return-home"> 返回首页 </router-link>
<router-link to="/home" class="bullshit__return-home"> 返回首页 </router-link>
</div>
</div>
</div>

1499
zdxt-web-client/zdxt-admin-plus-ui/src/views/home/index.vue

File diff suppressed because it is too large

9
zdxt-web-client/zdxt-admin-plus-ui/src/views/supervision/statistical/utils/chartOptions.ts

@ -43,7 +43,7 @@ export function buildAdminPieOption(data: { value: number; name: string }[]): EC
itemStyle: { borderRadius: 1 },
label: {
show: true,
formatter: '{b|{b}} :\n {a|{c}}{d| ({d}%)}',
formatter: '{b|{b}} ' + '{b|:}' + '\n ' + '{a|{c}}' + '{d| ({d}%)}',
rich: {
a: { color: '#3aaefb', fontSize: 12, lineHeight: 16 },
b: { color: 'rgba(255, 255, 255, .6)', fontSize: 12, lineHeight: 16 },
@ -85,7 +85,7 @@ export function buildNonAdminPieOption(data: { value: number; name: string }[]):
itemStyle: { borderRadius: 1 },
label: {
show: true,
formatter: '{b|{b}} :\n {a|{c}}{d| ({d}%)}',
formatter: '{b|{b}} ' + '{b|:}' + '\n ' + '{a|{c}}' + '{d| ({d}%)}',
rich: {
a: { color: '#3aaefb', fontSize: 12, lineHeight: 16 },
b: { color: 'rgba(255, 255, 255, .6)', fontSize: 12, lineHeight: 16 },
@ -105,7 +105,8 @@ export function buildTrendLineOption(
ent: number[],
insName: string,
entName: string,
leftAxisName: string
leftAxisName: string,
rightAxisName: string = '被执法企业数量(家)'
): EChartsOption {
const { max, interval } = getSmartMaxAndInterval(ins, ent, 5);
return {
@ -147,7 +148,7 @@ export function buildTrendLineOption(
},
{
type: 'value',
name: '被执法企业数量(家)',
name: rightAxisName,
position: 'right',
axisLine: { lineStyle: { color: 'rgba(245,144,54,.65)' } },
axisLabel: { formatter: '{value}', color: 'rgba(245,144,54,.65)' },

Loading…
Cancel
Save