样式问题优化

This commit is contained in:
BianLzhaoMin 2025-01-10 15:54:16 +08:00
parent 34ebf96d6e
commit 72a93cf695
16 changed files with 532 additions and 344 deletions

View File

@ -1,10 +1,10 @@
# 页面标题
VUE_APP_TITLE = 安徽机械化施工装备平台
VUE_APP_TITLE = 安徽机械化装备共享平台
# 开发环境配置
ENV = 'development'
# 安徽机械化施工装备平台/开发环境
# 安徽机械化装备共享平台/开发环境
VUE_APP_BASE_API = '/dev-api'
# 路由懒加载

View File

@ -1,8 +1,8 @@
# 页面标题
VUE_APP_TITLE = 安徽机械化施工装备平台
VUE_APP_TITLE = 安徽机械化装备共享平台
# 生产环境配置
ENV = 'production'
# 安徽机械化施工装备平台/生产环境
# 安徽机械化装备共享平台/生产环境
VUE_APP_BASE_API = '/prod-api'

View File

@ -1,10 +1,10 @@
# 页面标题
VUE_APP_TITLE = 安徽机械化施工装备平台
VUE_APP_TITLE = 安徽机械化装备共享平台
NODE_ENV = production
# 测试环境配置
ENV = 'staging'
# 安徽机械化施工装备平台/测试环境
# 安徽机械化装备共享平台/测试环境
VUE_APP_BASE_API = '/stage-api'

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@ -4,7 +4,8 @@
**/
/* theme color */
$--color-primary: #1890ff;
// $--color-primary: #1890ff;
$--color-primary: #00a288;
$--color-success: #13ce66;
$--color-warning: #ffba00;
$--color-danger: #ff4949;

View File

@ -9,17 +9,21 @@ $yellow:#FEC171;
$panGreen: #30B08F;
// 默认菜单主题风格
$base-menu-color:#bfcbd9;
// $base-menu-color:#bfcbd9;
$base-menu-color:#fff;
$base-menu-color-active:#f4f4f5;
$base-menu-background:#304156;
// $base-menu-background:#304156;
$base-menu-background:#00a288;
$base-logo-title-color: #ffffff;
$base-menu-light-color:rgba(0,0,0,.70);
$base-menu-light-background:#ffffff;
$base-logo-light-title-color: #001529;
$base-sub-menu-background:#1f2d3d;
$base-sub-menu-hover:#001528;
// $base-sub-menu-background:#1f2d3d;
// $base-sub-menu-hover:#001528;
$base-sub-menu-background:#00a288;
$base-sub-menu-hover:#00ad9d;
// 自定义暗色菜单风格
/**

View File

@ -1,8 +1,12 @@
<template>
<el-breadcrumb class="app-breadcrumb" separator="/">
<transition-group name="breadcrumb">
<el-breadcrumb-item v-for="(item,index) in levelList" :key="item.path">
<span v-if="item.redirect === 'noRedirect' || index == levelList.length - 1" class="no-redirect">{{ item.meta.title }}</span>
<el-breadcrumb-item v-for="(item, index) in levelList" :key="item.path">
<span
v-if="item.redirect === 'noRedirect' || index == levelList.length - 1"
class="no-redirect"
>{{ item.meta.title }}</span
>
<a v-else @click.prevent="handleLink(item)">{{ item.meta.title }}</a>
</el-breadcrumb-item>
</transition-group>
@ -13,50 +17,54 @@
export default {
data() {
return {
levelList: null
}
levelList: null,
};
},
watch: {
$route(route) {
// if you go to the redirect page, do not update the breadcrumbs
if (route.path.startsWith('/redirect/')) {
return
if (route.path.startsWith("/redirect/")) {
return;
}
this.getBreadcrumb()
}
this.getBreadcrumb();
},
},
created() {
this.getBreadcrumb()
this.getBreadcrumb();
},
methods: {
getBreadcrumb() {
// only show routes with meta.title
let matched = this.$route.matched.filter(item => item.meta && item.meta.title)
const first = matched[0]
let matched = this.$route.matched.filter(
(item) => item.meta && item.meta.title
);
const first = matched[0];
if (!this.isDashboard(first)) {
matched = [{ path: '/index', meta: { title: '首页' }}].concat(matched)
matched = [{ path: "/index", meta: { title: "首页" } }].concat(matched);
}
this.levelList = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false)
this.levelList = matched.filter(
(item) => item.meta && item.meta.title && item.meta.breadcrumb !== false
);
},
isDashboard(route) {
const name = route && route.name
const name = route && route.name;
if (!name) {
return false
return false;
}
return name.trim() === 'Index'
return name.trim() === "Index";
},
handleLink(item) {
const { redirect, path } = item
const { redirect, path } = item;
if (redirect) {
this.$router.push(redirect)
return
this.$router.push(redirect);
return;
}
this.$router.push(path)
}
}
}
this.$router.push(path);
},
},
};
</script>
<style lang="scss" scoped>

View File

@ -1,45 +1,79 @@
<template>
<div class="sidebar-logo-container" :class="{'collapse':collapse}" :style="{ backgroundColor: sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground }">
<div
class="sidebar-logo-container"
:class="{ collapse: collapse }"
:style="{
backgroundColor:
sideTheme === 'theme-dark'
? variables.menuBackground
: variables.menuLightBackground,
}"
>
<transition name="sidebarLogoFade">
<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" :style="{ color: sideTheme === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor }">{{ title }} </h1>
<router-link
v-if="collapse"
key="collapse"
class="sidebar-logo-link"
to="/"
>
<!-- <img v-if="logo" :src="logo" class="sidebar-logo" /> -->
<h1
class="sidebar-title"
:style="{
color:
sideTheme === 'theme-dark'
? variables.logoTitleColor
: variables.logoLightTitleColor,
}"
>
{{ title }}
</h1>
</router-link>
<router-link v-else key="expand" class="sidebar-logo-link" to="/">
<img v-if="logo" :src="logo" class="sidebar-logo" />
<h1 class="sidebar-title" :style="{ color: sideTheme === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor }">{{ title }} </h1>
<!-- <img v-if="logo" :src="logo" class="sidebar-logo" /> -->
<h1
class="sidebar-title"
:style="{
color:
sideTheme === 'theme-dark'
? variables.logoTitleColor
: variables.logoLightTitleColor,
}"
>
{{ title }}
</h1>
</router-link>
</transition>
</div>
</template>
<script>
import logoImg from '@/assets/logo/logo.png'
import variables from '@/assets/styles/variables.scss'
import logoImg from "@/assets/logo/logo.png";
import variables from "@/assets/styles/variables.scss";
export default {
name: 'SidebarLogo',
name: "SidebarLogo",
props: {
collapse: {
type: Boolean,
required: true
}
required: true,
},
},
computed: {
variables() {
return variables;
},
sideTheme() {
return this.$store.state.settings.sideTheme
}
return this.$store.state.settings.sideTheme;
},
},
data() {
return {
title: process.env.VUE_APP_TITLE,
logo: logoImg
}
}
}
logo: logoImg,
};
},
};
</script>
<style lang="scss" scoped>

View File

@ -1,26 +1,42 @@
<template>
<div :class="{'has-logo':showLogo}" :style="{ backgroundColor: settings.sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground }">
<logo v-if="showLogo" :collapse="isCollapse" />
<el-scrollbar :class="settings.sideTheme" wrap-class="scrollbar-wrapper">
<el-menu
:default-active="activeMenu"
:collapse="isCollapse"
:background-color="settings.sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground"
:text-color="settings.sideTheme === 'theme-dark' ? variables.menuColor : variables.menuLightColor"
:unique-opened="true"
:active-text-color="settings.theme"
:collapse-transition="false"
mode="vertical"
>
<sidebar-item
v-for="(route, index) in sidebarRouters"
:key="route.path + index"
:item="route"
:base-path="route.path"
/>
</el-menu>
</el-scrollbar>
</div>
<div
:class="{ 'has-logo': showLogo }"
:style="{
backgroundColor:
settings.sideTheme === 'theme-dark'
? variables.menuBackground
: variables.menuLightBackground,
}"
>
<logo v-if="showLogo" :collapse="isCollapse" />
<el-scrollbar :class="settings.sideTheme" wrap-class="scrollbar-wrapper">
<el-menu
:default-active="activeMenu"
:collapse="isCollapse"
:background-color="
settings.sideTheme === 'theme-dark'
? variables.menuBackground
: variables.menuLightBackground
"
:text-color="
settings.sideTheme === 'theme-dark'
? variables.menuColor
: variables.menuLightColor
"
:unique-opened="true"
active-text-color="#ffba00"
:collapse-transition="false"
mode="vertical"
>
<sidebar-item
v-for="(route, index) in sidebarRouters"
:key="route.path + index"
:item="route"
:base-path="route.path"
/>
</el-menu>
</el-scrollbar>
</div>
</template>
<script>
@ -30,28 +46,28 @@ import SidebarItem from "./SidebarItem";
import variables from "@/assets/styles/variables.scss";
export default {
components: { SidebarItem, Logo },
computed: {
...mapState(["settings"]),
...mapGetters(["sidebarRouters", "sidebar"]),
activeMenu() {
const route = this.$route;
const { meta, path } = route;
// if set path, the sidebar will highlight the path you set
if (meta.activeMenu) {
return meta.activeMenu;
}
return path;
},
showLogo() {
return this.$store.state.settings.sidebarLogo;
},
variables() {
return variables;
},
isCollapse() {
return !this.sidebar.opened;
}
}
components: { SidebarItem, Logo },
computed: {
...mapState(["settings"]),
...mapGetters(["sidebarRouters", "sidebar"]),
activeMenu() {
const route = this.$route;
const { meta, path } = route;
// if set path, the sidebar will highlight the path you set
if (meta.activeMenu) {
return meta.activeMenu;
}
return path;
},
showLogo() {
return this.$store.state.settings.sidebarLogo;
},
variables() {
return variables;
},
isCollapse() {
return !this.sidebar.opened;
},
},
};
</script>

View File

@ -1,36 +1,60 @@
<template>
<div id="tags-view-container" class="tags-view-container">
<scroll-pane ref="scrollPane" class="tags-view-wrapper" @scroll="handleScroll">
<scroll-pane
ref="scrollPane"
class="tags-view-wrapper"
@scroll="handleScroll"
>
<router-link
v-for="tag in visitedViews"
ref="tag"
:key="tag.path"
:class="isActive(tag)?'active':''"
:class="isActive(tag) ? 'active' : ''"
:to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
tag="span"
class="tags-view-item"
:style="activeStyle(tag)"
@click.middle.native="!isAffix(tag)?closeSelectedTag(tag):''"
@contextmenu.prevent.native="openMenu(tag,$event)"
@click.middle.native="!isAffix(tag) ? closeSelectedTag(tag) : ''"
@contextmenu.prevent.native="openMenu(tag, $event)"
>
{{ tag.title }}
<span v-if="!isAffix(tag)" class="el-icon-close" @click.prevent.stop="closeSelectedTag(tag)" />
<span
v-if="!isAffix(tag)"
class="el-icon-close"
@click.prevent.stop="closeSelectedTag(tag)"
/>
</router-link>
</scroll-pane>
<ul v-show="visible" :style="{left:left+'px',top:top+'px'}" class="contextmenu">
<li @click="refreshSelectedTag(selectedTag)"><i class="el-icon-refresh-right"></i> 刷新页面</li>
<li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)"><i class="el-icon-close"></i> 关闭当前</li>
<li @click="closeOthersTags"><i class="el-icon-circle-close"></i> 关闭其他</li>
<li v-if="!isFirstView()" @click="closeLeftTags"><i class="el-icon-back"></i> 关闭左侧</li>
<li v-if="!isLastView()" @click="closeRightTags"><i class="el-icon-right"></i> 关闭右侧</li>
<li @click="closeAllTags(selectedTag)"><i class="el-icon-circle-close"></i> 全部关闭</li>
<ul
v-show="visible"
:style="{ left: left + 'px', top: top + 'px' }"
class="contextmenu"
>
<li @click="refreshSelectedTag(selectedTag)">
<i class="el-icon-refresh-right"></i> 刷新页面
</li>
<li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)">
<i class="el-icon-close"></i> 关闭当前
</li>
<li @click="closeOthersTags">
<i class="el-icon-circle-close"></i> 关闭其他
</li>
<li v-if="!isFirstView()" @click="closeLeftTags">
<i class="el-icon-back"></i> 关闭左侧
</li>
<li v-if="!isLastView()" @click="closeRightTags">
<i class="el-icon-right"></i> 关闭右侧
</li>
<li @click="closeAllTags(selectedTag)">
<i class="el-icon-circle-close"></i> 全部关闭
</li>
</ul>
</div>
</template>
<script>
import ScrollPane from './ScrollPane'
import path from 'path'
import ScrollPane from "./ScrollPane";
import path from "path";
export default {
components: { ScrollPane },
@ -40,201 +64,207 @@ export default {
top: 0,
left: 0,
selectedTag: {},
affixTags: []
}
affixTags: [],
};
},
computed: {
visitedViews() {
return this.$store.state.tagsView.visitedViews
return this.$store.state.tagsView.visitedViews;
},
routes() {
return this.$store.state.permission.routes
return this.$store.state.permission.routes;
},
theme() {
return this.$store.state.settings.theme;
}
},
},
watch: {
$route() {
this.addTags()
this.moveToCurrentTag()
this.addTags();
this.moveToCurrentTag();
},
visible(value) {
if (value) {
document.body.addEventListener('click', this.closeMenu)
document.body.addEventListener("click", this.closeMenu);
} else {
document.body.removeEventListener('click', this.closeMenu)
document.body.removeEventListener("click", this.closeMenu);
}
}
},
},
mounted() {
this.initTags()
this.addTags()
this.initTags();
this.addTags();
},
methods: {
isActive(route) {
return route.path === this.$route.path
return route.path === this.$route.path;
},
activeStyle(tag) {
if (!this.isActive(tag)) return {};
return {
"background-color": this.theme,
"border-color": this.theme
"background-color": "#00ad9d",
"border-color": "#00ad9d",
};
},
isAffix(tag) {
return tag.meta && tag.meta.affix
return tag.meta && tag.meta.affix;
},
isFirstView() {
try {
return this.selectedTag.fullPath === '/index' || this.selectedTag.fullPath === this.visitedViews[1].fullPath
return (
this.selectedTag.fullPath === "/index" ||
this.selectedTag.fullPath === this.visitedViews[1].fullPath
);
} catch (err) {
return false
return false;
}
},
isLastView() {
try {
return this.selectedTag.fullPath === this.visitedViews[this.visitedViews.length - 1].fullPath
return (
this.selectedTag.fullPath ===
this.visitedViews[this.visitedViews.length - 1].fullPath
);
} catch (err) {
return false
return false;
}
},
filterAffixTags(routes, basePath = '/') {
let tags = []
routes.forEach(route => {
filterAffixTags(routes, basePath = "/") {
let tags = [];
routes.forEach((route) => {
if (route.meta && route.meta.affix) {
const tagPath = path.resolve(basePath, route.path)
const tagPath = path.resolve(basePath, route.path);
tags.push({
fullPath: tagPath,
path: tagPath,
name: route.name,
meta: { ...route.meta }
})
meta: { ...route.meta },
});
}
if (route.children) {
const tempTags = this.filterAffixTags(route.children, route.path)
const tempTags = this.filterAffixTags(route.children, route.path);
if (tempTags.length >= 1) {
tags = [...tags, ...tempTags]
tags = [...tags, ...tempTags];
}
}
})
return tags
});
return tags;
},
initTags() {
const affixTags = this.affixTags = this.filterAffixTags(this.routes)
const affixTags = (this.affixTags = this.filterAffixTags(this.routes));
for (const tag of affixTags) {
// Must have tag name
if (tag.name) {
this.$store.dispatch('tagsView/addVisitedView', tag)
this.$store.dispatch("tagsView/addVisitedView", tag);
}
}
},
addTags() {
const { name } = this.$route
const { name } = this.$route;
if (name) {
this.$store.dispatch('tagsView/addView', this.$route)
this.$store.dispatch("tagsView/addView", this.$route);
if (this.$route.meta.link) {
this.$store.dispatch('tagsView/addIframeView', this.$route)
this.$store.dispatch("tagsView/addIframeView", this.$route);
}
}
return false
return false;
},
moveToCurrentTag() {
const tags = this.$refs.tag
const tags = this.$refs.tag;
this.$nextTick(() => {
for (const tag of tags) {
if (tag.to.path === this.$route.path) {
this.$refs.scrollPane.moveToTarget(tag)
this.$refs.scrollPane.moveToTarget(tag);
// when query is different then update
if (tag.to.fullPath !== this.$route.fullPath) {
this.$store.dispatch('tagsView/updateVisitedView', this.$route)
this.$store.dispatch("tagsView/updateVisitedView", this.$route);
}
break
break;
}
}
})
});
},
refreshSelectedTag(view) {
this.$tab.refreshPage(view);
if (this.$route.meta.link) {
this.$store.dispatch('tagsView/delIframeView', this.$route)
this.$store.dispatch("tagsView/delIframeView", this.$route);
}
},
closeSelectedTag(view) {
this.$tab.closePage(view).then(({ visitedViews }) => {
if (this.isActive(view)) {
this.toLastView(visitedViews, view)
this.toLastView(visitedViews, view);
}
})
});
},
closeRightTags() {
this.$tab.closeRightPage(this.selectedTag).then(visitedViews => {
if (!visitedViews.find(i => i.fullPath === this.$route.fullPath)) {
this.toLastView(visitedViews)
this.$tab.closeRightPage(this.selectedTag).then((visitedViews) => {
if (!visitedViews.find((i) => i.fullPath === this.$route.fullPath)) {
this.toLastView(visitedViews);
}
})
});
},
closeLeftTags() {
this.$tab.closeLeftPage(this.selectedTag).then(visitedViews => {
if (!visitedViews.find(i => i.fullPath === this.$route.fullPath)) {
this.toLastView(visitedViews)
this.$tab.closeLeftPage(this.selectedTag).then((visitedViews) => {
if (!visitedViews.find((i) => i.fullPath === this.$route.fullPath)) {
this.toLastView(visitedViews);
}
})
});
},
closeOthersTags() {
this.$router.push(this.selectedTag.fullPath).catch(()=>{});
this.$router.push(this.selectedTag.fullPath).catch(() => {});
this.$tab.closeOtherPage(this.selectedTag).then(() => {
this.moveToCurrentTag()
})
this.moveToCurrentTag();
});
},
closeAllTags(view) {
this.$tab.closeAllPage().then(({ visitedViews }) => {
if (this.affixTags.some(tag => tag.path === this.$route.path)) {
return
if (this.affixTags.some((tag) => tag.path === this.$route.path)) {
return;
}
this.toLastView(visitedViews, view)
})
this.toLastView(visitedViews, view);
});
},
toLastView(visitedViews, view) {
const latestView = visitedViews.slice(-1)[0]
const latestView = visitedViews.slice(-1)[0];
if (latestView) {
this.$router.push(latestView.fullPath)
this.$router.push(latestView.fullPath);
} else {
// now the default is to redirect to the home page if there is no tags-view,
// you can adjust it according to your needs.
if (view.name === 'Dashboard') {
if (view.name === "Dashboard") {
// to reload home page
this.$router.replace({ path: '/redirect' + view.fullPath })
this.$router.replace({ path: "/redirect" + view.fullPath });
} else {
this.$router.push('/')
this.$router.push("/");
}
}
},
openMenu(tag, e) {
const menuMinWidth = 105
const offsetLeft = this.$el.getBoundingClientRect().left // container margin left
const offsetWidth = this.$el.offsetWidth // container width
const maxLeft = offsetWidth - menuMinWidth // left boundary
const left = e.clientX - offsetLeft + 15 // 15: margin right
const menuMinWidth = 105;
const offsetLeft = this.$el.getBoundingClientRect().left; // container margin left
const offsetWidth = this.$el.offsetWidth; // container width
const maxLeft = offsetWidth - menuMinWidth; // left boundary
const left = e.clientX - offsetLeft + 15; // 15: margin right
if (left > maxLeft) {
this.left = maxLeft
this.left = maxLeft;
} else {
this.left = left
this.left = left;
}
this.top = e.clientY
this.visible = true
this.selectedTag = tag
this.top = e.clientY;
this.visible = true;
this.selectedTag = tag;
},
closeMenu() {
this.visible = false
this.visible = false;
},
handleScroll() {
this.closeMenu()
}
}
}
this.closeMenu();
},
},
};
</script>
<style lang="scss" scoped>
@ -243,7 +273,7 @@ export default {
width: 100%;
background: #fff;
border-bottom: 1px solid #d8dce5;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .12), 0 0 3px 0 rgba(0, 0, 0, .04);
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 0 3px 0 rgba(0, 0, 0, 0.04);
.tags-view-wrapper {
.tags-view-item {
display: inline-block;
@ -269,7 +299,7 @@ export default {
color: #fff;
border-color: #42b983;
&::before {
content: '';
content: "";
background: #fff;
display: inline-block;
width: 8px;
@ -292,7 +322,7 @@ export default {
font-size: 12px;
font-weight: 400;
color: #333;
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3);
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3);
li {
margin: 0;
padding: 7px 16px;
@ -315,10 +345,10 @@ export default {
vertical-align: 2px;
border-radius: 50%;
text-align: center;
transition: all .3s cubic-bezier(.645, .045, .355, 1);
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
transform-origin: 100% 50%;
&:before {
transform: scale(.6);
transform: scale(0.6);
display: inline-block;
vertical-align: -3px;
}

View File

@ -2,11 +2,11 @@
<div class="app-container home">
<el-row :gutter="20">
<el-col :sm="24" :lg="12" style="padding-left: 20px">
<h2>安徽机械化施工装备平台</h2>
<h2>安徽机械化装备共享平台</h2>
<!-- <p>
<b>当前版本:</b> <span>v{{ version }}</span>
</p> -->
</el-col>
</el-col>
</el-row>
<el-divider />
<el-row :gutter="20">
@ -136,4 +136,3 @@ export default {
}
}
</style>

View File

@ -1,7 +1,12 @@
<template>
<div class="login">
<el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form">
<h3 class="title">安徽机械化施工装备平台</h3>
<el-form
ref="loginForm"
:model="loginForm"
:rules="loginRules"
class="login-form"
>
<h3 class="title">安徽机械化装备共享平台</h3>
<el-form-item prop="username">
<el-input
v-model="loginForm.username"
@ -9,7 +14,11 @@
auto-complete="off"
placeholder="账号"
>
<svg-icon slot="prefix" icon-class="user" class="el-input__icon input-icon" />
<svg-icon
slot="prefix"
icon-class="user"
class="el-input__icon input-icon"
/>
</el-input>
</el-form-item>
<el-form-item prop="password">
@ -20,7 +29,11 @@
placeholder="密码"
@keyup.enter.native="handleLogin"
>
<svg-icon slot="prefix" icon-class="password" class="el-input__icon input-icon" />
<svg-icon
slot="prefix"
icon-class="password"
class="el-input__icon input-icon"
/>
</el-input>
</el-form-item>
<el-form-item prop="code" v-if="captchaEnabled">
@ -31,26 +44,36 @@
style="width: 63%"
@keyup.enter.native="handleLogin"
>
<svg-icon slot="prefix" icon-class="validCode" class="el-input__icon input-icon" />
<svg-icon
slot="prefix"
icon-class="validCode"
class="el-input__icon input-icon"
/>
</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-checkbox
v-model="loginForm.rememberMe"
style="margin: 0px 0px 25px 0px"
>记住密码</el-checkbox
>
<el-form-item style="width: 100%">
<el-button
:loading="loading"
size="medium"
type="primary"
style="width:100%;"
style="width: 100%"
@click.native.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>
@ -64,7 +87,7 @@
<script>
import { getCodeImg } from "@/api/login";
import Cookies from "js-cookie";
import { encrypt, decrypt } from '@/utils/jsencrypt'
import { encrypt, decrypt } from "@/utils/jsencrypt";
export default {
name: "Login",
@ -76,32 +99,32 @@ export default {
password: "",
rememberMe: false,
code: "",
uuid: ""
uuid: "",
},
loginRules: {
username: [
{ required: true, trigger: "blur", message: "请输入您的账号" }
{ required: true, trigger: "blur", message: "请输入您的账号" },
],
password: [
{ required: true, trigger: "blur", message: "请输入您的密码" }
{ required: true, trigger: "blur", message: "请输入您的密码" },
],
code: [{ required: true, trigger: "change", message: "请输入验证码" }]
code: [{ required: true, trigger: "change", message: "请输入验证码" }],
},
loading: false,
//
captchaEnabled: true,
//
register: false,
redirect: undefined
redirect: undefined,
};
},
watch: {
$route: {
handler: function(route) {
handler: function (route) {
this.redirect = route.query && route.query.redirect;
},
immediate: true
}
immediate: true,
},
},
created() {
this.getCode();
@ -109,8 +132,9 @@ export default {
},
methods: {
getCode() {
getCodeImg().then(res => {
this.captchaEnabled = res.captchaEnabled === undefined ? true : res.captchaEnabled;
getCodeImg().then((res) => {
this.captchaEnabled =
res.captchaEnabled === undefined ? true : res.captchaEnabled;
if (this.captchaEnabled) {
this.codeUrl = "data:image/gif;base64," + res.img;
this.loginForm.uuid = res.uuid;
@ -120,38 +144,46 @@ export default {
getCookie() {
const username = Cookies.get("username");
const password = Cookies.get("password");
const rememberMe = Cookies.get('rememberMe')
const rememberMe = Cookies.get("rememberMe");
this.loginForm = {
username: username === undefined ? this.loginForm.username : username,
password: password === undefined ? this.loginForm.password : decrypt(password),
rememberMe: rememberMe === undefined ? false : Boolean(rememberMe)
password:
password === undefined ? this.loginForm.password : decrypt(password),
rememberMe: rememberMe === undefined ? false : Boolean(rememberMe),
};
},
handleLogin() {
this.$refs.loginForm.validate(valid => {
this.$refs.loginForm.validate((valid) => {
if (valid) {
this.loading = true;
if (this.loginForm.rememberMe) {
Cookies.set("username", this.loginForm.username, { expires: 30 });
Cookies.set("password", encrypt(this.loginForm.password), { expires: 30 });
Cookies.set('rememberMe', this.loginForm.rememberMe, { expires: 30 });
Cookies.set("password", encrypt(this.loginForm.password), {
expires: 30,
});
Cookies.set("rememberMe", this.loginForm.rememberMe, {
expires: 30,
});
} else {
Cookies.remove("username");
Cookies.remove("password");
Cookies.remove('rememberMe');
Cookies.remove("rememberMe");
}
this.$store.dispatch("Login", this.loginForm).then(() => {
this.$router.push({ path: this.redirect || "/" }).catch(()=>{});
}).catch(() => {
this.loading = false;
if (this.captchaEnabled) {
this.getCode();
}
});
this.$store
.dispatch("Login", this.loginForm)
.then(() => {
this.$router.push({ path: this.redirect || "/" }).catch(() => {});
})
.catch(() => {
this.loading = false;
if (this.captchaEnabled) {
this.getCode();
}
});
}
});
}
}
},
},
};
</script>

View File

@ -6,7 +6,7 @@
:rules="registerRules"
class="register-form"
>
<h3 class="title">安徽机械化施工装备平台</h3>
<h3 class="title">安徽机械化装备共享平台</h3>
<el-form-item prop="username">
<el-input
v-model="registerForm.username"

View File

@ -1,19 +1,43 @@
<template>
<div class="container">
<div class="register">
<el-form ref="registerForm" :model="registerForm" :rules="registerRules" class="register-form">
<h3 class="title">安徽机械化施工装备平台</h3>
<el-form
ref="registerForm"
:model="registerForm"
:rules="registerRules"
class="register-form"
>
<h3 class="title">安徽机械化装备共享平台</h3>
<el-form-item prop="nickName">
<el-input v-model="registerForm.nickName" type="text" auto-complete="off" placeholder="请输入姓名">
<svg-icon slot="prefix" icon-class="user" class="el-input__icon input-icon"/>
<el-input
v-model="registerForm.nickName"
type="text"
auto-complete="off"
placeholder="请输入姓名"
>
<svg-icon
slot="prefix"
icon-class="user"
class="el-input__icon input-icon"
/>
</el-input>
</el-form-item>
<el-form-item prop="mobile">
<el-input v-model="registerForm.mobile" type="text" auto-complete="off"
:placeholder="`请输入${config.registersConfig.phoneRegisters ? '手机号' : ''}${config.registersConfig.emailRegisters ? '/邮箱' : ''}`">
<svg-icon slot="prefix" icon-class="phone" class="el-input__icon input-icon"/>
<el-input
v-model="registerForm.mobile"
type="text"
auto-complete="off"
:placeholder="`请输入${
config.registersConfig.phoneRegisters ? '手机号' : ''
}${config.registersConfig.emailRegisters ? '/邮箱' : ''}`"
>
<svg-icon
slot="prefix"
icon-class="phone"
class="el-input__icon input-icon"
/>
</el-input>
</el-form-item>
@ -26,7 +50,11 @@
@keyup.enter.native="handleRegister"
show-password
>
<svg-icon slot="prefix" icon-class="password" class="el-input__icon input-icon"/>
<svg-icon
slot="prefix"
icon-class="password"
class="el-input__icon input-icon"
/>
</el-input>
</el-form-item>
@ -39,7 +67,11 @@
@keyup.enter.native="handleRegister"
show-password
>
<svg-icon slot="prefix" icon-class="password" class="el-input__icon input-icon"/>
<svg-icon
slot="prefix"
icon-class="password"
class="el-input__icon input-icon"
/>
</el-input>
</el-form-item>
@ -51,42 +83,61 @@
style="width: 63%"
@keyup.enter.native="handleRegister"
>
<svg-icon slot="prefix" icon-class="validCode" class="el-input__icon input-icon"/>
<svg-icon
slot="prefix"
icon-class="validCode"
class="el-input__icon input-icon"
/>
</el-input>
<div class="register-code">
<img :src="codeUrl" @click="getCode" class="register-code-img"/>
<img :src="codeUrl" @click="getCode" class="register-code-img" />
</div>
</el-form-item>
<el-form-item v-show="config.registersConfig.verificationCode" prop="verificationCode">
<el-input v-model="registerForm.verificationCode" :readonly="!isSendingCode" placeholder="请输入验证码">
<el-form-item
v-show="config.registersConfig.verificationCode"
prop="verificationCode"
>
<el-input
v-model="registerForm.verificationCode"
:readonly="!isSendingCode"
placeholder="请输入验证码"
>
<template slot="append">
<el-button
type="primary"
@click="sendCode"
:disabled="isSendingCode || captchaEnabled?!registerForm.code:false"
:disabled="
isSendingCode || captchaEnabled ? !registerForm.code : false
"
class="send-code-button"
>
{{ isSendingCode ? `${countdown}s` : '获取验证码' }}
{{ isSendingCode ? `${countdown}s` : "获取验证码" }}
</el-button>
</template>
<svg-icon slot="prefix" icon-class="message" class="el-input__icon input-icon"/>
<svg-icon
slot="prefix"
icon-class="message"
class="el-input__icon input-icon"
/>
</el-input>
</el-form-item>
<el-form-item style="width:100%;">
<el-form-item style="width: 100%">
<el-button
:loading="loading"
size="medium"
type="primary"
style="width:100%;"
style="width: 100%"
@click.native.prevent="handleRegister"
>
<span v-if="!loading"> </span>
<span v-else> 中...</span>
</el-button>
<div style="float: right;">
<router-link class="link-type" :to="'/login'">使用已有账户登录</router-link>
<div style="float: right">
<router-link class="link-type" :to="'/login'"
>使用已有账户登录</router-link
>
</div>
</el-form-item>
</el-form>
@ -99,169 +150,182 @@
</template>
<script>
import { getCodeImg, register } from '@/api/login'
import { validateNewPassword } from '@/utils/validate'
import { decryptCBC } from '@/utils/aescbc'
import { getCodeImg, register } from "@/api/login";
import { validateNewPassword } from "@/utils/validate";
import { decryptCBC } from "@/utils/aescbc";
export default {
name: 'Register',
name: "Register",
computed: {
LOGIN() {
return LOGIN
return LOGIN;
},
config() {
return JSON.parse(localStorage.getItem('systemConfig')) || {registersConfig: {
phoneRegisters: true,
emailRegisters: true,
verificationCode:true
}}; // JSON
return (
JSON.parse(localStorage.getItem("systemConfig")) || {
registersConfig: {
phoneRegisters: true,
emailRegisters: true,
verificationCode: true,
},
}
); // JSON
},
},
data() {
//
const validateNickName = (rule, value, callback) => {
const regex = /^[\u4e00-\u9fa5]{2,4}$/
const regex = /^[\u4e00-\u9fa5]{2,4}$/;
if (!value) {
callback(new Error('请输入姓名'))
callback(new Error("请输入姓名"));
} else if (!regex.test(value)) {
callback(new Error('名字必须是2到4个汉字'))
callback(new Error("名字必须是2到4个汉字"));
} else {
callback()
callback();
}
}
};
//
const equalToPassword = (rule, value, callback) => {
if (this.registerForm.password !== value) {
callback(new Error('两次输入的密码不一致'))
callback(new Error("两次输入的密码不一致"));
} else {
callback()
callback();
}
}
};
return {
codeUrl: '',
codeUrl: "",
isSendingCode: false,
countdown: 0,
registerForm: {
username: '',
password: '',
confirmPassword: '',
verificationCode: '',
mobile: '',
nickName: '',
code: '',
uuid: '',
phoneUuid: '',
mobileCodeType: 'REGISTER'
username: "",
password: "",
confirmPassword: "",
verificationCode: "",
mobile: "",
nickName: "",
code: "",
uuid: "",
phoneUuid: "",
mobileCodeType: "REGISTER",
},
registerRules: {
nickName: [
{ required: true, trigger: 'blur', validator: validateNickName }
{ required: true, trigger: "blur", validator: validateNickName },
],
mobile: [
{ required: true, message: '请输入手机号/邮箱', trigger: 'blur' },
{ validator: this.validateContact, trigger: 'blur' }
{ required: true, message: "请输入手机号/邮箱", trigger: "blur" },
{ validator: this.validateContact, trigger: "blur" },
],
password: [
{ required: true, trigger: 'blur', message: '请输入您的密码' },
{ validator: validateNewPassword, trigger: 'blur' }
{ required: true, trigger: "blur", message: "请输入您的密码" },
{ validator: validateNewPassword, trigger: "blur" },
],
confirmPassword: [
{ required: true, trigger: 'blur', message: '请再次输入您的密码' },
{ required: true, validator: equalToPassword, trigger: 'blur' }
{ required: true, trigger: "blur", message: "请再次输入您的密码" },
{ required: true, validator: equalToPassword, trigger: "blur" },
],
code: [{ required: true, trigger: 'change', message: '请输入验证码' }]
code: [{ required: true, trigger: "change", message: "请输入验证码" }],
},
loading: false,
captchaEnabled: true
}
captchaEnabled: true,
};
},
created() {
this.getCode()
this.getCode();
},
methods: {
//
getCode() {
getCodeImg().then(res => {
this.captchaEnabled = res.captchaEnabled === undefined ? true : res.captchaEnabled
getCodeImg().then((res) => {
this.captchaEnabled =
res.captchaEnabled === undefined ? true : res.captchaEnabled;
if (this.captchaEnabled) {
this.codeUrl = 'data:image/gif;base64,' + res.img
this.registerForm.uuid = res.uuid
this.codeUrl = "data:image/gif;base64," + res.img;
this.registerForm.uuid = res.uuid;
}
})
});
},
//
handleRegister() {
this.$refs.registerForm.validate(valid => {
this.$refs.registerForm.validate((valid) => {
if (valid) {
this.loading = true
register(this.registerForm).then(res => {
const username = this.registerForm.username
this.$alert(`<font color="red">恭喜你,您的账号 ${username} 注册成功!</font>`, '系统提示', {
dangerouslyUseHTMLString: true,
type: 'success'
}).then(() => {
this.$router.push('/login')
}).catch(() => {
this.loading = true;
register(this.registerForm)
.then((res) => {
const username = this.registerForm.username;
this.$alert(
`<font color="red">恭喜你,您的账号 ${username} 注册成功!</font>`,
"系统提示",
{
dangerouslyUseHTMLString: true,
type: "success",
}
)
.then(() => {
this.$router.push("/login");
})
.catch(() => {});
})
}).catch(() => {
this.loading = false
if (this.captchaEnabled) {
this.getCode()
}
})
.catch(() => {
this.loading = false;
if (this.captchaEnabled) {
this.getCode();
}
});
}
})
});
},
//
sendCode() {
if (!this.registerForm.mobile) {
this.$message.error('请先填写手机号')
return
this.$message.error("请先填写手机号");
return;
}
this.$store.dispatch('GetPhoneCode', this.registerForm)
.then(res => {
console.log(res)
this.isSendingCode = true
this.countdown = 60
this.$store
.dispatch("GetPhoneCode", this.registerForm)
.then((res) => {
console.log(res);
this.isSendingCode = true;
this.countdown = 60;
const timer = setInterval(() => {
this.countdown -= 1
this.countdown -= 1;
if (this.countdown <= 0) {
clearInterval(timer)
clearInterval(timer);
}
}, 1000)
}, 1000);
})
.catch(() => {
this.loading = false
this.loading = false;
if (this.captchaEnabled) {
this.getCode()
this.getCode();
}
this.isSendingCode = false
this.countdown = 0
})
this.isSendingCode = false;
this.countdown = 0;
});
},
//
validateContact(rule, value, callback) {
const phoneRegex = /^1[3-9]\d{9}$/
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/
const phoneRegex = /^1[3-9]\d{9}$/;
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
if (!value) {
return callback(new Error('请输入电子邮件或电话号码'))
return callback(new Error("请输入电子邮件或电话号码"));
}
if (!phoneRegex.test(value) && !emailRegex.test(value)) {
return callback(new Error('必须是有效的电子邮件或电话号码'))
return callback(new Error("必须是有效的电子邮件或电话号码"));
}
callback()
}
}
}
callback();
},
},
};
</script>
<style rel="stylesheet/scss" lang="scss">

View File

@ -7,7 +7,7 @@ function resolve(dir) {
const CompressionPlugin = require("compression-webpack-plugin");
const name = process.env.VUE_APP_TITLE || "安徽机械化施工装备平台"; // 网页标题
const name = process.env.VUE_APP_TITLE || "安徽机械化装备共享平台"; // 网页标题
const port = process.env.port || process.env.npm_config_port || 80; // 端口
@ -39,7 +39,7 @@ module.exports = {
// target: `http://192.168.2.246:18080`,//马
// target: `http://192.168.0.110:18080`,//洪
// target: `http://192.168.0.234:18080`,//阮
target: `http://192.168.2.127:28080`, //测试
target: `http://36.33.26.201:17788/proxyApi`, //测试
// target: `http://192.168.137.1:18080`,//
changeOrigin: true,
pathRewrite: {