mirror of
https://github.com/QingdaoU/OnlineJudgeFE.git
synced 2024-12-29 16:01:51 +00:00
support update password and send password reset email
This commit is contained in:
parent
69c3178a58
commit
f6b833594e
@ -18,7 +18,7 @@ module.exports = {
|
||||
'generator-star-spacing': 0,
|
||||
// allow debugger during development
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
|
||||
"space-before-function-paren": ["warn", {
|
||||
"space-before-function-paren": ["error", {
|
||||
"anonymous": "never",
|
||||
"named": "never",
|
||||
"asyncArrow": "always"
|
||||
@ -26,6 +26,7 @@ module.exports = {
|
||||
"no-irregular-whitespace": ["error", {
|
||||
"skipComments": true,
|
||||
"skipTemplates": true
|
||||
}]
|
||||
}],
|
||||
"no-unused-vars": ["warn"]
|
||||
}
|
||||
}
|
||||
|
@ -35,9 +35,6 @@ export default {
|
||||
getCaptcha() {
|
||||
return ajax('captcha', 'get')
|
||||
},
|
||||
getTwoFactorQrcode() {
|
||||
return ajax('two_factor_auth', 'get')
|
||||
},
|
||||
// 获取自身信息
|
||||
getUserInfo(username = undefined) {
|
||||
return ajax('profile', 'get', {
|
||||
@ -46,13 +43,25 @@ export default {
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 保存用户资料设置
|
||||
updateProfile(profile) {
|
||||
return ajax('profile', 'put', {
|
||||
data: profile
|
||||
})
|
||||
},
|
||||
getTwoFactorQrcode() {
|
||||
return ajax('two_factor_auth', 'get')
|
||||
},
|
||||
apply_reset_password(data) {
|
||||
return ajax('apply_reset_password', 'post', {
|
||||
data
|
||||
})
|
||||
},
|
||||
changePassword(data) {
|
||||
return ajax('change_password', 'post', {
|
||||
data
|
||||
})
|
||||
},
|
||||
getLanguages() {
|
||||
return ajax('languages', 'get')
|
||||
},
|
||||
|
@ -56,7 +56,7 @@
|
||||
</Dropdown>
|
||||
</template>
|
||||
</Menu>
|
||||
<Register :visible.sync="registerModalVisible" :mode.sync="modalMode"></Register>
|
||||
<LoginOrRegister :visible.sync="registerModalVisible" :mode.sync="modalMode"></LoginOrRegister>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
@ -65,11 +65,11 @@
|
||||
import api from '@/api'
|
||||
import auth from '../utils/auth'
|
||||
|
||||
import Register from '@/views/user/LoginORRegister'
|
||||
import LoginOrRegister from '@/views/user/LoginOrRegister'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Register
|
||||
LoginOrRegister
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
23
oj/src/components/mixins/form.js
Normal file
23
oj/src/components/mixins/form.js
Normal file
@ -0,0 +1,23 @@
|
||||
import api from '@/api'
|
||||
|
||||
export default {
|
||||
methods: {
|
||||
validateForm(formName) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.$refs[formName].validate(valid => {
|
||||
if (!valid) {
|
||||
this.$error('please validate the error fields')
|
||||
reject(valid)
|
||||
} else {
|
||||
resolve(valid)
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
getCaptchaSrc() {
|
||||
api.getCaptcha().then(res => {
|
||||
this.captchaSrc = res.data.data
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
import Emitter from './emitter'
|
||||
import ProblemMixin from './problem'
|
||||
import SettingMixin from './setting'
|
||||
import FormMixin from './form'
|
||||
|
||||
export {Emitter, ProblemMixin, SettingMixin}
|
||||
export {Emitter, ProblemMixin, SettingMixin, FormMixin}
|
||||
|
@ -50,6 +50,9 @@ Vue.component(Panel.name, Panel)
|
||||
// Vue.use(VueI18n)
|
||||
|
||||
// 注册全局消息提示
|
||||
Vue.prototype.$Message.config({
|
||||
duration: 1.8
|
||||
})
|
||||
Vue.prototype.$error = Vue.prototype.$Message.error
|
||||
Vue.prototype.$info = Vue.prototype.$Message.info
|
||||
Vue.prototype.$success = Vue.prototype.$Message.success
|
||||
|
@ -1,8 +1,10 @@
|
||||
// all routes here.
|
||||
import Test from '../views/test'
|
||||
import {
|
||||
ProblemList, ContestList, ContestDetails, ContestProblemList, ContestAnnouncement, ContestRank,
|
||||
Logout, ACMRank, Settings, ProfileSetting
|
||||
Logout, ApplyResetPassword, ResetPassword,
|
||||
ProfileSetting, SecuritySetting, Settings,
|
||||
ContestAnnouncement, ContestDetails, ContestList, ContestProblemList, ContestRank,
|
||||
ProblemList, ACMRank
|
||||
} from '../views'
|
||||
|
||||
export default [
|
||||
@ -18,6 +20,16 @@ export default [
|
||||
path: '/logout',
|
||||
component: Logout
|
||||
},
|
||||
{
|
||||
name: 'apply-reset-password',
|
||||
path: '/apply-reset-password',
|
||||
component: ApplyResetPassword
|
||||
},
|
||||
{
|
||||
name: 'reset-password',
|
||||
path: '/reset-password/:token',
|
||||
component: ResetPassword
|
||||
},
|
||||
{
|
||||
name: 'problem-list',
|
||||
path: '/problems',
|
||||
@ -93,6 +105,11 @@ export default [
|
||||
name: 'profile-setting',
|
||||
path: 'profile',
|
||||
component: ProfileSetting
|
||||
},
|
||||
{
|
||||
name: 'security-setting',
|
||||
path: 'security',
|
||||
component: SecuritySetting
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -100,8 +117,8 @@ export default [
|
||||
path: '/test',
|
||||
name: 'Test',
|
||||
component: Test
|
||||
},
|
||||
{
|
||||
path: '*', redirect: '/problems'
|
||||
}
|
||||
// {
|
||||
// path: '*', redirect: '/problems'
|
||||
// }
|
||||
]
|
||||
|
@ -8,8 +8,8 @@
|
||||
|
||||
.section-title {
|
||||
font-size: 21px;
|
||||
font-weight: 400;
|
||||
padding: 20px 20px;
|
||||
font-weight: 500;
|
||||
padding: 20px 25px;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
|
@ -28,3 +28,11 @@ table {
|
||||
color: #a94442;
|
||||
background-color: #f2dede;
|
||||
}
|
||||
|
||||
.ivu-modal-footer {
|
||||
border-top-width: 0;
|
||||
padding: 0 18px 20px 18px;
|
||||
}
|
||||
.ivu-modal-body {
|
||||
padding: 18px;
|
||||
}
|
||||
|
@ -148,7 +148,7 @@
|
||||
this.applyToTable(res.data.data.results)
|
||||
})
|
||||
},
|
||||
getContestAndProblems(contestID) {
|
||||
getContestAndProblems() {
|
||||
// 优先从localStorage中读取
|
||||
this.contest = utils.loadContest(this.contestID)
|
||||
let problems = storage.get(STORAGE_KEY.contestProblems + this.contestID)
|
||||
|
@ -1,12 +1,15 @@
|
||||
import ProblemList from './problem/ProblemList.vue'
|
||||
import ACMRank from './rank/ACMRank.vue'
|
||||
import Logout from './user/Logout.vue'
|
||||
import ApplyResetPassword from './user/ApplyResetPassword.vue'
|
||||
import ResetPassword from './user/ResetPassword.vue'
|
||||
|
||||
export {
|
||||
ProblemList, ACMRank, Logout
|
||||
Logout, ResetPassword, ApplyResetPassword,
|
||||
ProblemList, ACMRank
|
||||
}
|
||||
export {ContestRank, ContestProblemList, ContestList, ContestDetails, ContestAnnouncement} from './contest'
|
||||
export {Settings, ProfileSetting} from './setting'
|
||||
export {Settings, ProfileSetting, SecuritySetting} from './setting'
|
||||
/* 组件导出分为两类, 一类常用的直接导出,另一类诸如Login, Logout等用懒加载,懒加载不在此处导出
|
||||
* 在对应的route内加载
|
||||
* 见https://router.vuejs.org/en/advanced/lazy-loading.html
|
||||
|
@ -2,7 +2,7 @@
|
||||
<Row type="flex" :gutter="18">
|
||||
<Col :span=20>
|
||||
<Panel shadow>
|
||||
<div slot="title">Problems List</div>
|
||||
<div slot="title">Problem List</div>
|
||||
<div slot="extra">
|
||||
<ul class="filter">
|
||||
<li>
|
||||
|
@ -1,4 +1,6 @@
|
||||
<template>
|
||||
<Row type="flex" justify="space-around">
|
||||
<Col :span="22">
|
||||
<Card :padding="0" id="settings-card">
|
||||
<div class="flex-container">
|
||||
<div class="menu">
|
||||
@ -7,7 +9,7 @@
|
||||
<img class="avatar" src="../../assets/profile.jpg"/>
|
||||
</div>
|
||||
<Menu-item name="/setting/profile">Profile</Menu-item>
|
||||
<Menu-item name="/setting/1">Security</Menu-item>
|
||||
<Menu-item name="/setting/security">Security</Menu-item>
|
||||
<Menu-item name="/setting/2">Perference</Menu-item>
|
||||
</Menu>
|
||||
</div>
|
||||
@ -16,6 +18,8 @@
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
@ -65,3 +69,10 @@
|
||||
width: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
.setting-main {
|
||||
margin: 10px 40px;
|
||||
padding-bottom: 40px;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,9 +1,8 @@
|
||||
<template>
|
||||
<div>
|
||||
<panel :padding="0" :bordered="false" dis-hover>
|
||||
<div slot="title">Profile Settings</div>
|
||||
<div slot="title">Profile Setting</div>
|
||||
<div slot="extra">
|
||||
<Button type="primary" @click="updateProfile">Save All</Button>
|
||||
<Button type="primary" @click="updateProfile" :loading="btnLoading">Save All</Button>
|
||||
</div>
|
||||
<Form ref="formProfile" :model="formProfile">
|
||||
<Row type="flex" :gutter="30" justify="space-around">
|
||||
@ -21,7 +20,7 @@
|
||||
|
||||
<Col :span="10">
|
||||
<Form-item label="Major">
|
||||
<Input v-model="formProfile.major" />
|
||||
<Input v-model="formProfile.major"/>
|
||||
</Form-item>
|
||||
<Form-item label="Blog">
|
||||
<Input v-model="formProfile.blog"/>
|
||||
@ -33,16 +32,18 @@
|
||||
</Row>
|
||||
</Form>
|
||||
</panel>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import api from '@/api.js'
|
||||
import auth from '@/utils/auth'
|
||||
import {SettingMixin} from '~/mixins'
|
||||
|
||||
export default {
|
||||
mixins: [SettingMixin],
|
||||
data() {
|
||||
return {
|
||||
btnLoading: false,
|
||||
formProfile: {
|
||||
real_name: '',
|
||||
mood: '',
|
||||
@ -58,19 +59,23 @@
|
||||
},
|
||||
methods: {
|
||||
getProfile() {
|
||||
if (!auth.isAuthicated()) {
|
||||
this.$error('please login first.')
|
||||
} else {
|
||||
let profile = auth.getUser()
|
||||
let profile = this.loadProfile()
|
||||
if (profile !== null && profile !== undefined) {
|
||||
Object.keys(this.formProfile).forEach(element => {
|
||||
if (profile[element] !== undefined) {
|
||||
this.formProfile[element] = profile[element]
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
updateProfile() {
|
||||
this.btnLoading = true
|
||||
api.updateProfile(this.formProfile).then(res => {
|
||||
this.$success('Success')
|
||||
this.btnLoading = false
|
||||
auth.setUser(res.data.data)
|
||||
}, _ => {
|
||||
this.btnLoading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,159 @@
|
||||
<template>
|
||||
<Card :padding="0" :bordered="false" dis-hover>
|
||||
<div class="flex-container">
|
||||
<div class="left">
|
||||
<p class="section-title">Change Password</p>
|
||||
<Form class="setting-main" ref="formPassword" :model="formPassword" :rules="rulePassword">
|
||||
<FormItem label="Old password" prop="old_password">
|
||||
<Input v-model="formPassword.old_password" type="password"/>
|
||||
</FormItem>
|
||||
<FormItem label="New password" prop="new_password">
|
||||
<Input v-model="formPassword.new_password" type="password"/>
|
||||
</FormItem>
|
||||
<FormItem label="Confirm new password" prop="again_password">
|
||||
<Input v-model="formPassword.again_password" type="password"/>
|
||||
</FormItem>
|
||||
<FormItem v-if="visible.passwordAlert">
|
||||
<Alert type="success">Password successfully updated, you have to login again after 3 seconds..</Alert>
|
||||
</FormItem>
|
||||
<Button type="primary" @click="changePassword">Update password</Button>
|
||||
</Form>
|
||||
</div>
|
||||
|
||||
<div class="middle separator"></div>
|
||||
|
||||
<div class="right">
|
||||
<p class="section-title">Change Email</p>
|
||||
<Form class="setting-main" ref="formEmail" :model="formEmail">
|
||||
<FormItem label="Current password">
|
||||
<Input v-model="formEmail.password"/>
|
||||
</FormItem>
|
||||
<FormItem label="Old Email">
|
||||
<Input v-model="formEmail.old_email" disabled/>
|
||||
</FormItem>
|
||||
<FormItem label="New Email">
|
||||
<Input v-model="formEmail.new_email"/>
|
||||
</FormItem>
|
||||
<Button type="primary">Change Email</Button>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
<!--<img :src="qrcodeSrc" id="qr-img"/>-->
|
||||
</Card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import api from '@/api.js'
|
||||
import {SettingMixin} from '~/mixins'
|
||||
|
||||
export default {
|
||||
mixins: [SettingMixin],
|
||||
data() {
|
||||
const validatePass = (rule, value, callback) => {
|
||||
if (this.formPassword.old_password !== '') {
|
||||
if (this.formPassword.old_password === this.formPassword.new_password) {
|
||||
callback(new Error('The new password doesn\'t change'))
|
||||
} else {
|
||||
// 对第二个密码框再次验证
|
||||
this.$refs.formPassword.validateField('again_password')
|
||||
}
|
||||
}
|
||||
callback()
|
||||
}
|
||||
const validatePassAgain = (rule, value, callback) => {
|
||||
if (value !== this.formPassword.new_password) {
|
||||
callback(new Error('password does not match'))
|
||||
}
|
||||
callback()
|
||||
}
|
||||
return {
|
||||
qrcodeSrc: '',
|
||||
loading: {
|
||||
btnPassword: false,
|
||||
btnEmail: false
|
||||
},
|
||||
visible: {
|
||||
passwordAlert: false,
|
||||
emailAlert: false
|
||||
},
|
||||
formPassword: {
|
||||
old_password: '',
|
||||
new_password: '',
|
||||
again_password: ''
|
||||
},
|
||||
formEmail: {
|
||||
password: '',
|
||||
old_email: '',
|
||||
new_email: ''
|
||||
},
|
||||
rulePassword: {
|
||||
old_password: [
|
||||
{required: true, trigger: 'blur', min: 6, max: 20}
|
||||
],
|
||||
new_password: [
|
||||
{required: true, trigger: 'blur', min: 6, max: 20},
|
||||
{validator: validatePass, trigger: 'blur'}
|
||||
],
|
||||
again_password: [
|
||||
{required: true, validator: validatePassAgain, trigger: 'change'}
|
||||
]
|
||||
},
|
||||
ruleEmail: {}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getProfile()
|
||||
},
|
||||
methods: {
|
||||
getProfile() {
|
||||
let profile = this.loadProfile()
|
||||
if (profile !== null && profile !== undefined) {
|
||||
this.formEmail.old_email = profile.user.email
|
||||
}
|
||||
},
|
||||
changePassword() {
|
||||
this.loading.btnPassword = true
|
||||
let data = Object.assign({}, this.formPassword)
|
||||
delete data.again_password
|
||||
api.changePassword(data).then(res => {
|
||||
this.loading.btnPassword = false
|
||||
this.visible.passwordAlert = true
|
||||
setTimeout(() => {
|
||||
this.visible.passwordAlert = false
|
||||
this.$router.push({name: 'logout'})
|
||||
}, 3000)
|
||||
}, _ => {
|
||||
this.loading.btnPassword = false
|
||||
})
|
||||
},
|
||||
changeEmail() {
|
||||
this.btnEmailLoading = true
|
||||
// todo
|
||||
},
|
||||
getAuthImg() {
|
||||
api.getTwoFactorQrcode().then(res => {
|
||||
this.qrcodeSrc = res.data.data
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
.flex-container {
|
||||
justify-content: flex-start;
|
||||
.left {
|
||||
flex: 1 1;
|
||||
padding-right: 10%;
|
||||
}
|
||||
.middle {
|
||||
flex: none;
|
||||
}
|
||||
.right {
|
||||
flex: 1 1;
|
||||
padding-right: 10%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -1,4 +1,5 @@
|
||||
import Settings from './Settings.vue'
|
||||
import ProfileSetting from './children/ProfileSetting.vue'
|
||||
import SecuritySetting from './children/SecuritySetting.vue'
|
||||
|
||||
export {Settings, ProfileSetting}
|
||||
export {Settings, ProfileSetting, SecuritySetting}
|
||||
|
131
oj/src/views/user/ApplyResetPassword.vue
Normal file
131
oj/src/views/user/ApplyResetPassword.vue
Normal file
@ -0,0 +1,131 @@
|
||||
<template>
|
||||
<Panel :padding="30" class="container">
|
||||
<div slot="title" class="center">Lost Password</div>
|
||||
<template v-if="!successApply">
|
||||
<Form :rules="ruleResetPassword" :model=formResetPassword ref="formResetPassword">
|
||||
<Form-item prop="email">
|
||||
<Input v-model="formResetPassword.email" placeholder="Your Email Address" size="large">
|
||||
<Icon type="ios-email-outline" slot="prepend"></Icon>
|
||||
</Input>
|
||||
</Form-item>
|
||||
<Form-item prop="captcha" style="margin-bottom:10px">
|
||||
<div id="captcha">
|
||||
<div id="captchaCode">
|
||||
<Input v-model="formResetPassword.captcha" placeholder="Captcha" size="large">
|
||||
<Icon type="ios-lightbulb-outline" slot="prepend"></Icon>
|
||||
</Input>
|
||||
</div>
|
||||
<div id="captchaImg">
|
||||
<Tooltip content="Click to refresh" placement="top">
|
||||
<img :src="captchaSrc" @click="getCaptchaSrc"/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</Form-item>
|
||||
</Form>
|
||||
<Button type="primary"
|
||||
@click="sendEmail"
|
||||
class="btn" long
|
||||
:loading="btnLoading">Send Password Reset Email
|
||||
</Button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<Alert type="success" show-icon>
|
||||
Success
|
||||
<span slot="desc">Password reset mail has been sent to your email.</span>
|
||||
</Alert>
|
||||
</template>
|
||||
</Panel>
|
||||
</template>
|
||||
<script>
|
||||
import api from '@/api'
|
||||
import {FormMixin} from '~/mixins'
|
||||
|
||||
export default {
|
||||
mixins: [FormMixin],
|
||||
data() {
|
||||
const validateEmail = (rule, value, callback) => {
|
||||
if (value !== '') {
|
||||
api.checkUsernameOrEmail(undefined, value).then(res => {
|
||||
if (res.data.data.email === false) {
|
||||
callback(new Error('This email doesn\'t exist'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}, _ => callback())
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
return {
|
||||
captchaSrc: '',
|
||||
successApply: false,
|
||||
btnLoading: false,
|
||||
formResetPassword: {
|
||||
email: '',
|
||||
captcha: ''
|
||||
},
|
||||
ruleResetPassword: {
|
||||
email: [
|
||||
{required: true, type: 'email', trigger: 'blur'},
|
||||
{validator: validateEmail, trigger: 'blur'}
|
||||
],
|
||||
captcha: [
|
||||
{required: true, trigger: 'blur', min: 1, max: 10}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getCaptchaSrc()
|
||||
},
|
||||
methods: {
|
||||
sendEmail() {
|
||||
this.validateForm('formResetPassword').then(() => {
|
||||
this.btnLoading = true
|
||||
api.apply_reset_password(this.formResetPassword).then(res => {
|
||||
// 伪加载
|
||||
setTimeout(() => {
|
||||
this.btnLoading = false
|
||||
this.successApply = true
|
||||
}, 2000)
|
||||
}, _ => {
|
||||
this.btnLoading = false
|
||||
this.formResetPassword.captcha = ''
|
||||
this.getCaptchaSrc()
|
||||
})
|
||||
}, _ => {
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.container {
|
||||
width: 450px;
|
||||
margin: auto;
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
#captcha {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
height: 36px;
|
||||
#captchaCode {
|
||||
flex: auto;
|
||||
}
|
||||
#captchaImg {
|
||||
margin-left: 10px;
|
||||
padding: 3px;
|
||||
flex: initial;
|
||||
}
|
||||
}
|
||||
.btn {
|
||||
margin-top: 18px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<Modal :value="visible" @on-cancel="handleUpdateProp('update:visible', false)" :width="350">
|
||||
<Modal :value="visible" @on-cancel="handleUpdateProp('update:visible', false)" :width="400" className="modal">
|
||||
<div slot="header">
|
||||
<span class="title">Welcome to OJ</span>
|
||||
</div>
|
||||
@ -42,7 +42,7 @@
|
||||
<Form-item prop="captcha" style="margin-bottom:10px">
|
||||
<div id="captcha">
|
||||
<div id="captchaCode">
|
||||
<Input v-model="formRegister.captcha" placeholder="Capacha" size="large">
|
||||
<Input v-model="formRegister.captcha" placeholder="Captcha" size="large">
|
||||
<Icon type="ios-lightbulb-outline" slot="prepend"></Icon>
|
||||
</Input>
|
||||
</div>
|
||||
@ -57,12 +57,30 @@
|
||||
</template>
|
||||
<div slot="footer" class="footer">
|
||||
<template v-if="mode === 'login'">
|
||||
<Button type="primary" @click="handleLogin()" class="btn" long>Login</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
@click="handleLogin()"
|
||||
class="btn" long
|
||||
:loading="btnLoginLoading">
|
||||
Login
|
||||
</Button>
|
||||
<a @click.stop="handleUpdateProp('update:mode', 'register')">No account? Register now!</a>
|
||||
<a @click.stop="goResetPassword" style="float: right">Forget Password</a>
|
||||
</template>
|
||||
<template v-else>
|
||||
<Button type="primary" @click="handleRegister()" class="btn" long>Register Now</Button>
|
||||
<a @click.stop="handleUpdateProp('update:mode', 'login')">Already registed? Login now!</a>
|
||||
<Button
|
||||
type="primary"
|
||||
@click="handleRegister()"
|
||||
class="btn" long
|
||||
:loading="btnRegisterLoading">
|
||||
Register
|
||||
</Button>
|
||||
<Button
|
||||
type="ghost"
|
||||
@click="handleUpdateProp('update:mode', 'login')"
|
||||
class="btn" long>
|
||||
Already registed? Login now!
|
||||
</Button>
|
||||
</template>
|
||||
</div>
|
||||
</Modal>
|
||||
@ -71,8 +89,10 @@
|
||||
<script>
|
||||
import api from '@/api'
|
||||
import auth from '@/utils/auth'
|
||||
import {FormMixin} from '~/mixins'
|
||||
|
||||
export default {
|
||||
mixins: [FormMixin],
|
||||
props: {
|
||||
visible: {
|
||||
required: true,
|
||||
@ -90,7 +110,7 @@
|
||||
const validateUsername = (rule, value, callback) => {
|
||||
if (value !== '') {
|
||||
api.checkUsernameOrEmail(value, undefined).then(res => {
|
||||
if (res.data.data.username === false) {
|
||||
if (res.data.data.username === true) {
|
||||
callback(new Error('username already exists.'))
|
||||
} else {
|
||||
callback()
|
||||
@ -103,8 +123,8 @@
|
||||
const validateEmail = (rule, value, callback) => {
|
||||
if (value !== '') {
|
||||
api.checkUsernameOrEmail(undefined, value).then(res => {
|
||||
if (res.data.data.email === false) {
|
||||
callback(new Error('email already exists'))
|
||||
if (res.data.data.email === true) {
|
||||
callback(new Error('email already exist'))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
@ -129,6 +149,8 @@
|
||||
|
||||
return {
|
||||
captchaSrc: '',
|
||||
btnRegisterLoading: false,
|
||||
btnLoginLoading: false,
|
||||
formRegister: {
|
||||
username: '',
|
||||
password: '',
|
||||
@ -171,32 +193,30 @@
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
validateForm(formName) {
|
||||
let isValid = false
|
||||
this.$refs[formName].validate(valid => {
|
||||
if (!valid) {
|
||||
this.$error('please validate the error fields')
|
||||
}
|
||||
isValid = valid
|
||||
})
|
||||
return isValid
|
||||
handleUpdateProp(eventName, value) {
|
||||
this.$emit(eventName, value)
|
||||
},
|
||||
handleRegister() {
|
||||
if (this.validateForm('formRegister')) {
|
||||
let formData = Object.assign({}, this.formRegister)
|
||||
delete formData['passwordAgain']
|
||||
this.btnRegisterLoading = true
|
||||
api.register(formData).then(res => {
|
||||
this.$success('Register successed, go to login')
|
||||
this.handleUpdateProp('update:mode', 'login')
|
||||
this.btnRegisterLoading = false
|
||||
}, _ => {
|
||||
this.getCaptchaSrc()
|
||||
this.formRegister.captcha = ''
|
||||
this.btnRegisterLoading = false
|
||||
})
|
||||
}
|
||||
},
|
||||
handleLogin() {
|
||||
if (this.validateForm('formLogin')) {
|
||||
this.btnLoginLoading = true
|
||||
api.login(this.formLogin.uname, this.formLogin.passwd).then(res => {
|
||||
this.btnLoginLoading = false
|
||||
api.getUserInfo().then(res => {
|
||||
auth.setUser(res.data.data)
|
||||
this.$bus.$emit('login-success', res.data.data)
|
||||
@ -204,16 +224,14 @@
|
||||
this.handleUpdateProp('update:visible', false)
|
||||
})
|
||||
}, _ => {
|
||||
this.btnLoginLoading = false
|
||||
})
|
||||
}
|
||||
},
|
||||
handleUpdateProp(eventName, value) {
|
||||
this.$emit(eventName, value)
|
||||
},
|
||||
getCaptchaSrc() {
|
||||
api.getCaptcha().then(res => {
|
||||
this.captchaSrc = res.data.data
|
||||
})
|
||||
|
||||
goResetPassword() {
|
||||
this.handleUpdateProp('update:visible', false)
|
||||
this.$router.push({name: 'apply-reset-password'})
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@ -255,15 +273,18 @@
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 16px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.footer {
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
text-align: left;
|
||||
.btn {
|
||||
margin: 0 0 10px 0;
|
||||
margin: 0 0 15px 0;
|
||||
&:last-child {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
0
oj/src/views/user/ResetPassword.vue
Normal file
0
oj/src/views/user/ResetPassword.vue
Normal file
Loading…
Reference in New Issue
Block a user