<template>
<view class="container">
<!-- 顶部用户信息区域 -->
<view class="user-info-section">
<view class="avatar-container">
<image
src="/static/workbanch/banner/workbench/4.jpg"
class="avatar-image"
mode="aspectFill"
/>
<view class="avatar-frame"></view>
</view>
<!-- 用户信息 -->
<view class="user-details">
<text class="welcome-text">您好,</text>
<text class="username">{{ userName || '加载中...' }}</text>
<view class="user-meta">
<text class="user-role">{{ userRole }}</text>
<text class="user-dept">{{ userDept }}</text>
<text class="user-phone" v-if="userPhone">电话:{{ userPhone }}</text>
</view>
</view>
</view>
<!-- 功能板块区域 -->
<view class="function-section">
<!-- 重置密码按钮 -->
<view
class="function-item"
@click="handleResetPassword"
@touchstart="handleTouchStart('reset')"
@touchend="handleTouchEnd('reset')"
>
<view class="item-left">
<image
src="/static/workbanch/icons/reset-password.png"
class="item-icon"
/>
<text class="item-text">重置密码</text>
</view>
<view class="arrow-icon">›</view>
</view>
<!-- 分隔线 -->
<view class="divider"></view>
<!-- 退出登录按钮 -->
<view
class="function-item logout-item"
@click="handleLogout"
@touchstart="handleTouchStart('logout')"
@touchend="handleTouchEnd('logout')"
>
<view class="item-left">
<image
src="/static/workbanch/icons/logout.png"
class="item-icon"
/>
<text class="item-text">退出登录</text>
</view>
<view class="arrow-icon">›</view>
</view>
</view> <!-- 这里添加了function-section的结束标签 -->
<!-- 重置密码弹窗 -->
<view v-if="showResetModal" class="modal-overlay" @tap="closeModal">
<view class="modal-content" @tap.stop>
<view class="modal-header">
<text class="modal-title">重置密码</text>
<text class="modal-close" @tap="closeModal">×</text>
</view>
<view class="input-group">
<!-- 旧密码输入框 -->
<view class="input-item">
<image src="/static/workbanch/icons/lock.png" class="input-icon" />
<input
class="password-input"
:type="showOldPassword ? 'text' : 'password'"
v-model="oldPassword"
placeholder="请输入旧密码"
placeholder-style="color: #aaa;"
/>
<view class="eye-icon-wrapper" @tap="togglePasswordVisibility('old')">
<image
:src="showOldPassword ? '/static/workbanch/icons/eye-open.png' : '/static/workbanch/icons/eye-close.png'"
class="eye-icon"
/>
</view>
</view>
<!-- 新密码输入框 -->
<view class="input-item">
<image src="/static/workbanch/icons/lock.png" class="input-icon" />
<input
class="password-input"
:type="showNewPassword ? 'text' : 'password'"
v-model="newPassword"
placeholder="请输入新密码(6-10位)"
placeholder-style="color: #aaa;"
/>
<view class="eye-icon-wrapper" @tap="togglePasswordVisibility('new')">
<image
:src="showNewPassword ? '/static/workbanch/icons/eye-open.png' : '/static/workbanch/icons/eye-close.png'"
class="eye-icon"
/>
</view>
</view>
<!-- 确认密码输入框 -->
<view class="input-item">
<image src="/static/workbanch/icons/lock.png" class="input-icon" />
<input
class="password-input"
:type="showConfirmPassword ? 'text' : 'password'"
v-model="confirmPassword"
placeholder="请确认新密码"
placeholder-style="color: #aaa;"
/>
<view class="eye-icon-wrapper" @tap="togglePasswordVisibility('confirm')">
<image
:src="showConfirmPassword ? '/static/workbanch/icons/eye-open.png' : '/static/workbanch/icons/eye-close.png'"
class="eye-icon"
/>
</view>
</view>
</view>
<!-- 按钮区域 -->
<view class="modal-buttons">
<view class="cancel-btn" @tap="closeModal">取消</view>
<view class="confirm-btn" @tap="handleSavePassword">保存</view>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue';
import { onShow, onTabItemTap } from '@dcloudio/uni-app';
// 用户信息相关
const userName = ref('');
const userRole = ref('');
const userDept = ref('');
const userPhone = ref('');
//重置密码
const showResetModal = ref(false);
const oldPassword = ref('');
const newPassword = ref('');
const confirmPassword = ref('');
const showOldPassword = ref(false);
const showNewPassword = ref(false);
const showConfirmPassword = ref(false);
// 触摸状态
const touchState = ref({
reset: false,
logout: false
});
// 页面显示时加载数据
onShow(() => {
// console.log('onShow 被触发');
loadUserInfo(); // 加载用户信息
});
// tabbar 页面被点击时触发
onTabItemTap(() => {
// console.log('tabbar 页面被点击');
loadUserInfo(); // 强制刷新数据
});
const forceRefresh = true;
// 加载用户信息
const loadUserInfo = async () => {
try {
const userInfo = uni.getStorageSync('userInfo');
// console.log('本地缓存 userInfo:', userInfo);
if (!forceRefresh && userInfo && userInfo.userName) {
// 使用缓存
userName.value = userInfo.nickName || userInfo.userName || '未知用户';
userRole.value = userInfo.roles?.[0]?.roleName || '普通用户';
userDept.value = userInfo.dept?.deptName || '未分配部门';
userPhone.value = userInfo.phonenumber || '暂无电话';
} else {
// console.log('开始请求用户信息...');
const res = await uni.request({
url: 'http://172.26.26.43/dev-api/system/user/profile',
method: 'GET',
header: {
'Authorization': 'Bearer ' + uni.getStorageSync('token')
}
});
// console.log('接口返回结果:', res);
if (res.statusCode === 200 && res.data.code === 200) {
const userData = res.data.data;
uni.setStorageSync('userInfo', userData);
userName.value = userData.nickName || userData.userName || '未知用户';
userRole.value = userData.roles?.[0]?.roleName || '普通用户';
userDept.value = userData.dept?.deptName || '未分配部门';
userPhone.value = userData.phonenumber || '暂无电话';
} else {
uni.showToast({ title: '获取用户信息失败', icon: 'none' });
uni.redirectTo({ url: '/pages/login/login' });
}
}
} catch (error) {
console.error('加载用户信息失败:', error);
uni.showToast({ title: '加载用户信息失败', icon: 'none' });
uni.redirectTo({ url: '/pages/login/login' });
}
};
// 处理触摸开始
const handleTouchStart = (type) => {
touchState.value[type] = true;
};
// 处理触摸结束
const handleTouchEnd = (type) => {
touchState.value[type] = false;
};
// 处理重置密码按钮点击
const handleResetPassword = () => {
uni.vibrateShort(); // 添加震动反馈
showResetModal.value = true;
// 重置表单
oldPassword.value = '';
newPassword.value = '';
confirmPassword.value = '';
showOldPassword.value = false;
showNewPassword.value = false;
showConfirmPassword.value = false;
};
// 切换密码可见性
const togglePasswordVisibility = (type) => {
switch (type) {
case 'old':
showOldPassword.value = !showOldPassword.value;
break;
case 'new':
showNewPassword.value = !showNewPassword.value;
break;
case 'confirm':
showConfirmPassword.value = !showConfirmPassword.value;
break;
}
};
// 关闭弹窗
const closeModal = () => {
showResetModal.value = false;
};
// 保存密码
const handleSavePassword = async () => {
// 表单验证
if (!oldPassword.value || !newPassword.value || !confirmPassword.value) {
uni.showToast({ title: '请填写所有密码字段', icon: 'none' });
return;
}
if (newPassword.value.length < 6 || newPassword.value.length > 10) {
uni.showToast({ title: '密码长度需在6-10位之间', icon: 'none' });
return;
}
if (newPassword.value !== confirmPassword.value) {
uni.showToast({ title: '两次输入的新密码不一致', icon: 'none' });
return;
}
// 调用API
try {
const token = uni.getStorageSync('token');
if (!token) {
uni.showToast({ title: '请重新登录', icon: 'none' });
return;
}
const res = await uni.request({
url: 'http://172.26.26.43/dev-api/system/user/profile/updatePwd',
method: 'PUT',
header: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
data: {
oldPassword: oldPassword.value,
newPassword: newPassword.value
}
});
// 处理响应
if (res.statusCode === 200 && res.data.code === 200) {
uni.showToast({
title: '密码修改成功,请重新登录',
icon: 'success',
duration: 2000
});
// 延迟执行跳转
setTimeout(() => {
// 清除用户数据
uni.removeStorageSync('token');
uni.removeStorageSync('userInfo');
// uni.removeStorageSync('savedUsername');
// 跳转到登录页
uni.reLaunch({
url: '/pages/login/login'
});
}, 2000);
closeModal();
} else {
const errorMsg = res.data.msg || '密码修改失败';
uni.showToast({ title: errorMsg, icon: 'none' });
}
} catch (error) {
console.error('修改密码失败:', error);
uni.showToast({ title: '请求失败,请稍后再试', icon: 'none' });
}
};
// 处理退出登录
const handleLogout = () => {
uni.vibrateShort(); // 添加震动反馈
uni.showModal({
title: '确认退出',
content: '您确定要退出当前账号吗?',
confirmText: '退出登录',
confirmColor: '#e74c3c',
success: (res) => {
if (res.confirm) {
// 清除用户相关数据
uni.removeStorageSync('token');
uni.removeStorageSync('userInfo');
uni.removeStorageSync('savedUsername');
// 显示退出提示
uni.showToast({
title: '已退出登录',
icon: 'success',
duration: 1500
});
// 跳转到登录页
setTimeout(() => {
uni.reLaunch({
url: '/pages/login/login'
});
}, 1500);
}
}
});
};
</script>
<style lang="scss" scoped>
.container {
padding: 20rpx;
background-color: #f5f7fa;
min-height: 100vh;
}
/* 用户信息区域样式 */
.user-info-section {
display: flex;
align-items: center;
padding: 30rpx;
margin: 20rpx 0;
background: linear-gradient(135deg, #3498db, #8e44ad);
color: white;
position: relative;
overflow: hidden;
border-radius: 24rpx;
&::before {
content: '';
position: absolute;
top: -50%;
right: -50%;
width: 200%;
height: 200%;
background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0) 70%);
pointer-events: none;
}
}
.user-phone {
display: block;
font-size: 24rpx;
// margin-top: 10rpx;
color: rgba(255, 255, 255, 0.8);
background: rgba(255, 255, 255, 0.2);
padding: 4rpx 12rpx;
border-radius: 20rpx;
backdrop-filter: blur(10px);
}
.avatar-container {
position: relative;
width: 120rpx;
height: 120rpx;
margin-right: 30rpx;
}
.avatar-image {
width: 100%;
height: 100%;
border-radius: 50%;
background-color: #fff;
}
.avatar-frame {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border: 4rpx solid rgba(255, 255, 255, 0.3);
border-radius: 50%;
box-sizing: border-box;
}
.user-details {
flex: 1;
}
.welcome-text {
font-size: 28rpx;
opacity: 0.9;
}
.username {
display: block;
font-size: 40rpx;
font-weight: bold;
margin: 8rpx 0;
text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.2);
}
.user-meta {
display: flex;
flex-wrap: wrap;
gap: 15rpx;
margin-top: 15rpx;
}
.user-role, .user-dept {
font-size: 24rpx;
background: rgba(255, 255, 255, 0.2);
padding: 4rpx 12rpx;
border-radius: 20rpx;
backdrop-filter: blur(10px);
}
/* 功能板块区域 */
.function-section {
background-color: #fff;
border-radius: 24rpx;
margin: 30rpx 40rpx; /* 左右边距增加 */
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
overflow: hidden;
}
.function-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 30rpx 35rpx; /* 增加左右内边距 */
position: relative;
transition: all 0.2s ease;
/* 点击效果 - 缩放 */
&:active {
transform: scale(0.98);
background-color: #f8f8f8;
}
/* 触摸效果 - 高亮 */
&.touch-active {
background-color: #f0f9ff;
transform: scale(0.98);
.item-text {
font-weight: bold;
}
}
}
.logout-item {
.item-text {
color: #e74c3c;
}
&.touch-active {
background-color: #fff0f0;
}
}
.item-left {
display: flex;
align-items: center;
}
.item-icon {
width: 44rpx;
height: 44rpx;
margin-right: 20rpx;
transition: transform 0.2s ease;
.touch-active & {
transform: scale(1.1);
}
}
.item-text {
font-size: 32rpx;
color: #333;
transition: all 0.2s ease;
.touch-active & {
transform: translateX(5px);
}
}
.arrow-icon {
font-size: 40rpx;
color: #999;
transform: scale(1.5, 1.5);
transition: all 0.2s ease;
.touch-active & {
transform: scale(1.5, 1.5) translateX(-5px);
}
}
.divider {
height: 1rpx;
background-color: #f0f0f0;
margin: 0 35rpx; /* 与内边距一致 */
}
/* 动画效果 */
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
}
.pulse {
animation: pulse 0.3s ease;
}
/* 重置密码弹窗*/
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
animation: fadeIn 0.3s ease;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.modal-content {
background-color: #fff;
width: 80%;
max-width: 600rpx;
border-radius: 24rpx;
overflow: hidden;
box-shadow: 0 10rpx 40rpx rgba(0, 0, 0, 0.2);
animation: slideUp 0.3s ease;
}
@keyframes slideUp {
from { transform: translateY(100rpx); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx;
background-color: #f8f8f8;
border-bottom: 1rpx solid #eee;
}
.modal-title {
font-size: 34rpx;
font-weight: bold;
color: #333;
}
.modal-close {
font-size: 40rpx;
color: #999;
padding: 10rpx 20rpx;
}
.input-group {
padding: 30rpx;
}
.input-item {
display: flex;
align-items: center;
margin-bottom: 30rpx;
padding-bottom: 15rpx;
border-bottom: 1rpx solid #eee;
position: relative;
&:last-child {
margin-bottom: 0;
}
}
.input-icon {
width: 36rpx;
height: 36rpx;
margin-right: 20rpx;
opacity: 0.7;
}
.eye-icon-wrapper {
position: absolute;
right: 10rpx;
top: 50%;
transform: translateY(-50%);
width: 60rpx;
height: 60rpx;
display: none;
align-items: center;
justify-content: center;
z-index: 100;
touch-action: manipulation;
background-color: rgba(255, 255, 255, 0.8);
border-radius: 50%;
pointer-events: auto; // 确保点击事件有效
&:active {
background-color: rgba(200, 200, 200, 0.8);
transform: translateY(-50%) scale(0.95);
}
}
.eye-icon {
width: 40rpx;
height: 40rpx;
opacity: 0.7;
transition: opacity 0.2s;
}
.password-input {
flex: 1;
height: 70rpx; /* 增加高度 */
font-size: 32rpx;
color: #333;
padding-right: 80rpx; /* 增加右侧内边距 */
}
.modal-buttons {
display: flex;
border-top: 1rpx solid #eee;
}
.cancel-btn, .confirm-btn {
flex: 1;
height: 90rpx;
line-height: 90rpx;
font-size: 32rpx;
border-radius: 0;
background: none;
position: relative;
&::after {
content: '';
position: absolute;
top: 0;
bottom: 0;
width: 1rpx;
background-color: #eee;
}
}
.cancel-btn {
color: #666;
&:active {
background-color: #f8f8f8;
}
}
.confirm-btn {
color: #3498db;
font-weight: bold;
&:active {
background-color: #f0f9ff;
}
}
.confirm-btn::after {
left: 0;
}
.cancel-btn::after {
display: none;
}
</style>
我想要点击重置密码弹出的密码输入框动态绑定type,图标设置click事件,v-show控制图标,定义showPassword == false默认闭眼,点击图标时showPassword 取反。showPassword.value == false时,type为password(密码不显示);showPassword.value == true,type为text(密码显示)
————————————————
版权声明:本文为优快云博主「sirenaki」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.youkuaiyun.com/m0_69688393/article/details/128250281
最新发布