完成头像上传与更新
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();
|
||||||
|
|
||||||
|
// 清除所有cookie
|
||||||
|
$cookies = ['user_id', 'user_account', 'user_name', 'user_avatar',
|
||||||
|
'expire_time', 'is_auto_login', 'auto_login_attempted', 'PHPSESSID'];
|
||||||
|
foreach ($cookies as $cookie) {
|
||||||
|
cookie($cookie, null, ['expire' => -1]);
|
||||||
|
}
|
||||||
|
|
||||||
// 4. 返回成功状态,并告诉前端清除 localStorage
|
return json(['code' => 0, 'msg' => '退出成功', 'data' => ['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格式的图片']);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 移动到指定目录
|
// 移动到指定目录
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
<div class="avatar-section">
|
<div class="avatar-section">
|
||||||
<h2 class="section-title">修改头像</h2>
|
<h2 class="section-title">修改头像</h2>
|
||||||
|
|
||||||
<div class="avatar-upload-container">
|
<div class="avatar-upload-container">
|
||||||
<div class="current-avatar">
|
<div class="current-avatar">
|
||||||
<img src="{$user.avatar|default='/static/images/avatar.png'}" alt="当前头像" id="currentAvatar">
|
<img src="{$user.avatar|default='/static/images/avatar.png'}" alt="当前头像" id="currentAvatar">
|
||||||
<p class="avatar-tip">当前头像</p>
|
<p class="avatar-tip">当前头像</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="upload-area" id="uploadArea">
|
<div class="upload-area" id="uploadArea">
|
||||||
<i class="layui-icon layui-icon-upload"></i>
|
<i class="layui-icon layui-icon-upload"></i>
|
||||||
<p>点击或拖拽图片到此处上传</p>
|
<p>点击或拖拽图片到此处上传</p>
|
||||||
@ -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);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
</script>
|
||||||
// 取消上传
|
|
||||||
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>
|
|
||||||
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