5 changed files with 515 additions and 68 deletions
-
84src/components/PasswordSuccess.vue
-
303src/components/changePassword.vue
-
2src/router/index.js
-
35src/views/home.vue
-
159src/views/workspace/index.vue
@ -0,0 +1,84 @@ |
|||
<template> |
|||
<div class="success-popup"> |
|||
<div class="popup-content"> |
|||
<div class="icon"> |
|||
<el-icon :size="48" color="#67c23a"> |
|||
<SuccessFilled /> |
|||
</el-icon> |
|||
</div> |
|||
<div class="title">修改密码成功!</div> |
|||
<div class="desc">系统将在 {{ countdown }} 秒后自动跳转至登录页</div> |
|||
<el-button type="primary" @click="immediateJump">立即跳转</el-button> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import {ref, onMounted} from 'vue'; |
|||
import {ElIcon, ElButton} from 'element-plus'; |
|||
import {SuccessFilled} from '@element-plus/icons-vue'; |
|||
import {useRouter} from 'vue-router'; |
|||
|
|||
|
|||
const countdown = ref(3); |
|||
const router = useRouter(); |
|||
|
|||
// 倒计时逻辑,自动跳转 |
|||
const startCountdown = () => { |
|||
const timer = setInterval(() => { |
|||
countdown.value--; |
|||
if (countdown.value === 0) { |
|||
clearInterval(timer); |
|||
// 这里可根据实际需求选择刷新跳转还是路由跳转 |
|||
// 若需要刷新页面跳转,使用 window.location.href |
|||
// window.location.href = '/login'; |
|||
router.replace ('/login'); |
|||
// 若用路由跳转(路由配置好登录页路径前提下):router.push('/login'); |
|||
} |
|||
}, 1000); |
|||
}; |
|||
|
|||
const immediateJump = () => { |
|||
// window.location.href = '/login'; |
|||
// 路由跳转方式: |
|||
router.replace ('/login'); |
|||
}; |
|||
|
|||
onMounted(() => { |
|||
startCountdown(); |
|||
}); |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.success-popup { |
|||
position: fixed; |
|||
top: 0; |
|||
left: 0; |
|||
|
|||
background-color: rgba(255, 255, 255, 0.8); |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
z-index: 9999; |
|||
width: 100%; |
|||
height: 100%; |
|||
} |
|||
.popup-content { |
|||
background: #fff; |
|||
padding: 30px; |
|||
border-radius: 8px; |
|||
text-align: center; |
|||
} |
|||
.icon { |
|||
margin-bottom: 16px; |
|||
} |
|||
.title { |
|||
font-size: 18px; |
|||
font-weight: bold; |
|||
margin-bottom: 8px; |
|||
} |
|||
.desc { |
|||
margin-bottom: 24px; |
|||
color: #999; |
|||
} |
|||
</style> |
@ -0,0 +1,303 @@ |
|||
<template> |
|||
<div class="form-container"> |
|||
<el-form |
|||
ref="passwdFormRef" |
|||
:model="passwd" |
|||
:rules="rules" |
|||
label-width="100px" |
|||
class="password-form" |
|||
> |
|||
<h3 class="form-title">修改密码</h3> |
|||
|
|||
<!-- 原密码 --> |
|||
<el-form-item prop="oldVal" label="原密码"> |
|||
<el-input |
|||
v-model.trim="passwd.oldVal" |
|||
type="password" |
|||
placeholder="请输入原密码" |
|||
show-password |
|||
/> |
|||
</el-form-item> |
|||
|
|||
<!-- 新密码 --> |
|||
<el-form-item prop="newVal" label="新密码"> |
|||
<el-input |
|||
v-model.trim="passwd.newVal" |
|||
type="password" |
|||
placeholder="请输入新密码" |
|||
show-password |
|||
/> |
|||
</el-form-item> |
|||
|
|||
<!-- 重复密码 --> |
|||
<el-form-item prop="repeatNewVal" label="重复密码"> |
|||
<el-input |
|||
v-model.trim="passwd.repeatNewVal" |
|||
type="password" |
|||
placeholder="请再次输入新密码" |
|||
show-password |
|||
/> |
|||
</el-form-item> |
|||
|
|||
<!-- 密码规则提示 --> |
|||
<div class="rule-tips"> |
|||
<div :class="isLengthValid ? 'tip pass' : 'tip neutral'"> |
|||
<el-icon> |
|||
<component :is="isLengthValid ? SuccessFilled : SuccessFilled"/> |
|||
</el-icon> |
|||
密码由8-16位数字、字母或符号组成 |
|||
</div> |
|||
<div :class="isComplexValid ? 'tip pass' : 'tip neutral'"> |
|||
<el-icon> |
|||
<component :is="isComplexValid ? SuccessFilled : SuccessFilled"/> |
|||
</el-icon> |
|||
至少含2种以上字符 |
|||
</div> |
|||
<div v-if="errorMsg" class="tip fail"> |
|||
<el-icon> |
|||
<CircleCloseFilled/> |
|||
</el-icon> |
|||
{{ errorMsg }} |
|||
</div> |
|||
</div> |
|||
|
|||
<!-- 按钮 --> |
|||
<div class="button-group"> |
|||
<el-button |
|||
type="primary" |
|||
@click="onSubmit" |
|||
style="width: 300px" |
|||
:loading="loading" |
|||
:disabled="!isLengthValid || !isComplexValid" |
|||
> |
|||
{{ loading ? '修改中...' : '确定' }} |
|||
</el-button> |
|||
</div> |
|||
|
|||
</el-form> |
|||
</div> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import {ref, reactive, computed, watch} from 'vue' |
|||
import {CircleCloseFilled, SuccessFilled} from '@element-plus/icons-vue' |
|||
import {ElMessage} from "element-plus"; |
|||
import API from '@/util/http' |
|||
import PasswordSuccess from './PasswordSuccess.vue'; |
|||
import router from "@/router/index.js"; |
|||
|
|||
// 父组件调用 |
|||
const emit = defineEmits(['confirm']) |
|||
|
|||
const passwdFormRef = ref(null) |
|||
const passwd = reactive({ |
|||
oldVal: '', |
|||
newVal: '', |
|||
repeatNewVal: '' |
|||
}) |
|||
const errorMsg = ref('') |
|||
|
|||
// 实时密码规则校验 |
|||
const isLengthValid = computed(() => passwd.newVal.length >= 8 && passwd.newVal.length <= 16) |
|||
|
|||
const isComplexValid = computed(() => { |
|||
const rules = [/\d/, /[a-z]/, /[A-Z]/, /[^a-zA-Z0-9]/] |
|||
return rules.filter((r) => r.test(passwd.newVal)).length >= 2 |
|||
}) |
|||
|
|||
watch(() => passwd.newVal, (val) => { |
|||
if (val && val === passwd.oldVal) { |
|||
errorMsg.value = '新密码不能与旧密码一致' |
|||
} else { |
|||
errorMsg.value = '' |
|||
} |
|||
}) |
|||
// 提交加载状态 |
|||
const loading = ref(false) |
|||
// 表单校验规则 |
|||
const rules = reactive({ |
|||
oldVal: [{required: true, message: '请输入原密码', trigger: 'blur'}], |
|||
newVal: [ |
|||
{required: true, message: '新密码不能为空', trigger: 'blur'}, |
|||
{ |
|||
validator: (rule, value, callback) => { |
|||
if (value === passwd.oldVal) { |
|||
callback(new Error('新密码不能与旧密码一致')) |
|||
} else if (value.length < 8 || value.length > 16) { |
|||
callback(new Error('长度应在 8 到 16 个字符')) |
|||
} else { |
|||
const types = [/\d/, /[a-z]/, /[A-Z]/, /[^a-zA-Z0-9]/] |
|||
const matchCount = types.filter((r) => r.test(value)).length |
|||
if (matchCount < 2) { |
|||
callback(new Error('密码至少包含两种类型(数字、字母或符号)')) |
|||
} else { |
|||
callback() |
|||
} |
|||
} |
|||
}, |
|||
trigger: 'blur' |
|||
} |
|||
], |
|||
repeatNewVal: [ |
|||
{required: true, message: '请再次输入新密码', trigger: 'blur'}, |
|||
{ |
|||
validator: (rule, value, callback) => { |
|||
if (value !== passwd.newVal) { |
|||
callback(new Error('两次输入密码不一致')) |
|||
} else { |
|||
callback() |
|||
} |
|||
}, |
|||
trigger: 'blur' |
|||
} |
|||
] |
|||
}) |
|||
|
|||
// // 修改密码接口调用方法 |
|||
// const changePassword = async function () { |
|||
// try { |
|||
// const params = { |
|||
// oldVal: passwd.oldVal, |
|||
// newVal: passwd.newVal |
|||
// } |
|||
// |
|||
// const result = await API({url: '/user/changePassword', data: params}) |
|||
// console.log('@@@@@@@@@@@修改密码结果:', result) |
|||
// if(result.status === 200){ |
|||
// // 显示成功弹窗 |
|||
// |
|||
// ElMessage.success('修改密码成功') |
|||
// // 重置表单字段 |
|||
// resetFields() |
|||
// router.replace ('/PasswordSuccess'); |
|||
// }else if(result === 400){ |
|||
// //todo 待完善 |
|||
// } |
|||
// |
|||
// } catch (error) { |
|||
// console.log('re:', result) |
|||
// console.error('修改密码失败', error) |
|||
// ElMessage.error('操作失败') |
|||
// // 抛出错误让外层捕获,保持finally正常执行 |
|||
// throw error |
|||
// } |
|||
// } |
|||
|
|||
// 修改密码接口调用方法(模拟始终成功) |
|||
const changePassword = async function () { |
|||
try { |
|||
// 模拟API调用 |
|||
const result = { |
|||
status: 200, |
|||
data: {success: true, message: '密码修改成功'} |
|||
}; |
|||
|
|||
console.log('修改密码结果:', result); |
|||
|
|||
// 统一处理成功逻辑 |
|||
if (result.status === 200 ) { |
|||
// 显示成功提示 |
|||
ElMessage.success(result.data.message || '修改密码成功'); |
|||
|
|||
// 重置表单字段 |
|||
resetFields(); |
|||
|
|||
router.replace ('/PasswordSuccess'); |
|||
|
|||
|
|||
return result; |
|||
} else { |
|||
// 处理API返回但状态非成功的情况 |
|||
const errorMsg = result.data.message || '密码修改失败'; |
|||
ElMessage.error(errorMsg); |
|||
throw new Error(errorMsg); |
|||
} |
|||
} catch (error) { |
|||
// 处理网络错误或API异常 |
|||
console.error('修改密码失败:', error); |
|||
ElMessage.error(error.message || '操作失败,请重试'); |
|||
throw error; |
|||
} |
|||
}; |
|||
|
|||
// 表单提交与重置 |
|||
const resetFields = () => { |
|||
passwdFormRef.value.resetFields() |
|||
errorMsg.value = '' |
|||
} |
|||
|
|||
// 修改提交方法 |
|||
const onSubmit = () => { |
|||
// 若正在加载中则直接返回,防止重复提交 |
|||
if (loading.value) return |
|||
|
|||
passwdFormRef.value.validate(async (valid) => { |
|||
if (valid) { |
|||
loading.value = true // 开始加载 |
|||
try { |
|||
await changePassword() // 调用修改密码接口 |
|||
emit('confirm') |
|||
} finally { |
|||
loading.value = false // 无论成功失败都关闭加载态 |
|||
} |
|||
} else { |
|||
console.log('表单校验失败') |
|||
} |
|||
|
|||
}) |
|||
} |
|||
|
|||
</script> |
|||
|
|||
<style scoped> |
|||
.form-container { |
|||
justify-content: center; |
|||
align-items: center; |
|||
} |
|||
|
|||
.password-form { |
|||
|
|||
border-radius: 8px; |
|||
} |
|||
|
|||
.form-title { |
|||
text-align: left; |
|||
margin-bottom: 24px; |
|||
font-size: 20px; |
|||
font-weight: bold; |
|||
} |
|||
|
|||
.button-group { |
|||
display: flex; |
|||
justify-content: center; |
|||
margin-top: 24px; |
|||
} |
|||
|
|||
.rule-tips { |
|||
margin-left: 100px; |
|||
margin-bottom: 12px; |
|||
} |
|||
|
|||
.tip { |
|||
font-size: 14px; |
|||
margin: 4px 0; |
|||
display: flex; |
|||
align-items: center; |
|||
} |
|||
|
|||
.pass { |
|||
color: #67c23a; |
|||
} |
|||
|
|||
.neutral { |
|||
color: #999; |
|||
} |
|||
|
|||
.fail { |
|||
color: #f56c6c; |
|||
} |
|||
|
|||
.rule-tips .el-icon { |
|||
margin-right: 6px; |
|||
} |
|||
</style> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue