重建入口

This commit is contained in:
liaoboping 2025-09-15 19:01:43 +08:00
parent ef40b81a50
commit 71fa900582
15 changed files with 361 additions and 1220 deletions

4
public/config.js vendored
View File

@ -1,7 +1,7 @@
window._CONFIG = {
ImageryProviderUrl: 'http://127.0.0.1:8090/mapWX/{z}/{x}/{y}.jpg',
ImageryProviderUrl: '/map/mapWX/{z}/{x}/{y}.jpg',
RoadProviderUrl: '',
TerrainProviderUrl: 'http://127.0.0.1:8090/mapDem/',
TerrainProviderUrl: '/map/mapDem/',
thirdLoginUrl: 'http://127.0.0.1:8080/thirdLogin',
clientId: '0123456789',
evaluationSrc: 'http://127.0.0.1:8000',

View File

@ -129,4 +129,78 @@ export default {
.scroller-y {
overflow-y: auto;
}
.oh {
overflow: hidden;
}
.pr {
position: relative;
}
.pa {
position: absolute;
}
.pf {
position: fixed;
}
.zi0 {
z-index: 0;
}
.zi1 {
z-index: 1;
}
.zi2 {
z-index: 2;
}
.zi3 {
z-index: 3;
}
.zi4 {
z-index: 4;
}
.zi5 {
z-index: 5;
}
.zi6 {
z-index: 6;
}
.zi7 {
z-index: 7;
}
.zi8 {
z-index: 8;
}
.zi9 {
z-index: 9;
}
.zi10 {
z-index: 10;
}
.zi20 {
z-index: 20;
}
.zi50 {
z-index: 50;
}
.zi100 {
z-index: 100;
}
.zi200 {
z-index: 200;
}
.zi500 {
z-index: 500;
}
.zi1000 {
z-index: 1000;
}
.zi2000 {
z-index: 2000;
}
.zi5000 {
z-index: 5000;
}
.zi9999 {
z-index: 9999;
}
</style>

View File

@ -7,156 +7,7 @@ const RouteView = {
render: (h) => h('router-view'),
}
export const asyncRouterMap = [
{
path: '/',
name: 'index',
component: BasicLayout,
meta: { title: 'menu.home' },
redirect: '/user/thirdLogin',
children: [
// dashboard
{
path: '/dashboard',
name: 'dashboard',
redirect: '/dashboard/workplace',
component: RouteView,
// meta: { title: 'menu.dashboard', keepAlive: true, icon: bxAnaalyse, permission: ['dashboard'] },
meta: { title: '基础数据管理工具', keepAlive: true, icon: bxAnaalyse },
children: [
{
path: '/dashboard/workplace',
name: 'Workplace',
component: () => import('@/views/dashboard/WorkplaceEquipment'),
meta: { title: '主页', keepAlive: true },
},
],
},
{
path: '/map',
name: 'map',
redirect: '/map/index',
component: RouteView,
// meta: { title: 'menu.dashboard', keepAlive: true, icon: bxAnaalyse, permission: ['dashboard'] },
meta: { title: '地图资源库', keepAlive: true, icon: bxAnaalyse },
children: [
{
path: '/map/index',
name: 'mapIndex',
component: () => import('@/views/map/mapIndex'),
meta: { title: '主页', keepAlive: true },
},
],
},
// isystem
{
path: '/isystem',
name: 'isystem',
// redirect: '/isystem/workplace',
component: RouteView,
meta: { title: '后台管理', keepAlive: true, icon: bxAnaalyse, permission: ['isystem'] },
children: [
{
path: '/isystem/userlist',
name: 'userlist',
component: () => import('@/views/isystem/userList'),
meta: { title: '用户管理', keepAlive: true, permission: ['isystem'] },
},
{
path: '/isystem/rolelist',
name: 'rolelist',
component: () => import('@/views/isystem/roleList'),
meta: { title: '角色管理', keepAlive: true, permission: ['isystem'] },
},
{
path: '/isystem/cookieList',
name: 'cookieList',
component: () => import('@/views/isystem/cookieList'),
meta: { title: '缓存管理', keepAlive: true, permission: ['isystem'] },
},
],
},
// account
{
path: '/account',
component: RouteView,
redirect: '/account/center',
name: 'account',
hidden: true,
meta: { title: 'menu.account', icon: 'user', keepAlive: true, hidden: true, permission: ['user'] },
children: [
{
path: '/account/center',
name: 'center',
component: () => import('@/views/account/center'),
meta: { title: 'menu.account.center', keepAlive: true, permission: ['user'] },
},
{
path: '/account/settings',
name: 'settings',
component: () => import('@/views/account/settings/Index'),
meta: { title: 'menu.account.settings', hideHeader: true, permission: ['user'] },
redirect: '/account/settings/basic',
hideChildrenInMenu: true,
children: [
{
path: '/account/settings/basic',
name: 'BasicSettings',
component: () => import('@/views/account/settings/BasicSetting'),
meta: { title: 'account.settings.menuMap.basic', hidden: true, permission: ['user'] },
},
{
path: '/account/settings/security',
name: 'SecuritySettings',
component: () => import('@/views/account/settings/Security'),
meta: {
title: 'account.settings.menuMap.security',
hidden: true,
keepAlive: true,
permission: ['user'],
},
},
{
path: '/account/settings/custom',
name: 'CustomSettings',
component: () => import('@/views/account/settings/Custom'),
meta: { title: 'account.settings.menuMap.custom', hidden: true, keepAlive: true, permission: ['user'] },
},
{
path: '/account/settings/binding',
name: 'BindingSettings',
component: () => import('@/views/account/settings/Binding'),
meta: {
title: 'account.settings.menuMap.binding',
hidden: true,
keepAlive: true,
permission: ['user'],
},
},
{
path: '/account/settings/notification',
name: 'NotificationSettings',
component: () => import('@/views/account/settings/Notification'),
meta: {
title: 'account.settings.menuMap.notification',
hidden: true,
keepAlive: true,
permission: ['user'],
},
},
],
},
],
},
],
},
{
path: '*',
redirect: '/404',
hidden: true,
},
]
export const asyncRouterMap = []
/**
* 基础路由
@ -164,143 +15,17 @@ export const asyncRouterMap = [
*/
export const constantRouterMap = [
{
path: '/user',
component: UserLayout,
redirect: '/user/login',
hidden: true,
children: [
{
path: 'login',
name: 'login',
component: () => import(/* webpackChunkName: "user" */ '@/views/user/Login'),
},
{
path: 'thirdLogin',
name: 'thirdLogin',
component: () => import(/* webpackChunkName: "user" */ '@/views/user/ThirdLogin'),
},
{
path: 'register',
name: 'register',
component: () => import(/* webpackChunkName: "user" */ '@/views/user/Register'),
},
{
path: 'register-result',
name: 'registerResult',
component: () => import(/* webpackChunkName: "user" */ '@/views/user/RegisterResult'),
},
{
path: 'recover',
name: 'recover',
component: undefined,
},
],
path: '/',
name: 'index',
component: RouteView,
meta: { title: '某仿真分析方法工具' },
redirect: '/user/welcome',
},
{
path: '/other',
component: BasicLayout,
hidden: true,
children: [
{
path: 'resetPwd',
name: '重置密码',
meta: {
title: '重置密码',
},
component: () => import('@/views/user/ResetPwd.vue'),
},
],
},
{
path: '/simulationScene',
name: 'SimulationScene',
component: () => import(/* webpackChunkName: "fail" */ '@/views/simulationScene/index.vue'),
redirect: '/simulationScene/centralControl',
children: [
{
path: '/simulationScene/trainer',
name: 'SimulationSceneTrainer',
//component: () => import(/* webpackChunkName: "fail" */ '@/views/simulationScene/trainer/index.vue'),
component: () => import(/* webpackChunkName: "fail" */ '@/views/simulationScene/instructor/index.vue'),
meta: { title: '训练员系统' },
},
{
path: '/simulationScene/trainerSystem',
name: 'SimulationSceneTrainerSystem',
component: () => import(/* webpackChunkName: "fail" */ '@/views/simulationScene/trainer/system/index.vue'),
children: [
{
path: '/simulationScene/trainerSystem/simulationModel',
name: 'SimulationSceneTrainerSystemSimulationModel',
component: () =>
import(/* webpackChunkName: "fail" */ '@/views/simulationScene/trainer/system/simulationModel/index1.vue'),
},
{
path: '/simulationScene/trainerSystem/sceneEditing',
name: 'SimulationSceneTrainerSystemSceneEditing',
component: () =>
import(/* webpackChunkName: "fail" */ '@/views/simulationScene/trainer/system/sceneEditing/index.vue'),
},
{
path: '/simulationScene/trainerSystem/display',
name: 'SimulationSceneTrainerSystemDisplay',
component: () =>
import(/* webpackChunkName: "fail" */ '@/views/simulationScene/trainer/system/display/index.vue'),
},
{
path: '/simulationScene/trainerSystem/evaluation',
name: 'SimulationSceneTrainerSystemEvaluation',
component: () =>
import(/* webpackChunkName: "fail" */ '@/views/simulationScene/trainer/system/evaluation/index.vue'),
},
],
},
{
path: '/simulationScene/instructor',
name: 'SimulationSceneInstructor',
component: () => import(/* webpackChunkName: "fail" */ '@/views/simulationScene/instructor/index.vue'),
meta: { title: '教员系统' },
},
{
path: '/simulationScene/instructorSystem',
name: 'SimulationSceneInstructorSystem',
component: () => import(/* webpackChunkName: "fail" */ '@/views/simulationScene/instructor/system/index.vue'),
children: [
{
path: '/simulationScene/instructorSystem/simulationModel',
name: 'SimulationSceneInstructorSystemSimulationModel',
component: () =>
import(/* webpackChunkName: "fail" */ '@/views/simulationScene/instructor/system/simulationModel/index.vue'),
},
{
path: '/simulationScene/instructorSystem/sceneEditing',
name: 'SimulationSceneInstructorSystemSceneEditing',
component: () =>
import(/* webpackChunkName: "fail" */ '@/views/simulationScene/instructor/system/sceneEditing/index.vue'),
},
{
path: '/simulationScene/instructorSystem/display',
name: 'SimulationSceneInstructorSystemDisplay',
component: () =>
import(/* webpackChunkName: "fail" */ '@/views/simulationScene/instructor/system/display/index.vue'),
},
{
path: '/simulationScene/instructorSystem/evaluation',
name: 'SimulationSceneInstructorSystemEvaluation',
component: () =>
import(/* webpackChunkName: "fail" */ '@/views/simulationScene/instructor/system/evaluation/index.vue'),
},
],
},
{
path: '/simulationScene/systemSelect',
name: 'SimulationSceneSystemSelect',
component: () => import(/* webpackChunkName: "fail" */ '@/views/simulationScene/systemSelect/index.vue'),
},
{
path: '/simulationScene/centralControl',
name: 'SimulationSceneCentralControl',
@ -323,6 +48,7 @@ export const constantRouterMap = [
path: '/simulationScene/sceneEditing',
name: 'SimulationSceneSceneEditing',
component: () => import(/* webpackChunkName: "fail" */ '@/views/simulationScene/sceneEditing/index.vue'),
meta: { title: '场景编辑子系统' },
},
{
path: '/simulationScene/display',
@ -338,9 +64,30 @@ export const constantRouterMap = [
},
],
},
{
path: '/user',
component: UserLayout,
redirect: '/user/login',
hidden: true,
children: [
{
path: '/user/login',
name: 'login',
component: () => import(/* webpackChunkName: "user" */ '@/views/user/Login'),
},
{
path: '/user/welcome',
name: 'Welcome',
component: () => import(/* webpackChunkName: "user" */ '@/views/user/Welcome'),
},
],
},
{
path: '/404',
component: () => import(/* webpackChunkName: "fail" */ '@/views/exception/404'),
},
{
path: '*',
redirect: '/404',
},
]

View File

@ -9,71 +9,38 @@
:i18nRender="i18nRender"
v-bind="settings"
>
<!-- Ads begin
广告代码 真实项目中请移除
production remove this Ads
-->
<ads v-if="isProPreviewSite && !collapsed"/>
<!-- Ads end -->
<!-- 1.0.0+ 版本 pro-layout 提供 API
我们推荐使用这种方式进行 LOGO title 自定义
-->
<template v-slot:menuHeaderRender>
<div>
<logo-svg />
<h1>{{ title }}</h1>
</div>
<div></div>
</template>
<!-- 1.0.0+ 版本 pro-layout 提供 API,
增加 Header 左侧内容区自定义
-->
<template v-slot:headerContentRender>
<!-- <template v-slot:headerContentRender>
<div>
<a-tooltip title="刷新页面">
<a-icon type="reload" style="font-size: 18px;cursor: pointer;" @click="() => { $message.info('页面刷新') }" />
</a-tooltip>
</div>
</template>
</template> -->
<!-- <setting-drawer v-if="isDev" :settings="settings" @change="handleSettingChange">
<div style="margin: 12px 0;">
This is SettingDrawer custom footer content.
</div>
</setting-drawer> -->
<template v-slot:rightContentRender>
<right-content :top-menu="settings.layout === 'topmenu'" :is-mobile="isMobile" :theme="settings.theme" />
</template>
<!-- custom footer / 自定义Footer -->
<template v-slot:footerRender>
<global-footer />
</template>
<router-view />
</pro-layout>
</template>
<script>
import { SettingDrawer, updateTheme } from '@ant-design-vue/pro-layout'
import { i18nRender } from '@/locales'
import { mapState } from 'vuex'
import { CONTENT_WIDTH_TYPE, SIDEBAR_TYPE, TOGGLE_MOBILE_TYPE } from '@/store/mutation-types'
import defaultSettings from '@/config/defaultSettings'
import RightContent from '@/components/GlobalHeader/RightContent'
import GlobalFooter from '@/components/GlobalFooter'
import Ads from '@/components/Other/CarbonAds'
import LogoSvg from '../assets/logo.svg?inline'
export default {
name: 'BasicLayout',
components: {
SettingDrawer,
RightContent,
GlobalFooter,
LogoSvg,
Ads
},
data () {
data() {
return {
// preview.pro.antdv.com only use.
isProPreviewSite: process.env.VUE_APP_PREVIEW === 'true' && process.env.NODE_ENV !== 'development',
@ -99,24 +66,23 @@ export default {
colorWeak: defaultSettings.colorWeak,
hideHintAlert: false,
hideCopyButton: false
hideCopyButton: false,
},
//
query: {},
//
isMobile: false
isMobile: false,
}
},
computed: {
...mapState({
//
mainMenu: state => state.permission.addRouters
})
mainMenu: (state) => state.permission.addRouters,
}),
},
created () {
const routes = this.mainMenu.find(item => item.path === '/')
this.menus = (routes && routes.children) || []
created() {
this.menus = this.mainMenu || []
//
this.$watch('collapsed', () => {
this.$store.commit(SIDEBAR_TYPE, this.collapsed)
@ -125,7 +91,7 @@ export default {
this.$store.commit(TOGGLE_MOBILE_TYPE, this.isMobile)
})
},
mounted () {
mounted() {
const userAgent = navigator.userAgent
if (userAgent.indexOf('Edge') > -1) {
this.$nextTick(() => {
@ -135,16 +101,10 @@ export default {
}, 16)
})
}
// first update color
// TIPS: THEME COLOR HANDLER!! PLEASE CHECK THAT!!
if (process.env.NODE_ENV !== 'production' || process.env.VUE_APP_PREVIEW === 'true') {
updateTheme(this.settings.primaryColor)
}
},
methods: {
i18nRender,
handleMediaQuery (val) {
handleMediaQuery(val) {
this.query = val
if (this.isMobile && !val['screen-xs']) {
this.isMobile = false
@ -157,10 +117,10 @@ export default {
// this.settings.fixSiderbar = false
}
},
handleCollapse (val) {
handleCollapse(val) {
this.collapsed = val
},
handleSettingChange ({ type, value }) {
handleSettingChange({ type, value }) {
console.log('type', type, value)
type && (this.settings[type] = value)
switch (type) {
@ -176,11 +136,11 @@ export default {
}
break
}
}
}
},
},
}
</script>
<style lang="less">
@import "./BasicLayout.less";
@import './BasicLayout.less';
</style>

View File

@ -1,165 +1,27 @@
<template>
<div id="userLayout" :class="['user-layout-wrapper', isMobile && 'mobile']">
<img style="position: absolute; left: 0; top: 0; width: 100%; height: 100%; object-fit: cover" :src="bg" alt="" />
<div class="container">
<div class="user-layout-lang">
<!-- <select-lang class="select-lang-trigger" /> -->
</div>
<div class="user-layout-content">
<router-view />
<div class="footer">
<div class="copyright">
<!-- Copyright &copy; 2022 北京五木恒润科技有限公司 -->
</div>
</div>
</div>
<Grid class="user-layout-wrapper">
<div class="bg-wrapper" style="grid-area: 1 / 1 / 2 / 2">
<img style="width: 100%; height: 100%; object-fit: cover" :src="bg" alt="" />
</div>
</div>
<div class="page-wrapper" style="grid-area: 1 / 1 / 2 / 2">
<router-view />
</div>
</Grid>
</template>
<script>
import { deviceMixin } from '@/store/device-mixin'
// import SelectLang from '@/components/SelectLang'
export default {
name: 'UserLayout',
// components: {
// SelectLang,
// },
mixins: [deviceMixin],
data() {
return {
bg: require('@/assets/images/user/bg.jpg'),
}
},
mounted() {
document.body.classList.add('userLayout')
},
beforeDestroy() {
document.body.classList.remove('userLayout')
},
}
</script>
<style lang="less" scoped>
#userLayout.user-layout-wrapper {
.user-layout-wrapper {
height: 100%;
position: relative;
&.mobile {
.container {
.main {
max-width: 368px;
width: 98%;
}
}
}
.container {
width: 100%;
min-height: 100%;
// background: #f0f2f5 url(~@/assets/background.png) no-repeat 50%;
background-size: 100%;
//padding: 50px 0 84px;
position: relative;
.user-layout-lang {
width: 100%;
height: 40px;
line-height: 44px;
text-align: right;
.select-lang-trigger {
cursor: pointer;
padding: 12px;
margin-right: 24px;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 18px;
vertical-align: middle;
}
}
.user-layout-content {
padding: 32px 0 24px;
.top {
text-align: center;
.header {
height: 44px;
line-height: 44px;
.badge {
position: absolute;
display: inline-block;
line-height: 1;
vertical-align: middle;
margin-left: -12px;
margin-top: -10px;
opacity: 0.8;
}
.logo {
height: 44px;
vertical-align: top;
margin-right: 16px;
border-style: none;
}
.title {
font-size: 33px;
color: rgba(0, 0, 0, 0.85);
font-family: Avenir, 'Helvetica Neue', Arial, Helvetica, sans-serif;
font-weight: 600;
position: relative;
top: 2px;
}
}
.desc {
font-size: 14px;
color: rgba(0, 0, 0, 0.45);
margin-top: 12px;
margin-bottom: 40px;
}
}
.main {
min-width: 260px;
width: 368px;
margin: 0 auto;
}
.footer {
// position: absolute;
width: 100%;
bottom: 0;
padding: 0 16px;
margin: 48px 0 24px;
text-align: center;
.links {
margin-bottom: 8px;
font-size: 14px;
a {
color: rgba(0, 0, 0, 0.45);
transition: all 0.3s;
&:not(:last-child) {
margin-right: 40px;
}
}
}
.copyright {
color: rgba(0, 0, 0, 0.45);
font-size: 14px;
}
}
}
a {
text-decoration: none;
}
}
}
</style>

View File

@ -35,7 +35,6 @@ router.beforeEach((to, from, next) => {
// 根据roles权限生成可访问的路由表
// 动态添加可访问路由表
// VueRouter@3.5.0+ New API
console.log(2345)
store.getters.addRouters.forEach((r) => {
router.addRoute(r)

View File

@ -82,17 +82,12 @@ export const generatorDynamicRouter = token => {
.then(res => {
console.log('generatorDynamicRouter response:', res)
const data = res.data
const menuNav = []
const childrenNav = []
// console.log('data', res.data)
// 后端数据, 根级树数组, 根级 PID
listToTree(data, childrenNav, '')
console.log('childrenNav', childrenNav)
rootRouter.children = childrenNav
menuNav.push(rootRouter)
console.log('menuNav', menuNav)
const routers = generator(menuNav)
routers.push(notFoundRouter)
const routers = generator(childrenNav)
console.log('routers', routers)
resolve(routers)
})

View File

@ -7,26 +7,25 @@ import { generatorDynamicRouter } from '@/router/generator-routers'
const permission = {
state: {
routers: constantRouterMap,
addRouters: []
addRouters: [],
},
mutations: {
SET_ROUTERS: (state, routers) => {
state.addRouters = routers
state.routers = constantRouterMap.concat(routers)
}
state.routers = [].concat(routers, constantRouterMap)
},
},
actions: {
GenerateRoutes ({ commit }, data) {
return new Promise(resolve => {
GenerateRoutes({ commit }, data) {
return new Promise((resolve) => {
// const { token } = data
generatorDynamicRouter().then(routers => {
console.log(routers,'routers')
generatorDynamicRouter().then((routers) => {
commit('SET_ROUTERS', routers)
resolve()
})
})
}
}
},
},
}
export default permission

View File

@ -64,7 +64,6 @@ const permission = {
},
actions: {
GenerateRoutes ({ commit }, data) {
console.log('adfafadfasddf')
return new Promise(resolve => {
const { roles } = data
const routerMap = cloneDeep(asyncRouterMap)

View File

@ -2,7 +2,6 @@
<div class="simulation-scene-page flex-c">
<Flex v-if="title" jc="sb" ai="c" class="simulation-scene-header">
<div class="simulation-scene-title">{{ title }}</div>
<div><a-icon type="poweroff" title="退出系统" style="color: red" @click="handleQuit" /></div>
</Flex>
<div class="simulation-scene-main">
<router-view></router-view>
@ -22,11 +21,6 @@ export default {
}
},
},
methods: {
handleQuit() {
this.$router.push({ name: 'thirdLogin' })
},
},
}
</script>

View File

@ -1,177 +1,107 @@
<template>
<div class="flexColumnCenterCenter" style="width: 100%; height: 100vh; position: fixed; left: 0; top: 0">
<div
style="
height: 36px;
font-size: 36px;
line-height: 1;
font-weight: bolder;
letter-spacing: 7px;
color: #ffffff;
margin-bottom: 75px;
"
>
某仿真分析方法工具
</div>
<div style="width: 917px; height: 445px; position: relative; overflow: hidden" class="flexRowStart">
<img
style="position: absolute; left: -10px; top: -8px; z-index: 1; width: 937px; height: 465px"
:src="bgLogin"
alt=""
/>
<div style="width: 524px; height: 100%; position: relative; z-index: 2" class="flexColumnCenterCenter">
<!-- <img src="@/assets/loginLeft.png" alt="" style="width: 100%;height: 100%;"> -->
<Flex class="user-login-page" fd="co" ai="c" jc="c" style="height: 100%">
<div class="user-login-title">某仿真分析方法工具</div>
<Grid style="width: 917px; height: 445px" :columns="['524px', '329px', '0px']" gap="32px">
<div class="oh" style="grid-area: 1 / 1 / 2 / 4">
<img class="user-login-bg" :src="bgLogin" alt="" />
</div>
<div style="width: 393px; height: 100%; padding: 48px 32px; position: relative; z-index: 3">
<div class="main">
<div
style="
font-size: 24px;
line-height: 1;
font-weight: bolder;
font-stretch: normal;
letter-spacing: 5px;
color: #ffffff;
margin-bottom: 27px;
"
>
用户登录
</div>
<a-form id="formLogin" class="user-layout-login" ref="formLogin" :form="form" @submit="handleSubmit">
<!-- <a-alert v-if="isLoginError" type="error" showIcon style="margin-bottom: 24px;" :message="$t('user.login.message-invalid-credentials')" /> -->
<a-form-item>
<Flex class="pr zi1" fd="co" jc="c" style="grid-area: 1 / 2 / 2 / 3">
<div class="user-login-form-title">用户登录</div>
<a-form class="user-login-form-body" :form="form" @submit="handleSubmit">
<a-form-item>
<a-input
size="large"
type="text"
placeholder="请输入用户名"
v-decorator="[
'username',
{
rules: [
{ required: true, message: $t('user.userName.required') },
{ validator: handleUsernameOrEmail },
],
validateTrigger: 'change',
},
]"
>
<a-icon slot="prefix" type="user" :style="{ color: 'rgba(0,0,0,.25)' }" />
</a-input>
</a-form-item>
<a-form-item>
<a-input-password
size="large"
placeholder="请输入密码"
v-decorator="[
'password',
{ rules: [{ required: true, message: $t('user.password.required') }], validateTrigger: 'blur' },
]"
>
<a-icon slot="prefix" type="lock" :style="{ color: 'rgba(0,0,0,.25)' }" />
</a-input-password>
</a-form-item>
<a-form-item>
<div class="pr">
<a-input
size="large"
type="text"
placeholder="请输入用户名"
placeholder="验证码"
v-decorator="[
'username',
'code',
{
rules: [
{ required: true, message: $t('user.userName.required') },
{ validator: handleUsernameOrEmail },
],
rules: [{ required: true, message: '请输入验证码' }],
validateTrigger: 'change',
},
]"
>
<a-icon slot="prefix" type="user" :style="{ color: 'rgba(0,0,0,.25)' }" />
</a-input>
</a-form-item>
<img class="code pa" :src="codeImg" @click="codeClick" ref="code" alt="更新验证码" />
</div>
</a-form-item>
<a-form-item>
<a-input-password
size="large"
placeholder="请输入密码"
v-decorator="[
'password',
{ rules: [{ required: true, message: $t('user.password.required') }], validateTrigger: 'blur' },
]"
>
<a-icon slot="prefix" type="lock" :style="{ color: 'rgba(0,0,0,.25)' }" />
</a-input-password>
</a-form-item>
<a-form-item>
<div class="f-item">
<a-input
class="login-ipt"
size="large"
type="text"
placeholder="验证码"
v-decorator="[
'code',
{
rules: [{ required: true, message: '请输入验证码' }],
validateTrigger: 'change',
},
]"
>
</a-input>
<img class="code" :src="codeImg" @click="codeClick" ref="code" alt="更新验证码" />
</div>
</a-form-item>
<!-- <a-form-item>
<a-checkbox v-decorator="['rememberMe', { valuePropName: 'checked' }]">{{ $t('user.login.remember-me') }}</a-checkbox>
<router-link
:to="{ name: 'recover', params: { user: 'aaa'} }"
class="forge-password"
style="float: right;"
>{{ $t('user.login.forgot-password') }}</router-link>
</a-form-item> -->
<a-form-item style="margin-top: 16px">
<a-button
size="large"
type="primary"
htmlType="submit"
class="login-button"
:loading="state.loginBtn"
:disabled="state.loginBtn"
>
{{ $t('user.login.login') }}
</a-button>
</a-form-item>
</a-form>
<two-step-captcha
v-if="requiredTwoStepCaptcha"
:visible="stepCaptchaVisible"
@success="stepCaptchaSuccess"
@cancel="stepCaptchaCancel"
></two-step-captcha>
</div>
</div>
</div>
</div>
<a-form-item style="margin-top: 16px">
<a-button
size="large"
type="primary"
htmlType="submit"
class="login-button"
:loading="state.loginBtn"
:disabled="state.loginBtn"
>
登录
</a-button>
</a-form-item>
</a-form>
</Flex>
</Grid>
</Flex>
</template>
<script>
// import md5 from 'md5'
import TwoStepCaptcha from '@/components/tools/TwoStepCaptcha'
import { mapActions } from 'vuex'
import { timeFix } from '@/utils/util'
import { getSmsCaptcha } from '@/api/login'
export default {
components: {
TwoStepCaptcha,
},
data() {
return {
bgLogin: require('@/assets/images/user/bg-login.png'),
customActiveKey: 'tab1',
loginBtn: false,
// login type: 0 email, 1 username, 2 telephone
loginType: 0,
isLoginError: false,
requiredTwoStepCaptcha: false,
stepCaptchaVisible: false,
form: this.$form.createForm(this),
state: {
time: 60,
loginBtn: false,
// login type: 0 email, 1 username, 2 telephone
loginType: 0,
smsSendBtn: false,
},
clientCode: '',
codeImg: '',
}
},
created() {
// get2step({ })
// .then(res => {
// this.requiredTwoStepCaptcha = res.result.stepCode
// })
// .catch(() => {
// this.requiredTwoStepCaptcha = false
// })
// this.requiredTwoStepCaptcha = true
this.codeClick()
},
methods: {
...mapActions(['Login', 'Logout']),
...mapActions(['Login']),
codeClick() {
this.clientCode = 'c' + Math.random() * 100000000
this.codeImg = '/api/validateCode?t=' + new Date().getTime() + '&clientCode=' + this.clientCode
@ -187,26 +117,20 @@ export default {
}
callback()
},
handleTabClick(key) {
this.customActiveKey = key
// this.form.resetFields()
},
handleSubmit(e) {
e.preventDefault()
const {
form: { validateFields },
state,
customActiveKey,
Login,
} = this
state.loginBtn = true
const validateFieldsKey = customActiveKey === 'tab1' ? ['username', 'password', 'code'] : ['mobile', 'captcha']
const validateFieldsKey = ['username', 'password', 'code']
validateFields(validateFieldsKey, { force: true }, (err, values) => {
if (!err) {
console.log('login form', values)
const loginParams = { ...values, clientCode: this.clientCode }
delete loginParams.username
loginParams[!state.loginType ? 'email' : 'userName'] = values.username
@ -217,7 +141,6 @@ export default {
})
.catch((err) => {
this.requestFailed(err)
console.log(err)
})
.finally(() => {
state.loginBtn = false
@ -229,69 +152,9 @@ export default {
}
})
},
getCaptcha(e) {
e.preventDefault()
const {
form: { validateFields },
state,
} = this
validateFields(['mobile'], { force: true }, (err, values) => {
if (!err) {
state.smsSendBtn = true
const interval = window.setInterval(() => {
if (state.time-- <= 0) {
state.time = 60
state.smsSendBtn = false
window.clearInterval(interval)
}
}, 1000)
const hide = this.$message.loading('验证码发送中..', 0)
getSmsCaptcha({ mobile: values.mobile })
.then((res) => {
setTimeout(hide, 2500)
this.$notification['success']({
message: '提示',
description: '验证码获取成功,您的验证码为:' + res.result.captcha,
duration: 8,
})
})
.catch((err) => {
setTimeout(hide, 1)
clearInterval(interval)
state.time = 60
state.smsSendBtn = false
this.requestFailed(err)
})
}
})
},
stepCaptchaSuccess() {
this.loginSuccess()
},
stepCaptchaCancel() {
this.Logout().then(() => {
this.loginBtn = false
this.stepCaptchaVisible = false
})
},
loginSuccess(res) {
console.log(res)
// check res.homePage define, set $router.push name res.homePage
// Why not enter onComplete
/*
this.$router.push({ name: 'analysis' }, () => {
console.log('onComplete')
this.$notification.success({
message: '欢迎',
description: `${timeFix()},欢迎回来`
})
})
*/
// this.$router.push({ path: '/' })
this.$router.push({ path: '/user/thirdLogin' })
this.$router.push({ path: '/' })
// 1
setTimeout(() => {
this.$notification.success({
@ -299,10 +162,9 @@ export default {
description: `${timeFix()},欢迎回来`,
})
}, 1000)
this.isLoginError = false
},
requestFailed(err) {
this.isLoginError = true
console.log(err)
this.$notification['error']({
message: '错误',
description: ((err.response || {}).data || {}).message || '请求出现错误,请稍后再试',
@ -314,55 +176,30 @@ export default {
</script>
<style lang="less" scoped>
@import '../../assets/bootstrap.css';
#login-wrapper {
margin: 50px auto 0;
position: relative;
z-index: 5;
}
#logo-login {
background: rgba(48, 65, 96, 0.8);
border-radius: 3px 3px 0 0;
.user-login-title {
height: 36px;
font-size: 36px;
line-height: 1;
font-weight: bolder;
letter-spacing: 7px;
color: #ffffff;
padding: 1px 0 14px 25px;
margin-bottom: 75px;
}
.f-item {
position: relative;
.user-login-bg {
width: 937px;
height: 465px;
transform: translate(-10px, -8px);
}
.code {
position: absolute;
top: 0;
right: 0;
width: 50%;
height: 52px;
object-fit: cover;
.user-login-form-title {
font-size: 24px;
line-height: 1;
font-weight: bolder;
font-stretch: normal;
letter-spacing: 5px;
color: #ffffff;
margin-bottom: 27px;
}
.account-box {
-moz-border-radius: 0 0 4px 4px;
-webkit-border-radius: 0 0 4px 4px;
-khtml-border-radius: 0 0 4px 4px;
border-radius: 0 0 4px 4px;
z-index: 3;
font-size: 13px !important;
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
background-color: #ffffff;
padding: 20px;
}
.user-layout-login {
label {
font-size: 14px;
}
.getCaptcha {
display: block;
width: 100%;
height: 40px;
}
.forge-password {
font-size: 14px;
}
.user-login-form-body {
button.login-button {
padding: 0 15px;
font-size: 16px;
@ -372,28 +209,13 @@ export default {
border: none;
background-position: -2px 0px;
}
.user-login-other {
text-align: left;
margin-top: 24px;
line-height: 22px;
.item-icon {
font-size: 24px;
color: rgba(0, 0, 0, 0.2);
margin-left: 16px;
vertical-align: middle;
cursor: pointer;
transition: color 0.3s;
&:hover {
color: #1890ff;
}
}
.register {
float: right;
}
.code {
position: absolute;
top: 0;
right: 0;
width: 50%;
height: 52px;
object-fit: cover;
}
}
::v-deep {

View File

@ -1,316 +0,0 @@
<template>
<div class="main user-layout-register">
<h3><span>{{ $t('user.register.register') }}</span></h3>
<a-form ref="formRegister" :form="form" id="formRegister">
<a-form-item>
<a-input
size="large"
type="text"
:placeholder="$t('user.register.email.placeholder')"
v-decorator="['email', {rules: [{ required: true, type: 'email', message: $t('user.email.required') }], validateTrigger: ['change', 'blur']}]"
></a-input>
</a-form-item>
<a-popover
placement="rightTop"
:trigger="['focus']"
:getPopupContainer="(trigger) => trigger.parentElement"
v-model="state.passwordLevelChecked">
<template slot="content">
<div :style="{ width: '240px' }" >
<div :class="['user-register', passwordLevelClass]">{{ $t(passwordLevelName) }}</div>
<a-progress :percent="state.percent" :showInfo="false" :strokeColor=" passwordLevelColor " />
<div style="margin-top: 10px;">
<span>{{ $t('user.register.password.popover-message') }}
</span>
</div>
</div>
</template>
<a-form-item>
<a-input-password
size="large"
@click="handlePasswordInputClick"
:placeholder="$t('user.register.password.placeholder')"
v-decorator="['password', {rules: [{ required: true, message: $t('user.password.required') }, { validator: this.handlePasswordLevel }], validateTrigger: ['change', 'blur']}]"
></a-input-password>
</a-form-item>
</a-popover>
<a-form-item>
<a-input-password
size="large"
:placeholder="$t('user.register.confirm-password.placeholder')"
v-decorator="['password2', {rules: [{ required: true, message: $t('user.password.required') }, { validator: this.handlePasswordCheck }], validateTrigger: ['change', 'blur']}]"
></a-input-password>
</a-form-item>
<a-form-item>
<a-input size="large" :placeholder="$t('user.login.mobile.placeholder')" v-decorator="['mobile', {rules: [{ required: true, message: $t('user.phone-number.required'), pattern: /^1[3456789]\d{9}$/ }, { validator: this.handlePhoneCheck } ], validateTrigger: ['change', 'blur'] }]">
<a-select slot="addonBefore" size="large" defaultValue="+86">
<a-select-option value="+86">+86</a-select-option>
<a-select-option value="+87">+87</a-select-option>
</a-select>
</a-input>
</a-form-item>
<!--<a-input-group size="large" compact>
<a-select style="width: 20%" size="large" defaultValue="+86">
<a-select-option value="+86">+86</a-select-option>
<a-select-option value="+87">+87</a-select-option>
</a-select>
<a-input style="width: 80%" size="large" placeholder="11 位手机号"></a-input>
</a-input-group>-->
<a-row :gutter="16">
<a-col class="gutter-row" :span="16">
<a-form-item>
<a-input size="large" type="text" :placeholder="$t('user.login.mobile.verification-code.placeholder')" v-decorator="['captcha', {rules: [{ required: true, message: '请输入验证码' }], validateTrigger: 'blur'}]">
<a-icon slot="prefix" type="mail" :style="{ color: 'rgba(0,0,0,.25)' }"/>
</a-input>
</a-form-item>
</a-col>
<a-col class="gutter-row" :span="8">
<a-button
class="getCaptcha"
size="large"
:disabled="state.smsSendBtn"
@click.stop.prevent="getCaptcha"
v-text="!state.smsSendBtn && $t('user.register.get-verification-code')||(state.time+' s')"></a-button>
</a-col>
</a-row>
<a-form-item>
<a-button
size="large"
type="primary"
htmlType="submit"
class="register-button"
:loading="registerBtn"
@click.stop.prevent="handleSubmit"
:disabled="registerBtn">{{ $t('user.register.register') }}
</a-button>
<router-link class="login" :to="{ name: 'login' }">{{ $t('user.register.sign-in') }}</router-link>
</a-form-item>
</a-form>
</div>
</template>
<script>
import { getSmsCaptcha } from '@/api/login'
import { deviceMixin } from '@/store/device-mixin'
import { scorePassword } from '@/utils/util'
const levelNames = {
0: 'user.password.strength.short',
1: 'user.password.strength.low',
2: 'user.password.strength.medium',
3: 'user.password.strength.strong'
}
const levelClass = {
0: 'error',
1: 'error',
2: 'warning',
3: 'success'
}
const levelColor = {
0: '#ff0000',
1: '#ff0000',
2: '#ff7e05',
3: '#52c41a'
}
export default {
name: 'Register',
components: {
},
mixins: [deviceMixin],
data () {
return {
form: this.$form.createForm(this),
state: {
time: 60,
level: 0,
smsSendBtn: false,
passwordLevel: 0,
passwordLevelChecked: false,
percent: 10,
progressColor: '#FF0000'
},
registerBtn: false
}
},
computed: {
passwordLevelClass () {
return levelClass[this.state.passwordLevel]
},
passwordLevelName () {
return levelNames[this.state.passwordLevel]
},
passwordLevelColor () {
return levelColor[this.state.passwordLevel]
}
},
methods: {
handlePasswordLevel (rule, value, callback) {
if (value === '') {
return callback()
}
console.log('scorePassword ; ', scorePassword(value))
if (value.length >= 6) {
if (scorePassword(value) >= 30) {
this.state.level = 1
}
if (scorePassword(value) >= 60) {
this.state.level = 2
}
if (scorePassword(value) >= 80) {
this.state.level = 3
}
} else {
this.state.level = 0
callback(new Error(this.$t('user.password.strength.msg')))
}
this.state.passwordLevel = this.state.level
this.state.percent = this.state.level * 33
callback()
},
handlePasswordCheck (rule, value, callback) {
const password = this.form.getFieldValue('password')
// console.log('value', value)
if (value === undefined) {
callback(new Error(this.$t('user.password.required')))
}
if (value && password && value.trim() !== password.trim()) {
callback(new Error(this.$t('user.password.twice.msg')))
}
callback()
},
handlePhoneCheck (rule, value, callback) {
console.log('handlePhoneCheck, rule:', rule)
console.log('handlePhoneCheck, value', value)
console.log('handlePhoneCheck, callback', callback)
callback()
},
handlePasswordInputClick () {
if (!this.isMobile) {
this.state.passwordLevelChecked = true
return
}
this.state.passwordLevelChecked = false
},
handleSubmit () {
const { form: { validateFields }, state, $router } = this
validateFields({ force: true }, (err, values) => {
if (!err) {
state.passwordLevelChecked = false
$router.push({ name: 'registerResult', params: { ...values } })
}
})
},
getCaptcha (e) {
e.preventDefault()
const { form: { validateFields }, state, $message, $notification } = this
validateFields(['mobile'], { force: true },
(err, values) => {
if (!err) {
state.smsSendBtn = true
const interval = window.setInterval(() => {
if (state.time-- <= 0) {
state.time = 60
state.smsSendBtn = false
window.clearInterval(interval)
}
}, 1000)
const hide = $message.loading('验证码发送中..', 0)
getSmsCaptcha({ mobile: values.mobile }).then(res => {
setTimeout(hide, 2500)
$notification['success']({
message: '提示',
description: '验证码获取成功,您的验证码为:' + res.result.captcha,
duration: 8
})
}).catch(err => {
setTimeout(hide, 1)
clearInterval(interval)
state.time = 60
state.smsSendBtn = false
this.requestFailed(err)
})
}
}
)
},
requestFailed (err) {
this.$notification['error']({
message: '错误',
description: ((err.response || {}).data || {}).message || '请求出现错误,请稍后再试',
duration: 4
})
this.registerBtn = false
}
},
watch: {
'state.passwordLevel' (val) {
console.log(val)
}
}
}
</script>
<style lang="less">
.user-register {
&.error {
color: #ff0000;
}
&.warning {
color: #ff7e05;
}
&.success {
color: #52c41a;
}
}
.user-layout-register {
.ant-input-group-addon:first-child {
background-color: #fff;
}
}
</style>
<style lang="less" scoped>
.user-layout-register {
& > h3 {
font-size: 16px;
margin-bottom: 20px;
}
.getCaptcha {
display: block;
width: 100%;
height: 40px;
}
.register-button {
width: 50%;
}
.login {
float: right;
line-height: 40px;
}
}
</style>

View File

@ -1,44 +0,0 @@
<template>
<a-result
:isSuccess="true"
:content="false"
:title="email"
:sub-title="description">
<template #extra>
<a-button size="large" type="primary">查看邮箱</a-button>
<a-button size="large" style="margin-left: 8px" @click="goHomeHandle">返回首页</a-button>
</template>
</a-result>
</template>
<script>
export default {
name: 'RegisterResult',
data () {
return {
description: '激活邮件已发送到你的邮箱中邮件有效期为24小时。请及时登录邮箱点击邮件中的链接激活帐户。',
form: {}
}
},
computed: {
email () {
const v = this.form && this.form.email || 'xxx'
return `你的账户:${v} 注册成功`
}
},
created () {
this.form = this.$route.params
},
methods: {
goHomeHandle () {
this.$router.push({ name: 'login' })
}
}
}
</script>
<style scoped>
</style>

View File

@ -1,75 +0,0 @@
<template>
<a-card>
<a-form-model ref="form" :model="model" :rules="rules" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-form-model-item label="旧密码" prop="oldPassword">
<a-input v-model="model.oldPassword" type="password" placeholder="请输入旧密码" />
</a-form-model-item>
<a-form-model-item label="新密码" prop="password">
<a-input v-model="model.password" type="password" placeholder="请输入新密码" />
</a-form-model-item>
<a-form-model-item label="确认密码" prop="confirm">
<a-input v-model="model.confirm" type="password" placeholder="请输入重新输入密码" />
</a-form-model-item>
<a-form-model-item label=" " :colon="false">
<a-button type="primary" @click="handleResetPwd">确认修改</a-button>
<a-button @click="returnPage" style="margin-left: 10px;">返回</a-button>
</a-form-model-item>
</a-form-model>
</a-card>
</template>
<script>
import { resetPwd } from '@/api/system/user'
export default {
name: 'ResetPwd',
data () {
const comparePassword = (rule, value, callback) => {
if (value && value !== this.model.password) {
callback(new Error('两次密码不一致!'))
} else {
callback()
}
}
return {
model: {},
labelCol: { xs: { span: 24 }, sm: { span: 6 } },
wrapperCol: { xs: { span: 24 }, sm: { span: 15 } },
rules: {
oldPassword: [{ required: true, message: '请输入旧密码', trigger: 'blur' }],
password: [{ required: true, message: '请输入新密码', trigger: 'blur' }],
confirm: [
{ required: true, message: '请输入重新输入密码', trigger: 'blur' },
{ validator: comparePassword, trigger: 'change' }
]
}
}
},
methods: {
handleResetPwd () {
this.$refs.form.validate(valid => {
if (valid) {
const target = { ...this.model }
console.log(target)
const userInfo = this.$store.getters.userInfo
resetPwd({ ...target, id: userInfo.id }).then(res => {
if (res.data) {
this.$notification.success({ message: '修改密码成功' })
} else {
this.$notification.error({ message: res.message })
}
})
} else {
return false
}
})
},
returnPage () {
this.$router.back()
}
}
}
</script>
<style>
</style>

125
src/views/user/Welcome.vue Normal file
View File

@ -0,0 +1,125 @@
<template>
<Grid :rows="['50px', 1]" style="height: 100%">
<Flex jc="fe" style="grid-area: 1 / 1 / 2 / 2">
<span class="welcome-content">{{ userInfo.nickName }}欢迎您</span>
</Flex>
<Flex ai="c" jc="c" style="grid-area: 1 / 1 / 3 / 2">
<Grid style="width: 917px; height: 445px" :columns="[1, 1, 1]" :rows="[1, 1]">
<Flex
v-for="module in systemModules"
:key="module.moduleCode"
fd="co"
ai="c"
jc="sb"
class="module-block"
@click="handleClick(module)"
>
<img class="icon" :src="module.icon" alt="" />
<div class="title">{{ module.moduleName }}</div>
</Flex>
</Grid>
</Flex>
</Grid>
</template>
<script>
import { mapState } from 'vuex'
export default {
data() {
return {
systemPathMap: {
db_system: '/simulationScene/database',
simulation_system: '/simulationScene/simulationModel',
scenario_system: '/simulationScene/sceneEditing',
display_system: '/simulationScene/display',
evaluation_system: '/simulationScene/evaluation',
},
systemModules: [
{
moduleCode: 'db_system',
moduleName: '数据库子系统',
icon: require('@/assets/images/simulation-scene/system-icon/database.png'),
modulePath: '/simulationScene/database',
},
{
moduleCode: 'simulation_system',
moduleName: '仿真模型子系统',
icon: require('@/assets/images/simulation-scene/system-icon/model.png'),
modulePath: '/simulationScene/simulationModel',
},
{
moduleCode: 'scenario_system',
moduleName: '场景编辑子系统',
icon: require('@/assets/images/simulation-scene/system-icon/editing.png'),
modulePath: '/simulationScene/sceneEditing',
},
{
moduleCode: 'display_system',
moduleName: '显示子系统',
icon: require('@/assets/images/simulation-scene/system-icon/display.png'),
modulePath: '/simulationScene/display',
},
{
moduleCode: 'evaluation_system',
moduleName: '评估子系统',
icon: require('@/assets/images/simulation-scene/system-icon/evaluation.png'),
modulePath: '/simulationScene/evaluation',
},
{
moduleCode: 'control_system',
moduleName: '系统控制子系统',
icon: require('@/assets/images/simulation-scene/system-icon/database.png'),
modulePath: '/simulationScene/centralControl',
},
],
}
},
computed: {
...mapState({
userInfo: (state) => state.user.info,
}),
},
methods: {
handleClick(module) {
window.open(
window.location.origin + module.modulePath,
'_blank',
'height=' +
window.screen.height +
',width=' +
window.screen.width +
',top=0,left=0,toolbar=no,menubar=no,scrollbars=no,resizable=no,location=no,status=no'
)
},
},
}
</script>
<style lang="less" scoped>
.welcome-content {
font-size: 20px;
line-height: 50px;
font-weight: bolder;
color: #00deff;
letter-spacing: 2px;
margin: 0 20px;
}
.module-block {
border: 1px solid #06445f;
background-color: #062e45;
padding-bottom: 32px;
cursor: pointer;
.icon {
transform: translateY(calc(80px - 50%));
}
.title {
color: #9cd3f5;
font-size: 24px;
line-height: 32px;
}
}
.module-block:hover {
border-color: #00deff;
}
</style>