文件
mashangyoujiu/Application/Retail/View/Rider/password_reset.html

350 行
15 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>重置密码</title>
<link rel="stylesheet" type="text/css" href="__PUBLIC__/layui-v2.4.3/layui/css/layui.css"/>
<link rel="stylesheet" type="text/css" href="__PUBLIC__/vue/element-ui.css?v=2.13.2">
<script type="text/javascript" charset="utf-8" src="__PUBLIC__/vue/js/vue2.5.16.js"></script>
<script type="text/javascript" charset="utf-8" src="__PUBLIC__/vue/element-ui.js?v=2.13.2"></script>
<script type="text/javascript" charset="utf-8" src="__PUBLIC__/jquery/js/jquery-3.2.1.min.js"></script>
<script type="text/javascript" charset="utf-8" src="__PUBLIC__/layui-v2.4.3/layui/layui.js"></script>
<style>
body { margin: 0; padding: 20px; background: #f5f7fa; }
.reset-section { margin-bottom: 20px; background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 12px rgba(0,0,0,0.1); }
.reset-section-title { font-size: 16px; font-weight: bold; color: #303133; margin-bottom: 15px; padding-left: 10px; border-left: 4px solid #409EFF; }
.rider-info { display: flex; align-items: center; margin-bottom: 20px; padding: 15px; background: #f8f9fa; border-radius: 8px; }
.rider-avatar { width: 60px; height: 60px; border-radius: 50%; object-fit: cover; margin-right: 15px; }
.rider-details { flex: 1; }
.rider-name { font-size: 16px; font-weight: bold; color: #303133; margin-bottom: 4px; }
.rider-phone { color: #606266; font-size: 14px; }
.password-tips { background: #fff7e6; border: 1px solid #ffd591; border-radius: 6px; padding: 12px; margin-bottom: 20px; }
.password-tips .el-icon-warning { color: #faad14; margin-right: 8px; }
.password-tips-title { color: #d48806; font-weight: bold; margin-bottom: 8px; }
.password-tips-content { color: #ad6800; font-size: 14px; line-height: 1.5; }
.password-strength { margin-top: 8px; }
.strength-bar { height: 6px; border-radius: 3px; background: #f0f0f0; margin-top: 8px; overflow: hidden; }
.strength-fill { height: 100%; transition: all 0.3s; }
.strength-weak { background: #ff4d4f; width: 33%; }
.strength-medium { background: #faad14; width: 66%; }
.strength-strong { background: #52c41a; width: 100%; }
.strength-text { font-size: 12px; margin-top: 4px; }
</style>
</head>
<body>
<div id="PasswordResetApp" v-cloak>
<div v-loading="loading">
<!-- 骑手信息 -->
<div class="reset-section">
<div class="reset-section-title">骑手信息</div>
<div class="rider-info">
<img v-if="riderInfo.rider_avatar" :src="riderInfo.rider_avatar" class="rider-avatar" alt="骑手头像">
<div v-else style="width: 60px; height: 60px; background: #f5f7fa; border-radius: 50%; display: flex; align-items: center; justify-content: center; color: #c0c4cc; margin-right: 15px;">
<i class="el-icon-user" style="font-size: 24px;"></i>
</div>
<div class="rider-details">
<div class="rider-name">{{ riderInfo.rider_name }}</div>
<div class="rider-phone">手机号:{{ riderInfo.rider_phone }}</div>
</div>
</div>
</div>
<!-- 密码重置表单 -->
<div class="reset-section">
<div class="reset-section-title">重置密码</div>
<!-- 密码安全提示 -->
<div class="password-tips">
<div class="password-tips-title">
<i class="el-icon-warning"></i>
密码安全提示
</div>
<div class="password-tips-content">
• 密码长度至少8位,最多20位<br>
• 必须包含字母和数字<br>
• 建议包含特殊字符以提高安全性<br>
• 不要使用过于简单的密码组合
</div>
</div>
<el-form :model="passwordForm" :rules="passwordRules" ref="passwordForm" label-width="120px">
<el-form-item label="重置方式" prop="resetType">
<el-radio-group v-model="passwordForm.resetType">
<el-radio label="manual">手动设置密码</el-radio>
<el-radio label="auto">系统生成密码</el-radio>
</el-radio-group>
</el-form-item>
<template v-if="passwordForm.resetType === 'manual'">
<el-form-item label="新密码" prop="newPassword">
<el-input
v-model="passwordForm.newPassword"
type="password"
placeholder="请输入新密码"
show-password
@input="checkPasswordStrength">
</el-input>
<div class="password-strength" v-if="passwordForm.newPassword">
<div class="strength-bar">
<div :class="['strength-fill', strengthClass]"></div>
</div>
<div :class="['strength-text', strengthTextClass]">
密码强度:{{ strengthText }}
</div>
</div>
</el-form-item>
<el-form-item label="确认密码" prop="confirmPassword">
<el-input
v-model="passwordForm.confirmPassword"
type="password"
placeholder="请再次输入新密码"
show-password>
</el-input>
</el-form-item>
</template>
<template v-if="passwordForm.resetType === 'auto'">
<el-form-item label="生成密码">
<el-input
v-model="generatedPassword"
readonly
placeholder="点击生成密码按钮">
<el-button slot="append" @click="generatePassword" :loading="generating">
{{ generatedPassword ? '重新生成' : '生成密码' }}
</el-button>
</el-input>
</el-form-item>
</template>
</el-form>
</div>
<!-- 操作按钮 -->
<div style="text-align: center; margin-top: 30px;">
<el-button @click="resetForm">重 置</el-button>
<el-button type="primary" @click="submitReset" :loading="submitting">确认重置</el-button>
</div>
</div>
</div>
<script>
new Vue({
el: '#PasswordResetApp',
data() {
// 自定义密码验证器
const validatePassword = (rule, value, callback) => {
if (!value) {
callback(new Error('请输入新密码'));
} else if (value.length < 8 || value.length > 20) {
callback(new Error('密码长度必须在8-20位之间'));
} else if (!/(?=.*[a-zA-Z])(?=.*\d)/.test(value)) {
callback(new Error('密码必须包含字母和数字'));
} else {
callback();
}
};
const validateConfirmPassword = (rule, value, callback) => {
if (!value) {
callback(new Error('请确认密码'));
} else if (value !== this.passwordForm.newPassword) {
callback(new Error('两次输入的密码不一致'));
} else {
callback();
}
};
return {
loading: false,
submitting: false,
generating: false,
riderInfo: {},
generatedPassword: '',
passwordStrength: 0,
passwordForm: {
id: null,
resetType: 'manual',
newPassword: '',
confirmPassword: ''
},
passwordRules: {
resetType: [
{ required: true, message: '请选择重置方式', trigger: 'change' }
],
newPassword: [
{ validator: validatePassword, trigger: 'blur' }
],
confirmPassword: [
{ validator: validateConfirmPassword, trigger: 'blur' }
]
}
}
},
computed: {
strengthClass() {
if (this.passwordStrength <= 1) return 'strength-weak';
if (this.passwordStrength <= 2) return 'strength-medium';
return 'strength-strong';
},
strengthText() {
if (this.passwordStrength <= 1) return '弱';
if (this.passwordStrength <= 2) return '中';
return '强';
},
strengthTextClass() {
if (this.passwordStrength <= 1) return 'color: #ff4d4f';
if (this.passwordStrength <= 2) return 'color: #faad14';
return 'color: #52c41a';
}
},
mounted() {
// 获取URL参数中的骑手ID
const urlParams = new URLSearchParams(window.location.search);
const riderId = urlParams.get('id');
if (riderId) {
this.passwordForm.id = riderId;
this.loadRiderInfo(riderId);
}
},
methods: {
async loadRiderInfo(id) {
this.loading = true;
try {
const response = await $.ajax({
url: '/Retail/Rider/getRiderInfo',
type: 'POST',
data: { id: id },
dataType: 'json'
});
if (response.code === 1) {
this.riderInfo = response.data;
} else {
this.$message.error(response.message || '获取骑手信息失败');
}
} catch (error) {
this.$message.error('网络错误,请重试');
console.error('获取骑手信息异常:', error);
} finally {
this.loading = false;
}
},
checkPasswordStrength(password) {
let strength = 0;
// 长度检查
if (password.length >= 8) strength++;
// 复杂度检查
if (/[a-z]/.test(password)) strength++;
if (/[A-Z]/.test(password)) strength++;
if (/\d/.test(password)) strength++;
if (/[^a-zA-Z\d]/.test(password)) strength++;
// 综合评分
if (strength >= 4) this.passwordStrength = 3;
else if (strength >= 3) this.passwordStrength = 2;
else this.passwordStrength = 1;
},
generatePassword() {
this.generating = true;
// 模拟生成过程
setTimeout(() => {
const chars = 'ABCDEFGHJKMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz23456789!@#$%&*';
const length = 12;
let password = '';
// 确保包含各种字符类型
password += 'ABCDEFGHJKMNPQRSTUVWXYZ'[Math.floor(Math.random() * 23)]; // 大写字母
password += 'abcdefghjkmnpqrstuvwxyz'[Math.floor(Math.random() * 23)]; // 小写字母
password += '23456789'[Math.floor(Math.random() * 8)]; // 数字
password += '!@#$%&*'[Math.floor(Math.random() * 7)]; // 特殊字符
// 填充剩余位数
for (let i = 4; i < length; i++) {
password += chars[Math.floor(Math.random() * chars.length)];
}
// 打乱顺序
this.generatedPassword = password.split('').sort(() => Math.random() - 0.5).join('');
this.generating = false;
}, 800);
},
async submitReset() {
this.$refs.passwordForm.validate(async (valid) => {
if (!valid) return;
// 检查密码设置
let finalPassword = '';
if (this.passwordForm.resetType === 'manual') {
finalPassword = this.passwordForm.newPassword;
} else {
if (!this.generatedPassword) {
this.$message.error('请先生成密码');
return;
}
finalPassword = this.generatedPassword;
}
this.submitting = true;
try {
const response = await $.ajax({
url: '/Retail/Rider/resetPassword',
type: 'POST',
data: {
id: this.passwordForm.id,
new_password: finalPassword
},
dataType: 'json'
});
if (response.code === 1) {
this.$message.success('密码重置成功');
// 显示新密码(如果是生成的)
if (this.passwordForm.resetType === 'auto') {
this.$alert(`新密码:${finalPassword}`, '密码重置成功', {
confirmButtonText: '确定',
type: 'success'
});
}
// 通知父窗口刷新
if (parent && parent.layer) {
parent.layer.closeAll();
if (parent.vm && parent.vm.loadRiders) {
parent.vm.loadRiders();
}
}
} else {
this.$message.error(response.message || '密码重置失败');
}
} catch (error) {
this.$message.error('网络错误,请重试');
console.error('密码重置异常:', error);
} finally {
this.submitting = false;
}
});
},
resetForm() {
this.$refs.passwordForm.resetFields();
this.generatedPassword = '';
this.passwordStrength = 0;
this.passwordForm = {
id: this.passwordForm.id,
resetType: 'manual',
newPassword: '',
confirmPassword: ''
};
}
}
});
</script>
</body>
</html>