744 lines
18 KiB
Vue
744 lines
18 KiB
Vue
<template>
|
|
<div class="personal-center">
|
|
<!-- 用户信息区域 -->
|
|
<div class="user-info-section">
|
|
<div class="user-avatar">
|
|
<el-avatar
|
|
:size="76"
|
|
:src="avatarUrl"
|
|
class="avatar-img"
|
|
>
|
|
<el-icon><User /></el-icon>
|
|
</el-avatar>
|
|
<el-button
|
|
type="primary"
|
|
size="small"
|
|
@click="showAvatarUpload = true"
|
|
class="upload-btn"
|
|
>
|
|
更换头像
|
|
</el-button>
|
|
</div>
|
|
<div class="user-actions">
|
|
<template v-if="userStore.isAuthenticated">
|
|
<span class="username">{{ userStore.user?.username || '用户' }}</span>
|
|
<button class="logout-btn" @click="handleLogout">退出登录</button>
|
|
</template>
|
|
<template v-else>
|
|
<div class="auth-buttons">
|
|
<router-link to="/mylogin">
|
|
<button class="login-btn">立即登录</button>
|
|
</router-link>
|
|
<router-link to="/register">
|
|
<button class="register-btn">注册</button>
|
|
</router-link>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 功能入口区域 -->
|
|
<div class="function-section">
|
|
<div class="function-grid">
|
|
<router-link
|
|
v-for="(item, index) in functionItems"
|
|
:key="index"
|
|
:to="item.path"
|
|
class="function-item"
|
|
custom
|
|
v-slot="{ navigate }"
|
|
>
|
|
<div @click="navigate" class="function-item-content">
|
|
<div class="function-icon">
|
|
<img :src="item.image" :alt="item.text" class="function-image">
|
|
</div>
|
|
<div class="function-text">{{ item.text }}</div>
|
|
</div>
|
|
</router-link>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 余额和记录区域 -->
|
|
<div class="balance-section">
|
|
<div class="balance-card">
|
|
<div class="balance-item">
|
|
<span class="balance-label">我的融豆</span>
|
|
<div class="balance-content">
|
|
<img src='/imgs/profile/rongdou.png' alt="融豆" class="bean-image">
|
|
<div class="balance-value">{{ Math.abs(accountInfo.balance) }}</div>
|
|
</div>
|
|
</div>
|
|
<el-checkbox
|
|
v-model="accountInfo.is_distribute"
|
|
@change="handleDistributeChange"
|
|
class="distribute-checkbox"
|
|
:true-label="true"
|
|
:false-label="false"
|
|
>
|
|
默认自动匹配
|
|
</el-checkbox>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 我的订单 -->
|
|
<div class="order-section">
|
|
<div class="section-header">
|
|
<h3>我的订单</h3>
|
|
<router-link to="/orders">
|
|
<span class="view-all">查看全部 ></span>
|
|
</router-link>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 设置选项区域 -->
|
|
<div class="settings-section">
|
|
<div class="setting-item" v-for="(item, index) in settings" :key="index">
|
|
<router-link :to="item.path" class="setting-link"> <!-- 添加 class 便于样式控制 -->
|
|
<div class="setting-content"> <!-- 添加 class 便于样式控制 -->
|
|
<span class="setting-text">{{ item.text }}</span>
|
|
<span class="setting-arrow">></span>
|
|
</div>
|
|
</router-link>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 头像上传对话框 -->
|
|
<el-dialog
|
|
v-model="showAvatarUpload"
|
|
title="更换头像"
|
|
width="400px"
|
|
>
|
|
<el-upload
|
|
class="avatar-uploader"
|
|
action="#"
|
|
:show-file-list="false"
|
|
:before-upload="beforeAvatarUpload"
|
|
:http-request="uploadAvatar"
|
|
>
|
|
<img v-if="newAvatar" :src="newAvatar" class="avatar-preview" />
|
|
<el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
|
|
</el-upload>
|
|
<template #footer>
|
|
<span class="dialog-footer">
|
|
<el-button @click="showAvatarUpload = false">取消</el-button>
|
|
<el-button type="primary" @click="confirmAvatarUpload" :disabled="!newAvatar">
|
|
确定
|
|
</el-button>
|
|
</span>
|
|
</template>
|
|
</el-dialog>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import { ref, onMounted } from 'vue';
|
|
import { useUserStore } from '@/stores/user';
|
|
import { useRouter } from 'vue-router';
|
|
import { ElMessage, ElMessageBox } from 'element-plus';
|
|
import { User, Plus } from '@element-plus/icons-vue';
|
|
import api from '@/utils/api';
|
|
import { getImageUrl } from '@/config';
|
|
|
|
export default {
|
|
setup() {
|
|
const userStore = useUserStore();
|
|
const router = useRouter();
|
|
const avatarUrl = ref('');
|
|
const newAvatar = ref('');
|
|
const uploadedAvatarData = ref(null);
|
|
const showAvatarUpload = ref(false);
|
|
const accountInfo = ref({ balance: '0.00', is_distribute: false });
|
|
const isLoading = ref(false);
|
|
const settings = ref([
|
|
{text:'账号安全',path:'/editpasswordpage'},
|
|
{text:'商户资料',path:'/editdetailspage'},
|
|
{text:'通知设置'},
|
|
{text:'积分获取规则'},
|
|
{text:'隐私协议'},
|
|
]);
|
|
const functionItems = ref([
|
|
{ image: "/imgs/mainpage/jiaoyijilu.png", text: "购物车", path: "/cart" },
|
|
{ image: "/imgs/mainpage/dingdanchaxun.png", text: "地址", path: "/address" },
|
|
{ image: "/imgs/mainpage/kefuzhongxin.png", text: "收藏", path: "" }
|
|
]);
|
|
|
|
// 加载账户信息
|
|
const loadAccountInfo = async () => {
|
|
try {
|
|
console.log(userStore.user,'userStore.user');
|
|
|
|
if (userStore.user?.id) {
|
|
const response = await api.get(`/user/profile`);
|
|
console.log(response.data);
|
|
if (response.data.success) {
|
|
accountInfo.value = {
|
|
...accountInfo.value,
|
|
...response.data.user,
|
|
balance: response.data.user?.balance || '0.00',
|
|
is_distribute: response.data.user?.is_distribute || false
|
|
};
|
|
// 确保加载头像
|
|
if (response.data.user?.avatar) {
|
|
// 使用getImageUrl处理头像路径
|
|
const processedAvatarUrl = getImageUrl(response.data.user.avatar);
|
|
avatarUrl.value = processedAvatarUrl;
|
|
// 更新store中的头像
|
|
if(userStore.user) {
|
|
userStore.setUser({
|
|
...userStore.user,
|
|
avatar: processedAvatarUrl
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('加载账户信息失败:', error);
|
|
}
|
|
};
|
|
|
|
// 处理默认自动匹配状态变化
|
|
const handleDistributeChange = async (value) => {
|
|
try {
|
|
const action = value ? '开启' : '关闭';
|
|
await ElMessageBox.confirm(
|
|
`确定要${action}默认自动匹配功能吗?`,
|
|
'确认操作',
|
|
{
|
|
confirmButtonText: '确定',
|
|
cancelButtonText: '取消',
|
|
type: 'warning'
|
|
}
|
|
);
|
|
|
|
const response = await api.put(`/user/${userStore.user.id}/distribute`, {
|
|
is_distribute: value
|
|
});
|
|
|
|
if (response.data.success) {
|
|
ElMessage.success('默认自动匹配状态更新成功');
|
|
} else {
|
|
// 如果更新失败,恢复原状态
|
|
accountInfo.value.is_distribute = !value;
|
|
ElMessage.error(response.data.message || '默认自动匹配状态更新失败');
|
|
}
|
|
} catch (error) {
|
|
if (error === 'cancel') {
|
|
// 用户取消操作,恢复原状态
|
|
accountInfo.value.is_distribute = !value;
|
|
} else {
|
|
console.error('更新默认自动匹配状态失败:', error);
|
|
// 如果请求失败,恢复原状态
|
|
accountInfo.value.is_distribute = !value;
|
|
ElMessage.error('默认自动匹配状态更新失败');
|
|
}
|
|
}
|
|
};
|
|
|
|
// 头像上传前的验证
|
|
const beforeAvatarUpload = (file) => {
|
|
const isJPG = file.type === 'image/jpeg' || file.type === 'image/png';
|
|
const isLt2M = file.size / 1024 / 1024 < 2;
|
|
|
|
if (!isJPG) {
|
|
ElMessage.error('头像只能是 JPG/PNG 格式!');
|
|
return false;
|
|
}
|
|
if (!isLt2M) {
|
|
ElMessage.error('头像大小不能超过 2MB!');
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
// 上传头像
|
|
const uploadAvatar = async (options) => {
|
|
try {
|
|
const formData = new FormData();
|
|
formData.append('file', options.file);
|
|
|
|
const response = await api.post('/upload', formData, {
|
|
headers: {
|
|
'Content-Type': 'multipart/form-data',
|
|
'Authorization': `Bearer ${userStore.token}`
|
|
}
|
|
});
|
|
|
|
if (response.data.success) {
|
|
// 保存上传响应数据
|
|
uploadedAvatarData.value = response.data.data;
|
|
// 使用url作为显示地址
|
|
newAvatar.value = response.data.data.url;
|
|
ElMessage.success('头像上传成功');
|
|
} else {
|
|
ElMessage.error(response.data.message || '上传失败');
|
|
}
|
|
} catch (error) {
|
|
console.error('头像上传失败:', error);
|
|
ElMessage.error('头像上传失败');
|
|
}
|
|
}
|
|
|
|
// 确认更新头像
|
|
const confirmAvatarUpload = async () => {
|
|
try {
|
|
if (!newAvatar.value || !uploadedAvatarData.value) {
|
|
ElMessage.error('请先选择头像');
|
|
return;
|
|
}
|
|
|
|
// 使用path提交给后端
|
|
const avatarPath = uploadedAvatarData.value.path;
|
|
const response = await api.put('/user/profile', {
|
|
avatar: avatarPath
|
|
});
|
|
|
|
if (response.data.success) {
|
|
// 使用url作为显示地址
|
|
avatarUrl.value = uploadedAvatarData.value.url;
|
|
// 更新用户store中的头像信息
|
|
if(userStore.user) {
|
|
userStore.setUser({
|
|
...userStore.user,
|
|
avatar: uploadedAvatarData.value.url
|
|
});
|
|
}
|
|
showAvatarUpload.value = false;
|
|
newAvatar.value = '';
|
|
uploadedAvatarData.value = null;
|
|
ElMessage.success('头像更新成功');
|
|
} else {
|
|
ElMessage.error(response.data.message || '头像更新失败');
|
|
}
|
|
} catch (error) {
|
|
console.error('头像更新失败:', error);
|
|
if (error.response) {
|
|
ElMessage.error(error.response.data.message || '头像更新失败');
|
|
} else {
|
|
ElMessage.error('头像更新失败');
|
|
}
|
|
}
|
|
}
|
|
|
|
const handleLogout = async () => {
|
|
try {
|
|
await ElMessageBox.confirm('确定要退出登录吗?', '提示', {
|
|
confirmButtonText: '确定',
|
|
cancelButtonText: '取消',
|
|
type: 'warning'
|
|
});
|
|
|
|
userStore.logout();
|
|
router.push('/mylogin');
|
|
ElMessage.success('已退出登录');
|
|
} catch {
|
|
// 用户取消
|
|
}
|
|
};
|
|
|
|
onMounted(() => {
|
|
// 初始化时从store中获取头像
|
|
if (userStore.user?.avatar) {
|
|
avatarUrl.value = userStore.user.avatar;
|
|
}
|
|
loadAccountInfo();
|
|
});
|
|
|
|
return {
|
|
avatarUrl,
|
|
newAvatar,
|
|
showAvatarUpload,
|
|
uploadedAvatarData,
|
|
accountInfo,
|
|
isLoading,
|
|
functionItems,
|
|
settings,
|
|
beforeAvatarUpload,
|
|
uploadAvatar,
|
|
confirmAvatarUpload,
|
|
handleLogout,
|
|
handleDistributeChange,
|
|
userStore,
|
|
getImageUrl
|
|
};
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<style scoped>
|
|
.personal-center {
|
|
max-width: 600px;
|
|
margin: 0 auto;
|
|
padding: 20px;
|
|
font-family: 'PingFang SC', 'Helvetica Neue', Arial, sans-serif;
|
|
background-color: #f8f8f8;
|
|
min-height: 100vh;
|
|
background: linear-gradient(to bottom, #72c9ffae, #f3f3f3);
|
|
}
|
|
|
|
/* 用户信息区域 - 修改部分 */
|
|
.user-info-section {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 20px;
|
|
background-color: transparent;
|
|
border-radius: 12px;
|
|
color: #333;
|
|
margin-bottom: 20px;
|
|
position: relative;
|
|
}
|
|
|
|
.user-avatar {
|
|
position: relative;
|
|
width: 76px;
|
|
height: 76px;
|
|
margin-right: 15px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.avatar-img {
|
|
width: 76px;
|
|
height: 76px;
|
|
border-radius: 50%;
|
|
object-fit: cover;
|
|
border: 3px solid rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.upload-btn {
|
|
position: absolute;
|
|
bottom: -10px;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
font-size: 12px;
|
|
padding: 4px 8px;
|
|
border-radius: 12px;
|
|
}
|
|
|
|
.user-actions {
|
|
display: flex;
|
|
align-items: center;
|
|
flex-grow: 1;
|
|
min-width: 0;
|
|
}
|
|
|
|
.username {
|
|
font-size: 16px;
|
|
font-weight: 500;
|
|
color: #333;
|
|
margin-right: auto;
|
|
padding-left: 0;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
max-width: 60%;
|
|
}
|
|
|
|
.auth-buttons {
|
|
display: flex;
|
|
gap: 10px;
|
|
margin-left: auto;
|
|
}
|
|
|
|
.login-btn, .register-btn {
|
|
padding: 8px 16px;
|
|
border-radius: 20px;
|
|
font-weight: 500;
|
|
font-size: 14px;
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
background-color: transparent;
|
|
border: 1px solid #6a11cb;
|
|
color: #6a11cb;
|
|
}
|
|
|
|
.register-btn {
|
|
border: 1px solid #6a11cb;
|
|
color: #6a11cb;
|
|
}
|
|
|
|
.login-btn:hover, .register-btn:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.logout-btn {
|
|
padding: 8px 16px;
|
|
border-radius: 20px;
|
|
font-weight: 500;
|
|
font-size: 14px;
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
background-color: transparent;
|
|
border: 1px solid #ff4d4f;
|
|
color: #ff4d4f;
|
|
flex-shrink: 0;
|
|
margin-left: 10px;
|
|
}
|
|
|
|
.logout-btn:hover {
|
|
background-color: #fff2f0;
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
/* 功能入口区域 */
|
|
.function-section {
|
|
border-radius: 12px;
|
|
padding: 15px 0;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.function-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(3, 1fr);
|
|
gap: 15px;
|
|
padding: 0 15px;
|
|
}
|
|
|
|
.function-item-content {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 10px 0;
|
|
cursor: pointer;
|
|
transition: all 0.2s ease;
|
|
background-color: white;
|
|
border-radius: 12px;
|
|
height: 80px;
|
|
}
|
|
|
|
.function-item-content:hover {
|
|
transform: scale(1.05);
|
|
}
|
|
|
|
.function-icon {
|
|
width: 40px;
|
|
height: 40px;
|
|
margin-bottom: 8px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.function-image {
|
|
width: 30px;
|
|
height: 30px;
|
|
object-fit: contain;
|
|
}
|
|
|
|
.function-text {
|
|
font-size: 13px;
|
|
color: #333;
|
|
}
|
|
|
|
/* 余额和记录区域 */
|
|
.balance-section {
|
|
background-color: #2f89ff;
|
|
border-radius: 12px;
|
|
padding: 15px;
|
|
margin-bottom: 20px;
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
|
color: white;
|
|
position: relative;
|
|
height: 85px;
|
|
}
|
|
|
|
.balance-card {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
height: 100%;
|
|
}
|
|
|
|
.balance-item {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 8px;
|
|
flex: 1;
|
|
}
|
|
|
|
.balance-label {
|
|
font-size: 14px;
|
|
color: rgba(255, 255, 255, 0.9);
|
|
letter-spacing: 0.5px;
|
|
font-weight: 400;
|
|
}
|
|
|
|
.distribute-checkbox {
|
|
color: white;
|
|
}
|
|
|
|
.distribute-checkbox :deep(.el-checkbox__label) {
|
|
color: rgba(255, 255, 255, 0.9);
|
|
font-size: 12px;
|
|
}
|
|
|
|
.distribute-checkbox :deep(.el-checkbox__input.is-checked .el-checkbox__inner) {
|
|
background-color: rgba(255, 255, 255, 0.9) !important;
|
|
border-color: rgba(255, 255, 255, 0.9) !important;
|
|
}
|
|
|
|
.distribute-checkbox :deep(.el-checkbox__input.is-checked .el-checkbox__inner::after) {
|
|
border-color: #2f89ff !important;
|
|
}
|
|
|
|
.distribute-checkbox :deep(.el-checkbox__inner) {
|
|
border-color: rgba(255, 255, 255, 0.6) !important;
|
|
background-color: transparent !important;
|
|
}
|
|
|
|
.distribute-checkbox :deep(.el-checkbox__input.is-checked) {
|
|
.el-checkbox__inner {
|
|
background-color: rgba(255, 255, 255, 0.9) !important;
|
|
border-color: rgba(255, 255, 255, 0.9) !important;
|
|
}
|
|
.el-checkbox__inner::after {
|
|
border-color: #2f89ff !important;
|
|
}
|
|
}
|
|
|
|
.balance-value {
|
|
font-size: 28px;
|
|
font-weight: 600;
|
|
color: white;
|
|
letter-spacing: 0.5px;
|
|
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
/* 我的订单 */
|
|
.order-section {
|
|
background-color: white;
|
|
border-radius: 12px;
|
|
padding: 15px;
|
|
margin-bottom: 20px;
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
|
}
|
|
|
|
.section-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
.section-header h3 {
|
|
font-size: 16px;
|
|
color: #333;
|
|
margin: 0;
|
|
}
|
|
|
|
.view-all {
|
|
font-size: 13px;
|
|
color: #999;
|
|
cursor: pointer;
|
|
}
|
|
|
|
/* 设置区域 */
|
|
.settings-section {
|
|
background-color: white;
|
|
border-radius: 12px;
|
|
overflow: hidden;
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
|
}
|
|
|
|
.setting-item {
|
|
display: block;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 0px;
|
|
border-bottom: 1px solid #f0f0f0;
|
|
cursor: pointer;
|
|
transition: background-color 0.2s ease;
|
|
}
|
|
|
|
.setting-link {
|
|
display: block; /* 转为块级元素,占满父容器宽度 */
|
|
width: 100%;
|
|
height: 100%;
|
|
padding: 15px; /* 将原有的 padding 移到这里,确保点击区域完整 */
|
|
text-decoration: none; /* 去除链接默认下划线 */
|
|
}
|
|
|
|
.setting-content {
|
|
display: flex; /* 使用 flex 布局,方便内容左右对齐 */
|
|
justify-content: space-between; /* 文字左对齐,箭头右对齐 */
|
|
align-items: center; /* 垂直居中 */
|
|
width: 100%; /* 占满父容器宽度 */
|
|
height: 100%; /* 占满父容器高度 */
|
|
}
|
|
|
|
.setting-item:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.setting-item:hover {
|
|
background-color: #f9f9f9;
|
|
}
|
|
|
|
.setting-text {
|
|
font-size: 15px;
|
|
color: #333;
|
|
}
|
|
|
|
.setting-arrow {
|
|
color: #ccc;
|
|
font-size: 14px;
|
|
}
|
|
|
|
/* 头像上传对话框样式 */
|
|
.avatar-uploader {
|
|
text-align: center;
|
|
}
|
|
|
|
.avatar-uploader :deep(.el-upload) {
|
|
border: 1px dashed #d9d9d9;
|
|
border-radius: 6px;
|
|
cursor: pointer;
|
|
position: relative;
|
|
overflow: hidden;
|
|
transition: 0.2s;
|
|
width: 178px;
|
|
height: 178px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
.avatar-uploader :deep(.el-upload:hover) {
|
|
border-color: #409eff;
|
|
}
|
|
|
|
.avatar-uploader-icon {
|
|
font-size: 28px;
|
|
color: #8c939d;
|
|
}
|
|
|
|
.avatar-preview {
|
|
width: 178px;
|
|
height: 178px;
|
|
object-fit: cover;
|
|
}
|
|
|
|
.balance-content {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px; /* 控制图标和数值之间的间距 */
|
|
}
|
|
|
|
.bean-image {
|
|
width: 20px;
|
|
height: 20px;
|
|
flex-shrink: 0; /* 防止图片被压缩 */
|
|
}
|
|
|
|
.balance-value {
|
|
font-size: 28px;
|
|
font-weight: 600;
|
|
color: white;
|
|
letter-spacing: 0.5px;
|
|
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
white-space: nowrap; /* 防止文本换行 */
|
|
}
|
|
</style> |