登录页以及系统相关配置

main
许宏杰 4 weeks ago
parent 69fe731e60
commit 5dc4cb3596

1
.gitignore vendored

@ -3,6 +3,7 @@ node_modules/
public/
dist/
src/assets/images/
src/assets/font/
npm-debug.log*
yarn-debug.log*
yarn-error.log*

@ -19,6 +19,7 @@
"@element-plus/icons-vue": "2.3.1",
"@vueup/vue-quill": "1.2.0",
"@vueuse/core": "10.11.0",
"autofit.js": "^2.0.1",
"axios": "0.28.1",
"clipboard": "2.0.11",
"echarts": "5.5.1",

@ -3,13 +3,22 @@
</template>
<script setup>
import useSettingsStore from '@/store/modules/settings'
import { handleThemeStyle } from '@/utils/theme'
import useSettingsStore from "@/store/modules/settings";
import { handleThemeStyle } from "@/utils/theme";
import autofit from "autofit.js";
onMounted(() => {
autofit.init(
{
designHeight: 1080,
designWidth: 1920,
renderDom: "#app",
resize: true,
},
false
); //
nextTick(() => {
//
handleThemeStyle(useSettingsStore().theme)
})
})
handleThemeStyle(useSettingsStore().theme);
});
});
</script>

@ -0,0 +1,5 @@
<svg width="18" height="22" viewBox="0 0 18 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="&#229;&#175;&#134;&#231;&#160;&#129;icon">
<path id="Vector" d="M15.6514 8.64209H14.8676V5.89209C14.8703 2.63721 12.2407 0 9.00134 0C5.75925 0 3.12974 2.63721 3.12974 5.89209V8.64209H2.34864C1.72537 8.64209 1.12885 8.88916 0.687472 9.33228C0.246099 9.77539 0 10.3743 0 11V12.9631C0 17.9529 4.02853 22 9.00134 22C13.9741 22 18.0027 17.9556 18.0027 12.9631V11C18.0027 10.3743 17.7566 9.77539 17.3152 9.33228C16.8712 8.89185 16.2746 8.64209 15.6514 8.64209ZM5.47838 5.89209C5.47838 3.9397 7.05395 2.35522 9.00134 2.35522C10.9487 2.35522 12.5243 3.93701 12.5243 5.89209V8.64209H5.47838V5.89209ZM9.78243 15.7131C9.78243 16.1482 9.43201 16.5 8.99866 16.5C8.56531 16.5 8.21489 16.1482 8.21489 15.7131V12.571C8.21489 12.136 8.56531 11.7842 8.99866 11.7842C9.43201 11.7842 9.78243 12.136 9.78243 12.571V15.7131Z" fill="#4776EB"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 936 B

@ -0,0 +1,5 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="&#231;&#148;&#168;&#230;&#136;&#183;&#229;&#144;&#141;icon">
<path id="Union" d="M13.3867 10.7217C15.9242 10.7195 17.9999 12.6446 18 14.9932C18 15.7921 17.976 16.4529 17.7793 17.1816L0.212891 16.9102C0.0180178 16.282 0 15.6787 0 14.9932C8.32562e-05 12.6425 2.07538 10.7219 4.61035 10.7217H13.3867ZM9.00098 0.818359C11.4602 0.818359 13.4558 2.66613 13.4561 4.94434C13.4561 7.22273 11.4604 9.07129 9.00098 9.07129C6.5417 9.07116 4.54688 7.22265 4.54688 4.94434C4.54712 2.66621 6.54185 0.818487 9.00098 0.818359Z" fill="#4776EB"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 643 B

@ -93,4 +93,19 @@
.el-dropdown .el-dropdown-link{
color: var(--el-color-primary) !important;
}
}
.el-input__wrapper{
background: #F1F3F4;
border-radius: 10px;
input{
font-size: 16px;
font-weight: 400;
}
input::placeholder{
color: #949494;
}
}

@ -0,0 +1,19 @@
@font-face {
font-family: 'Aboreto-Regular';
src: url("../font/Aboreto-Regular.ttf");
}
@font-face {
font-family: 'MiSans-Medium';
src: url("../font/MiSans-Medium.ttf");
}
@font-face {
font-family: 'MiSans-Regular';
src: url("../font/MiSans-Regular.ttf");
}
@font-face {
font-family: 'Aboreto-Bold';
src: url("../font/MiSans-Bold.ttf");
}

@ -5,6 +5,7 @@
@import './sidebar.scss';
@import './btn.scss';
@import './ruoyi.scss';
@import './font.css';
body {
height: 100%;
@ -12,7 +13,8 @@ body {
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
font-family: 'MiSans-Regular';
// font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
}
label {

@ -6,9 +6,9 @@
<div class="right-menu">
<template v-if="appStore.device !== 'mobile'">
<header-search id="header-search" class="right-menu-item" />
<!-- <header-search id="header-search" class="right-menu-item" /> -->
<el-tooltip content="源码地址" effect="dark" placement="bottom">
<!-- <el-tooltip content="源码地址" effect="dark" placement="bottom">
<ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" />
</el-tooltip>
@ -23,11 +23,11 @@
<svg-icon v-if="settingsStore.isDark" icon-class="sunny" />
<svg-icon v-if="!settingsStore.isDark" icon-class="moon" />
</div>
</el-tooltip>
</el-tooltip> -->
<el-tooltip content="布局大小" effect="dark" placement="bottom">
<!-- <el-tooltip content="布局大小" effect="dark" placement="bottom">
<size-select id="size-select" class="right-menu-item hover-effect" />
</el-tooltip>
</el-tooltip> -->
</template>
<div class="avatar-container">
<el-dropdown @command="handleCommand" class="right-menu-item hover-effect" trigger="click">
@ -40,9 +40,9 @@
<router-link to="/user/profile">
<el-dropdown-item>个人中心</el-dropdown-item>
</router-link>
<el-dropdown-item command="setLayout" v-if="settingsStore.showSettings">
<!-- <el-dropdown-item command="setLayout" v-if="settingsStore.showSettings">
<span>布局设置</span>
</el-dropdown-item>
</el-dropdown-item> -->
<el-dropdown-item divided command="logout">
<span>退出登录</span>
</el-dropdown-item>
@ -126,7 +126,6 @@ function toggleTheme() {
cursor: pointer;
transition: background 0.3s;
-webkit-tap-highlight-color: transparent;
&:hover {
background: rgba(0, 0, 0, 0.025);
}

@ -107,6 +107,7 @@ function topNavChange(val) {
}
function themeChange(val) {
console.log(val)
settingsStore.theme = val
handleThemeStyle(val)
}

@ -11,7 +11,7 @@ import usePermissionStore from '@/store/modules/permission'
NProgress.configure({ showSpinner: false })
const whiteList = ['/login', '/register','/map']
const whiteList = ['/login', '/register',]
const isWhiteList = (path) => {
return whiteList.some(pattern => isPathMatch(pattern, path))

@ -62,8 +62,8 @@ export const constantRoutes = [
hidden: true,
},
{
path: "/map",
component: () => import("@/views/map/index"),
path: "/visualization",
component: () => import("@/views/visualization/index"),
hidden: true,
},
{

@ -14,7 +14,7 @@ const useSettingsStore = defineStore(
{
state: () => ({
title: '',
theme: storageSetting.theme || '#409EFF',
theme: storageSetting.theme || '#4776EB',
sideTheme: storageSetting.sideTheme || sideTheme,
showSettings: showSettings,
topNav: storageSetting.topNav === undefined ? topNav : storageSetting.topNav,

@ -10,6 +10,9 @@
>
{{ item.meta.title }}
</div>
<div class="menu-item" @click="handlerVisualization()">
可视化驾驶舱
</div>
</div>
</div>
</div>
@ -17,18 +20,31 @@
<script setup>
import { constantRoutes } from "@/router";
import { isHttp } from '@/utils/validate'
import { isHttp } from "@/utils/validate";
const { proxy } = getCurrentInstance();
import useAppStore from "@/store/modules/app";
import usePermissionStore from "@/store/modules/permission";
const appStore = useAppStore();
const permissionStore = usePermissionStore();
//
const disableList = [
"/green-conservation",
"/park-conservation",
"/bug-disaster",
"/proprietary",
];
const router = useRouter();
/**
* 点击菜单
* @param key
*/
function handleSelect(key) {
console.log(key);
if (disableList.includes(key)) {
proxy.$modal.msgWarning("功能开发中,敬请期待");
return;
}
const route = routers.value.find((item) => item.path === key);
if (!route || !route.children) {
//
@ -51,6 +67,13 @@ function handleSelect(key) {
}
}
/**
* 跳转大屏驾驶舱
*/
const handlerVisualization = () => {
router.push({ path: "/visualization" });
};
function activeRoutes(key) {
let routes = [];
if (childrenMenus.value && childrenMenus.value.length > 0) {
@ -122,7 +145,7 @@ const topMenus = computed(() => {
.middle-view {
width: 50%;
display: grid;
grid-template-columns: repeat(3, 1fr); /* 关键属性:一行三列 */
grid-template-columns: repeat(4, 1fr); /* 关键属性:一行三列 */
gap: 20px; /* 网格间距 */
.menu-item {
height: 200px;

@ -1,7 +1,16 @@
<template>
<div class="login">
<el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form">
<h3 class="title">{{ title }}</h3>
<loginTitle></loginTitle>
<el-form
ref="loginRef"
:model="loginForm"
:rules="loginRules"
class="login-form"
>
<div class="title">
<div class="title-main">登录系统</div>
<div class="title-sub">欢迎使用徐汇园林管理系统</div>
</div>
<el-form-item prop="username">
<el-input
v-model="loginForm.username"
@ -10,7 +19,9 @@
auto-complete="off"
placeholder="账号"
>
<template #prefix><svg-icon icon-class="user" class="el-input__icon input-icon" /></template>
<template #prefix>
<svg-icon icon-class="loginUser" class="el-input__icon input-icon"
/></template>
</el-input>
</el-form-item>
<el-form-item prop="password">
@ -21,11 +32,17 @@
auto-complete="off"
placeholder="密码"
@keyup.enter="handleLogin"
show-password
>
<template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon" /></template>
<template #prefix
><svg-icon icon-class="loginPass" class="el-input__icon input-icon"
/></template>
</el-input>
</el-form-item>
<el-form-item prop="code" v-if="captchaEnabled">
<!-- <el-form-item prop="code" v-show="captchaEnabled">
<el-input
v-model="loginForm.code"
size="large"
@ -34,155 +51,190 @@
style="width: 63%"
@keyup.enter="handleLogin"
>
<template #prefix><svg-icon icon-class="validCode" class="el-input__icon input-icon" /></template>
<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"/>
<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-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%;"
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 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'
import loginTitle from "./loginTitle.vue";
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 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",
username: "",
password: "",
rememberMe: false,
code: "",
uuid: ""
})
uuid: "",
});
const loginRules = {
username: [{ required: true, trigger: "blur", message: "请输入您的账号" }],
password: [{ required: true, trigger: "blur", message: "请输入您的密码" }],
code: [{ required: true, trigger: "change", message: "请输入验证码" }]
}
code: [{ required: true, trigger: "change", message: "请输入验证码" }],
};
const codeUrl = ref("")
const loading = ref(false)
const codeUrl = ref("");
const loading = ref(false);
//
const captchaEnabled = ref(true)
const captchaEnabled = ref(true);
//
const register = ref(false)
const redirect = ref(undefined)
const register = ref(false);
const redirect = ref(undefined);
watch(route, (newRoute) => {
redirect.value = newRoute.query && newRoute.query.redirect
}, { immediate: true })
watch(
route,
(newRoute) => {
redirect.value = newRoute.query && newRoute.query.redirect;
},
{ immediate: true }
);
function handleLogin() {
proxy.$refs.loginRef.validate(valid => {
proxy.$refs.loginRef.validate((valid) => {
if (valid) {
loading.value = true
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 })
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")
Cookies.remove("username");
Cookies.remove("password");
Cookies.remove("rememberMe");
}
// action
userStore.login(loginForm.value).then(() => {
const query = route.query
const otherQueryParams = Object.keys(query).reduce((acc, cur) => {
if (cur !== "redirect") {
acc[cur] = query[cur]
userStore
.login(loginForm.value)
.then(() => {
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();
}
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
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
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")
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)
}
password:
password === undefined ? loginForm.value.password : decrypt(password),
rememberMe: rememberMe === undefined ? false : Boolean(rememberMe),
};
}
getCode()
getCookie()
getCode();
getCookie();
</script>
<style lang='scss' scoped>
<style lang="scss" scoped>
.login {
display: flex;
justify-content: center;
align-items: center;
position: relative;
height: 100%;
background-image: url("../assets/images/login-background.jpg");
background-size: cover;
}
.title {
margin: 0px auto 30px auto;
text-align: center;
color: #707070;
display: flex;
align-items: center;
justify-content: space-between;
align-items: flex-end;
margin-bottom: 38px;
.title-main {
font-size: 32px;
color: #000000;
font-weight: 500;
font-family: "MiSans-Medium";
}
.title-sub {
font-size: 15px;
color: #bebebe;
font-weight: 400;
font-family: "MiSans-Regular";
padding-bottom: 3px;
}
}
.login-form {
border-radius: 6px;
background: #ffffff;
position: absolute;
top: 50%;
transform: translateY(-50%);
right: 22%;
border-radius: 10px;
background: linear-gradient(180deg, #d5e2fb 0%, #ffffff 25%);
box-shadow: 0px 0px 70px 0px #02021a;
width: 400px;
padding: 25px 25px 5px 25px;
padding: 40px;
z-index: 1;
.el-input {
height: 40px;

@ -0,0 +1,53 @@
<template>
<div class="login-title">
<div class="welcome-text">
<div class="sub-welcome">WELLOCOME</div>
<div class="main-welcome">欢迎使用</div>
</div>
<div class="system-name">徐汇园林智慧管理系统</div>
</div>
</template>
<script setup></script>
<style lang="scss" scoped>
.login-title {
position: absolute;
left: 0;
top: 0;
height: 100%;
width: 35%;
background: rgba(7, 124, 234, 0.67);
backdrop-filter: blur(3px);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 69px;
}
.welcome-text {
position: relative;
font-size: 64px;
font-weight: 400;
.sub-welcome {
color: rgba(255, 255, 255, 0.25);
font-family: 'Aboreto-Regular';
}
.main-welcome {
position: absolute;
z-index: 10;
top: -8px;
width: 100%;
text-align: center;
color: rgba(255, 255, 255, 0.77);
}
}
.system-name {
font-size: 56px;
color: #ffffff;
font-weight: 500;
font-family: 'MiSans-Medium';
}
</style>
Loading…
Cancel
Save