Files
sgxt_web/src/views/backOfficeSystem/luntan copy/components/UserCard.vue
2026-03-29 19:46:50 +08:00

347 lines
7.9 KiB
Vue

<template>
<div class="user-card">
<div class="user-card-head">
<div class="user-avatar">
<div class="avatar-wrapper" @click="showAvatarDialog = true">
<el-avatar :size="56" :src="avatarUrl">
<img src="@/assets/images/mr.png" />
</el-avatar>
<div class="avatar-overlay">
<el-icon class="upload-icon">
<Camera />
</el-icon>
</div>
</div>
</div>
<div class="user-card-head-text">
<div class="name-row">
<span class="nickname">{{ userInfo.nickname || '用户信息' }}</span>
</div>
<div class="sub-stats">内部论坛 · 已登录</div>
</div>
</div>
<div class="user-info">
<div class="info-item clickable" @click="showNicknameDialog = true">
<span class="label">昵称</span>
<span class="value">{{ userInfo.nickname || '-' }}</span>
<el-icon class="edit-icon">
<Edit />
</el-icon>
</div>
<div class="info-item">
<span class="label">账号</span>
<span class="value">{{ userInfo.account || '-' }}</span>
</div>
<div class="info-item">
<span class="label">姓名</span>
<span class="value">{{ userInfo.name || '-' }}</span>
</div>
<div class="info-item">
<span class="label">部门</span>
<span class="value">{{ userInfo.department || '-' }}</span>
</div>
</div>
</div>
<!-- 更换头像对话框 -->
<ChangeAvatar v-model="showAvatarDialog" title="更换头像" @avatarUpdated="handleAvatarUpdated" />
<!-- 编辑昵称对话框 -->
<el-dialog
v-model="showNicknameDialog"
class="luntan-tech-dialog"
title="编辑昵称"
width="400px"
center
:close-on-click-modal="false"
>
<el-form ref="nicknameFormRef" :model="nicknameForm" :rules="nicknameRules" label-width="80px">
<el-form-item label="昵称" prop="nickname">
<el-input v-model="nicknameForm.nickname" placeholder="请输入昵称" maxlength="20" show-word-limit />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="showNicknameDialog = false">取消</el-button>
<el-button type="primary" @click="handleSaveNickname">保存</el-button>
</template>
</el-dialog>
</template>
<script setup>
import { ref, reactive, computed, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { Camera, Edit } from '@element-plus/icons-vue'
import { getItem, setItem, removeItem } from '@/utils/storage.js'
import { setAddress } from '@/utils/tools'
import { tbGsxtXxltTxTxQueryBySfzh, tbGsxtXxltTxTxSave } from '@/api/tbGsxtXxltHf.js'
import ChangeAvatar from './ChangeAvatar.vue'
const showAvatarDialog = ref(false)
const showNicknameDialog = ref(false)
const nicknameFormRef = ref()
const userInfo = ref({
avatar: '',
account: '',
name: '',
department: '',
nickname: ''
})
const nicknameForm = reactive({
nickname: ''
})
const nicknameRules = {
nickname: [
{ required: true, message: '请输入昵称', trigger: 'blur' },
{ min: 2, max: 20, message: '昵称长度在 2 到 20 个字符', trigger: 'blur' }
]
}
const avatarUrl = computed(() => {
return userInfo.value.avatar ? setAddress(userInfo.value.avatar) : ''
})
// 加载用户信息
const loadUserInfo = async () => {
const sfzh = getItem('idEntityCard')
let ltmasg = getItem('ltmasg')
if (!ltmasg) {
try {
const res = await tbGsxtXxltTxTxQueryBySfzh({ sfzh: sfzh })
console.log(res);
const deptId = getItem('deptId')?.[0]
ltmasg = {
...res,
deptName: deptId?.deptName || ''
}
setItem('ltmasg', ltmasg)
} catch (error) {
console.error('加载用户信息失败:', error)
}
}
if (ltmasg) {
userInfo.value = {
avatar: ltmasg.tx || '',
account: ltmasg.sfzh || '',
name: ltmasg.xm || '',
department: ltmasg.deptName || ltmasg.bm || '',
nickname: ltmasg.nc || ''
}
}
}
// 处理头像更新
const handleAvatarUpdated = async (newAvatar) => {
try {
const ltmasg = getItem('ltmasg')
const updateData = {
...ltmasg,
tx: newAvatar
}
await tbGsxtXxltTxTxSave(updateData)
removeItem('ltmasg')
await loadUserInfo()
ElMessage.success('头像更新成功')
} catch (error) {
console.error('更新头像失败:', error)
ElMessage.error('头像更新失败,请重试')
}
}
// 处理保存昵称
const handleSaveNickname = async () => {
if (!nicknameFormRef.value) return
await nicknameFormRef.value.validate(async (valid) => {
if (valid) {
try {
const ltmasg = getItem('ltmasg')
const updateData = {
...ltmasg,
nc: nicknameForm.nickname
}
await tbGsxtXxltTxTxSave(updateData)
removeItem('ltmasg')
await loadUserInfo()
showNicknameDialog.value = false
ElMessage.success('昵称保存成功')
} catch (error) {
console.error('保存昵称失败:', error)
ElMessage.error('昵称保存失败,请重试')
}
}
})
}
// 监听昵称对话框打开,初始化表单
const openNicknameDialog = () => {
nicknameForm.nickname = userInfo.value.nickname
}
// 监听对话框显示状态
const unwatchNickname = () => {
if (showNicknameDialog.value) {
openNicknameDialog()
}
}
onMounted(() => {
loadUserInfo()
})
// 监听昵称对话框
const stopWatch = () => {
if (showNicknameDialog.value) {
nicknameForm.nickname = userInfo.value.nickname
}
}
// 使用 watch 监听对话框状态
import { watch } from 'vue'
watch(showNicknameDialog, (newVal) => {
if (newVal) {
nicknameForm.nickname = userInfo.value.nickname
}
})
</script>
<style scoped lang="scss">
@import '../styles/luntan-tech.scss';
.user-card {
border-radius: 4px;
padding: 18px 16px 16px;
@include lt-panel-frame;
}
.user-card-head {
display: flex;
align-items: flex-start;
gap: 14px;
margin-bottom: 16px;
padding-bottom: 14px;
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
.user-avatar {
flex-shrink: 0;
.avatar-wrapper {
position: relative;
cursor: pointer;
border-radius: 50%;
overflow: hidden;
border: 2px solid rgba(100, 180, 255, 0.35);
&:hover .avatar-overlay {
opacity: 1;
}
.avatar-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.55);
display: flex;
justify-content: center;
align-items: center;
opacity: 0;
transition: opacity 0.3s ease;
border-radius: 50%;
.upload-icon {
font-size: 22px;
color: white;
}
}
}
}
.user-card-head-text {
flex: 1;
min-width: 0;
}
.name-row {
margin-bottom: 6px;
}
.nickname {
font-size: 16px;
font-weight: 600;
color: #f0f6ff;
word-break: break-all;
}
.sub-stats {
font-size: 12px;
color: rgba(180, 200, 230, 0.55);
line-height: 1.4;
}
.user-info {
.info-item {
display: flex;
align-items: flex-start;
margin-bottom: 10px;
font-size: 13px;
position: relative;
&:last-child {
margin-bottom: 0;
}
&.clickable {
cursor: pointer;
padding: 6px 8px;
margin-left: -8px;
margin-right: -8px;
border-radius: 6px;
transition: background-color 0.2s ease;
&:hover {
background-color: rgba(255, 255, 255, 0.06);
.edit-icon {
opacity: 1;
}
}
}
.label {
color: rgba(160, 185, 215, 0.55);
min-width: 40px;
flex-shrink: 0;
}
.value {
color: rgba(220, 230, 245, 0.88);
flex: 1;
word-break: break-all;
}
.edit-icon {
margin-left: 6px;
color: #5eb8ff;
font-size: 14px;
opacity: 0;
transition: opacity 0.2s ease;
}
}
}
</style>
<style lang="scss">
@import '../styles/luntan-dialog-tech.scss';
</style>