Files
jshERP/jshERP-web/src/views/user/Login.vue

625 lines
21 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!-- b y 7 5 2 7 1 8 9 2 0 -->
<template>
<div class="login-page">
<!-- Background -->
<div class="login-bg"></div>
<div class="login-overlay"></div>
<!-- Content -->
<div class="login-content">
<!-- Left: Branding (desktop) -->
<div class="login-brand" v-if="device !== 'mobile'">
<div class="brand-inner">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 320" width="200" height="160" class="brand-logo">
<defs>
<linearGradient id="lg1" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:#c8962a" />
<stop offset="50%" style="stop-color:#f0c040" />
<stop offset="100%" style="stop-color:#c8962a" />
</linearGradient>
<filter id="s1" x="-5%" y="-5%" width="115%" height="115%">
<feDropShadow dx="2" dy="4" stdDeviation="4" flood-color="#000" flood-opacity="0.3"/>
</filter>
<filter id="g1">
<feGaussianBlur stdDeviation="2" result="blur"/>
<feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge>
</filter>
</defs>
<polygon points="70,210 130,70 190,210" fill="rgba(255,255,255,0.85)" filter="url(#s1)" opacity="0.9"/>
<polygon points="130,70 135,78 75,210 70,210" fill="rgba(255,255,255,0.5)" opacity="0.6"/>
<polygon points="140,210 200,30 260,210" fill="rgba(255,255,255,0.95)" filter="url(#s1)"/>
<polygon points="200,30 206,40 146,210 140,210" fill="rgba(255,255,255,0.6)" opacity="0.7"/>
<polygon points="210,210 270,70 330,210" fill="rgba(255,255,255,0.85)" filter="url(#s1)" opacity="0.9"/>
<polygon points="270,70 275,78 215,210 210,210" fill="rgba(255,255,255,0.5)" opacity="0.6"/>
<rect x="60" y="218" width="280" height="5" rx="2.5" fill="url(#lg1)" filter="url(#g1)"/>
<text x="200" y="258" text-anchor="middle" font-family="'Arial Black','Helvetica Neue',sans-serif" font-size="38" font-weight="900" letter-spacing="8" fill="#ffffff">MILESTONE</text>
<line x1="95" y1="266" x2="305" y2="266" stroke="url(#lg1)" stroke-width="1.5" opacity="0.6"/>
<text x="200" y="290" text-anchor="middle" font-family="'Helvetica Neue',Arial,sans-serif" font-size="11" letter-spacing="4" fill="rgba(255,255,255,0.7)" font-weight="300">INDUSTRIAL DEVELOPMENT CORPORATION</text>
</svg>
<h2 class="brand-slogan">Building A Better Tomorrow</h2>
<p class="brand-desc">
Milestone Industrial Development Corporation is committed to delivering excellence in construction and industrial development. With years of experience and a dedication to quality, we build the foundations for a stronger future.
</p>
</div>
</div>
<!-- Right: Login Form -->
<div class="login-form-wrapper">
<!-- Mobile logo -->
<div class="mobile-logo" v-if="device === 'mobile'">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 320" width="140" height="112">
<defs>
<linearGradient id="lg1m" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:#c8962a" />
<stop offset="50%" style="stop-color:#f0c040" />
<stop offset="100%" style="stop-color:#c8962a" />
</linearGradient>
</defs>
<polygon points="70,210 130,70 190,210" fill="rgba(255,255,255,0.85)" opacity="0.9"/>
<polygon points="140,210 200,30 260,210" fill="rgba(255,255,255,0.95)"/>
<polygon points="210,210 270,70 330,210" fill="rgba(255,255,255,0.85)" opacity="0.9"/>
<rect x="60" y="218" width="280" height="5" rx="2.5" fill="url(#lg1m)"/>
<text x="200" y="258" text-anchor="middle" font-family="'Arial Black','Helvetica Neue',sans-serif" font-size="38" font-weight="900" letter-spacing="8" fill="#ffffff">MILESTONE</text>
<text x="200" y="290" text-anchor="middle" font-family="'Helvetica Neue',Arial,sans-serif" font-size="11" letter-spacing="4" fill="rgba(255,255,255,0.7)" font-weight="300">INDUSTRIAL DEVELOPMENT CORPORATION</text>
</svg>
</div>
<div class="login-card">
<h3 class="login-title">Welcome Back</h3>
<p class="login-subtitle">Sign in to your account</p>
<a-form :form="form" class="user-layout-login" ref="formLogin" id="formLogin">
<a-form-item>
<a-input
size="large"
v-decorator="['loginName',{initialValue:'', rules: validatorRules.loginName.rules}]"
type="text"
placeholder="请输入用户名">
<a-icon slot="prefix" type="user" :style="{ color: 'rgba(0,0,0,.25)' }"/>
</a-input>
</a-form-item>
<a-form-item>
<a-input-password
v-decorator="['password',{initialValue:'', rules: validatorRules.password.rules}]"
size="large"
type="password"
autocomplete="false"
placeholder="请输入密码">
<a-icon slot="prefix" type="lock" :style="{ color: 'rgba(0,0,0,.25)' }"/>
</a-input-password>
</a-form-item>
<a-row :gutter="0" v-if="checkcodeFlag==='1'">
<a-col :span="14">
<a-form-item>
<a-input
v-decorator="['inputCode',{initialValue:'', rules: validatorRules.inputCode.rules}]"
size="large"
type="text"
default-value=""
placeholder="请输入验证码">
<a-icon slot="prefix" type="smile" :style="{ color: 'rgba(0,0,0,.25)' }"/>
</a-input>
</a-form-item>
</a-col>
<a-col :span="10" style="text-align: right">
<img v-if="requestCodeSuccess" style="margin-top: 2px;" :src="randCodeImage" @click="handleChangeCheckCode"/>
<img v-else style="margin-top: 2px;" src="../../assets/checkcode.png" @click="handleChangeCheckCode"/>
</a-col>
</a-row>
<a-form-item>
<a-checkbox :checked="checked" @change="handleChange">记住密码</a-checkbox>
<router-link v-if="registerFlag==='1'" :to="{ name: 'register'}" class="forge-password" style="float: right;margin-right: 10px;" >
注册租户
</router-link>
</a-form-item>
<a-form-item style="margin-top:16px">
<a-button
size="large"
type="primary"
htmlType="submit"
class="login-button"
:loading="loginBtn"
@click.stop.prevent="handleSubmit"
:disabled="loginBtn">登 录
</a-button>
</a-form-item>
</a-form>
</div>
</div>
</div>
<!-- Footer -->
<div class="login-footer">
© 2015-2030 MILESTONE INDUSTRIAL DEVELOPMENT CORPORATION. All Rights Reserved.
<a style="color:rgba(255,255,255,0.6);margin-left:8px;" :href="systemUrl" target="_blank">Official Website</a>
</div>
</div>
</template>
<!-- BY cao_yu_li -->
<script>
import md5 from 'md5'
import TwoStepCaptcha from '@/components/tools/TwoStepCaptcha'
import { mapActions } from 'vuex'
import { timeFix } from '@/utils/util'
import Vue from 'vue'
import { getPlatformConfigByKey} from '@/api/api'
import { ACCESS_TOKEN, ENCRYPTED_STRING } from '@/store/mutation-types'
import { getAction } from '@/api/manage'
import { getEncryptedString } from '@/utils/encryption/aesEncrypt'
import { mixinDevice } from '@/utils/mixin.js'
export default {
components: {
TwoStepCaptcha
},
mixins: [mixinDevice],
data () {
return {
customActiveKey: "tab1",
systemTitle: window.SYS_TITLE,
systemUrl: window.SYS_URL,
loginBtn: false,
loginType: 0,
requiredTwoStepCaptcha: false,
stepCaptchaVisible: false,
form: this.$form.createForm(this),
encryptedString:{
key:"",
iv:"",
},
state: {
time: 60,
smsSendBtn: false,
},
validatorRules:{
loginName:{rules: [{ required: true, message: '请输入用户名!'},{validator: this.handleLoginName}]},
password:{rules: [{ required: true, message: '请输入密码!',validator: 'click'}]},
inputCode:{rules: [{ required: true, message: '请输入验证码!',validator: 'click'}]}
},
verifiedCode:"",
inputCodeContent:"",
inputCodeNull:true,
departList:[],
departVisible:false,
departSelected:"",
currentUsername:"",
validate_status:"",
currdatetime:'',
uuid:'',
randCodeImage:'',
registerFlag:'',
checkcodeFlag:'',
mainStyle: '',
btnStyle: 'margin-top:16px',
requestCodeSuccess:false,
checked: false
}
},
created () {
this.loadInfo()
this.currdatetime = new Date().getTime();
Vue.ls.remove(ACCESS_TOKEN)
this.getRouterData()
this.getRegisterFlag()
this.getCheckcodeFlag()
this.handleChangeCheckCode()
},
methods: {
...mapActions([ "Login", "Logout" ]),
loadInfo() {
this.$nextTick(() => {
if(Vue.ls.get('cache_loginName') && Vue.ls.get('cache_password')) {
this.form.setFieldsValue({'loginName': Vue.ls.get('cache_loginName')})
this.form.setFieldsValue({'password': Vue.ls.get('cache_password')})
this.checked = true
}
})
if(this.$route.params.loginName) {
this.$nextTick(() => {
Vue.ls.remove('cache_loginName')
Vue.ls.remove('cache_password')
this.form.setFieldsValue({'loginName':this.$route.params.loginName})
this.form.setFieldsValue({'password': ''})
this.checked = false
})
}
},
handleLoginName (rule, value, callback) {
const regex = /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+((\.[a-zA-Z0-9_-]{2,3}){1,2})$/;
if (regex.test(value)) {
this.loginType = 0
} else {
this.loginType = 1
}
callback()
},
handleChange(e) {
this.checked = e.target.checked
},
handleChangeCheckCode(){
getAction('/user/randomImage').then(res=>{
if(res.code == 200){
this.uuid = res.data.uuid
this.randCodeImage = res.data.base64
this.requestCodeSuccess=true
}else{
this.$message.error(res.data)
this.requestCodeSuccess=false
}
}).catch(()=>{
this.requestCodeSuccess=false
})
},
handleSubmit () {
let that = this
let loginParams = {};
that.loginBtn = true;
if (that.customActiveKey === 'tab1') {
that.form.validateFields([ 'loginName', 'password', 'inputCode' ], { force: true }, (err, values) => {
if (!err) {
loginParams.loginName = values.loginName
loginParams.password = md5(values.password)
loginParams.code = values.inputCode
loginParams.uuid = that.uuid
if(that.checked) {
Vue.ls.set('cache_loginName', values.loginName)
Vue.ls.set('cache_password', values.password)
} else {
Vue.ls.remove('cache_loginName')
Vue.ls.remove('cache_password')
}
that.Login(loginParams).then((res) => {
this.departConfirm(res, loginParams.loginName)
}).catch((err) => {
that.requestFailed(err);
})
}else {
that.loginBtn = false;
}
})
}
},
loginSuccess (res) {
let that = this
this.$router.push({ path: "/dashboard/analysis" })
this.$notification.success({
message: '欢迎',
description: `${timeFix()},欢迎回来`,
})
if(res.data.pwdSimple) {
setTimeout(function () {
that.$notification.warning({
message: '友情提醒',
description: '密码过于简单,请尽快修改',
})
},3000)
}
if(res.data && res.data.user) {
if(res.data.user.loginName === 'admin'){
let desc = 'admin只是平台运维用户真正的管理员是租户(测试账号为jshadmin不能编辑任何业务数据只能配置平台菜单和创建租户'
this.$message.info(desc,30)
} else {
getPlatformConfigByKey({ "platformKey": "bill_excel_url" }).then((res) => {
if (res && res.code === 200) {
if(res.data.platformValue) {
Vue.ls.set('isShowExcel', true);
} else {
Vue.ls.set('isShowExcel', false);
}
}
})
getAction("/user/infoWithTenant",{}).then(res=>{
if(res && res.code === 200) {
let currentTime = new Date();
let expireTime = new Date(res.data.expireTime);
let type = res.data.type
let difftime = expireTime - currentTime;
let tipInfo = '您好,服务即将到期,请及时续费!'
if(type === '0' && difftime<86400000*5) {
this.$message.warning(tipInfo,8)
}
if(type === '1' && difftime<86400000*15) {
this.$message.warning(tipInfo,8)
}
}
})
}
}
this.initMPropertyShort();
},
cmsFailed(err){
this.$notification[ 'error' ]({
message: "登录失败",
description:err,
duration: 4,
});
},
requestFailed (err) {
this.$notification[ 'error' ]({
message: '登录失败',
description: ((err.response || {}).data || {}).message || err.message || err.data.message || "请求出现错误请稍后再试",
duration: 4,
});
this.form.setFieldsValue({'inputCode':''})
this.handleChangeCheckCode()
this.loginBtn = false;
},
generateCode(value){
this.verifiedCode = value.toLowerCase()
},
departConfirm(res, loginName){
if(res.code === 200){
let err = {};
if(res.data.msgTip === 'user can login'){
this.loginSuccess(res)
} else if(res.data.msgTip === 'user is not exist'){
err.message = '用户不存在';
this.requestFailed(err)
this.Logout();
} else if(res.data.msgTip === 'user password error'){
err.message = '用户密码不正确';
this.requestFailed(err)
this.Logout();
} else if(res.data.msgTip === 'user is black'){
err.message = '用户被禁用';
this.requestFailed(err)
this.Logout();
} else if(res.data.msgTip === 'tenant is black'){
if(loginName === 'jsh') {
err.message = 'jsh用户已停用请注册租户进行体验';
} else {
err.message = '用户所属的租户被禁用';
}
this.requestFailed(err)
this.Logout();
} else if(res.data.msgTip === 'tenant is expire'){
err.message = '试用期已结束,请联系客服续费';
this.requestFailed(err)
this.Logout();
} else if(res.data.msgTip === 'access service error'){
err.message = '查询服务异常';
this.requestFailed(err)
this.Logout();
}
} else{
this.requestFailed(res)
this.Logout();
}
},
getRouterData(){
this.$nextTick(() => {
if (this.$route.params.username) {
this.form.setFieldsValue({
'username': this.$route.params.username
});
}
})
},
getRegisterFlag(){
getAction('/platformConfig/getPlatform/registerFlag').then((res) => {
this.registerFlag = res + ''
})
},
getCheckcodeFlag(){
getAction('/platformConfig/getPlatform/checkcodeFlag').then((res) => {
this.checkcodeFlag = res + ''
})
},
getEncrypte(){
var encryptedString = Vue.ls.get(ENCRYPTED_STRING);
if(encryptedString == null){
getEncryptedString().then((data) => {
this.encryptedString = data
});
}else{
this.encryptedString = encryptedString;
}
},
initMPropertyShort(){
getAction('/materialProperty/getAllList').then((res) => {
if(res && res.code === 200){
if(res.data) {
let thisRows = res.data;
Vue.ls.set('materialPropertyList', thisRows, 7 * 24 * 60 * 60 * 1000);
}
}
})
},
}
}
</script>
<style lang="less" scoped>
.login-page {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: auto;
}
.login-bg {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: url('/static/bg-construction.jpg') center center / cover no-repeat;
z-index: 0;
}
.login-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 26, 58, 0.75);
z-index: 1;
}
.login-content {
position: relative;
z-index: 2;
display: flex;
align-items: center;
justify-content: center;
min-height: calc(100vh - 50px);
padding: 40px;
max-width: 1200px;
margin: 0 auto;
}
.login-brand {
flex: 0 0 55%;
padding-right: 60px;
.brand-inner {
max-width: 500px;
}
.brand-logo {
margin-bottom: 24px;
}
.brand-slogan {
color: #f0c040;
font-size: 28px;
font-weight: 300;
letter-spacing: 2px;
margin-bottom: 20px;
font-family: 'Georgia', serif;
font-style: italic;
}
.brand-desc {
color: rgba(255, 255, 255, 0.7);
font-size: 15px;
line-height: 1.8;
letter-spacing: 0.3px;
}
}
.login-form-wrapper {
flex: 0 0 400px;
max-width: 400px;
width: 100%;
}
.login-card {
background: rgba(255, 255, 255, 0.12);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.18);
border-radius: 16px;
padding: 40px 36px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
.login-title {
color: #ffffff;
font-size: 26px;
font-weight: 600;
margin-bottom: 4px;
}
.login-subtitle {
color: rgba(255, 255, 255, 0.6);
font-size: 14px;
margin-bottom: 30px;
}
}
.user-layout-login {
label {
font-size: 14px;
}
.ant-form-item {
margin-bottom: 16px;
}
.forge-password {
font-size: 14px;
font-weight: bolder;
}
button.login-button {
padding: 0 15px;
font-size: 16px;
height: 44px;
width: 100%;
border-radius: 8px;
background: linear-gradient(135deg, #0077cc, #00458a);
border: none;
font-weight: 600;
letter-spacing: 2px;
&:hover {
background: linear-gradient(135deg, #0088ee, #0055aa);
}
}
}
.login-footer {
position: relative;
z-index: 2;
text-align: center;
padding: 15px;
color: rgba(255, 255, 255, 0.45);
font-size: 12px;
}
.mobile-logo {
text-align: center;
margin-bottom: 24px;
}
// Mobile responsive
@media (max-width: 768px) {
.login-content {
flex-direction: column;
padding: 20px;
justify-content: center;
}
.login-form-wrapper {
flex: none;
max-width: 100%;
}
.login-card {
padding: 30px 24px;
}
}
</style>
<style>
.login-card .ant-input {
background: rgba(255, 255, 255, 0.9) !important;
border: 1px solid rgba(255, 255, 255, 0.3) !important;
border-radius: 6px !important;
}
.login-card .ant-input:focus,
.login-card .ant-input-focused {
background: #ffffff !important;
border-color: #0077cc !important;
}
.login-card .ant-checkbox-wrapper {
color: rgba(255, 255, 255, 0.8);
}
.login-card .forge-password {
color: #f0c040 !important;
}
.login-card .ant-form-explain {
color: #ff6b6b;
}
.valid-error .ant-select-selection__placeholder{
color: #f5222d;
}
</style>