完成头像上传与更新
This commit is contained in:
		
							parent
							
								
									b8d3757397
								
							
						
					
					
						commit
						efa390294d
					
				| @ -374,7 +374,7 @@ class IndexController extends BaseController | |||||||
|         try { |         try { | ||||||
|             // 验证上传的文件
 |             // 验证上传的文件
 | ||||||
|             validate([ |             validate([ | ||||||
|                 'image' => 'filesize:51200|fileExt:jpg,png,gif,jpeg' |                 'image' => 'filesize:51200|fileExt:jpg,png,gif,jpeg,webp' | ||||||
|             ])->check($file); |             ])->check($file); | ||||||
| 
 | 
 | ||||||
|             // 存储文件到public磁盘的uploads目录
 |             // 存储文件到public磁盘的uploads目录
 | ||||||
|  | |||||||
| @ -140,34 +140,20 @@ class UserController extends BaseController | |||||||
|     public function logout() |     public function logout() | ||||||
|     { |     { | ||||||
|         try { |         try { | ||||||
|             // 记录退出日志
 |  | ||||||
|             Log::record('用户退出登录', 'info'); |             Log::record('用户退出登录', 'info'); | ||||||
|              |              | ||||||
|             // 1. 清除所有 session
 |             // 清除所有会话和缓存数据
 | ||||||
|             session(null); |             session(null); | ||||||
| 
 |  | ||||||
|             // 2. 清除所有 cookie
 |  | ||||||
|             // 正确的删除 cookie 方式
 |  | ||||||
|             cookie('user_id', null, ['expire' => -1]); |  | ||||||
|             cookie('user_account', null, ['expire' => -1]); |  | ||||||
|             cookie('user_name', null, ['expire' => -1]); |  | ||||||
|             cookie('user_avatar', null, ['expire' => -1]); |  | ||||||
|             cookie('expire_time', null, ['expire' => -1]); |  | ||||||
|             cookie('is_auto_login', null, ['expire' => -1]); |  | ||||||
|             cookie('auto_login_attempted', null, ['expire' => -1]); |  | ||||||
| 
 |  | ||||||
|             // 3. 清除缓存
 |  | ||||||
|             Cache::tag('user_cache')->clear(); |             Cache::tag('user_cache')->clear(); | ||||||
|              |              | ||||||
|             // 4. 返回成功状态,并告诉前端清除 localStorage
 |             // 清除所有cookie
 | ||||||
|             return json([ |             $cookies = ['user_id', 'user_account', 'user_name', 'user_avatar',  | ||||||
|                 'code' => 0, |                        'expire_time', 'is_auto_login', 'auto_login_attempted', 'PHPSESSID']; | ||||||
|                 'msg' => '退出成功', |             foreach ($cookies as $cookie) { | ||||||
|                 'data' => [ |                 cookie($cookie, null, ['expire' => -1]); | ||||||
|                     'clear_storage' => true |             } | ||||||
|                 ] |  | ||||||
|             ]); |  | ||||||
| 
 | 
 | ||||||
|  |             return json(['code' => 0, 'msg' => '退出成功', 'data' => ['clear_storage' => true]]); | ||||||
|         } catch (\Exception $e) { |         } catch (\Exception $e) { | ||||||
|             Log::record('退出登录失败:' . $e->getMessage(), 'error'); |             Log::record('退出登录失败:' . $e->getMessage(), 'error'); | ||||||
|             return json(['code' => 1, 'msg' => '退出失败:' . $e->getMessage()]); |             return json(['code' => 1, 'msg' => '退出失败:' . $e->getMessage()]); | ||||||
| @ -395,8 +381,8 @@ class UserController extends BaseController | |||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             $ext = strtolower($file->getOriginalExtension()); |             $ext = strtolower($file->getOriginalExtension()); | ||||||
|             if (!in_array($ext, ['jpg', 'jpeg', 'png', 'gif'])) { |             if (!in_array($ext, ['jpg', 'jpeg', 'png', 'gif', 'webp'])) { | ||||||
|                 return json(['code' => 1, 'msg' => '只支持jpg、jpeg、png、gif格式的图片']); |                 return json(['code' => 1, 'msg' => '只支持jpg、jpeg、png、gif、webp格式的图片']); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             // 移动到指定目录
 |             // 移动到指定目录
 | ||||||
|  | |||||||
| @ -28,228 +28,241 @@ | |||||||
| </div> | </div> | ||||||
| 
 | 
 | ||||||
| <style> | <style> | ||||||
| .avatar-section { |     .avatar-section { | ||||||
|     max-width: 800px; |         max-width: 800px; | ||||||
|     margin: 0 auto; |         margin: 0 auto; | ||||||
| } |     } | ||||||
| 
 | 
 | ||||||
| .section-title { |     .section-title { | ||||||
|     font-size: 20px; |         font-size: 20px; | ||||||
|     font-weight: 600; |         font-weight: 600; | ||||||
|     color: #333;
 |         color: #333;
 | ||||||
|     margin-bottom: 24px; |         margin-bottom: 24px; | ||||||
|     padding-bottom: 16px; |         padding-bottom: 16px; | ||||||
|     border-bottom: 1px solid #f0f0f0;
 |         border-bottom: 1px solid #f0f0f0;
 | ||||||
| } |     } | ||||||
| 
 | 
 | ||||||
| .avatar-upload-container { |  | ||||||
|     display: flex; |  | ||||||
|     gap: 40px; |  | ||||||
|     margin-bottom: 32px; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .current-avatar { |  | ||||||
|     text-align: center; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .current-avatar img { |  | ||||||
|     width: 160px; |  | ||||||
|     height: 160px; |  | ||||||
|     border-radius: 50%; |  | ||||||
|     object-fit: cover; |  | ||||||
|     border: 3px solid #f5f5f5;
 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .avatar-tip { |  | ||||||
|     margin-top: 12px; |  | ||||||
|     color: #666;
 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .upload-area { |  | ||||||
|     flex: 1; |  | ||||||
|     border: 2px dashed #d9d9d9;
 |  | ||||||
|     border-radius: 8px; |  | ||||||
|     padding: 40px; |  | ||||||
|     text-align: center; |  | ||||||
|     cursor: pointer; |  | ||||||
|     transition: all 0.3s; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .upload-area:hover { |  | ||||||
|     border-color: #1677ff;
 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .upload-area .layui-icon { |  | ||||||
|     font-size: 48px; |  | ||||||
|     color: #999;
 |  | ||||||
|     margin-bottom: 16px; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .upload-area p { |  | ||||||
|     margin: 8px 0; |  | ||||||
|     color: #666;
 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .upload-tip { |  | ||||||
|     font-size: 12px; |  | ||||||
|     color: #999;
 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .avatar-preview { |  | ||||||
|     margin-top: 32px; |  | ||||||
|     padding-top: 32px; |  | ||||||
|     border-top: 1px solid #f0f0f0;
 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .avatar-preview h3 { |  | ||||||
|     font-size: 16px; |  | ||||||
|     color: #333;
 |  | ||||||
|     margin-bottom: 16px; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .preview-container { |  | ||||||
|     text-align: center; |  | ||||||
|     margin-bottom: 24px; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .preview-container img { |  | ||||||
|     max-width: 200px; |  | ||||||
|     max-height: 200px; |  | ||||||
|     border-radius: 8px; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .preview-actions { |  | ||||||
|     text-align: center; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .preview-actions .layui-btn { |  | ||||||
|     margin: 0 8px; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| @media (max-width: 768px) { |  | ||||||
|     .avatar-upload-container { |     .avatar-upload-container { | ||||||
|         flex-direction: column; |         display: flex; | ||||||
|         gap: 24px; |         gap: 40px; | ||||||
|  |         margin-bottom: 32px; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .current-avatar { | ||||||
|  |         text-align: center; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     .current-avatar img { |     .current-avatar img { | ||||||
|         width: 120px; |         width: 160px; | ||||||
|         height: 120px; |         height: 160px; | ||||||
|  |         border-radius: 50%; | ||||||
|  |         object-fit: cover; | ||||||
|  |         border: 3px solid #f5f5f5;
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .avatar-tip { | ||||||
|  |         margin-top: 12px; | ||||||
|  |         color: #666;
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     .upload-area { |     .upload-area { | ||||||
|         padding: 24px; |         flex: 1; | ||||||
|  |         border: 2px dashed #d9d9d9;
 | ||||||
|  |         border-radius: 8px; | ||||||
|  |         padding: 40px; | ||||||
|  |         text-align: center; | ||||||
|  |         cursor: pointer; | ||||||
|  |         transition: all 0.3s; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .upload-area:hover { | ||||||
|  |         border-color: #1677ff;
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .upload-area .layui-icon { | ||||||
|  |         font-size: 48px; | ||||||
|  |         color: #999;
 | ||||||
|  |         margin-bottom: 16px; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .upload-area p { | ||||||
|  |         margin: 8px 0; | ||||||
|  |         color: #666;
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .upload-tip { | ||||||
|  |         font-size: 12px; | ||||||
|  |         color: #999;
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .avatar-preview { | ||||||
|  |         margin-top: 32px; | ||||||
|  |         padding-top: 32px; | ||||||
|  |         border-top: 1px solid #f0f0f0;
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .avatar-preview h3 { | ||||||
|  |         font-size: 16px; | ||||||
|  |         color: #333;
 | ||||||
|  |         margin-bottom: 16px; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .preview-container { | ||||||
|  |         text-align: center; | ||||||
|  |         margin-bottom: 24px; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .preview-container img { | ||||||
|  |         max-width: 200px; | ||||||
|  |         max-height: 200px; | ||||||
|  |         border-radius: 8px; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .preview-actions { | ||||||
|  |         text-align: center; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .preview-actions .layui-btn { | ||||||
|  |         margin: 0 8px; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @media (max-width: 768px) { | ||||||
|  |         .avatar-upload-container { | ||||||
|  |             flex-direction: column; | ||||||
|  |             gap: 24px; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         .current-avatar img { | ||||||
|  |             width: 120px; | ||||||
|  |             height: 120px; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         .upload-area { | ||||||
|  |             padding: 24px; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } |  | ||||||
| </style> | </style> | ||||||
| 
 | 
 | ||||||
| <script> | <script> | ||||||
| layui.use(['upload', 'layer'], function(){ |     layui.use(['upload', 'layer'], function () { | ||||||
|     var upload = layui.upload; |         var upload = layui.upload; | ||||||
|     var layer = layui.layer; |         var layer = layui.layer; | ||||||
| 
 | 
 | ||||||
|     // 点击上传区域触发文件选择
 |         // 点击上传区域触发文件选择
 | ||||||
|     document.getElementById('uploadArea').addEventListener('click', function() { |         document.getElementById('uploadArea').addEventListener('click', function () { | ||||||
|         document.getElementById('avatarFile').click(); |             document.getElementById('avatarFile').click(); | ||||||
|     }); |         }); | ||||||
| 
 | 
 | ||||||
|     // 处理文件选择
 |         // 处理文件选择
 | ||||||
|     document.getElementById('avatarFile').addEventListener('change', function(e) { |         document.getElementById('avatarFile').addEventListener('change', function (e) { | ||||||
|         var file = e.target.files[0]; |             var file = e.target.files[0]; | ||||||
|         if (!file) return; |             if (!file) return; | ||||||
| 
 | 
 | ||||||
|         // 检查文件类型
 |             // 检查文件类型
 | ||||||
|         if (!['image/jpeg', 'image/png', 'image/gif'].includes(file.type)) { |             if (!['image/jpeg', 'image/png', 'image/gif', 'image/webp'].includes(file.type)) { | ||||||
|             layer.msg('请上传 jpg、png 或 gif 格式的图片'); |                 layer.msg('请上传 jpg、png、webp 或 gif 格式的图片'); | ||||||
|             return; |                 return; | ||||||
|         } |  | ||||||
|          |  | ||||||
|         // 检查文件大小
 |  | ||||||
|         if (file.size > 2 * 1024 * 1024) { |  | ||||||
|             layer.msg('图片大小不能超过 2MB'); |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|          |  | ||||||
|         // 预览图片
 |  | ||||||
|         var reader = new FileReader(); |  | ||||||
|         reader.onload = function(e) { |  | ||||||
|             document.getElementById('previewImage').src = e.target.result; |  | ||||||
|             document.querySelector('.avatar-preview').style.display = 'block'; |  | ||||||
|         }; |  | ||||||
|         reader.readAsDataURL(file); |  | ||||||
|     }); |  | ||||||
|      |  | ||||||
|     // 确认上传
 |  | ||||||
|     document.getElementById('confirmUpload').addEventListener('click', function() { |  | ||||||
|         var file = document.getElementById('avatarFile').files[0]; |  | ||||||
|         if (!file) return; |  | ||||||
|          |  | ||||||
|         var formData = new FormData(); |  | ||||||
|         formData.append('avatar', file); |  | ||||||
|          |  | ||||||
|         // 显示上传中
 |  | ||||||
|         var loadIndex = layer.load(2); |  | ||||||
|          |  | ||||||
|         // 发送上传请求
 |  | ||||||
|         fetch('/index/user/update_avatar', { |  | ||||||
|             method: 'POST', |  | ||||||
|             body: formData |  | ||||||
|         }) |  | ||||||
|         .then(response => response.json()) |  | ||||||
|         .then(data => { |  | ||||||
|             layer.close(loadIndex); |  | ||||||
|             if (data.code === 0) { |  | ||||||
|                 layer.msg('头像上传成功', {icon: 1}); |  | ||||||
|                 // 更新当前头像显示
 |  | ||||||
|                 document.getElementById('currentAvatar').src = data.data.url; |  | ||||||
|                 // 隐藏预览区域
 |  | ||||||
|                 document.querySelector('.avatar-preview').style.display = 'none'; |  | ||||||
|                 // 清空文件输入
 |  | ||||||
|                 document.getElementById('avatarFile').value = ''; |  | ||||||
|             } else { |  | ||||||
|                 layer.msg(data.msg || '上传失败', {icon: 2}); |  | ||||||
|             } |             } | ||||||
|         }) | 
 | ||||||
|         .catch(error => { |             // 检查文件大小
 | ||||||
|             layer.close(loadIndex); |             if (file.size > 2 * 1024 * 1024) { | ||||||
|             layer.msg('上传失败,请重试', {icon: 2}); |                 layer.msg('图片大小不能超过 2MB'); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             // 预览图片
 | ||||||
|  |             var reader = new FileReader(); | ||||||
|  |             reader.onload = function (e) { | ||||||
|  |                 document.getElementById('previewImage').src = e.target.result; | ||||||
|  |                 document.querySelector('.avatar-preview').style.display = 'block'; | ||||||
|  |             }; | ||||||
|  |             reader.readAsDataURL(file); | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         // 确认上传
 | ||||||
|  |         document.getElementById('confirmUpload').addEventListener('click', function () { | ||||||
|  |             var file = document.getElementById('avatarFile').files[0]; | ||||||
|  |             if (!file) return; | ||||||
|  | 
 | ||||||
|  |             var formData = new FormData(); | ||||||
|  |             formData.append('avatar', file); | ||||||
|  | 
 | ||||||
|  |             // 显示上传中
 | ||||||
|  |             var loadIndex = layer.load(2); | ||||||
|  | 
 | ||||||
|  |             // 发送上传请求
 | ||||||
|  |             fetch('/index/user/update_avatar', { | ||||||
|  |                 method: 'POST', | ||||||
|  |                 body: formData | ||||||
|  |             }) | ||||||
|  |                 .then(response => response.json()) | ||||||
|  |                 .then(data => { | ||||||
|  |                     layer.close(loadIndex); | ||||||
|  |                     if (data.code === 0) { | ||||||
|  |                         // 更新cookie中的头像
 | ||||||
|  |                         document.cookie = "user_avatar=" + data.data.url + "; path=/"; | ||||||
|  | 
 | ||||||
|  |                         // 更新localStorage中的头像
 | ||||||
|  |                         localStorage.setItem('user_avatar', data.data.url); | ||||||
|  | 
 | ||||||
|  |                         layer.msg('头像上传成功', { | ||||||
|  |                             icon: 1, | ||||||
|  |                             time: 1000 | ||||||
|  |                         }, function () { | ||||||
|  |                             // 更新当前头像显示
 | ||||||
|  |                             document.getElementById('currentAvatar').src = data.data.url; | ||||||
|  |                             // 隐藏预览区域
 | ||||||
|  |                             document.querySelector('.avatar-preview').style.display = 'none'; | ||||||
|  |                             // 清空文件输入
 | ||||||
|  |                             document.getElementById('avatarFile').value = ''; | ||||||
|  | 
 | ||||||
|  |                             // 刷新页面以更新所有显示的头像
 | ||||||
|  |                             window.location.reload(); | ||||||
|  |                         }); | ||||||
|  |                     } else { | ||||||
|  |                         layer.msg(data.msg || '上传失败', { icon: 2 }); | ||||||
|  |                     } | ||||||
|  |                 }) | ||||||
|  |                 .catch(error => { | ||||||
|  |                     layer.close(loadIndex); | ||||||
|  |                     layer.msg('上传失败,请重试', { icon: 2 }); | ||||||
|  |                 }); | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         // 取消上传
 | ||||||
|  |         document.getElementById('cancelUpload').addEventListener('click', function () { | ||||||
|  |             document.querySelector('.avatar-preview').style.display = 'none'; | ||||||
|  |             document.getElementById('avatarFile').value = ''; | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         // 拖拽上传
 | ||||||
|  |         var uploadArea = document.getElementById('uploadArea'); | ||||||
|  | 
 | ||||||
|  |         uploadArea.addEventListener('dragover', function (e) { | ||||||
|  |             e.preventDefault(); | ||||||
|  |             this.style.borderColor = '#1677ff'; | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         uploadArea.addEventListener('dragleave', function (e) { | ||||||
|  |             e.preventDefault(); | ||||||
|  |             this.style.borderColor = '#d9d9d9'; | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         uploadArea.addEventListener('drop', function (e) { | ||||||
|  |             e.preventDefault(); | ||||||
|  |             this.style.borderColor = '#d9d9d9'; | ||||||
|  | 
 | ||||||
|  |             var file = e.dataTransfer.files[0]; | ||||||
|  |             if (!file) return; | ||||||
|  | 
 | ||||||
|  |             // 触发文件选择事件
 | ||||||
|  |             var dataTransfer = new DataTransfer(); | ||||||
|  |             dataTransfer.items.add(file); | ||||||
|  |             document.getElementById('avatarFile').files = dataTransfer.files; | ||||||
|  | 
 | ||||||
|  |             // 手动触发change事件
 | ||||||
|  |             var event = new Event('change'); | ||||||
|  |             document.getElementById('avatarFile').dispatchEvent(event); | ||||||
|         }); |         }); | ||||||
|     }); |     }); | ||||||
|      |  | ||||||
|     // 取消上传
 |  | ||||||
|     document.getElementById('cancelUpload').addEventListener('click', function() { |  | ||||||
|         document.querySelector('.avatar-preview').style.display = 'none'; |  | ||||||
|         document.getElementById('avatarFile').value = ''; |  | ||||||
|     }); |  | ||||||
|      |  | ||||||
|     // 拖拽上传
 |  | ||||||
|     var uploadArea = document.getElementById('uploadArea'); |  | ||||||
|      |  | ||||||
|     uploadArea.addEventListener('dragover', function(e) { |  | ||||||
|         e.preventDefault(); |  | ||||||
|         this.style.borderColor = '#1677ff'; |  | ||||||
|     }); |  | ||||||
|      |  | ||||||
|     uploadArea.addEventListener('dragleave', function(e) { |  | ||||||
|         e.preventDefault(); |  | ||||||
|         this.style.borderColor = '#d9d9d9'; |  | ||||||
|     }); |  | ||||||
|      |  | ||||||
|     uploadArea.addEventListener('drop', function(e) { |  | ||||||
|         e.preventDefault(); |  | ||||||
|         this.style.borderColor = '#d9d9d9'; |  | ||||||
|          |  | ||||||
|         var file = e.dataTransfer.files[0]; |  | ||||||
|         if (!file) return; |  | ||||||
|          |  | ||||||
|         // 触发文件选择事件
 |  | ||||||
|         var dataTransfer = new DataTransfer(); |  | ||||||
|         dataTransfer.items.add(file); |  | ||||||
|         document.getElementById('avatarFile').files = dataTransfer.files; |  | ||||||
|          |  | ||||||
|         // 手动触发change事件
 |  | ||||||
|         var event = new Event('change'); |  | ||||||
|         document.getElementById('avatarFile').dispatchEvent(event); |  | ||||||
|     }); |  | ||||||
| }); |  | ||||||
| </script> | </script> | ||||||
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 74 KiB | 
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 74 KiB | 
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 74 KiB | 
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user