feat: 修改密码
continuous-integration/drone/push Build is passing Details

This commit is contained in:
张哲铜 2025-10-19 22:56:31 +08:00
parent 235e71fba3
commit a92743dda1
6 changed files with 286 additions and 5 deletions

View File

@ -166,3 +166,4 @@ const answerSheetData = computed(() =>
适合在 App 中使用,代码更简洁、性能更好、易于维护。 适合在 App 中使用,代码更简洁、性能更好、易于维护。

View File

@ -91,3 +91,4 @@ answer-sheet-viewer/
3. 适用于 H5/小程序/APP 多端 3. 适用于 H5/小程序/APP 多端
4. 图片和批注坐标使用原始像素值,组件内部处理缩放 4. 图片和批注坐标使用原始像素值,组件内部处理缩放

View File

@ -238,3 +238,4 @@ export function useMarkingRenderer() {
renderMarking, renderMarking,
} }
} }

View File

@ -2,3 +2,4 @@ export * from './ai-types'
export { default as AnswerSheetWithMarking } from './AnswerSheetWithMarking.vue' export { default as AnswerSheetWithMarking } from './AnswerSheetWithMarking.vue'
export * from './composables/useDataParser' export * from './composables/useDataParser'
export * from './types' export * from './types'

View File

@ -0,0 +1,253 @@
<script setup lang="ts">
import { ref } from 'vue'
import { myChangePasswordUsingPost } from '@/service/wode'
// Props
interface Props {
show: boolean
}
const props = defineProps<Props>()
// Emits
const emit = defineEmits<{
(e: 'update:show', value: boolean): void
(e: 'success'): void
}>()
//
const formData = ref({
oldPassword: '',
newPassword: '',
confirmPassword: '',
})
//
const showPassword = ref({
old: false,
new: false,
confirm: false,
})
//
const submitting = ref(false)
//
function handleClose() {
emit('update:show', false)
//
setTimeout(() => {
resetForm()
}, 300)
}
//
function resetForm() {
formData.value = {
oldPassword: '',
newPassword: '',
confirmPassword: '',
}
showPassword.value = {
old: false,
new: false,
confirm: false,
}
}
//
function showToast(title: string) {
uni.showToast({
title,
icon: 'none',
duration: 2000,
mask: true,
})
}
//
function validateForm() {
if (!formData.value.oldPassword) {
showToast('请输入原密码')
return false
}
if (!formData.value.newPassword) {
showToast('请输入新密码')
return false
}
if (formData.value.newPassword.length < 6) {
showToast('新密码长度不能少于6位')
return false
}
if (!formData.value.confirmPassword) {
showToast('请确认新密码')
return false
}
if (formData.value.newPassword !== formData.value.confirmPassword) {
showToast('两次输入的密码不一致')
return false
}
if (formData.value.oldPassword === formData.value.newPassword) {
showToast('新密码不能与原密码相同')
return false
}
return true
}
//
async function handleSubmit() {
if (!validateForm()) {
return
}
submitting.value = true
try {
const res = await myChangePasswordUsingPost({
body: {
old_password: formData.value.oldPassword,
new_password: formData.value.newPassword,
confirm_password: formData.value.confirmPassword,
},
})
if (res.success) {
uni.showToast({
title: '密码修改成功',
icon: 'success',
duration: 2000,
mask: true,
})
handleClose()
emit('success')
}
else {
uni.showToast({
title: res.message || '修改失败',
icon: 'none',
duration: 2000,
mask: true,
})
}
}
catch (error: any) {
uni.showToast({
title: error.data?.message || '修改失败,请稍后重试',
icon: 'none',
duration: 2000,
mask: true,
})
}
finally {
submitting.value = false
}
}
//
function togglePasswordVisibility(type: 'old' | 'new' | 'confirm') {
showPassword.value[type] = !showPassword.value[type]
}
</script>
<template>
<view v-if="show" class="fixed inset-0 z-999 flex items-center justify-center" @tap.self="handleClose">
<!-- 遮罩层 -->
<view class="absolute inset-0 bg-black/50" @tap="handleClose" />
<!-- 弹窗内容 -->
<view class="relative mx-6 max-w-400px w-full rounded-2xl bg-white p-6 shadow-xl">
<!-- 标题 -->
<view class="mb-6 flex items-center justify-between">
<text class="text-xl text-slate-800 font-semibold">修改密码</text>
<view class="i-carbon:close h-6 w-6 text-slate-400" @tap="handleClose" />
</view>
<!-- 表单 -->
<view class="space-y-4">
<!-- 原密码 -->
<view class="space-y-2">
<text class="text-sm text-slate-600">原密码</text>
<view class="relative flex items-center border border-slate-200 rounded-xl bg-slate-50 px-4 py-3">
<view class="i-carbon:locked mr-2 text-lg text-slate-400" />
<input
v-model="formData.oldPassword"
class="flex-1 bg-transparent text-base text-slate-800"
:type="(showPassword.old ? 'text' : 'password') as any"
placeholder="请输入原密码"
:maxlength="20"
>
<view
:class="showPassword.old ? 'i-carbon:view' : 'i-carbon:view-off'"
class="ml-2 text-lg text-slate-400"
@tap="togglePasswordVisibility('old')"
/>
</view>
</view>
<!-- 新密码 -->
<view class="space-y-2">
<text class="text-sm text-slate-600">新密码</text>
<view class="relative flex items-center border border-slate-200 rounded-xl bg-slate-50 px-4 py-3">
<view class="i-carbon:locked mr-2 text-lg text-slate-400" />
<input
v-model="formData.newPassword"
class="flex-1 bg-transparent text-base text-slate-800"
:type="(showPassword.new ? 'text' : 'password') as any"
placeholder="请输入新密码(至少6位)"
:maxlength="20"
>
<view
:class="showPassword.new ? 'i-carbon:view' : 'i-carbon:view-off'"
class="ml-2 text-lg text-slate-400"
@tap="togglePasswordVisibility('new')"
/>
</view>
</view>
<!-- 确认新密码 -->
<view class="space-y-2">
<text class="text-sm text-slate-600">确认新密码</text>
<view class="relative flex items-center border border-slate-200 rounded-xl bg-slate-50 px-4 py-3">
<view class="i-carbon:locked mr-2 text-lg text-slate-400" />
<input
v-model="formData.confirmPassword"
class="flex-1 bg-transparent text-base text-slate-800"
:type="(showPassword.confirm ? 'text' : 'password') as any"
placeholder="请再次输入新密码"
:maxlength="20"
>
<view
:class="showPassword.confirm ? 'i-carbon:view' : 'i-carbon:view-off'"
class="ml-2 text-lg text-slate-400"
@tap="togglePasswordVisibility('confirm')"
/>
</view>
</view>
</view>
<!-- 按钮组 -->
<view class="mt-6 flex gap-3">
<button
class="h-44px flex-1 border border-slate-200 rounded-xl bg-white text-slate-600 font-medium !outline-none"
@tap="handleClose"
>
取消
</button>
<button
class="h-44px flex-1 rounded-xl from-blue-500 to-cyan-500 bg-gradient-to-r text-white font-medium !border-none !outline-none"
:class="{ 'opacity-50': submitting }"
:disabled="submitting"
@tap="handleSubmit"
>
{{ submitting ? '提交中...' : '确认修改' }}
</button>
</view>
</view>
</view>
</template>

View File

@ -1,9 +1,13 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue' import { computed, ref } from 'vue'
import ChangePasswordDialog from '@/components/mine/ChangePasswordDialog.vue'
import { useUserStore } from '@/store/user' import { useUserStore } from '@/store/user'
const userStore = useUserStore() const userStore = useUserStore()
//
const showChangePasswordDialog = ref(false)
// //
let safeAreaInsets let safeAreaInsets
let systemInfo let systemInfo
@ -53,10 +57,7 @@ const menuItems = [
function handleMenuClick(type: string) { function handleMenuClick(type: string) {
switch (type) { switch (type) {
case 'password': case 'password':
uni.showToast({ showChangePasswordDialog.value = true
title: '修改密码功能开发中',
icon: 'none',
})
break break
case 'privacy': case 'privacy':
uni.showToast({ uni.showToast({
@ -73,6 +74,23 @@ function handleMenuClick(type: string) {
} }
} }
//
function handlePasswordChangeSuccess() {
//
uni.showModal({
title: '提示',
content: '密码已修改,请重新登录',
showCancel: false,
confirmColor: '#3B82F6',
success: () => {
userStore.logOut()
uni.reLaunch({
url: '/pages/auth/index',
})
},
})
}
// 退 // 退
function handleLogout() { function handleLogout() {
uni.showModal({ uni.showModal({
@ -227,6 +245,12 @@ function getIconColorClass(index: number) {
<text class="text-xs text-slate-400">版本号 v1.0.0</text> <text class="text-xs text-slate-400">版本号 v1.0.0</text>
</view> </view>
</view> </view>
<!-- 修改密码弹窗 -->
<ChangePasswordDialog
v-model:show="showChangePasswordDialog"
@success="handlePasswordChangeSuccess"
/>
</view> </view>
</template> </template>