303 lines
7.3 KiB
Vue
303 lines
7.3 KiB
Vue
<template>
|
||
<Dialog v-model="showDialog" :options="{ title: '重置密码' }">
|
||
<template #body-content>
|
||
<div class="space-y-4">
|
||
<div v-if="!isResetMode" class="space-y-4">
|
||
<FormControl
|
||
v-model="oldPassword"
|
||
type="password"
|
||
:fieldtype="'Password'"
|
||
:label="'当前密码'"
|
||
:fieldname="'old_password'"
|
||
:reqd="1"
|
||
:placeholder="'请输入当前密码'"
|
||
/>
|
||
</div>
|
||
<div class="space-y-4">
|
||
<FormControl
|
||
v-model="newPassword"
|
||
type="password"
|
||
:fieldtype="'Password'"
|
||
:label="'新密码'"
|
||
:fieldname="'new_password'"
|
||
:reqd="1"
|
||
:placeholder="'请输入新密码'"
|
||
/>
|
||
<div v-if="passwordStrengthMessage" class="text-sm" :class="passwordStrengthClass">
|
||
{{ passwordStrengthMessage }}
|
||
</div>
|
||
<FormControl
|
||
v-model="confirmPassword"
|
||
type="password"
|
||
:fieldtype="'Password'"
|
||
:label="'确认密码'"
|
||
:fieldname="'confirm_password'"
|
||
:reqd="1"
|
||
:placeholder="'请再次输入新密码'"
|
||
/>
|
||
<div v-if="passwordMismatchMessage" class="text-sm text-red-600">
|
||
{{ passwordMismatchMessage }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<ErrorMessage class="mt-2" :message="error" />
|
||
</template>
|
||
<template #actions>
|
||
<div class="flex gap-2 w-full">
|
||
<Button
|
||
variant="outline"
|
||
class="flex-1"
|
||
@click="hide"
|
||
>
|
||
取消
|
||
</Button>
|
||
<Button
|
||
variant="solid"
|
||
class="flex-1"
|
||
:loading="isLoading"
|
||
:disabled="!isFormValid"
|
||
@click="onConfirm"
|
||
>
|
||
确认
|
||
</Button>
|
||
</div>
|
||
</template>
|
||
</Dialog>
|
||
</template>
|
||
|
||
<script>
|
||
import { ErrorMessage, FormControl } from 'jingrow-ui';
|
||
import { toast } from 'vue-sonner';
|
||
|
||
export default {
|
||
name: 'ResetPasswordDialog',
|
||
props: {
|
||
isResetMode: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
modelValue: {
|
||
type: Boolean,
|
||
default: false
|
||
}
|
||
},
|
||
emits: ['update:modelValue'],
|
||
expose: ['show', 'hide'],
|
||
data() {
|
||
return {
|
||
error: null,
|
||
isLoading: false,
|
||
oldPassword: '',
|
||
newPassword: '',
|
||
confirmPassword: '',
|
||
passwordStrengthMessage: '',
|
||
passwordStrengthClass: '',
|
||
passwordMismatchMessage: '',
|
||
passwordStrengthTimeout: null
|
||
};
|
||
},
|
||
computed: {
|
||
showDialog: {
|
||
get() {
|
||
return this.modelValue;
|
||
},
|
||
set(value) {
|
||
this.$emit('update:modelValue', value);
|
||
}
|
||
},
|
||
isFormValid() {
|
||
const hasNewPassword = this.newPassword && this.newPassword.length > 0;
|
||
const hasConfirmPassword = this.confirmPassword && this.confirmPassword.length > 0;
|
||
const passwordsMatch = this.newPassword === this.confirmPassword;
|
||
const passwordsDifferent = this.oldPassword !== this.newPassword;
|
||
|
||
if (this.isResetMode) {
|
||
return hasNewPassword && hasConfirmPassword && passwordsMatch;
|
||
} else {
|
||
return this.oldPassword && hasNewPassword && hasConfirmPassword && passwordsMatch && passwordsDifferent;
|
||
}
|
||
}
|
||
},
|
||
components: { FormControl, ErrorMessage },
|
||
watch: {
|
||
newPassword() {
|
||
this.checkPasswordStrength();
|
||
},
|
||
confirmPassword() {
|
||
this.checkPasswordMismatch();
|
||
},
|
||
oldPassword() {
|
||
this.checkPasswordMismatch();
|
||
}
|
||
},
|
||
methods: {
|
||
checkPasswordStrength() {
|
||
if (this.passwordStrengthTimeout) {
|
||
clearTimeout(this.passwordStrengthTimeout);
|
||
}
|
||
|
||
this.passwordStrengthTimeout = setTimeout(() => {
|
||
if (!this.newPassword) {
|
||
this.passwordStrengthMessage = '';
|
||
return;
|
||
}
|
||
|
||
// 调用密码强度检查API
|
||
this.$resources.checkPasswordStrength.submit({
|
||
new_password: this.newPassword,
|
||
old_password: this.oldPassword
|
||
});
|
||
}, 200);
|
||
},
|
||
|
||
checkPasswordMismatch() {
|
||
if (this.oldPassword && this.newPassword && this.oldPassword === this.newPassword) {
|
||
this.passwordMismatchMessage = '新密码不能与当前密码相同';
|
||
this.passwordStrengthMessage = '';
|
||
} else if (this.confirmPassword && this.newPassword !== this.confirmPassword) {
|
||
this.passwordMismatchMessage = '两次输入的密码不一致';
|
||
this.passwordStrengthMessage = '';
|
||
} else {
|
||
this.passwordMismatchMessage = '';
|
||
}
|
||
},
|
||
|
||
async onConfirm() {
|
||
this.error = null;
|
||
|
||
if (!this.validateForm()) {
|
||
return;
|
||
}
|
||
|
||
this.isLoading = true;
|
||
try {
|
||
await this.$resources.updatePassword.submit({
|
||
old_password: this.oldPassword,
|
||
new_password: this.newPassword,
|
||
confirm_password: this.confirmPassword,
|
||
logout_all_sessions: 1
|
||
});
|
||
} catch (error) {
|
||
this.error = error.message || '密码更新失败';
|
||
} finally {
|
||
this.isLoading = false;
|
||
}
|
||
},
|
||
|
||
validateForm() {
|
||
if (!this.isResetMode && !this.oldPassword) {
|
||
this.error = '请输入当前密码';
|
||
return false;
|
||
}
|
||
|
||
if (!this.newPassword) {
|
||
this.error = '请输入新密码';
|
||
return false;
|
||
}
|
||
|
||
if (this.newPassword !== this.confirmPassword) {
|
||
this.error = '两次输入的密码不一致';
|
||
return false;
|
||
}
|
||
|
||
if (!this.isResetMode && this.oldPassword === this.newPassword) {
|
||
this.error = '新密码不能与当前密码相同';
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
},
|
||
|
||
show() {
|
||
this.showDialog = true;
|
||
this.resetForm();
|
||
},
|
||
|
||
hide() {
|
||
this.showDialog = false;
|
||
this.resetForm();
|
||
},
|
||
|
||
resetForm() {
|
||
this.oldPassword = '';
|
||
this.newPassword = '';
|
||
this.confirmPassword = '';
|
||
this.error = null;
|
||
this.passwordStrengthMessage = '';
|
||
this.passwordStrengthClass = '';
|
||
this.passwordMismatchMessage = '';
|
||
|
||
if (this.passwordStrengthTimeout) {
|
||
clearTimeout(this.passwordStrengthTimeout);
|
||
this.passwordStrengthTimeout = null;
|
||
}
|
||
}
|
||
},
|
||
resources: {
|
||
checkPasswordStrength() {
|
||
return {
|
||
url: 'jingrow.core.pagetype.user.user.test_password_strength',
|
||
params: {
|
||
old_password: this.oldPassword,
|
||
new_password: this.newPassword
|
||
},
|
||
onSuccess(data) {
|
||
if (data.message) {
|
||
const score = data.message.score;
|
||
const feedback = data.message.feedback;
|
||
|
||
if (feedback.password_policy_validation_passed) {
|
||
this.passwordStrengthMessage = '密码强度良好 👍';
|
||
this.passwordStrengthClass = 'text-green-600';
|
||
} else {
|
||
let message = [];
|
||
if (feedback.suggestions && feedback.suggestions.length) {
|
||
message = message.concat(feedback.suggestions);
|
||
} else if (feedback.warning) {
|
||
message.push(feedback.warning);
|
||
}
|
||
message.push('提示:密码应包含符号、数字和大写字母');
|
||
this.passwordStrengthMessage = message.join(' ');
|
||
this.passwordStrengthClass = 'text-red-600';
|
||
}
|
||
}
|
||
},
|
||
onError() {
|
||
this.passwordStrengthMessage = '';
|
||
}
|
||
};
|
||
},
|
||
|
||
updatePassword() {
|
||
return {
|
||
url: 'jingrow.core.pagetype.user.user.update_password',
|
||
onSuccess(data) {
|
||
// 显示成功提示,5秒后自动消失
|
||
toast.success('密码更新成功', {
|
||
duration: 3000,
|
||
description: '您的密码已更新'
|
||
});
|
||
|
||
// 立即关闭对话框
|
||
this.hide();
|
||
|
||
// 如果有重定向消息,在关闭对话框后重定向
|
||
if (data.message) {
|
||
setTimeout(() => {
|
||
window.location.href = data.message;
|
||
}, 1000);
|
||
}
|
||
},
|
||
onError(error) {
|
||
if (error.status === 401) {
|
||
this.error = '当前密码不正确';
|
||
} else {
|
||
this.error = error.message || '密码更新失败';
|
||
}
|
||
}
|
||
};
|
||
}
|
||
}
|
||
};
|
||
</script>
|