后台管理 登陆权限问题解决
This commit is contained in:
		
							
								
								
									
										17
									
								
								frontend/src/api/user.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								frontend/src/api/user.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
import send_request from '../utils/send_request';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 用户登录
 | 
			
		||||
 * @returns
 | 
			
		||||
 */
 | 
			
		||||
export function userLogin({ username, password }) {
 | 
			
		||||
    return send_request({
 | 
			
		||||
        url: '/user/login',
 | 
			
		||||
        method: 'POST',
 | 
			
		||||
        useQS: true,
 | 
			
		||||
        params: {
 | 
			
		||||
            "username": username,
 | 
			
		||||
            "password": password,
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
};
 | 
			
		||||
@@ -1,47 +1,47 @@
 | 
			
		||||
<template>
 | 
			
		||||
	<div class="header">
 | 
			
		||||
		<!-- 折叠按钮 -->
 | 
			
		||||
		<div class="collapse-btn" @click="collapseChage">
 | 
			
		||||
			<el-icon v-if="sidebar.collapse">
 | 
			
		||||
				<Expand />
 | 
			
		||||
			</el-icon>
 | 
			
		||||
			<el-icon v-else>
 | 
			
		||||
				<Fold />
 | 
			
		||||
			</el-icon>
 | 
			
		||||
		</div>
 | 
			
		||||
		<div class="logo">{{ settings.siteFullTitle }}</div>
 | 
			
		||||
		<div class="header-right">
 | 
			
		||||
			<div class="header-user-con">
 | 
			
		||||
				<!-- 消息中心 -->
 | 
			
		||||
				<div class="btn-bell" @click="router.push('/tabs')">
 | 
			
		||||
					<el-tooltip effect="dark" :content="message ? `有${message}条未读消息` : `消息中心`" placement="bottom">
 | 
			
		||||
						<i class="el-icon-lx-notice"></i>
 | 
			
		||||
					</el-tooltip>
 | 
			
		||||
					<span class="btn-bell-badge" v-if="message"></span>
 | 
			
		||||
				</div>
 | 
			
		||||
				<!-- 用户头像 -->
 | 
			
		||||
				<el-avatar class="user-avator" :size="30" :src="imgurl" />
 | 
			
		||||
				<!-- 用户名下拉菜单 -->
 | 
			
		||||
				<el-dropdown class="user-name" trigger="click" @command="handleCommand">
 | 
			
		||||
					<span class="el-dropdown-link">
 | 
			
		||||
						{{ username }}
 | 
			
		||||
						<el-icon class="el-icon--right">
 | 
			
		||||
							<arrow-down />
 | 
			
		||||
						</el-icon>
 | 
			
		||||
					</span>
 | 
			
		||||
					<template #dropdown>
 | 
			
		||||
						<el-dropdown-menu>
 | 
			
		||||
							<a href="https://github.com/lin-xin/vue-manage-system" target="_blank">
 | 
			
		||||
								<el-dropdown-item>项目仓库</el-dropdown-item>
 | 
			
		||||
							</a>
 | 
			
		||||
							<el-dropdown-item command="user">个人中心</el-dropdown-item>
 | 
			
		||||
							<el-dropdown-item divided command="loginout">退出登录</el-dropdown-item>
 | 
			
		||||
						</el-dropdown-menu>
 | 
			
		||||
					</template>
 | 
			
		||||
				</el-dropdown>
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>
 | 
			
		||||
	</div>
 | 
			
		||||
    <div class="header">
 | 
			
		||||
        <!-- 折叠按钮 -->
 | 
			
		||||
        <div class="collapse-btn" @click="collapseChage">
 | 
			
		||||
            <el-icon v-if="sidebar.collapse">
 | 
			
		||||
                <Expand />
 | 
			
		||||
            </el-icon>
 | 
			
		||||
            <el-icon v-else>
 | 
			
		||||
                <Fold />
 | 
			
		||||
            </el-icon>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="logo">{{ settings.siteFullTitle }}</div>
 | 
			
		||||
        <div class="header-right">
 | 
			
		||||
            <div class="header-user-con">
 | 
			
		||||
                <!-- 消息中心 -->
 | 
			
		||||
                <div class="btn-bell" @click="router.push('/tabs')">
 | 
			
		||||
                    <el-tooltip effect="dark" :content="message ? `有${message}条未读消息` : `消息中心`" placement="bottom">
 | 
			
		||||
                        <i class="el-icon-lx-notice"></i>
 | 
			
		||||
                    </el-tooltip>
 | 
			
		||||
                    <span class="btn-bell-badge" v-if="message"></span>
 | 
			
		||||
                </div>
 | 
			
		||||
                <!-- 用户头像 -->
 | 
			
		||||
                <el-avatar class="user-avator" :size="30" :src="imgurl" />
 | 
			
		||||
                <!-- 用户名下拉菜单 -->
 | 
			
		||||
                <el-dropdown class="user-name" trigger="click" @command="handleCommand">
 | 
			
		||||
                    <span class="el-dropdown-link">
 | 
			
		||||
                        {{ username }}
 | 
			
		||||
                        <el-icon class="el-icon--right">
 | 
			
		||||
                            <arrow-down />
 | 
			
		||||
                        </el-icon>
 | 
			
		||||
                    </span>
 | 
			
		||||
                    <template #dropdown>
 | 
			
		||||
                        <el-dropdown-menu>
 | 
			
		||||
                            <a href="https://github.com/lin-xin/vue-manage-system" target="_blank">
 | 
			
		||||
                                <el-dropdown-item>项目仓库</el-dropdown-item>
 | 
			
		||||
                            </a>
 | 
			
		||||
                            <el-dropdown-item command="user">个人中心</el-dropdown-item>
 | 
			
		||||
                            <el-dropdown-item divided command="loginout">退出登录</el-dropdown-item>
 | 
			
		||||
                        </el-dropdown-menu>
 | 
			
		||||
                    </template>
 | 
			
		||||
                </el-dropdown>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</template>
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { onMounted } from 'vue';
 | 
			
		||||
@@ -59,131 +59,131 @@ const message: number = 2;
 | 
			
		||||
const sidebar = useSidebarStore();
 | 
			
		||||
// 侧边栏折叠
 | 
			
		||||
const collapseChage = () => {
 | 
			
		||||
	sidebar.handleCollapse();
 | 
			
		||||
    sidebar.handleCollapse();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
	// 网页打开时不折叠
 | 
			
		||||
	// if (document.body.clientWidth < 1500) {
 | 
			
		||||
	// 	collapseChage();
 | 
			
		||||
	// }
 | 
			
		||||
    // 网页打开时不折叠
 | 
			
		||||
    // if (document.body.clientWidth < 1500) {
 | 
			
		||||
    // 	collapseChage();
 | 
			
		||||
    // }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// 用户名下拉菜单选择事件
 | 
			
		||||
const router = useRouter();
 | 
			
		||||
const handleCommand = (command: string) => {
 | 
			
		||||
	if (command == 'loginout') {
 | 
			
		||||
		// 发送退出登录请求
 | 
			
		||||
		send_request('v1/user/logout', "POST");
 | 
			
		||||
		// 关闭全部标签 (销毁页面对象)
 | 
			
		||||
		const tags = useTagsStore();
 | 
			
		||||
		tags.clearTags();
 | 
			
		||||
		// 清除本地 localStorage
 | 
			
		||||
		localStorage.clear();
 | 
			
		||||
		// localStorage.removeItem('ms_username');
 | 
			
		||||
		// localStorage.removeItem('ms_user_id');
 | 
			
		||||
		// localStorage.removeItem('ms_role_id');
 | 
			
		||||
		// 跳转到登录页面
 | 
			
		||||
		router.push({
 | 
			
		||||
			path: '/login',
 | 
			
		||||
			query: {
 | 
			
		||||
				redirectTo: router.currentRoute.value.path // window.location.href
 | 
			
		||||
			},
 | 
			
		||||
		});
 | 
			
		||||
		ElMessage.success('已退出登录');
 | 
			
		||||
	} else if (command == 'user') {
 | 
			
		||||
		router.push('/user');
 | 
			
		||||
	}
 | 
			
		||||
    if (command == 'loginout') {
 | 
			
		||||
        // 发送退出登录请求
 | 
			
		||||
        send_request({
 | 
			
		||||
            url: 'v1/user/logout',
 | 
			
		||||
            method: "POST",
 | 
			
		||||
        } as any);
 | 
			
		||||
        // 关闭全部标签 (销毁页面对象)
 | 
			
		||||
        const tags = useTagsStore();
 | 
			
		||||
        tags.clearTags();
 | 
			
		||||
        // 清除本地 localStorage
 | 
			
		||||
        localStorage.clear();
 | 
			
		||||
        // 跳转到登录页面
 | 
			
		||||
        router.push({
 | 
			
		||||
            path: '/login',
 | 
			
		||||
            query: {
 | 
			
		||||
                redirectTo: router.currentRoute.value.path // window.location.href
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
        ElMessage.success('已退出登录');
 | 
			
		||||
    } else if (command == 'user') {
 | 
			
		||||
        router.push('/user');
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
</script>
 | 
			
		||||
<style scoped>
 | 
			
		||||
.header {
 | 
			
		||||
	position: relative;
 | 
			
		||||
	box-sizing: border-box;
 | 
			
		||||
	width: 100%;
 | 
			
		||||
	height: 70px;
 | 
			
		||||
	font-size: 22px;
 | 
			
		||||
	color: #fff;
 | 
			
		||||
    position: relative;
 | 
			
		||||
    box-sizing: border-box;
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    height: 70px;
 | 
			
		||||
    font-size: 22px;
 | 
			
		||||
    color: #fff;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.collapse-btn {
 | 
			
		||||
	display: flex;
 | 
			
		||||
	justify-content: center;
 | 
			
		||||
	align-items: center;
 | 
			
		||||
	height: 100%;
 | 
			
		||||
	float: left;
 | 
			
		||||
	padding: 0 21px;
 | 
			
		||||
	cursor: pointer;
 | 
			
		||||
    display: flex;
 | 
			
		||||
    justify-content: center;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
    height: 100%;
 | 
			
		||||
    float: left;
 | 
			
		||||
    padding: 0 21px;
 | 
			
		||||
    cursor: pointer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.header .logo {
 | 
			
		||||
	float: left;
 | 
			
		||||
	/* width: 250px; */
 | 
			
		||||
	line-height: 70px;
 | 
			
		||||
	/* 系统名称不换行 */
 | 
			
		||||
	white-space: nowrap;
 | 
			
		||||
    float: left;
 | 
			
		||||
    /* width: 250px; */
 | 
			
		||||
    line-height: 70px;
 | 
			
		||||
    /* 系统名称不换行 */
 | 
			
		||||
    white-space: nowrap;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.header-right {
 | 
			
		||||
	float: right;
 | 
			
		||||
	padding-right: 50px;
 | 
			
		||||
    float: right;
 | 
			
		||||
    padding-right: 50px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.header-user-con {
 | 
			
		||||
	display: flex;
 | 
			
		||||
	height: 70px;
 | 
			
		||||
	align-items: center;
 | 
			
		||||
    display: flex;
 | 
			
		||||
    height: 70px;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.btn-fullscreen {
 | 
			
		||||
	transform: rotate(45deg);
 | 
			
		||||
	margin-right: 5px;
 | 
			
		||||
	font-size: 24px;
 | 
			
		||||
    transform: rotate(45deg);
 | 
			
		||||
    margin-right: 5px;
 | 
			
		||||
    font-size: 24px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.btn-bell,
 | 
			
		||||
.btn-fullscreen {
 | 
			
		||||
	position: relative;
 | 
			
		||||
	width: 30px;
 | 
			
		||||
	height: 30px;
 | 
			
		||||
	text-align: center;
 | 
			
		||||
	border-radius: 15px;
 | 
			
		||||
	cursor: pointer;
 | 
			
		||||
	display: flex;
 | 
			
		||||
	align-items: center;
 | 
			
		||||
    position: relative;
 | 
			
		||||
    width: 30px;
 | 
			
		||||
    height: 30px;
 | 
			
		||||
    text-align: center;
 | 
			
		||||
    border-radius: 15px;
 | 
			
		||||
    cursor: pointer;
 | 
			
		||||
    display: flex;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.btn-bell-badge {
 | 
			
		||||
	position: absolute;
 | 
			
		||||
	right: 4px;
 | 
			
		||||
	top: 0px;
 | 
			
		||||
	width: 8px;
 | 
			
		||||
	height: 8px;
 | 
			
		||||
	border-radius: 4px;
 | 
			
		||||
	background: #f56c6c;
 | 
			
		||||
	color: #fff;
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    right: 4px;
 | 
			
		||||
    top: 0px;
 | 
			
		||||
    width: 8px;
 | 
			
		||||
    height: 8px;
 | 
			
		||||
    border-radius: 4px;
 | 
			
		||||
    background: #f56c6c;
 | 
			
		||||
    color: #fff;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.btn-bell .el-icon-lx-notice {
 | 
			
		||||
	color: #fff;
 | 
			
		||||
    color: #fff;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.user-name {
 | 
			
		||||
	margin-left: 10px;
 | 
			
		||||
    margin-left: 10px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.user-avator {
 | 
			
		||||
	margin-left: 20px;
 | 
			
		||||
    margin-left: 20px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.el-dropdown-link {
 | 
			
		||||
	color: #fff;
 | 
			
		||||
	cursor: pointer;
 | 
			
		||||
	display: flex;
 | 
			
		||||
	align-items: center;
 | 
			
		||||
    color: #fff;
 | 
			
		||||
    cursor: pointer;
 | 
			
		||||
    display: flex;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.el-dropdown-menu__item {
 | 
			
		||||
	text-align: center;
 | 
			
		||||
    text-align: center;
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
 
 | 
			
		||||
@@ -15,11 +15,13 @@ app.use(router);
 | 
			
		||||
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
 | 
			
		||||
    app.component(key, component);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 自定义权限指令
 | 
			
		||||
const permiss = usePermissStore();
 | 
			
		||||
app.directive('permiss', {
 | 
			
		||||
app.directive('permiss', { // 元素级权限控制
 | 
			
		||||
    mounted(el, binding) {
 | 
			
		||||
        if (!permiss.key.includes(String(binding.value))) {
 | 
			
		||||
        const role = localStorage.getItem('ms_role_id');
 | 
			
		||||
        if (!permiss[role as string] || !permiss[role as string].includes(binding.value as string)) {
 | 
			
		||||
            el['hidden'] = true;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 
 | 
			
		||||
@@ -198,18 +198,26 @@ const router = createRouter({
 | 
			
		||||
 | 
			
		||||
router.beforeEach((to, from, next) => {
 | 
			
		||||
    document.title = `${to.meta.title} | ${settings.siteTitle}`;
 | 
			
		||||
    const role = localStorage.getItem('ms_username');
 | 
			
		||||
    const username = localStorage.getItem('ms_username');
 | 
			
		||||
    const role = localStorage.getItem('ms_role_id');
 | 
			
		||||
    const permiss = usePermissStore();
 | 
			
		||||
    if (!role && to.path !== '/login') {
 | 
			
		||||
    const currentUserPermiss = permiss[role as string];
 | 
			
		||||
    // console.log("currentUserPermiss", currentUserPermiss)
 | 
			
		||||
    if (!username && to.path !== '/login') {
 | 
			
		||||
        next({
 | 
			
		||||
            path: '/login',
 | 
			
		||||
            query: {
 | 
			
		||||
                redirectTo: router.currentRoute.value.path // window.location.href
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
    } else if (to.meta.permiss && !permiss.key.includes(to.meta.permiss)) {
 | 
			
		||||
        return
 | 
			
		||||
    } else if (
 | 
			
		||||
        to.meta.permiss &&
 | 
			
		||||
        !(currentUserPermiss && currentUserPermiss.includes(to.meta.permiss as string))
 | 
			
		||||
    ) {
 | 
			
		||||
        // 如果没有权限,则进入403
 | 
			
		||||
        next('/403');
 | 
			
		||||
        return
 | 
			
		||||
    } else {
 | 
			
		||||
        next();
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -2,21 +2,61 @@ import { defineStore } from 'pinia';
 | 
			
		||||
import send_request from '../utils/send_request';
 | 
			
		||||
 | 
			
		||||
interface ObjectList {
 | 
			
		||||
	[key: string]: string[];
 | 
			
		||||
    [key: string]: string[];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 不同 role 的用户分别有哪些权限
 | 
			
		||||
export const usePermissStore = defineStore('permiss', {
 | 
			
		||||
	state: () => {
 | 
			
		||||
		const keys = localStorage.getItem('ms_keys');
 | 
			
		||||
		const defaultList = localStorage.getItem('ms_default_list');
 | 
			
		||||
		return {
 | 
			
		||||
			key: keys ? JSON.parse(keys) : <string[]>[],
 | 
			
		||||
			defaultList: JSON.stringify(defaultList)
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
	actions: {
 | 
			
		||||
		handleSet(val: string[]) {
 | 
			
		||||
			this.key = val;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
    state: () => {
 | 
			
		||||
        return {
 | 
			
		||||
            "1": [
 | 
			
		||||
                "dashboard",
 | 
			
		||||
                "data-integration",
 | 
			
		||||
                "default",
 | 
			
		||||
                "equipment",
 | 
			
		||||
                "equipment-setting",
 | 
			
		||||
                "equipment-setting-manage",
 | 
			
		||||
                "equipment-view",
 | 
			
		||||
                "monitor-data",
 | 
			
		||||
                "monitor-data-statistics",
 | 
			
		||||
                "monitor-data-view",
 | 
			
		||||
                "privilege",
 | 
			
		||||
                "privilege-role-setting",
 | 
			
		||||
                "privilege-user-setting",
 | 
			
		||||
                "report",
 | 
			
		||||
                "report-upload",
 | 
			
		||||
                "resource",
 | 
			
		||||
                "resource-staff-setting",
 | 
			
		||||
                "resource-vehicle-setting",
 | 
			
		||||
                "site-info",
 | 
			
		||||
                "warning",
 | 
			
		||||
                "warning-log",
 | 
			
		||||
                "warning-setting",
 | 
			
		||||
                "warning-view"
 | 
			
		||||
            ],
 | 
			
		||||
            "2": [
 | 
			
		||||
                "dashboard",
 | 
			
		||||
                "data-integration",
 | 
			
		||||
                "default",
 | 
			
		||||
                "equipment",
 | 
			
		||||
                "equipment-setting",
 | 
			
		||||
                "equipment-view",
 | 
			
		||||
                "monitor-data",
 | 
			
		||||
                "monitor-data-statistics",
 | 
			
		||||
                "monitor-data-view",
 | 
			
		||||
                "report",
 | 
			
		||||
                "report-upload",
 | 
			
		||||
                "site-info",
 | 
			
		||||
                "warning",
 | 
			
		||||
                "warning-log",
 | 
			
		||||
                "warning-setting",
 | 
			
		||||
                "warning-view"
 | 
			
		||||
            ]
 | 
			
		||||
        };
 | 
			
		||||
    },
 | 
			
		||||
    // actions: {
 | 
			
		||||
    //     handleSet(val: string[]) {
 | 
			
		||||
    //         this.key = val;
 | 
			
		||||
    //     }
 | 
			
		||||
    // }
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -1,52 +1,67 @@
 | 
			
		||||
import request from './request';
 | 
			
		||||
import settings from './settings';
 | 
			
		||||
 | 
			
		||||
async function send_request(url, method = "POST", params, callback) {
 | 
			
		||||
    if (!url) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let returnData = await request({
 | 
			
		||||
        baseURL: settings.backendHost,
 | 
			
		||||
        url: url,
 | 
			
		||||
        method: method,
 | 
			
		||||
        withCredentials: true,
 | 
			
		||||
        // POST 请求参数
 | 
			
		||||
        data: method.toUpperCase() == "POST" ? params : null,
 | 
			
		||||
        // GET 请求参数
 | 
			
		||||
        params: method.toUpperCase() == "GET" ? params : null,
 | 
			
		||||
    }).then((response) => {
 | 
			
		||||
        let result = response.data;
 | 
			
		||||
        // 判断后端是否处理成功
 | 
			
		||||
        if (!result.isSuccess) {
 | 
			
		||||
            // 用户未登录情况
 | 
			
		||||
            if (result.data && result.data.errCode == 20003) {
 | 
			
		||||
                ElMessage.error(result?.data?.errMsg || "用户未登录");
 | 
			
		||||
                localStorage.clear();
 | 
			
		||||
                // window.location.reload();
 | 
			
		||||
                // 如果同时发出多个请求,可能会多次进来,第二次及之后进入时,hash已经变成 #/login 了
 | 
			
		||||
                if (!window.location.hash.includes("/login")) {
 | 
			
		||||
                    let newUrl = '/#/login?redirectTo=' + encodeURIComponent(window.location.hash.substring(1).split('?')[0])
 | 
			
		||||
                    console.log("newUrl", newUrl)
 | 
			
		||||
                    window.location.href = newUrl;
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                ElMessage.error(result?.data?.errMsg || "服务器错误");
 | 
			
		||||
            }
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        let data = result.data;
 | 
			
		||||
        if (typeof (callback) === "function") {
 | 
			
		||||
            callback(data);
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }).catch((err) => {
 | 
			
		||||
        console.error(err);
 | 
			
		||||
        ElMessage.error(err.message);
 | 
			
		||||
        // ElMessage.error('请求超时,请检查网络连接');
 | 
			
		||||
        return false;
 | 
			
		||||
    })
 | 
			
		||||
    return returnData;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default send_request;
 | 
			
		||||
import request from './request';
 | 
			
		||||
import settings from './settings';
 | 
			
		||||
import { ElMessage, ElLoading } from 'element-plus';
 | 
			
		||||
import qs from 'qs';
 | 
			
		||||
 | 
			
		||||
async function send_request({ url, method = "POST", params, useQS = false, callback }) {
 | 
			
		||||
    if (!url) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const loading = ElLoading.service({
 | 
			
		||||
        lock: true,
 | 
			
		||||
        text: '请稍候',
 | 
			
		||||
        background: 'rgba(0, 0, 0, 0.7)',
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    let opt = {
 | 
			
		||||
        baseURL: settings.backendHost,
 | 
			
		||||
        url: url,
 | 
			
		||||
        method: method,
 | 
			
		||||
        withCredentials: true,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (method.toUpperCase() == "POST") {
 | 
			
		||||
        // POST 请求参数
 | 
			
		||||
        opt.headers = { 'content-type': 'application/x-www-form-urlencoded' }
 | 
			
		||||
        opt.data = useQS ? qs.stringify(params) : params
 | 
			
		||||
    } else if (method.toUpperCase() == "GET") {
 | 
			
		||||
        // GET 请求参数
 | 
			
		||||
        opt.params = params
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return request(opt).then((response) => {
 | 
			
		||||
        let result = response.data;
 | 
			
		||||
        // 判断后端是否处理成功
 | 
			
		||||
        if (!result.success) {
 | 
			
		||||
            // 用户未登录情况
 | 
			
		||||
            // if (result.data && result.data.errCode == 20003) {
 | 
			
		||||
            //     ElMessage.error(result?.data?.msg || "用户未登录");
 | 
			
		||||
            //     localStorage.clear();
 | 
			
		||||
            //     // window.location.reload();
 | 
			
		||||
            //     // 如果同时发出多个请求,可能会多次进来,第二次及之后进入时,hash已经变成 #/login 了
 | 
			
		||||
            //     if (!window.location.hash.includes("/login")) {
 | 
			
		||||
            //         let newUrl = '/#/login?redirectTo=' + encodeURIComponent(window.location.hash.substring(1).split('?')[0])
 | 
			
		||||
            //         console.log("newUrl", newUrl)
 | 
			
		||||
            //         window.location.href = newUrl;
 | 
			
		||||
            //     }
 | 
			
		||||
            // } else {
 | 
			
		||||
                ElMessage.error(result?.data?.msg || "服务器错误");
 | 
			
		||||
            // }
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        let data = result.data;
 | 
			
		||||
        if (typeof (callback) === "function") {
 | 
			
		||||
            callback(data);
 | 
			
		||||
        }
 | 
			
		||||
        return data;
 | 
			
		||||
    }).catch((err) => {
 | 
			
		||||
        console.error(err);
 | 
			
		||||
        ElMessage.error(err.message);
 | 
			
		||||
        return false;
 | 
			
		||||
    }).finally(() => {
 | 
			
		||||
        loading.close();
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default send_request;
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,7 @@ export default {
 | 
			
		||||
     * (网页标题 / 登录页显示)
 | 
			
		||||
     */
 | 
			
		||||
    siteTitle: "社区疫情防控系统",
 | 
			
		||||
    siteFullTitle: "社区疫情防控系统 - 后台管理系统(社区管理员)",
 | 
			
		||||
    siteFullTitle: "社区疫情防控系统 - 后台管理系统",
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 开发公司名称
 | 
			
		||||
@@ -21,7 +21,7 @@ export default {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 后端接口请求地址
 | 
			
		||||
     * (以 / 结尾)
 | 
			
		||||
     * (结尾加不加 / 都可)
 | 
			
		||||
     */
 | 
			
		||||
    backendHost: "http://epp.only4.work/",
 | 
			
		||||
    backendHost: "https://epp.only4.work/",
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,163 +1,136 @@
 | 
			
		||||
<template>
 | 
			
		||||
	<div class="login-wrap">
 | 
			
		||||
		<div class="login-container">
 | 
			
		||||
			<div class="ms-login">
 | 
			
		||||
				<div class="ms-title">{{ settings.siteFullTitle }}</div>
 | 
			
		||||
				<el-form :model="param" :rules="rules" ref="login" label-width="0px" class="ms-content">
 | 
			
		||||
					<el-form-item prop="username">
 | 
			
		||||
						<el-input v-model="param.username" placeholder="用户名">
 | 
			
		||||
							<template #prepend>
 | 
			
		||||
								<el-button :icon="User"></el-button>
 | 
			
		||||
							</template>
 | 
			
		||||
						</el-input>
 | 
			
		||||
					</el-form-item>
 | 
			
		||||
					<el-form-item prop="password">
 | 
			
		||||
						<el-input type="password" placeholder="密码" v-model="param.password"
 | 
			
		||||
							@keyup.enter="submitForm(login)">
 | 
			
		||||
							<template #prepend>
 | 
			
		||||
								<el-button :icon="Lock"></el-button>
 | 
			
		||||
							</template>
 | 
			
		||||
						</el-input>
 | 
			
		||||
					</el-form-item>
 | 
			
		||||
					<div class="login-btn">
 | 
			
		||||
						<el-button type="primary" @click="submitForm(login)">
 | 
			
		||||
							<!-- <el-icon><UserFilled /></el-icon>-->
 | 
			
		||||
							登 录 <el-icon>
 | 
			
		||||
								<Right />
 | 
			
		||||
							</el-icon>
 | 
			
		||||
						</el-button>
 | 
			
		||||
					</div>
 | 
			
		||||
				</el-form>
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>
 | 
			
		||||
		<div class="company-info" v-if="settings.companyName">
 | 
			
		||||
			{{ settings.companyName }}
 | 
			
		||||
		</div>
 | 
			
		||||
	</div>
 | 
			
		||||
    <div class="login-wrap">
 | 
			
		||||
        <div class="login-container">
 | 
			
		||||
            <div class="ms-login">
 | 
			
		||||
                <div class="ms-title">{{ settings.siteFullTitle }}</div>
 | 
			
		||||
                <el-form :model="param" :rules="rules" ref="login" label-width="0px" class="ms-content">
 | 
			
		||||
                    <el-form-item prop="username">
 | 
			
		||||
                        <el-input v-model="param.username" placeholder="用户名">
 | 
			
		||||
                            <template #prepend>
 | 
			
		||||
                                <el-button :icon="User"></el-button>
 | 
			
		||||
                            </template>
 | 
			
		||||
                        </el-input>
 | 
			
		||||
                    </el-form-item>
 | 
			
		||||
                    <el-form-item prop="password">
 | 
			
		||||
                        <el-input type="password" placeholder="密码" v-model="param.password"
 | 
			
		||||
                            @keyup.enter="submitForm(login)">
 | 
			
		||||
                            <template #prepend>
 | 
			
		||||
                                <el-button :icon="Lock"></el-button>
 | 
			
		||||
                            </template>
 | 
			
		||||
                        </el-input>
 | 
			
		||||
                    </el-form-item>
 | 
			
		||||
                    <div class="login-btn">
 | 
			
		||||
                        <el-button type="primary" @click="submitForm(login)">
 | 
			
		||||
                            <!-- <el-icon><UserFilled /></el-icon>-->
 | 
			
		||||
                            登 录 <el-icon>
 | 
			
		||||
                                <Right />
 | 
			
		||||
                            </el-icon>
 | 
			
		||||
                        </el-button>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </el-form>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="company-info" v-if="settings.companyName">
 | 
			
		||||
            {{ settings.companyName }}
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { ref, reactive } from 'vue';
 | 
			
		||||
import { useTagsStore } from '../store/tags';
 | 
			
		||||
import { usePermissStore } from '../store/permiss';
 | 
			
		||||
import { useRouter } from 'vue-router';
 | 
			
		||||
import { ElMessage, ElLoading } from 'element-plus';
 | 
			
		||||
import { ElMessage } from 'element-plus';
 | 
			
		||||
import type { FormInstance, FormRules } from 'element-plus';
 | 
			
		||||
import { Lock, User } from '@element-plus/icons-vue';
 | 
			
		||||
import send_request from '../utils/send_request';
 | 
			
		||||
import settings from '../utils/settings';
 | 
			
		||||
 | 
			
		||||
import * as userApi from '../api/user';
 | 
			
		||||
 | 
			
		||||
interface LoginInfo {
 | 
			
		||||
	username: string;
 | 
			
		||||
	password: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface UserInfo {
 | 
			
		||||
	username: string;
 | 
			
		||||
	id: string;
 | 
			
		||||
	roleId: string;
 | 
			
		||||
    username: string;
 | 
			
		||||
    password: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface PrivilegeInfo {
 | 
			
		||||
	"id": Number,
 | 
			
		||||
	"roleId": Number,
 | 
			
		||||
	"privilegeName": string,
 | 
			
		||||
	"module": string
 | 
			
		||||
    "id": Number,
 | 
			
		||||
    "roleId": Number,
 | 
			
		||||
    "privilegeName": string,
 | 
			
		||||
    "module": string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface RoleInfo {
 | 
			
		||||
	id: Number,
 | 
			
		||||
	roleName: string,
 | 
			
		||||
	comment: any,
 | 
			
		||||
	privileges: Array<PrivilegeInfo>;
 | 
			
		||||
    id: Number,
 | 
			
		||||
    roleName: string,
 | 
			
		||||
    comment: any,
 | 
			
		||||
    privileges: Array<PrivilegeInfo>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const router = useRouter();
 | 
			
		||||
const param = reactive<LoginInfo>({
 | 
			
		||||
	username: 'admin',
 | 
			
		||||
	password: '123123'
 | 
			
		||||
    username: 'root',
 | 
			
		||||
    password: 'root'
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const rules: FormRules = {
 | 
			
		||||
	username: [
 | 
			
		||||
		{
 | 
			
		||||
			required: true,
 | 
			
		||||
			message: '请输入用户名',
 | 
			
		||||
			trigger: 'blur'
 | 
			
		||||
		}
 | 
			
		||||
	],
 | 
			
		||||
	password: [
 | 
			
		||||
		{
 | 
			
		||||
			required: true,
 | 
			
		||||
			message: '请输入密码',
 | 
			
		||||
			trigger: 'blur'
 | 
			
		||||
		}
 | 
			
		||||
	]
 | 
			
		||||
    username: [
 | 
			
		||||
        {
 | 
			
		||||
            required: true,
 | 
			
		||||
            message: '请输入用户名',
 | 
			
		||||
            trigger: 'blur'
 | 
			
		||||
        }
 | 
			
		||||
    ],
 | 
			
		||||
    password: [
 | 
			
		||||
        {
 | 
			
		||||
            required: true,
 | 
			
		||||
            message: '请输入密码',
 | 
			
		||||
            trigger: 'blur'
 | 
			
		||||
        }
 | 
			
		||||
    ]
 | 
			
		||||
};
 | 
			
		||||
const permiss: any = usePermissStore();
 | 
			
		||||
 | 
			
		||||
const login = ref<FormInstance>();
 | 
			
		||||
const submitForm = (formEl: FormInstance | undefined) => {
 | 
			
		||||
	if (!formEl) return;
 | 
			
		||||
	formEl.validate(async (valid: boolean, invalidFields: any) => {
 | 
			
		||||
		if (!valid) {
 | 
			
		||||
			// ElMessage.error('请填写用户名或密码');
 | 
			
		||||
    if (!formEl) return;
 | 
			
		||||
    formEl.validate(async (valid: boolean, invalidFields: any) => {
 | 
			
		||||
        if (!valid) {
 | 
			
		||||
            // ElMessage.error('请填写用户名或密码');
 | 
			
		||||
 | 
			
		||||
			console.log("invalidFields", invalidFields);
 | 
			
		||||
			// 对表单中的每一个不合法输入框进行遍历
 | 
			
		||||
			Object.values(invalidFields).forEach((input: any) => {
 | 
			
		||||
				// console.log("input", input)
 | 
			
		||||
				// 对该不合法输入框的提示信息进行遍历
 | 
			
		||||
				input.forEach((element: any) => {
 | 
			
		||||
					// console.log("element", element)
 | 
			
		||||
					ElMessage.error({ message: element.message, grouping: true });
 | 
			
		||||
				});
 | 
			
		||||
			});
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
            console.log("invalidFields", invalidFields);
 | 
			
		||||
            // 对表单中的每一个不合法输入框进行遍历
 | 
			
		||||
            Object.values(invalidFields).forEach((input: any) => {
 | 
			
		||||
                // console.log("input", input)
 | 
			
		||||
                // 对该不合法输入框的提示信息进行遍历
 | 
			
		||||
                input.forEach((element: any) => {
 | 
			
		||||
                    // console.log("element", element)
 | 
			
		||||
                    ElMessage.error({ message: element.message, grouping: true });
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
		const loading = ElLoading.service({
 | 
			
		||||
			lock: true,
 | 
			
		||||
			text: '请稍候',
 | 
			
		||||
			background: 'rgba(0, 0, 0, 0.7)',
 | 
			
		||||
		});
 | 
			
		||||
        userApi.userLogin({
 | 
			
		||||
            username: param.username,
 | 
			
		||||
            password: param.password
 | 
			
		||||
        }).then(async (data: any) => {
 | 
			
		||||
            // 判断用户是否登录成功
 | 
			
		||||
            if (!data) return;
 | 
			
		||||
 | 
			
		||||
            console.log("login data", data, data.userInfo);
 | 
			
		||||
 | 
			
		||||
		await send_request('v1/user/login', "POST", {
 | 
			
		||||
			userName: param.username,
 | 
			
		||||
			passWord: param.password
 | 
			
		||||
		}, async (data: UserInfo) => {
 | 
			
		||||
			// 判断用户是否登录成功
 | 
			
		||||
			if (!data) {
 | 
			
		||||
				ElMessage.error("用户名或密码错误");
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
			ElMessage.success('登录成功');
 | 
			
		||||
			localStorage.setItem('ms_username', data.username);
 | 
			
		||||
			localStorage.setItem('ms_user_id', data.id);
 | 
			
		||||
			localStorage.setItem('ms_role_id', data.roleId);
 | 
			
		||||
            ElMessage.success('登录成功');
 | 
			
		||||
            localStorage.setItem('ms_username', data.userInfo?.username);
 | 
			
		||||
            localStorage.setItem('ms_realname', data.userInfo?.realname);
 | 
			
		||||
            localStorage.setItem('ms_user_id', data.userInfo?.id);
 | 
			
		||||
            localStorage.setItem('ms_role_id', data.userInfo?.role);
 | 
			
		||||
 | 
			
		||||
			let defaultList = {};
 | 
			
		||||
			await send_request('v1/role/list', "GET", {}, (roleList: Array<RoleInfo>) => {
 | 
			
		||||
				for (let role of roleList) {
 | 
			
		||||
					defaultList[role.id.toString()] = role.privileges.map((i: any) => i.module)
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			permiss.defaultList = defaultList;
 | 
			
		||||
			permiss.key = defaultList[data.roleId];
 | 
			
		||||
			if (typeof (permiss.key) === "undefined") return;
 | 
			
		||||
			localStorage.setItem('ms_keys', JSON.stringify(permiss.key));
 | 
			
		||||
			localStorage.setItem('ms_default_list', JSON.stringify(defaultList));
 | 
			
		||||
			let targetRoute: any = router.currentRoute?.value?.query?.redirectTo
 | 
			
		||||
			if (targetRoute && !targetRoute.includes('/login')) {
 | 
			
		||||
				router.push(targetRoute);
 | 
			
		||||
			} else {
 | 
			
		||||
				router.push('/');
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
		loading.close();
 | 
			
		||||
	});
 | 
			
		||||
            let targetRoute: any = router.currentRoute?.value?.query?.redirectTo
 | 
			
		||||
            if (targetRoute && !targetRoute.includes('/login')) {
 | 
			
		||||
                router.push(targetRoute);
 | 
			
		||||
            } else {
 | 
			
		||||
                router.push('/');
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
    });
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const tags = useTagsStore();
 | 
			
		||||
@@ -166,57 +139,57 @@ tags.clearTags();
 | 
			
		||||
 | 
			
		||||
<style scoped>
 | 
			
		||||
.login-wrap {
 | 
			
		||||
	width: 100%;
 | 
			
		||||
	height: 100%;
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    height: 100%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.login-container {
 | 
			
		||||
	width: 100%;
 | 
			
		||||
	height: 100%;
 | 
			
		||||
	display: grid;
 | 
			
		||||
	place-items: center;
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    height: 100%;
 | 
			
		||||
    display: grid;
 | 
			
		||||
    place-items: center;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.ms-title {
 | 
			
		||||
	width: 100%;
 | 
			
		||||
	padding: 18px 24px;
 | 
			
		||||
	box-sizing: border-box;
 | 
			
		||||
	text-align: center;
 | 
			
		||||
	font-size: 20px;
 | 
			
		||||
	color: #fff;
 | 
			
		||||
	border-bottom: 1px solid #ddd;
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    padding: 18px 24px;
 | 
			
		||||
    box-sizing: border-box;
 | 
			
		||||
    text-align: center;
 | 
			
		||||
    font-size: 20px;
 | 
			
		||||
    color: #fff;
 | 
			
		||||
    border-bottom: 1px solid #ddd;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.ms-login {
 | 
			
		||||
	width: min(380px, 95vw);
 | 
			
		||||
	padding: 5px 10px;
 | 
			
		||||
	border-radius: 5px;
 | 
			
		||||
	background: rgba(255, 255, 255, 0.3);
 | 
			
		||||
	overflow: hidden;
 | 
			
		||||
    width: min(380px, 95vw);
 | 
			
		||||
    padding: 5px 10px;
 | 
			
		||||
    border-radius: 5px;
 | 
			
		||||
    background: rgba(255, 255, 255, 0.3);
 | 
			
		||||
    overflow: hidden;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.ms-content {
 | 
			
		||||
	padding: 30px 30px;
 | 
			
		||||
    padding: 30px 30px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.login-btn {
 | 
			
		||||
	text-align: center;
 | 
			
		||||
    text-align: center;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.login-btn button {
 | 
			
		||||
	width: 100%;
 | 
			
		||||
	height: 36px;
 | 
			
		||||
	margin-bottom: 10px;
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    height: 36px;
 | 
			
		||||
    margin-bottom: 10px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.company-info {
 | 
			
		||||
	color: #7589b6;
 | 
			
		||||
	text-align: center;
 | 
			
		||||
	position: absolute;
 | 
			
		||||
	left: 0;
 | 
			
		||||
	right: 0;
 | 
			
		||||
	bottom: 10px;
 | 
			
		||||
	font-size: 13px;
 | 
			
		||||
	letter-spacing: 1px;
 | 
			
		||||
    color: #7589b6;
 | 
			
		||||
    text-align: center;
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    left: 0;
 | 
			
		||||
    right: 0;
 | 
			
		||||
    bottom: 10px;
 | 
			
		||||
    font-size: 13px;
 | 
			
		||||
    letter-spacing: 1px;
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user