调整部分配置,菜单为前端固定路由,无接口无权限配置仅展示用

This commit is contained in:
2090205686@qq.com 2025-05-30 13:11:39 +08:00
parent 8207f7b4cd
commit 5ebb858e83
19 changed files with 1158 additions and 336 deletions

View File

@ -1,8 +1,9 @@
# 页面标题
VITE_APP_TITLE = 后台管理系统
VITE_APP_TITLE = 政务云后台管理系统
# 开发环境配置
VITE_APP_ENV = 'development'
# 开发环境
VITE_APP_BASE_API = 'http://192.168.18.9:8080/'
VITE_APP_PLATFORM = 'http://192.168.18.99/platform'

View File

@ -1,11 +1,12 @@
# 页面标题
VITE_APP_TITLE = 后台管理系统
VITE_APP_TITLE = 政务云后台管理系统
# 生产环境配置
VITE_APP_ENV = 'production'
# 生产环境
VITE_APP_BASE_API = 'http://47.109.205.240:8080/'
VITE_APP_PLATFORM = 'http://47.109.205.240/platform'
# 是否在打包时开启压缩,支持 gzip 和 brotli
VITE_BUILD_COMPRESS = gzip

View File

@ -1,5 +1,5 @@
# 页面标题
VITE_APP_TITLE = 后台管理系统
VITE_APP_TITLE = 政务云后台管理系统
# 生产环境配置
VITE_APP_ENV = 'staging'

2
.gitignore vendored
View File

@ -19,6 +19,8 @@ selenium-debug.log
*.sln
*.local
*.history
*.zip
*.rar
package-lock.json
yarn.lock

View File

@ -43,7 +43,7 @@ export function getInfo() {
export function logout() {
return request({
url: '/auth/logout',
method: 'post'
method: 'delete'
})
}

BIN
src/assets/logo/yunying.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

BIN
src/assets/logo/zhengwu.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

12
src/components/icons.vue Normal file
View File

@ -0,0 +1,12 @@
<template>
<div>
<div v-for="icon in icons" :key="icon" style="display: inline-block; margin: 10px; text-align: center; width: 100px">
<component :is="icon" />
<div>{{ icon }}</div>
</div>
</div>
</template>
<script setup>
import * as icons from '@element-plus/icons-vue'
</script>

View File

@ -6,6 +6,8 @@
<div class="right-menu">
<template v-if="appStore.device !== 'mobile'">
<div class="right-menu-back" @click="goHome()">门户首页</div>
<header-search id="header-search" class="right-menu-item" />
<!-- <el-tooltip content="源码地址" effect="dark" placement="bottom">
@ -33,7 +35,7 @@
<el-dropdown @command="handleCommand" class="avatar-container right-menu-item hover-effect" trigger="hover">
<div class="avatar-wrapper">
<!-- <img :src="userStore.avatar" class="user-avatar" /> -->
<span class="user-avatar user-icon">{{ userStore.nickName.slice(0, 1) }}</span>
<span class="user-avatar user-icon">{{ userStore.nickName?.slice(0, 1) ?? '管' }}</span>
<span class="user-nickname"> {{ userStore.nickName }} </span>
</div>
<template #dropdown>
@ -67,6 +69,7 @@ import RuoYiDoc from '@/components/RuoYi/Doc'
import useAppStore from '@/store/modules/app'
import useUserStore from '@/store/modules/user'
import useSettingsStore from '@/store/modules/settings'
const { VITE_APP_PLATFORM } = import.meta.env;
const appStore = useAppStore()
const userStore = useUserStore()
@ -89,6 +92,10 @@ function handleCommand(command) {
}
}
function goHome() {
window.location.href = VITE_APP_PLATFORM;
}
function logout() {
ElMessageBox.confirm('确定注销并退出系统吗?', '提示', {
confirmButtonText: '确定',
@ -156,6 +163,16 @@ function toggleTheme() {
outline: none;
}
.right-menu-back {
cursor: pointer;
display: inline-block;
padding-right: 10px;
font-size: 14px;
}
.right-menu-back:hover {
color: var(--el-menu-active-color);
}
.right-menu-item {
display: inline-block;
padding: 0 8px;

View File

@ -1,7 +1,12 @@
<template>
<div class="sidebar-logo-container" :class="{ 'collapse': collapse }">
<div class="sidebar-logo-container" :class="{ collapse: collapse }">
<transition name="sidebarLogoFade">
<router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
<router-link
v-if="collapse"
key="collapse"
class="sidebar-logo-link"
to="/"
>
<img v-if="logo" :src="logo" class="sidebar-logo" />
<h1 v-else class="sidebar-title">{{ title }}</h1>
</router-link>
@ -14,40 +19,61 @@
</template>
<script setup>
import logo from '@/assets/logo/logo.png'
import useSettingsStore from '@/store/modules/settings'
import variables from '@/assets/styles/variables.module.scss'
import { computed, reactive, onMounted } from "vue";
import logo from "@/assets/logo/zhengwu.png";
import logoY from "@/assets/logo/yunying.png";
import useSettingsStore from "@/store/modules/settings";
import variables from "@/assets/styles/variables.module.scss";
defineProps({
collapse: {
type: Boolean,
required: true
}
})
required: true,
},
});
const title = import.meta.env.VITE_APP_TITLE
const settingsStore = useSettingsStore()
const sideTheme = computed(() => settingsStore.sideTheme)
const title = import.meta.env.VITE_APP_TITLE;
const settingsStore = useSettingsStore();
const sideTheme = computed(() => settingsStore.sideTheme);
// Logo
const getLogoBackground = computed(() => {
if (settingsStore.isDark) {
return 'var(--sidebar-bg)'
return "var(--sidebar-bg)";
}
return sideTheme.value === 'theme-dark' ? variables.menuBg : variables.menuLightBg
})
return sideTheme.value === "theme-dark"
? variables.menuBg
: variables.menuLightBg;
});
// Logo
const getLogoTextColor = computed(() => {
if (settingsStore.isDark) {
return 'var(--sidebar-text)'
return "var(--sidebar-text)";
}
return sideTheme.value === 'theme-dark' ? '#fff' : variables.menuLightText
})
return sideTheme.value === "theme-dark" ? "#fff" : variables.menuLightText;
});
// logo
import { getUserProfile } from "@/api/system/user";
const state = reactive({
user: {},
roleGroup: {},
postGroup: {},
});
function getUser() {
getUserProfile().then((response) => {
state.user = response.data;
state.roleGroup = response.roleGroup;
state.postGroup = response.postGroup;
console.log(state.user); //
});
}
getUser();
</script>
<style lang="scss" scoped>
@import '@/assets/styles/variables.module.scss';
@import "@/assets/styles/variables.module.scss";
.sidebarLogoFade-enter-active {
transition: opacity 1.5s;
@ -96,4 +122,4 @@ const getLogoTextColor = computed(() => {
}
}
}
</style>
</style>

View File

@ -14,7 +14,7 @@
:class="sideTheme"
>
<sidebar-item
v-for="(route, index) in sidebarRouters"
v-for="(route, index) in baseRoutes"
:key="route.path + index"
:item="route"
:base-path="route.path"
@ -25,53 +25,276 @@
</template>
<script setup>
import Logo from './Logo'
import SidebarItem from './SidebarItem'
import variables from '@/assets/styles/variables.module.scss'
import useAppStore from '@/store/modules/app'
import useSettingsStore from '@/store/modules/settings'
import usePermissionStore from '@/store/modules/permission'
import { onMounted, nextTick } from "vue";
import Logo from "./Logo";
import SidebarItem from "./SidebarItem";
import variables from "@/assets/styles/variables.module.scss";
import useAppStore from "@/store/modules/app";
import useSettingsStore from "@/store/modules/settings";
import usePermissionStore from "@/store/modules/permission";
const route = useRoute()
const appStore = useAppStore()
const settingsStore = useSettingsStore()
const permissionStore = usePermissionStore()
const route = useRoute();
const appStore = useAppStore();
const settingsStore = useSettingsStore();
const permissionStore = usePermissionStore();
const sidebarRouters = computed(() => permissionStore.sidebarRouters)
const showLogo = computed(() => settingsStore.sidebarLogo)
const sideTheme = computed(() => settingsStore.sideTheme)
const theme = computed(() => settingsStore.theme)
const isCollapse = computed(() => !appStore.sidebar.opened)
const baseRoutes = ref([]);
const sidebarRouters = computed(() => permissionStore.sidebarRouters);
const showLogo = computed(() => settingsStore.sidebarLogo);
const sideTheme = computed(() => settingsStore.sideTheme);
const theme = computed(() => settingsStore.theme);
const isCollapse = computed(() => !appStore.sidebar.opened);
import { ref } from "vue";
import { reactive } from "vue";
//
const yunying = reactive([
{
component: "Layout",
hidden: false,
path: "/plant",
alwaysShow: true,
name: "Plant",
redirect: "noRedirect",
meta: {
icon: "trace",
link: null,
noCache: false,
title: "智慧种植",
},
children: [
{
component: "plant/ambient",
hidden: false,
meta: {
title: "农业环境监测",
icon: "",
noCache: false,
link: null,
},
name: "Ambient",
path: "ambient",
},
{
component: "plant/control",
hidden: false,
meta: {
title: "生产管理控制",
icon: "",
noCache: false,
link: null,
},
name: "Control",
path: "control",
},
],
},
{
component: "Layout",
hidden: false,
path: "/trade",
alwaysShow: true,
name: "Trade",
redirect: "noRedirect",
meta: {
icon: "trace",
link: null,
noCache: false,
title: "电商交易",
},
children: [
{
component: "trade/agriculturalinputstrading",
hidden: false,
meta: {
title: "农资交易",
icon: "",
noCache: false,
link: null,
},
name: "Agriculturalinputstrading",
path: "agriculturalinputstrading",
},
{
component: "trade/supplierServices",
hidden: false,
meta: {
title: "供应商服务",
icon: "",
noCache: false,
link: null,
},
name: "SupplierServices",
path: "supplierServices",
},
{
component: "trade/procurementServices",
hidden: false,
meta: {
title: "采购商服务",
icon: "",
noCache: false,
link: null,
},
name: "ProcurementServices",
path: "procurementServices",
},
{
component: "trade/landTransaction",
hidden: false,
meta: {
title: "土地交易",
icon: "",
noCache: false,
link: null,
},
name: "LandTransaction",
path: "landTransaction",
},
],
},
{
component: "Layout",
hidden: false,
path: "/",
children: [
{
component: "brand/index",
hidden: false,
meta: {
title: "投入品监管平台",
icon: "",
noCache: false,
link: null,
},
name: "Brand",
path: "brand",
},
],
},
]);
//
const zhengwu = reactive([
{
component: "Layout",
hidden: false,
path: "/",
name: "mainbody",
children: [
{
component: "mainbody/index",
hidden: false,
meta: {
title: "生产经营主体",
icon: "icon-entities",
noCache: false,
link: null,
},
name: "Mainbody",
path: "mainbody",
},
],
},
{
component: "Layout",
hidden: false,
path: "/",
children: [
{
component: "inputs/index",
hidden: false,
meta: {
title: "投入品监管平台",
icon: "icon-input",
noCache: false,
link: null,
},
name: "Inputs",
path: "inputs",
},
],
},
{
component: "Layout",
hidden: false,
path: "/",
alwaysShow: true,
name: "Trace",
redirect: "noRedirect",
meta: {
icon: "trace",
link: null,
noCache: false,
title: "溯源管理",
},
children: [
{
component: "traceability/query",
hidden: false,
meta: {
title: "溯源查询",
icon: "icon-search",
noCache: false,
link: null,
},
name: "Query",
path: "query",
},
{
component: "traceability/statistics",
hidden: false,
meta: {
title: "溯源统计",
icon: "icon-data",
noCache: false,
link: null,
},
name: "Statistics",
path: "statistics",
},
],
},
]);
//
const getMenuBackground = computed(() => {
if (settingsStore.isDark) {
return 'var(--sidebar-bg)'
return "var(--sidebar-bg)";
}
return sideTheme.value === 'theme-dark' ? variables.menuBg : variables.menuLightBg
})
return sideTheme.value === "theme-dark"
? variables.menuBg
: variables.menuLightBg;
});
//
const getMenuTextColor = computed(() => {
if (settingsStore.isDark) {
return 'var(--sidebar-text)'
return "var(--sidebar-text)";
}
return sideTheme.value === 'theme-dark' ? variables.menuText : variables.menuLightText
})
return sideTheme.value === "theme-dark"
? variables.menuText
: variables.menuLightText;
});
const activeMenu = computed(() => {
const { meta, path } = route
const { meta, path } = route;
if (meta.activeMenu) {
return meta.activeMenu
return meta.activeMenu;
}
return path
})
return path;
});
onMounted(async () => {
baseRoutes.value = [...sidebarRouters.value];
await nextTick();
console.log("政务云菜单");
console.log(baseRoutes.value);
});
</script>
<style lang="scss" scoped>
.sidebar-container {
background-color: v-bind(getMenuBackground);
.scrollbar-wrapper {
background-color: v-bind(getMenuBackground);
}
@ -80,8 +303,9 @@ const activeMenu = computed(() => {
border: none;
height: 100%;
width: 100% !important;
.el-menu-item, .el-sub-menu__title {
.el-menu-item,
.el-sub-menu__title {
&:hover {
background-color: var(--menu-hover, rgba(0, 0, 0, 0.06)) !important;
}
@ -89,7 +313,7 @@ const activeMenu = computed(() => {
.el-menu-item {
color: v-bind(getMenuTextColor);
&.is-active {
color: var(--menu-active-text, #409eff);
background-color: var(--menu-hover, rgba(0, 0, 0, 0.06)) !important;

View File

@ -1,6 +1,6 @@
import { createWebHistory, createRouter } from 'vue-router'
import { createWebHistory, createRouter } from "vue-router";
/* Layout */
import Layout from '@/layout'
import Layout from "@/layout";
/**
* Note: 路由配置项
@ -27,148 +27,233 @@ import Layout from '@/layout'
// 公共路由
export const constantRoutes = [
{
path: '/redirect',
path: "/redirect",
component: Layout,
hidden: true,
children: [
{
path: '/redirect/:path(.*)',
component: () => import('@/views/redirect/index.vue')
}
]
path: "/redirect/:path(.*)",
component: () => import("@/views/redirect/index.vue"),
},
],
},
{
path: '/login',
component: () => import('@/views/login'),
hidden: true
path: "/login",
component: () => import("@/views/login"),
hidden: true,
},
{
path: '/register',
component: () => import('@/views/register'),
hidden: true
path: "/register",
component: () => import("@/views/register"),
hidden: true,
},
{
path: "/:pathMatch(.*)*",
component: () => import('@/views/error/404'),
hidden: true
component: () => import("@/views/error/404"),
hidden: true,
},
{
path: '/401',
component: () => import('@/views/error/401'),
hidden: true
path: "/401",
component: () => import("@/views/error/401"),
hidden: true,
},
{
path: '',
path: "/icons",
component: () => import("@/components/icons"),
hidden: true,
},
{
path: "",
component: Layout,
redirect: '/index',
redirect: "/index",
children: [
{
path: '/index',
component: () => import('@/views/index'),
name: 'Index',
meta: { title: '首页', icon: 'dashboard', affix: true }
}
]
path: "/index",
component: () => import("@/views/index"),
name: "Index",
meta: { title: "首页", icon: "dashboard", affix: true },
},
],
},
{
path: '/user',
path: "/user",
component: Layout,
hidden: true,
redirect: 'noredirect',
redirect: "noredirect",
children: [
{
path: 'profile/:activeTab?',
component: () => import('@/views/system/user/profile/index'),
name: 'Profile',
meta: { title: '个人中心', icon: 'user' }
}
]
}
]
path: "profile/:activeTab?",
component: () => import("@/views/system/user/profile/index"),
name: "Profile",
meta: { title: "个人中心", icon: "user" },
},
],
},
{
path: "/mainbody",
name: "mainbody",
component: Layout,
hidden: false,
children: [
{
path: "mainbody/index",
component: () => import("@/views/mainbody/index"),
hidden: false,
meta: {
title: "生产经营主体",
icon: "user",
noCache: false,
link: null,
},
name: "mainbody",
},
],
},
{
path: "/inputs",
name: "inputs",
component: Layout,
hidden: false,
children: [
{
path: "inputs/index",
component: () => import("@/views/inputs/index"),
hidden: false,
meta: {
title: "投入品监管平台",
icon: "tree",
noCache: false,
link: null,
},
name: "index",
},
],
},
{
path: "/traceability",
name: "traceability",
component: Layout,
hidden: false,
alwaysShow: true,
redirect: "noredirect",
meta: {
icon: "tree-table",
link: null,
noCache: false,
title: "溯源管理",
},
children: [
{
path: "traceability/query",
component: () => import("@/views/traceability/query"),
name: "query",
hidden: false,
meta: {
title: "溯源查询",
icon: "",
noCache: false,
link: null,
},
},
{
path: "traceability/statistics",
component: () => import("@/views/traceability/statistics"),
name: "溯源统计",
hidden: false,
meta: {
title: "生产管理控制",
icon: "icon-data",
noCache: false,
link: null,
},
},
],
},
];
// 动态路由,基于用户权限动态去加载
export const dynamicRoutes = [
{
path: '/system/user-auth',
path: "/system/user-auth",
component: Layout,
hidden: true,
permissions: ['system:user:edit'],
permissions: ["system:user:edit"],
children: [
{
path: 'role/:userId(\\d+)',
component: () => import('@/views/system/user/authRole'),
name: 'AuthRole',
meta: { title: '分配角色', activeMenu: '/system/user' }
}
]
path: "role/:userId(\\d+)",
component: () => import("@/views/system/user/authRole"),
name: "AuthRole",
meta: { title: "分配角色", activeMenu: "/system/user" },
},
],
},
{
path: '/system/role-auth',
path: "/system/role-auth",
component: Layout,
hidden: true,
permissions: ['system:role:edit'],
permissions: ["system:role:edit"],
children: [
{
path: 'user/:roleId(\\d+)',
component: () => import('@/views/system/role/authUser'),
name: 'AuthUser',
meta: { title: '分配用户', activeMenu: '/system/role' }
}
]
path: "user/:roleId(\\d+)",
component: () => import("@/views/system/role/authUser"),
name: "AuthUser",
meta: { title: "分配用户", activeMenu: "/system/role" },
},
],
},
{
path: '/system/dict-data',
path: "/system/dict-data",
component: Layout,
hidden: true,
permissions: ['system:dict:list'],
permissions: ["system:dict:list"],
children: [
{
path: 'index/:dictId(\\d+)',
component: () => import('@/views/system/dict/data'),
name: 'Data',
meta: { title: '字典数据', activeMenu: '/system/dict' }
}
]
path: "index/:dictId(\\d+)",
component: () => import("@/views/system/dict/data"),
name: "Data",
meta: { title: "字典数据", activeMenu: "/system/dict" },
},
],
},
{
path: '/monitor/job-log',
path: "/monitor/job-log",
component: Layout,
hidden: true,
permissions: ['monitor:job:list'],
permissions: ["monitor:job:list"],
children: [
{
path: 'index/:jobId(\\d+)',
component: () => import('@/views/monitor/job/log'),
name: 'JobLog',
meta: { title: '调度日志', activeMenu: '/monitor/job' }
}
]
path: "index/:jobId(\\d+)",
component: () => import("@/views/monitor/job/log"),
name: "JobLog",
meta: { title: "调度日志", activeMenu: "/monitor/job" },
},
],
},
{
path: '/tool/gen-edit',
path: "/tool/gen-edit",
component: Layout,
hidden: true,
permissions: ['tool:gen:edit'],
permissions: ["tool:gen:edit"],
children: [
{
path: 'index/:tableId(\\d+)',
component: () => import('@/views/tool/gen/editTable'),
name: 'GenEdit',
meta: { title: '修改生成配置', activeMenu: '/tool/gen' }
}
]
}
]
path: "index/:tableId(\\d+)",
component: () => import("@/views/tool/gen/editTable"),
name: "GenEdit",
meta: { title: "修改生成配置", activeMenu: "/tool/gen" },
},
],
},
];
const router = createRouter({
history: createWebHistory(),
routes: constantRoutes,
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition
return savedPosition;
}
return { top: 0 }
return { top: 0 };
},
})
});
export default router
export default router;

View File

@ -1,127 +1,263 @@
import auth from '@/plugins/auth'
import router, { constantRoutes, dynamicRoutes } from '@/router'
import { getRouters } from '@/api/menu'
import Layout from '@/layout/index'
import ParentView from '@/components/ParentView'
import InnerLink from '@/layout/components/InnerLink'
import auth from "@/plugins/auth";
import router, { constantRoutes, dynamicRoutes } from "@/router";
import { getRouters } from "@/api/menu";
import Layout from "@/layout/index";
import ParentView from "@/components/ParentView";
import InnerLink from "@/layout/components/InnerLink";
// 匹配views里面所有的.vue文件
const modules = import.meta.glob('./../../views/**/*.vue')
const modules = import.meta.glob("./../../views/**/*.vue");
const usePermissionStore = defineStore(
'permission',
{
state: () => ({
routes: [],
addRoutes: [],
defaultRoutes: [],
topbarRouters: [],
sidebarRouters: []
}),
actions: {
setRoutes(routes) {
this.addRoutes = routes
this.routes = constantRoutes.concat(routes)
},
setDefaultRoutes(routes) {
this.defaultRoutes = constantRoutes.concat(routes)
},
setTopbarRoutes(routes) {
this.topbarRouters = routes
},
setSidebarRouters(routes) {
this.sidebarRouters = routes
},
generateRoutes(roles) {
return new Promise(resolve => {
// 向后端请求路由数据
getRouters().then(res => {
const sdata = JSON.parse(JSON.stringify(res.data))
const rdata = JSON.parse(JSON.stringify(res.data))
const defaultData = JSON.parse(JSON.stringify(res.data))
const sidebarRoutes = filterAsyncRouter(sdata)
const rewriteRoutes = filterAsyncRouter(rdata, false, true)
const defaultRoutes = filterAsyncRouter(defaultData)
const asyncRoutes = filterDynamicRoutes(dynamicRoutes)
asyncRoutes.forEach(route => { router.addRoute(route) })
this.setRoutes(rewriteRoutes)
this.setSidebarRouters(constantRoutes.concat(sidebarRoutes))
this.setDefaultRoutes(sidebarRoutes)
this.setTopbarRoutes(defaultRoutes)
resolve(rewriteRoutes)
})
})
}
}
})
const usePermissionStore = defineStore("permission", {
state: () => ({
routes: [],
addRoutes: [],
defaultRoutes: [],
topbarRouters: [],
sidebarRouters: [],
}),
actions: {
setRoutes(routes) {
this.addRoutes = routes;
this.routes = constantRoutes.concat(routes);
},
setDefaultRoutes(routes) {
this.defaultRoutes = constantRoutes.concat(routes);
},
setTopbarRoutes(routes) {
this.topbarRouters = routes;
},
setSidebarRouters(routes) {
this.sidebarRouters = routes;
},
generateRoutes(roles) {
return new Promise((resolve) => {
// 向后端请求路由数据
// getRouters().then(res => {
// const sdata = JSON.parse(JSON.stringify(res.data))
// const rdata = JSON.parse(JSON.stringify(res.data))
// const defaultData = JSON.parse(JSON.stringify(res.data))
// const sidebarRoutes = filterAsyncRouter(sdata)
// const rewriteRoutes = filterAsyncRouter(rdata, false, true)
// const defaultRoutes = filterAsyncRouter(defaultData)
// const asyncRoutes = filterDynamicRoutes(dynamicRoutes)
// asyncRoutes.forEach(route => { router.addRoute(route) })
// this.setRoutes(rewriteRoutes)
// this.setSidebarRouters(constantRoutes.concat(sidebarRoutes))
// this.setDefaultRoutes(sidebarRoutes)
// this.setTopbarRoutes(defaultRoutes)
// resolve(rewriteRoutes)
// })
// 改为固定菜单数据演示用
const res = {
code: 200,
msg: "success",
data: [
{
alwaysShow: true,
path: "/system",
component: "Layout",
redirect: "noRedirect",
name: "System",
hidden: false,
meta: {
icon: "system",
link: null,
noCache: false,
title: "系统管理",
},
children: [
{
component: "system/user/index",
hidden: false,
meta: {
title: "用户管理",
icon: "user",
noCache: false,
link: null,
},
name: "User",
path: "user",
},
{
component: "system/role/index",
hidden: false,
meta: {
title: "角色管理",
icon: "peoples",
noCache: false,
link: null,
},
name: "Role",
path: "role",
},
{
component: "system/menu/index",
hidden: false,
meta: {
title: "菜单管理",
icon: "tree-table",
noCache: false,
link: null,
},
name: "Menu",
path: "menu",
},
{
component: "system/dept/index",
hidden: false,
meta: {
title: "部门管理",
icon: "tree",
noCache: false,
link: null,
},
name: "Dept",
path: "dept",
},
{
component: "system/notice/index",
hidden: false,
meta: {
title: "通知公告",
icon: "message",
noCache: false,
link: null,
},
name: "Notice",
path: "notice",
},
{
component: "ParentView",
hidden: false,
meta: {
title: "日志管理",
icon: "log",
noCache: false,
link: null,
},
name: "Log",
path: "log",
children: [
{
component: "monitor/operlog/index",
hidden: false,
meta: {
title: "操作日志",
icon: "form",
noCache: false,
link: null,
},
name: "Operlog",
path: "operlog",
},
{
component: "monitor/logininfor/index",
hidden: false,
meta: {
title: "登录日志",
icon: "logininfor",
noCache: false,
link: null,
},
name: "Logininfor",
path: "logininfor",
},
],
},
],
},
],
};
const sdata = JSON.parse(JSON.stringify(res.data));
const rdata = JSON.parse(JSON.stringify(res.data));
const defaultData = JSON.parse(JSON.stringify(res.data));
const sidebarRoutes = filterAsyncRouter(sdata);
const rewriteRoutes = filterAsyncRouter(rdata, false, true);
const defaultRoutes = filterAsyncRouter(defaultData);
const asyncRoutes = filterDynamicRoutes(dynamicRoutes);
asyncRoutes.forEach((route) => {
router.addRoute(route);
});
this.setRoutes(rewriteRoutes);
this.setSidebarRouters(constantRoutes.concat(sidebarRoutes));
this.setDefaultRoutes(sidebarRoutes);
this.setTopbarRoutes(defaultRoutes);
resolve(rewriteRoutes);
});
},
},
});
// 遍历后台传来的路由字符串,转换为组件对象
function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) {
return asyncRouterMap.filter(route => {
return asyncRouterMap.filter((route) => {
if (type && route.children) {
route.children = filterChildren(route.children)
route.children = filterChildren(route.children);
}
if (route.component) {
// Layout ParentView 组件特殊处理
if (route.component === 'Layout') {
route.component = Layout
} else if (route.component === 'ParentView') {
route.component = ParentView
} else if (route.component === 'InnerLink') {
route.component = InnerLink
if (route.component === "Layout") {
route.component = Layout;
} else if (route.component === "ParentView") {
route.component = ParentView;
} else if (route.component === "InnerLink") {
route.component = InnerLink;
} else {
route.component = loadView(route.component)
route.component = loadView(route.component);
}
}
if (route.children != null && route.children && route.children.length) {
route.children = filterAsyncRouter(route.children, route, type)
route.children = filterAsyncRouter(route.children, route, type);
} else {
delete route['children']
delete route['redirect']
delete route["children"];
delete route["redirect"];
}
return true
})
return true;
});
}
function filterChildren(childrenMap, lastRouter = false) {
var children = []
childrenMap.forEach(el => {
el.path = lastRouter ? lastRouter.path + '/' + el.path : el.path
if (el.children && el.children.length && el.component === 'ParentView') {
children = children.concat(filterChildren(el.children, el))
var children = [];
childrenMap.forEach((el) => {
el.path = lastRouter ? lastRouter.path + "/" + el.path : el.path;
if (el.children && el.children.length && el.component === "ParentView") {
children = children.concat(filterChildren(el.children, el));
} else {
children.push(el)
children.push(el);
}
})
return children
});
return children;
}
// 动态路由遍历,验证是否具备权限
export function filterDynamicRoutes(routes) {
const res = []
routes.forEach(route => {
const res = [];
routes.forEach((route) => {
if (route.permissions) {
if (auth.hasPermiOr(route.permissions)) {
res.push(route)
res.push(route);
}
} else if (route.roles) {
if (auth.hasRoleOr(route.roles)) {
res.push(route)
res.push(route);
}
}
})
return res
});
return res;
}
export const loadView = (view) => {
let res
let res;
for (const path in modules) {
const dir = path.split('views/')[1].split('.vue')[0]
const dir = path.split("views/")[1].split(".vue")[0];
if (dir === view) {
res = () => modules[path]()
res = () => modules[path]();
}
}
return res
}
return res;
};
export default usePermissionStore
export default usePermissionStore;

View File

@ -1,95 +1,125 @@
import router from '@/router'
import { ElMessageBox, } from 'element-plus'
import { login, logout, getInfo } from '@/api/login'
import { getToken, setToken, removeToken } from '@/utils/auth'
import { isHttp, isEmpty } from "@/utils/validate"
import defAva from '@/assets/images/profile.jpg'
import router from "@/router";
import { ElMessageBox } from "element-plus";
import { login, logout, getInfo } from "@/api/login";
import { getToken, setToken, removeToken } from "@/utils/auth";
import { isHttp, isEmpty } from "@/utils/validate";
import defAva from "@/assets/images/profile.jpg";
const useUserStore = defineStore(
'user',
{
state: () => ({
token: getToken(),
id: '',
name: '',
nickName: '',
avatar: '',
roles: [],
permissions: []
}),
actions: {
// 登录
login(userInfo) {
const username = userInfo.username.trim()
const password = userInfo.password
const code = userInfo.code
const uuid = userInfo.uuid
return new Promise((resolve, reject) => {
login(username, password, code, uuid).then(res => {
console.log(res.data.access_token)
setToken(res.data.access_token)
this.token = res.data.access_token
resolve()
}).catch(error => {
reject(error)
const useUserStore = defineStore("user", {
state: () => ({
token: getToken(),
id: "",
name: "",
nickName: "",
avatar: "",
roles: [],
permissions: [],
}),
actions: {
// 登录
login(userInfo) {
const username = userInfo.username.trim();
const password = userInfo.password;
const code = userInfo.code;
const uuid = userInfo.uuid;
return new Promise((resolve, reject) => {
login(username, password, code, uuid)
.then((res) => {
console.log(res.data.access_token);
setToken(res.data.access_token);
this.token = res.data.access_token;
resolve();
})
})
},
// 获取用户信息
getInfo() {
return new Promise((resolve, reject) => {
getInfo().then(res => {
const user = res.user
let avatar = user.avatar || ""
.catch((error) => {
reject(error);
});
});
},
// 获取用户信息
getInfo() {
return new Promise((resolve, reject) => {
getInfo()
.then((res) => {
const user = res.user;
let avatar = user.avatar || "";
if (!isHttp(avatar)) {
avatar = (isEmpty(avatar)) ? defAva : import.meta.env.VITE_APP_BASE_API + avatar
avatar = isEmpty(avatar)
? defAva
: import.meta.env.VITE_APP_BASE_API + avatar;
}
if (res.roles && res.roles.length > 0) { // 验证返回的roles是否是一个非空数组
this.roles = res.roles
this.permissions = res.permissions
if (res.roles && res.roles.length > 0) {
// 验证返回的roles是否是一个非空数组
this.roles = res.roles;
this.permissions = res.permissions;
} else {
this.roles = ['ROLE_DEFAULT']
this.roles = ["ROLE_DEFAULT"];
}
this.id = user.userId
this.name = user.userName
this.nickName = user.nickName
this.avatar = avatar
this.id = user.userId;
this.name = user.userName;
this.nickName = user.nickName;
this.avatar = avatar;
/* 初始密码提示 */
if(res.isDefaultModifyPwd) {
ElMessageBox.confirm('您的密码还是初始密码,请修改密码!', '安全提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => {
router.push({ name: 'Profile', params: { activeTab: 'resetPwd' } })
}).catch(() => {})
if (res.isDefaultModifyPwd) {
ElMessageBox.confirm(
"您的密码还是初始密码,请修改密码!",
"安全提示",
{
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
}
)
.then(() => {
router.push({
name: "Profile",
params: { activeTab: "resetPwd" },
});
})
.catch(() => {});
}
/* 过期密码提示 */
if(!res.isDefaultModifyPwd && res.isPasswordExpired) {
ElMessageBox.confirm('您的密码已过期,请尽快修改密码!', '安全提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => {
router.push({ name: 'Profile', params: { activeTab: 'resetPwd' } })
}).catch(() => {})
if (!res.isDefaultModifyPwd && res.isPasswordExpired) {
ElMessageBox.confirm(
"您的密码已过期,请尽快修改密码!",
"安全提示",
{
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
}
)
.then(() => {
router.push({
name: "Profile",
params: { activeTab: "resetPwd" },
});
})
.catch(() => {});
}
resolve(res)
}).catch(error => {
reject(error)
resolve(res);
})
})
},
// 退出系统
logOut() {
// return new Promise((resolve, reject) => {
// logout(this.token).then(() => {
// this.token = ''
// this.roles = []
// this.permissions = []
// removeToken()
// resolve()
// }).catch(error => {
// reject(error)
// })
// })
.catch((error) => {
reject(error);
});
});
},
// 退出系统
logOut() {
// return new Promise((resolve, reject) => {
// logout(this.token).then(() => {
// this.token = ''
// this.roles = []
// this.permissions = []
// removeToken()
// resolve()
// }).catch(error => {
// reject(error)
// })
// })
removeToken();
location.href = "/index";
},
},
});
removeToken()
location.href = '/index'
}
}
})
export default useUserStore
export default useUserStore;

View File

@ -2,22 +2,22 @@
<div class="app-container home">
<el-row :gutter="20">
<el-col :sm="24" :lg="16" style="padding-left: 20px">
<h2>后台管理系统</h2>
<p>后台管理系统 是为政府机关事业单位及公共服务机构量身打造的一体化智能管理平台旨在提升政务办公效率优化业务流程保障数据安全实现政务服务的数字化规范化和透明化</p>
<b>
核心功能
</b>
<h2>农业产业政务云平台 - 后台管理系统</h2>
<p>
权限分级管理支持多角色如超级管理员部门管理员审核员精细化权限控制确保数据安全与操作合规<br>
业务协同办理集成公文流转事项审批数据填报等功能实现跨部门高效协作<br>
数据可视化分析动态展示民生服务财政支出等关键指标辅助科学决策<br>
安全审计追溯操作日志全记录支持敏感行为预警与责任追溯符合等保要求<br>
移动端适配支持PC端与移动端同步办公满足紧急事务处理需求
是为县域监管部门打造的一体化智能管理平台旨在提升农业政务监管效率优化业务流程保障数据安全实现农业产业监管的数字化规范化和透明化
</p>
<b> 核心功能 </b>
<p>
权限分级管理支持多角色如超级管理员部门管理员审核员精细化权限控制确保数据安全与操作合规<br />
业务协同办理集成工单流转事项审批数据填报等功能实现跨域高效协作<br />
数据可视化分析动态展示农资监管农产品溯源等关键要素辅助监管部门管理决策<br />
安全审计追溯操作日志全记录支持敏感行为预警与责任追溯符合等保要求<br />
移动端适配支持PC端与移动端同步操作满足农业监管事务处理需求
</p>
<b>系统价值</b>
<p>
🔹 提效减负自动化流程减少人工干预缩短办事周期<br>
🔹 阳光政务全流程电子化留痕增强公众监督与信任<br>
🔹 提效减负自动化流程减少人工干预缩短办事周期<br />
🔹 阳光政务全流程电子化留痕增强公众监督与信任<br />
🔹 资源整合打破信息孤岛实现跨系统数据互通共享
</p>
<p>
@ -76,7 +76,7 @@
>http://192.168.18.99:88/platform</el-link
>
</p>
<div style="height: 200px;"></div>
<div style="height: 200px"></div>
</div>
</el-card>
</el-col>
@ -263,7 +263,7 @@
<span>客户端</span>
</div>
</template>
<div class="body" v-if="state.user.userName == 'super'">
<div class="body">
<img
src="@/assets/images/MXd3uUrO.png"
alt="donate"
@ -271,14 +271,14 @@
/>
<p style="width: 200px; text-align: center">政务云APP客户端</p>
</div>
<div class="body" v-else>
<!-- <div class="body" v-else>
<img
src="@/assets/images/4SVVtpob.png"
alt="donate"
style="width: 196px"
/>
<p style="width: 200px; text-align: center">运营云APP客户端</p>
</div>
</div> -->
</el-card>
</el-col>
</el-row>
@ -286,22 +286,24 @@
</template>
<script setup name="Index">
const version = ref("1.0.1");
import { getUserProfile } from "@/api/system/user"
const version = ref("4.0.1");
// app
import { getUserProfile } from "@/api/system/user";
const state = reactive({
user: {},
roleGroup: {},
postGroup: {}
})
postGroup: {},
});
function getUser() {
getUserProfile().then(response => {
state.user = response.data
state.roleGroup = response.roleGroup
state.postGroup = response.postGroup
console.log(state.user) //
})
getUserProfile().then((response) => {
state.user = response.data;
state.roleGroup = response.roleGroup;
state.postGroup = response.postGroup;
console.log(state.user); //
});
}
getUser()
getUser();
function goTarget(url) {
window.open(url, "__blank");

256
src/views/login copy.vue Normal file
View File

@ -0,0 +1,256 @@
<template>
<div class="login">
<el-form
ref="loginRef"
:model="loginForm"
:rules="loginRules"
class="login-form"
>
<h3 class="title">{{ title }}</h3>
<el-form-item prop="username">
<el-input
v-model="loginForm.username"
type="text"
size="large"
auto-complete="off"
placeholder="账号"
>
<template #prefix
><svg-icon icon-class="user" class="el-input__icon input-icon"
/></template>
</el-input>
</el-form-item>
<el-form-item prop="password">
<el-input
v-model="loginForm.password"
type="password"
size="large"
auto-complete="off"
placeholder="密码"
@keyup.enter="handleLogin"
>
<template #prefix
><svg-icon icon-class="password" class="el-input__icon input-icon"
/></template>
</el-input>
</el-form-item>
<!-- <el-form-item prop="code" v-if="captchaEnabled">
<el-input
v-model="loginForm.code"
size="large"
auto-complete="off"
placeholder="验证码"
style="width: 63%"
@keyup.enter="handleLogin"
>
<template #prefix><svg-icon icon-class="validCode" class="el-input__icon input-icon" /></template>
</el-input>
<div class="login-code">
<img :src="codeUrl" @click="getCode" class="login-code-img"/>
</div>
</el-form-item> -->
<el-checkbox
v-model="loginForm.rememberMe"
style="margin: 0px 0px 25px 0px"
>记住密码</el-checkbox
>
<el-form-item style="width: 100%">
<el-button
:loading="loading"
size="large"
type="primary"
style="width: 100%"
@click.prevent="handleLogin"
>
<span v-if="!loading"> </span>
<span v-else> 中...</span>
</el-button>
<div style="float: right" v-if="register">
<router-link class="link-type" :to="'/register'"
>立即注册</router-link
>
</div>
</el-form-item>
</el-form>
<!-- 底部 -->
<div class="el-login-footer">
<span>Copyright © 2018-2025 ruoyi.vip All Rights Reserved.</span>
</div>
</div>
</template>
<script setup>
import { getCodeImg } from "@/api/login";
import Cookies from "js-cookie";
import { encrypt, decrypt } from "@/utils/jsencrypt";
import useUserStore from "@/store/modules/user";
const title = import.meta.env.VITE_APP_TITLE;
const userStore = useUserStore();
const route = useRoute();
const router = useRouter();
const { proxy } = getCurrentInstance();
const loginForm = ref({
username: "admin",
password: "admin123",
rememberMe: false,
code: "",
uuid: "",
});
const loginRules = {
username: [{ required: true, trigger: "blur", message: "请输入您的账号" }],
password: [{ required: true, trigger: "blur", message: "请输入您的密码" }],
code: [{ required: true, trigger: "change", message: "请输入验证码" }],
};
const codeUrl = ref("");
const loading = ref(false);
//
const captchaEnabled = ref(true);
//
const register = ref(false);
const redirect = ref(undefined);
watch(
route,
(newRoute) => {
console.log(newRoute);
redirect.value = newRoute.query && newRoute.query.redirect;
},
{ immediate: true }
);
function handleLogin() {
proxy.$refs.loginRef.validate(async (valid) => {
if (valid) {
loading.value = true;
// cookie
if (loginForm.value.rememberMe) {
Cookies.set("username", loginForm.value.username, { expires: 30 });
Cookies.set("password", encrypt(loginForm.value.password), {
expires: 30,
});
Cookies.set("rememberMe", loginForm.value.rememberMe, { expires: 30 });
} else {
//
Cookies.remove("username");
Cookies.remove("password");
Cookies.remove("rememberMe");
}
// action
userStore
.login(loginForm.value)
.then((res) => {
const query = route.query;
const otherQueryParams = Object.keys(query).reduce((acc, cur) => {
if (cur !== "redirect") {
acc[cur] = query[cur];
}
return acc;
}, {});
router.push({ path: redirect.value || "/", query: otherQueryParams });
})
.catch(() => {
loading.value = false;
//
// if (captchaEnabled.value) {
// getCode();
// }
});
}
});
}
function getCode() {
getCodeImg().then((res) => {
captchaEnabled.value =
res.captchaEnabled === undefined ? true : res.captchaEnabled;
if (captchaEnabled.value) {
codeUrl.value = "data:image/gif;base64," + res.img;
loginForm.value.uuid = res.uuid;
}
});
}
function getCookie() {
const username = Cookies.get("username");
const password = Cookies.get("password");
const rememberMe = Cookies.get("rememberMe");
loginForm.value = {
username: username === undefined ? loginForm.value.username : username,
password:
password === undefined ? loginForm.value.password : decrypt(password),
rememberMe: rememberMe === undefined ? false : Boolean(rememberMe),
};
}
// getCode();
getCookie();
</script>
<style lang="scss" scoped>
.login {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
background-image: url("../assets/images/login-background.jpg");
background-size: cover;
}
.title {
margin: 0px auto 30px auto;
text-align: center;
color: #707070;
}
.login-form {
border-radius: 6px;
background: #ffffff;
width: 400px;
padding: 25px 25px 5px 25px;
z-index: 1;
.el-input {
height: 40px;
input {
height: 40px;
}
}
.input-icon {
height: 39px;
width: 14px;
margin-left: 0px;
}
}
.login-tip {
font-size: 13px;
text-align: center;
color: #bfbfbf;
}
.login-code {
width: 33%;
height: 40px;
float: right;
img {
cursor: pointer;
vertical-align: middle;
}
}
.el-login-footer {
height: 40px;
line-height: 40px;
position: fixed;
bottom: 0;
width: 100%;
text-align: center;
color: #fff;
font-family: Arial;
font-size: 12px;
letter-spacing: 1px;
}
.login-code-img {
height: 40px;
padding-left: 12px;
}
</style>

View File

@ -1,5 +1,13 @@
<template>
<div class="login">
<div>
<div id="loader-wrapper" v-show="showType == 1">
<div id="loader"></div>
<div class="loader-section section-left"></div>
<div class="loader-section section-right"></div>
<div class="load_title">正在加载系统资源请耐心等待</div>
</div>
</div>
<div class="login" v-show="showType == 2">
<el-form
ref="loginRef"
:model="loginForm"
@ -80,16 +88,19 @@
</template>
<script setup>
import { onMounted } from "vue";
import { getCodeImg } from "@/api/login";
import Cookies from "js-cookie";
import { encrypt, decrypt } from "@/utils/jsencrypt";
import useUserStore from "@/store/modules/user";
import { ref } from "vue";
const title = import.meta.env.VITE_APP_TITLE;
const userStore = useUserStore();
const route = useRoute();
const router = useRouter();
const { proxy } = getCurrentInstance();
const showType = ref(1);
const loginForm = ref({
username: "admin",
@ -113,9 +124,19 @@ const captchaEnabled = ref(true);
const register = ref(false);
const redirect = ref(undefined);
onMounted(() => {
// const urlType = route.query.loginType
// userStore.clearLoginType();
// if (urlType && ['1', '2'].includes(urlType)) {
// userStore.setLoginType(Number(urlType)) // 使action
// handleLogin()
// }
handleLogin()
});
watch(
route,
(newRoute) => {
console.log(newRoute);
redirect.value = newRoute.query && newRoute.query.redirect;
},
{ immediate: true }

View File

@ -97,7 +97,7 @@
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="访问编号" align="center" prop="infoId" />
<el-table-column label="用户名称" align="center" prop="userName" :show-overflow-tooltip="true" sortable="custom" :sort-orders="['descending', 'ascending']" />
<el-table-column label="地址" align="center" prop="ipaddr" :show-overflow-tooltip="true" />
<el-table-column label="地址" align="center" prop="ipaddr" :show-overflow-tooltip="true" width="180px" />
<el-table-column label="登录地点" align="center" prop="loginLocation" :show-overflow-tooltip="true" />
<el-table-column label="操作系统" align="center" prop="os" :show-overflow-tooltip="true" />
<el-table-column label="浏览器" align="center" prop="browser" :show-overflow-tooltip="true" />

View File

@ -1,5 +1,5 @@
<template>
<div class="app-container">
<div class="app-container custom-container">
<el-row :gutter="20">
<splitpanes :horizontal="appStore.device === 'mobile'" class="default-theme">
<!--部门数据-->
@ -542,3 +542,12 @@ onMounted(() => {
})
})
</script>
<style lang="scss" scoped>
.custom-container {
position: absolute;
height: 100%;
.el-row {
height: 100%;
}
}
</style>