Files
jurong_circle_frontdesk/src/views/MyProfile.vue
Sun_sun acfb4c3c8d [修改]
个人中心-自愿委托出售
2025-09-09 10:36:34 +08:00

883 lines
23 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>
<div class="agreement-section">
<el-checkbox
v-model="accountInfo.is_distribute"
@change="handleDistributeChange"
class="distribute-checkbox"
:true-label="true"
:false-label="false"
>
自愿委托出售
</el-checkbox>
<el-link
type="primary"
@click="showAgreement"
>
委托出售协议
</el-link>
</div>
</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:'分销',path:'/distribution'},
// {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 {
// 判断融豆状态为0提示其余正常
if (accountInfo.value.balance==0){
ElMessageBox.alert(
'请获取融豆后,可开通此服务', '',{
confirmButtonText: 'OK',
}
)
accountInfo.value.is_distribute = !value;
return
}
// 检查是否为第一次勾选
const isFirstTimeCheck = localStorage.getItem('hasCheckedDistribute') !== 'true';
// 如果是第一次勾选且为开启状态,强制显示协议
if (value && isFirstTimeCheck) {
try {
await ElMessageBox.alert(
`<div style="text-align: left; line-height: 1.6;">
<h3>委托出售协议</h3>
<p>1. 委托方同意将其持有的融豆委托给平台进行出售。</p>
<p>2. 平台将根据市场情况和委托方的要求进行出售操作。</p>
<p>3. 委托方有权随时撤销委托,但已进行的交易不可撤销。</p>
<p>4. 出售所得资金将按约定时间结算给委托方。</p>
<p>5. 平台收取合理的服务费用,具体标准详见费率说明。</p>
<p>6. 双方应遵守相关法律法规,确保交易合法合规。</p>
</div>`,
'委托出售协议',
{
confirmButtonText: '我已了解并同意',
dangerouslyUseHTMLString: true,
customClass: 'agreement-dialog',
showCancelButton: false,
closeOnClickModal: false,
closeOnPressEscape: false
}
);
// 用户同意协议后,标记为已勾选过
localStorage.setItem('hasCheckedDistribute', 'true');
} catch (error) {
// 用户关闭协议弹窗,恢复原状态
accountInfo.value.is_distribute = false;
return;
}
}
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('默认自动匹配状态更新成功');
// 如果是关闭状态,不需要标记为已勾选过
if (value) {
localStorage.setItem('hasCheckedDistribute', 'true');
}
} 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 {
// 用户取消
}
};
// 显示委托出售协议
const showAgreement = () => {
ElMessageBox.alert(
`<div style="text-align: left; line-height: 1.6;">
<h3>委托出售协议</h3>
<p>1. 委托方同意将其持有的融豆委托给平台进行出售。</p>
<p>2. 平台将根据市场情况和委托方的要求进行出售操作。</p>
<p>3. 委托方有权随时撤销委托,但已进行的交易不可撤销。</p>
<p>4. 出售所得资金将按约定时间结算给委托方。</p>
<p>5. 平台收取合理的服务费用,具体标准详见费率说明。</p>
<p>6. 双方应遵守相关法律法规,确保交易合法合规。</p>
</div>`,
'委托出售协议',
{
confirmButtonText: '我已了解',
dangerouslyUseHTMLString: true,
customClass: 'agreement-dialog'
}
);
};
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,
showAgreement,
userStore,
getImageUrl
};
}
};
</script>
<style lang="scss" 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: -30px;
left: 50%;
transform: translateX(-50%);
border-radius: 12px;
min-width: 85px; /* 设置足够的最小宽度 */
display: flex;
align-items: center;
justify-content: center;
padding: 6px 12px;
white-space: nowrap; /* 防止文字换行 */
}
/* 确保按钮文字始终居中 */
.upload-btn :deep(.el-button__content) {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
text-align: center;
letter-spacing: 0.5px; /* 微调字间距,使文字分布更均匀 */
}
/* 响应式设计 - 小屏幕适配 */
@media (max-width: 375px) {
.upload-btn {
min-width: 80px; /* 确保足够宽度容纳四个字 */
font-size: 12px;
padding: 5px 10px;
}
}
/* 针对更小的屏幕进一步调整 */
@media (max-width: 320px) {
.upload-btn {
min-width: 75px;
font-size: 11px;
padding: 4px 8px;
}
}
.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;
}
.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%;
flex-wrap: nowrap;
}
.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; /* 防止文本换行 */
}
/* 协议区域样式 */
.agreement-section {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 8px;
flex-shrink: 0;
}
/* 委托出售协议链接样式 */
.agreement-section :deep(.el-link) {
color: #ffcc40 !important;
font-weight: 500;
}
.agreement-section :deep(.el-link:hover) {
color: #40ffe6 !important;
}
/* 协议对话框样式 */
:deep(.agreement-dialog) {
.el-message-box__content {
max-height: 400px;
overflow-y: auto;
}
}
</style>