feat:新增修改密码

This commit is contained in:
JiXinHui 2026-03-18 16:59:20 +08:00
parent 1536637159
commit 11c286c717
6 changed files with 483 additions and 23 deletions

View File

@ -205,9 +205,15 @@ const install = (Vue, vm) => {
// 密码登录-教师
let TeacherLogin = (params = {}) =>
vm.$u.post("api/Login/LoginManagementEnd", params);
// 验证码登录-教师
let TeacherLoginByCode = (params = {}) =>
vm.$u.post("api/Login/PhoneLoginManagementEnd", params);
// 验证码登录-教师
let TeacherLoginByCode = (params = {}) =>
vm.$u.post("api/Login/PhoneLoginManagementEnd", params);
// 忘记密码-获取短信验证码
let RequestForgotPasswordSMSCode = (params = {}) =>
vm.$u.post("api/Login/RequestForgotPasswordSMSCode", params);
// 忘记密码-修改密码
let ForgotPasswordChangePassword = (params = {}) =>
vm.$u.post("api/Login/ForgotPasswordChangePassword", params);
/** 用户-个人中心 */
// 获取用户信息
@ -310,9 +316,11 @@ const install = (Vue, vm) => {
GetHotQuestionsFromId,
DeleteDialogueManagement,
GetTeacherVerifyCode,
TeacherLogin,
TeacherLoginByCode,
GetUserApi,
TeacherLogin,
TeacherLoginByCode,
RequestForgotPasswordSMSCode,
ForgotPasswordChangePassword,
GetUserApi,
UpdateUserApi,
GetTeacherListApi,
GetDialogueListApi,

View File

@ -50,13 +50,20 @@
"navigationStyle": "custom"
}
},
{
"path": "pages/my/schedule",
"style": {
"navigationBarTitleText": "我的排班",
"navigationStyle": "custom"
}
},
{
"path": "pages/my/schedule",
"style": {
"navigationBarTitleText": "我的排班",
"navigationStyle": "custom"
}
},
{
"path": "pages/my/change-password",
"style": {
"navigationBarTitleText": "修改密码",
"navigationStyle": "custom"
}
},
{
"path": "pages/home/admissions/index",
"style": {

View File

@ -106,7 +106,7 @@
<text>图形验证码</text>
</view>
<view class="input-wrapper">
<input
<inputx
type="text"
class="form-input"
placeholder="请输入验证码"
@ -464,7 +464,7 @@ export default {
//
const redirect = () => {
const url = this.isTeacher
? "/pages/consultation/index"
? "/pages/transfer/index"
: "/pages/home/index/index";
uni.reLaunch({
url: url,
@ -821,4 +821,3 @@ export default {
}
</style>

View File

@ -0,0 +1,372 @@
<template>
<view class="change-password-page">
<PageHeader
title="修改密码"
:is-back="true"
:border-bottom="false"
:background="headerBackground"
/>
<view class="content-wrapper">
<view class="form-card single-row-card">
<view class="form-row">
<text class="label">手机号</text>
<input
class="input"
type="number"
v-model="form.phone"
placeholder="请输入手机号"
placeholder-style="color: #c0c0c0;"
/>
</view>
</view>
<view class="form-card single-row-card">
<view class="form-row captcha-row">
<text class="label">图形验证码</text>
<input
class="input"
v-model="form.captcha"
placeholder="请输入验证码"
placeholder-style="color: #c0c0c0;"
/>
<image
class="captcha-image"
:src="captchaUrl"
mode="aspectFill"
@click="refreshCaptcha"
/>
</view>
</view>
<view class="form-card single-row-card">
<view class="form-row">
<text class="label">验证码</text>
<input
class="input"
type="number"
v-model="form.code"
placeholder="请输入验证码"
placeholder-style="color: #c0c0c0;"
/>
<text
class="get-code"
:class="{ disabled: codeText !== '获取验证码' }"
@click="handleGetCode"
>
{{ codeText }}
</text>
</view>
</view>
<view class="form-card">
<view class="form-row">
<text class="label">新密码</text>
<input
class="input"
:password="!showPassword"
v-model="form.pwd"
placeholder="请输入新密码"
placeholder-style="color: #c0c0c0;"
/>
</view>
<view class="form-row">
<text class="label">确认新密码</text>
<input
class="input"
:password="!showPassword"
v-model="form.confirmPwd"
placeholder="请再次输入新密码"
placeholder-style="color: #c0c0c0;"
/>
</view>
</view>
<button class="submit-button" @click="handleSubmit">确定</button>
</view>
<u-toast ref="uToast" />
</view>
</template>
<script>
import PageHeader from "@/components/PageHeader.vue";
import { generateSign } from "@/utils/signUtil.js";
import md5 from "js-md5";
export default {
name: "ChangePassword",
components: {
PageHeader,
},
data() {
return {
headerBackground: {
background: "transparent",
},
form: {
phone: "",
code: "",
captcha: "",
pwd: "",
confirmPwd: "",
},
showPassword: false,
codeText: "获取验证码",
countdown: 60,
timer: null,
captchaId: "",
captchaUrl: "",
};
},
onLoad() {
const userInfo = this.vuex_user || {};
const phone = userInfo.phone || userInfo.Phone || "";
if (phone) {
this.form.phone = phone;
}
this.refreshCaptcha();
},
beforeDestroy() {
if (this.timer) {
clearInterval(this.timer);
}
},
methods: {
refreshCaptcha() {
this.$u.api.GetCaptcha().then((res) => {
this.captchaId = res.captchaId || "";
this.captchaUrl = res.imageBase64
? `data:image/png;base64,${res.imageBase64}`
: "";
});
},
handleGetCode() {
if (this.codeText !== "获取验证码") return;
if (!this.validatePhone()) return;
if (!this.validateCaptcha()) return;
this.requestSmsCode();
},
requestSmsCode() {
const sign = generateSign(this.form.phone);
const params = {
phone: this.form.phone,
sign,
captchaId: this.captchaId,
captchaCode: this.form.captcha,
ip: "",
};
this.$u.api.RequestForgotPasswordSMSCode(params).then((res) => {
if (res && res.succeed) {
this.$refs.uToast.show({
title: "发送成功",
type: "success",
});
this.startCountdown();
} else {
this.$refs.uToast.show({
title: res?.error || "发送失败",
type: "error",
});
this.refreshCaptcha();
}
});
},
startCountdown() {
this.countdown = 60;
this.codeText = `${this.countdown}秒后重试`;
if (this.timer) {
clearInterval(this.timer);
}
this.timer = setInterval(() => {
this.countdown -= 1;
this.codeText = `${this.countdown}秒后重试`;
if (this.countdown <= 0) {
clearInterval(this.timer);
this.codeText = "获取验证码";
}
}, 1000);
},
validatePhone() {
if (!this.form.phone) {
this.$refs.uToast.show({
title: "请输入手机号",
type: "warning",
});
return false;
}
if (!/^1[3-9]\d{9}$/.test(this.form.phone)) {
this.$refs.uToast.show({
title: "手机号格式不正确",
type: "warning",
});
return false;
}
return true;
},
validateCaptcha() {
if (!this.form.captcha) {
this.$refs.uToast.show({
title: "请输入图形验证码",
type: "warning",
});
return false;
}
return true;
},
validateForm() {
if (!this.validatePhone()) return false;
if (!this.validateCaptcha()) return false;
if (!this.form.code) {
this.$refs.uToast.show({
title: "请输入验证码",
type: "warning",
});
return false;
}
if (!this.form.pwd) {
this.$refs.uToast.show({
title: "请输入新密码",
type: "warning",
});
return false;
}
if (!this.form.confirmPwd) {
this.$refs.uToast.show({
title: "请再次输入新密码",
type: "warning",
});
return false;
}
if (this.form.pwd !== this.form.confirmPwd) {
this.$refs.uToast.show({
title: "两次密码输入不一致",
type: "warning",
});
return false;
}
return true;
},
handleSubmit() {
if (!this.validateForm()) return;
const params = {
phone: this.form.phone,
code: this.form.code,
pwd: md5(this.form.pwd),
};
this.$u.api.ForgotPasswordChangePassword(params).then((res) => {
if (res && res.succeed) {
this.$refs.uToast.show({
title: "修改成功",
type: "success",
});
setTimeout(() => {
uni.navigateBack();
}, 800);
} else {
this.$refs.uToast.show({
title: res?.error || "修改失败",
type: "error",
});
}
});
},
},
};
</script>
<style scoped>
.change-password-page {
min-height: 100vh;
background-image: url("@/static/notes/bg.png");
background-position: center top;
background-repeat: no-repeat;
background-size: 100% auto;
display: flex;
flex-direction: column;
}
.content-wrapper {
flex: 1;
padding: 30rpx;
padding-top: 40rpx;
box-sizing: border-box;
}
.form-card {
background-color: #ffffff;
border-radius: 20rpx;
padding: 0 30rpx;
margin-bottom: 24rpx;
}
.form-row {
display: flex;
align-items: center;
padding: 28rpx 0;
/* border-bottom: 1rpx solid #f1f1f1; */
}
.form-row:last-child {
border-bottom: none;
}
.form-row.captcha-row {
align-items: center;
}
.label {
width: 150rpx;
font-weight: 500;
font-size: 27rpx;
color: #333333;
}
.input {
flex: 1;
font-size: 28rpx;
color: #333333;
text-align: right;
}
.get-code {
font-size: 26rpx;
color: #4f6aff;
padding-left: 16rpx;
border-left: 1rpx solid #e5e5e5;
margin-left: 16rpx;
}
.get-code.disabled {
color: #b8b8b8;
}
.captcha-image {
width: 180rpx;
height: 72rpx;
border-radius: 10rpx;
background-color: #f5f5f5;
margin-left: 16rpx;
}
.submit-button {
margin-top: 30rpx;
width: 100%;
height: 88rpx;
line-height: 88rpx;
background-color: #4f6aff;
color: #ffffff;
border-radius: 16rpx;
font-size: 30rpx;
font-weight: 600;
}
.single-row-card .form-row {
border-bottom: none;
}
.captcha-row .input {
text-align: left;
}
</style>

View File

@ -123,9 +123,8 @@ export default {
url: '/pages/my/personalInfo'
});
} else if (route === 'change-password') {
uni.showToast({
title: '功能开发中',
icon: 'none'
uni.navigateTo({
url: '/pages/my/change-password'
});
} else if (route === 'logout-records') {
uni.showModal({

View File

@ -10,7 +10,18 @@
<view class="content-wrapper">
<view class="form-container">
<view class="form-card calendar-card">
<view class="card-title">{{ monthTitle }}</view>
<view class="calendar-header">
<text class="month-action" @click="handlePrevMonth"></text>
<picker
mode="date"
fields="month"
:value="monthValue"
@change="handleMonthPickerChange"
>
<view class="card-title month-title">{{ monthTitle }}</view>
</picker>
<text class="month-action" @click="handleNextMonth"></text>
</view>
<view class="week-row">
<text
v-for="(label, index) in weekLabels"
@ -82,6 +93,9 @@ export default {
monthTitle() {
return `${this.currentYear}${String(this.currentMonth).padStart(2, "0")}`;
},
monthValue() {
return `${this.currentYear}-${String(this.currentMonth).padStart(2, "0")}`;
},
selectedDateTitle() {
const dayText = String(this.selectedDay).padStart(2, "0");
return `${this.currentYear}${String(this.currentMonth).padStart(2, "0")}${dayText}`;
@ -97,16 +111,21 @@ export default {
},
},
methods: {
fetchSchedule() {
fetchSchedule(
year = this.currentYear,
month = this.currentMonth,
day = this.selectedDay
) {
const userInfo = this.vuex_user || {};
const teacherId = userInfo.id || userInfo.Id;
if (!teacherId) {
this.$u.toast("缺少教师ID");
return;
}
const scheduleDate = new Date(year, month - 1, day, 12, 0, 0);
this.$u.api
.getShiftSchedulingData({
SchedulingTime: new Date().toISOString(),
SchedulingTime: scheduleDate.toISOString(),
TeacherManagementId: teacherId,
})
.then((res) => {
@ -177,6 +196,40 @@ export default {
this.scheduleList
);
},
handlePrevMonth() {
this.changeMonth(-1);
},
handleNextMonth() {
this.changeMonth(1);
},
handleMonthPickerChange(event) {
const value = event?.detail?.value || "";
const [yearText, monthText] = value.split("-");
const year = Number(yearText);
const month = Number(monthText);
if (!year || !month) return;
this.updateMonth(year, month);
},
changeMonth(offset) {
const date = new Date(this.currentYear, this.currentMonth - 1 + offset, 1);
this.updateMonth(date.getFullYear(), date.getMonth() + 1);
},
updateMonth(year, month) {
this.currentYear = year;
this.currentMonth = month;
const today = new Date();
if (today.getFullYear() === year && today.getMonth() + 1 === month) {
this.selectedDay = today.getDate();
} else {
this.selectedDay = 1;
}
this.calendarDays = this.buildCalendarDays(
this.currentYear,
this.currentMonth,
this.scheduleList
);
this.fetchSchedule(year, month, this.selectedDay);
},
},
};
</script>
@ -213,11 +266,33 @@ export default {
margin-bottom: 20rpx;
}
.calendar-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 20rpx;
}
.card-title {
font-size: 30rpx;
font-weight: 600;
color: #333333;
margin-bottom: 20rpx;
}
.month-title {
padding: 6rpx 18rpx;
border-radius: 999rpx;
background-color: #f5f6ff;
text-align: center;
min-width: 200rpx;
}
.month-action {
font-size: 40rpx;
color: #4f6aff;
width: 40rpx;
text-align: center;
line-height: 40rpx;
}
.week-row {