231 lines
5.8 KiB
Vue
231 lines
5.8 KiB
Vue
|
<template>
|
|||
|
<div class="change-password-container">
|
|||
|
<div class="header">修改密码</div>
|
|||
|
<el-form
|
|||
|
:model="form"
|
|||
|
:rules="rules"
|
|||
|
label-width="120px"
|
|||
|
ref="passwordForm"
|
|||
|
label-position="top"
|
|||
|
status-icon
|
|||
|
>
|
|||
|
<!-- 新密码输入 -->
|
|||
|
<el-form-item label="新密码" prop="newPassword">
|
|||
|
<el-input
|
|||
|
v-model="form.newPassword"
|
|||
|
type="password"
|
|||
|
show-password
|
|||
|
clearable
|
|||
|
placeholder="请输入8-20位包含字母和数字的组合"
|
|||
|
>
|
|||
|
</el-input>
|
|||
|
<!-- 强度指示条 -->
|
|||
|
<div class="strength-indicator" :data-strength="strengthLevel">
|
|||
|
<div class="indicator-bar" :style="barStyle"></div>
|
|||
|
</div>
|
|||
|
</el-form-item>
|
|||
|
<!-- 警告提示 -->
|
|||
|
<el-alert
|
|||
|
title="注意:您是首次登录或被重置密码,为了账号安全,请务必修改密码"
|
|||
|
type="warning"
|
|||
|
:closable="false"
|
|||
|
show-icon
|
|||
|
class="custom-alert"
|
|||
|
/>
|
|||
|
<!-- 确认密码输入 -->
|
|||
|
<el-form-item label="确认新密码" prop="confirmPassword">
|
|||
|
<el-input
|
|||
|
v-model="form.confirmPassword"
|
|||
|
type="password"
|
|||
|
show-password
|
|||
|
clearable
|
|||
|
placeholder="请再次输入新密码"
|
|||
|
></el-input>
|
|||
|
</el-form-item>
|
|||
|
|
|||
|
|
|||
|
|
|||
|
<!-- 提交按钮 -->
|
|||
|
<el-form-item class="submit-btn">
|
|||
|
<el-button
|
|||
|
type="primary"
|
|||
|
@click="submitForm"
|
|||
|
style="width: 100%;"
|
|||
|
:loading="loading"
|
|||
|
>
|
|||
|
{{ loading ? '提交中...' : '确认' }}
|
|||
|
</el-button>
|
|||
|
</el-form-item>
|
|||
|
</el-form>
|
|||
|
</div>
|
|||
|
</template>
|
|||
|
|
|||
|
<script setup>
|
|||
|
import { ref } from 'vue';
|
|||
|
import { ElMessage } from 'element-plus';
|
|||
|
import { updateUserPassword } from "@/api/system/user";
|
|||
|
const router = useRouter();
|
|||
|
// 表单数据
|
|||
|
const form = ref({
|
|||
|
newPassword: '',
|
|||
|
confirmPassword: ''
|
|||
|
});
|
|||
|
|
|||
|
// 表单引用
|
|||
|
const passwordForm = ref(null);
|
|||
|
|
|||
|
// 加载状态
|
|||
|
const loading = ref(false);
|
|||
|
|
|||
|
// 密码复杂度验证函数
|
|||
|
const validatePassword = (rule, value, callback) => {
|
|||
|
if (!value) {
|
|||
|
return callback(new Error('密码不能为空'));
|
|||
|
}
|
|||
|
if (value.length < 6 || value.length > 20) {
|
|||
|
return callback(new Error('密码长度需在8-20位之间'));
|
|||
|
}
|
|||
|
if (!/[A-Za-z]/.test(value) || !/[0-9]/.test(value)) {
|
|||
|
return callback(new Error('需包含字母和数字组合'));
|
|||
|
}
|
|||
|
callback();
|
|||
|
};
|
|||
|
|
|||
|
// 确认密码验证
|
|||
|
const validateConfirm = (rule, value, callback) => {
|
|||
|
if (value !== form.value.newPassword) {
|
|||
|
callback(new Error('两次输入密码不一致'));
|
|||
|
} else {
|
|||
|
callback();
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
// 表单验证规则
|
|||
|
const rules = {
|
|||
|
newPassword: [
|
|||
|
{ required: true, message: '请输入新密码', trigger: 'change' },
|
|||
|
{ validator: validatePassword, trigger: 'change' }
|
|||
|
],
|
|||
|
confirmPassword: [
|
|||
|
{ required: true, message: '请输入确认密码', trigger: 'change' },
|
|||
|
{ validator: validateConfirm, trigger: 'change' }
|
|||
|
]
|
|||
|
};
|
|||
|
|
|||
|
// 提交处理
|
|||
|
const submitForm = async () => {
|
|||
|
try {
|
|||
|
loading.value = true;
|
|||
|
await passwordForm.value.validate();
|
|||
|
let objForm = {
|
|||
|
password:form.value.newPassword
|
|||
|
}
|
|||
|
// 这里添加实际的API调用
|
|||
|
updateUserPassword(objForm).then(response => {
|
|||
|
|
|||
|
ElMessage.success('密码修改成功,3秒后自动跳转');
|
|||
|
|
|||
|
// 模拟跳转(根据实际需求修改)
|
|||
|
setTimeout(() => {
|
|||
|
router.replace({ path: "/index", query: '' });
|
|||
|
}, 3000);
|
|||
|
});
|
|||
|
|
|||
|
|
|||
|
} catch (error) {
|
|||
|
ElMessage.error(error.message || '提交失败,请检查表单');
|
|||
|
} finally {
|
|||
|
loading.value = false;
|
|||
|
}
|
|||
|
};
|
|||
|
// 密码强度计算模型 :ml-citation{ref="2,4" data="citationList"}
|
|||
|
const strengthLevel = computed(() => {
|
|||
|
const value = form.value.newPassword;
|
|||
|
if (!value) return 0;
|
|||
|
|
|||
|
let score = 0;
|
|||
|
// 长度检测
|
|||
|
if (value.length >= 6) score++;
|
|||
|
if (value.length >= 12) score++;
|
|||
|
// 字符类型检测
|
|||
|
if (/[A-Za-z]/.test(value)) score++;
|
|||
|
if (/\d/.test(value)) score++;
|
|||
|
if (/[^\w]/.test(value)) score++;
|
|||
|
|
|||
|
return Math.min(Math.floor(score/2), 3);
|
|||
|
});
|
|||
|
|
|||
|
// 进度条动态样式
|
|||
|
const barStyle = computed(() => ({
|
|||
|
width: `${(strengthLevel.value / 3) * 100}%`,
|
|||
|
backgroundColor: ['#ff4d4f', '#faad14', '#52c41a'][strengthLevel.value - 1]
|
|||
|
}));
|
|||
|
</script>
|
|||
|
|
|||
|
<style scoped lang="scss">
|
|||
|
.change-password-container {
|
|||
|
width: 100%;
|
|||
|
max-width: 500px;
|
|||
|
margin: 300px auto;
|
|||
|
padding: 30px;
|
|||
|
background: rgba(255, 255, 255, 0.9);
|
|||
|
border-radius: 12px;
|
|||
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
|
|||
|
}
|
|||
|
|
|||
|
.header {
|
|||
|
font-size: 26px;
|
|||
|
font-weight: 600;
|
|||
|
color: #2c3e50;
|
|||
|
text-align: center;
|
|||
|
margin-bottom: 30px;
|
|||
|
letter-spacing: 2px;
|
|||
|
}
|
|||
|
/* 强度指示条容器 */
|
|||
|
.strength-indicator {
|
|||
|
width: 100%;
|
|||
|
height: 4px;
|
|||
|
background: #f0f0f0;
|
|||
|
border-radius: 2px;
|
|||
|
overflow: hidden;
|
|||
|
margin-top: 8px;
|
|||
|
|
|||
|
.indicator-bar {
|
|||
|
height: 100%;
|
|||
|
transition: all 0.3s ease;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
.custom-alert {
|
|||
|
margin: 20px 0;
|
|||
|
}
|
|||
|
|
|||
|
.submit-btn {
|
|||
|
margin-top: 30px;
|
|||
|
display: flex;
|
|||
|
justify-content: flex-end;
|
|||
|
}
|
|||
|
|
|||
|
/* 响应式设计 */
|
|||
|
@media (max-width: 768px) {
|
|||
|
.change-password-container {
|
|||
|
margin: 20px;
|
|||
|
padding: 20px;
|
|||
|
}
|
|||
|
|
|||
|
.header {
|
|||
|
font-size: 22px;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* 输入框动画效果 */
|
|||
|
:deep(.el-input__inner) {
|
|||
|
transition: all 0.3s ease;
|
|||
|
}
|
|||
|
|
|||
|
:deep(.el-input__inner:focus) {
|
|||
|
border-color: #409eff;
|
|||
|
box-shadow: 0 0 8px rgba(64, 158, 255, 0.3);
|
|||
|
}
|
|||
|
</style>
|
|||
|
|