样式问题优化

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' ENV = 'development'
# 安徽机械化施工装备平台/开发环境 # 安徽机械化装备共享平台/开发环境
VUE_APP_BASE_API = '/dev-api' VUE_APP_BASE_API = '/dev-api'
# 路由懒加载 # 路由懒加载

View File

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

View File

@ -1,10 +1,10 @@
# 页面标题 # 页面标题
VUE_APP_TITLE = 安徽机械化施工装备平台 VUE_APP_TITLE = 安徽机械化装备共享平台
NODE_ENV = production NODE_ENV = production
# 测试环境配置 # 测试环境配置
ENV = 'staging' ENV = 'staging'
# 安徽机械化施工装备平台/测试环境 # 安徽机械化装备共享平台/测试环境
VUE_APP_BASE_API = '/stage-api' 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 */ /* theme color */
$--color-primary: #1890ff; // $--color-primary: #1890ff;
$--color-primary: #00a288;
$--color-success: #13ce66; $--color-success: #13ce66;
$--color-warning: #ffba00; $--color-warning: #ffba00;
$--color-danger: #ff4949; $--color-danger: #ff4949;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,7 +7,7 @@ function resolve(dir) {
const CompressionPlugin = require("compression-webpack-plugin"); 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; // 端口 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.2.246:18080`,//马
// target: `http://192.168.0.110:18080`,//洪 // target: `http://192.168.0.110:18080`,//洪
// target: `http://192.168.0.234: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`,// // target: `http://192.168.137.1:18080`,//
changeOrigin: true, changeOrigin: true,
pathRewrite: { pathRewrite: {