342 lines
8.9 KiB
Plaintext
342 lines
8.9 KiB
Plaintext
<template>
|
|
<view class="user-center-container">
|
|
<view class="page-header">
|
|
<text class="page-title">个人中心</text>
|
|
</view>
|
|
|
|
<view class="form-container">
|
|
<view class="form-item">
|
|
<text class="form-label">头像</text>
|
|
<view class="form-content avatar-uploader">
|
|
<!-- 默认使用一个占位头像或 Logo -->
|
|
<image class="avatar-img" :src="avatarUrl" mode="aspectFill"></image>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="form-item">
|
|
<text class="form-label">账号</text>
|
|
<view class="form-content">
|
|
<input class="uni-input disabled" :value="userAccount" disabled placeholder="请输入账号" />
|
|
</view>
|
|
</view>
|
|
|
|
<view class="form-item">
|
|
<text class="form-label"><text class="required">*</text>姓名</text>
|
|
<view class="form-content">
|
|
<input class="uni-input" v-model="formData.name" placeholder="请输入姓名" />
|
|
</view>
|
|
</view>
|
|
|
|
<view class="form-item">
|
|
<text class="form-label">原始密码</text>
|
|
<view class="form-content password-input-wrapper">
|
|
<input class="uni-input" :type="showOldPassword ? 'text' : 'password'" v-model="formData.oldPassword" placeholder="请输入原始密码" />
|
|
<view class="password-eye" @click="showOldPassword = !showOldPassword">
|
|
<text class="eye-icon">{{ showOldPassword ? '🙉' : '🙈' }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="form-item">
|
|
<text class="form-label">新密码</text>
|
|
<view class="form-content password-input-wrapper">
|
|
<input class="uni-input" :type="showNewPassword ? 'text' : 'password'" v-model="formData.newPassword" placeholder="请输入新密码" />
|
|
<view class="password-eye" @click="showNewPassword = !showNewPassword">
|
|
<text class="eye-icon">{{ showNewPassword ? '🙉' : '🙈' }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="form-item">
|
|
<text class="form-label">确认密码</text>
|
|
<view class="form-content password-input-wrapper">
|
|
<input class="uni-input" :type="showConfirmPassword ? 'text' : 'password'" v-model="formData.confirmPassword" placeholder="请再次输入新密码" />
|
|
<view class="password-eye" @click="showConfirmPassword = !showConfirmPassword">
|
|
<text class="eye-icon">{{ showConfirmPassword ? '🙉' : '🙈' }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="form-actions">
|
|
<button class="submit-btn" type="primary" @click="onSubmit">保存修改</button>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script setup lang="uts">
|
|
import { reactive, computed, onMounted, ref } from 'vue'
|
|
import { state, logout } from '@/utils/store.uts'
|
|
import supa from '@/components/supadb/aksupainstance.uts'
|
|
import { createTempClient } from '@/components/supadb/aksupa.uts'
|
|
import { SUPA_URL, SUPA_KEY } from '@/ak/config.uts'
|
|
|
|
const userAccount = computed((): string => state.userProfile.email || 'demo@example.com')
|
|
const avatarUrl = computed((): string => state.userProfile.avatar_url || '/static/logo.png')
|
|
|
|
const formData = reactive({
|
|
name: '',
|
|
oldPassword: '',
|
|
newPassword: '',
|
|
confirmPassword: ''
|
|
})
|
|
|
|
const showOldPassword = ref(false)
|
|
const showNewPassword = ref(false)
|
|
const showConfirmPassword = ref(false)
|
|
|
|
const isSubmitting = ref(false)
|
|
|
|
onMounted(() => {
|
|
formData.name = state.userProfile.username || ''
|
|
})
|
|
|
|
const onSubmit = async () => {
|
|
if (isSubmitting.value) return
|
|
|
|
const nameTrimmed = formData.name.trim()
|
|
if (nameTrimmed == '') {
|
|
uni.showToast({ title: '姓名不能为空', icon: 'none' })
|
|
return
|
|
}
|
|
|
|
isSubmitting.value = true
|
|
uni.showLoading({ title: '正在处理...', mask: true })
|
|
|
|
try {
|
|
// 1. 如果姓名有变化,更新姓名
|
|
if (nameTrimmed !== state.userProfile.username) {
|
|
const resName = await supa.from('ak_users').update({
|
|
username: nameTrimmed
|
|
}).eq('id', state.userProfile.id).execute()
|
|
|
|
if (resName.error != null) {
|
|
throw new Error('更新姓名失败: ' + (resName.error?.message ?? '网络异常'))
|
|
}
|
|
// 更新本地状态
|
|
state.userProfile.username = nameTrimmed
|
|
}
|
|
|
|
// 2. 处理密码修改
|
|
const wantsChangePassword = formData.oldPassword != '' || formData.newPassword != '' || formData.confirmPassword != ''
|
|
if (wantsChangePassword) {
|
|
if (formData.oldPassword == '') {
|
|
throw new Error('需要输入原始密码')
|
|
}
|
|
if (formData.newPassword == '') {
|
|
throw new Error('新密码不能为空')
|
|
}
|
|
// 关键修复:确保比较前去除可能的多余空格,或至少确保值确实一致
|
|
const newPwd = formData.newPassword
|
|
const confPwd = formData.confirmPassword
|
|
|
|
if (newPwd !== confPwd) {
|
|
throw new Error('两次输入的新密码不一致')
|
|
}
|
|
if (newPwd === formData.oldPassword) {
|
|
throw new Error('新密码不能与原始密码相同')
|
|
}
|
|
|
|
const email = state.userProfile.email
|
|
if (email == '') {
|
|
throw new Error('账号信息缺失,请重新登录后再试')
|
|
}
|
|
|
|
// 使用临时客户端校验密码,不影响当前会话
|
|
const tempSupa = createTempClient(SUPA_URL, SUPA_KEY)
|
|
|
|
try {
|
|
await tempSupa.auth.signInWithPassword({
|
|
email: email,
|
|
password: formData.oldPassword,
|
|
options: { persistSession: false }
|
|
} as UTSJSONObject)
|
|
} catch (e : any) {
|
|
const errMsg = e.message || '原始密码校验失败'
|
|
if (errMsg.toLowerCase().includes('invalid login credentials')) {
|
|
throw new Error('原始密码错误')
|
|
}
|
|
throw new Error(errMsg)
|
|
}
|
|
|
|
// 密码更新(使用主客户端)
|
|
const resUpdate = await supa.auth.updateUser({
|
|
password: formData.newPassword
|
|
} as UTSJSONObject)
|
|
|
|
if (resUpdate.error != null) {
|
|
throw new Error('密码修改失败: ' + (resUpdate.error?.message ?? '网络异常'))
|
|
}
|
|
|
|
uni.hideLoading()
|
|
uni.showToast({ title: '修改成功, 请重新登录', icon: 'success' })
|
|
|
|
// 退出登录并强制跳转
|
|
setTimeout(() => {
|
|
logout()
|
|
// 同时清理管理端特定的角色缓存和状态
|
|
try {
|
|
const { clearAdminRoleCache } = require('@/layouts/admin/utils/role.uts')
|
|
clearAdminRoleCache()
|
|
} catch (e) {}
|
|
|
|
uni.removeStorageSync('adminRole') // 确保清理旧的缓存标识
|
|
uni.removeStorageSync('token')
|
|
uni.reLaunch({ url: '/pages/user/login' })
|
|
}, 1500)
|
|
return
|
|
}
|
|
|
|
// 如果只是更新了姓名
|
|
uni.hideLoading()
|
|
uni.showToast({ title: '基本信息已更新', icon: 'success' })
|
|
isSubmitting.value = false
|
|
|
|
} catch (err : any) {
|
|
uni.hideLoading()
|
|
isSubmitting.value = false
|
|
const msg = err.message || '操作失败,请重试'
|
|
console.error('Submit user update failed:', err)
|
|
uni.showToast({
|
|
title: msg,
|
|
icon: 'none',
|
|
duration: 3000
|
|
})
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.user-center-container {
|
|
padding: 20px;
|
|
background-color: #f5f7f9;
|
|
min-height: 100%;
|
|
}
|
|
|
|
.page-header {
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.page-title {
|
|
font-size: 16px;
|
|
font-weight: bold;
|
|
color: #333;
|
|
}
|
|
|
|
.form-container {
|
|
background-color: #fff;
|
|
border-radius: 4px;
|
|
padding: 30px 20px;
|
|
max-width: 800px;
|
|
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
|
|
}
|
|
|
|
.form-item {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
margin-bottom: 24px;
|
|
}
|
|
|
|
.form-label {
|
|
width: 100px;
|
|
text-align: right;
|
|
margin-right: 16px;
|
|
font-size: 14px;
|
|
color: #606266;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.required {
|
|
color: #f5222d;
|
|
margin-right: 4px;
|
|
}
|
|
|
|
.form-content {
|
|
flex: 1;
|
|
max-width: 400px;
|
|
}
|
|
|
|
.uni-input {
|
|
width: 100%;
|
|
height: 36px;
|
|
line-height: 36px;
|
|
padding: 0 12px;
|
|
border: 1px solid #dcdfe6;
|
|
border-radius: 4px;
|
|
font-size: 14px;
|
|
color: #333;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.uni-input:focus {
|
|
border-color: #1890ff;
|
|
outline: none;
|
|
}
|
|
|
|
.password-input-wrapper {
|
|
position: relative;
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.password-eye {
|
|
position: absolute;
|
|
right: 12px;
|
|
height: 100%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
cursor: pointer;
|
|
z-index: 10;
|
|
padding: 0 4px;
|
|
}
|
|
|
|
.eye-icon {
|
|
font-size: 18px;
|
|
color: #999;
|
|
}
|
|
|
|
.password-eye:active .eye-icon {
|
|
color: #1890ff;
|
|
}
|
|
|
|
.disabled {
|
|
background-color: #f5f7fa;
|
|
color: #c0c4cc;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
.avatar-uploader {
|
|
display: flex;
|
|
justify-content: flex-start;
|
|
}
|
|
|
|
.avatar-img {
|
|
width: 80px;
|
|
height: 80px;
|
|
border-radius: 4px;
|
|
background-color: #f0f2f5;
|
|
border: 1px dashed #d9d9d9;
|
|
}
|
|
|
|
.form-actions {
|
|
margin-top: 40px;
|
|
padding-left: 116px;
|
|
}
|
|
|
|
.submit-btn {
|
|
background-color: #1890ff;
|
|
color: #fff;
|
|
font-size: 14px;
|
|
height: 36px;
|
|
line-height: 36px;
|
|
width: 120px;
|
|
border-radius: 4px;
|
|
border: none;
|
|
}
|
|
.submit-btn:active {
|
|
background-color: #096dd9;
|
|
}
|
|
</style>
|