feat: chat left menu avatar

This commit is contained in:
teukkk 2025-06-25 16:49:17 +08:00
parent fe8f87834d
commit d252a2546e
4 changed files with 160 additions and 28 deletions

View File

@ -11,6 +11,7 @@ import {
} from '@/request/chat/index'
import { type ChatProfile } from '@/api/type/chat'
import { type Ref } from 'vue'
import type { ResetPasswordRequest } from "@/api/type/user.ts";
import useStore from '@/stores'
import type { LoginRequest } from '@/api/type/user'
@ -201,6 +202,23 @@ const pageChatRecord: (
loading,
)
}
/**
*
*/
const logout: (loading?: Ref<boolean>) => Promise<Result<boolean>> = (loading) => {
return post('/auth/logout', undefined, undefined, loading)
}
/**
*
*/
const resetCurrentPassword: (
request: ResetPasswordRequest,
loading?: Ref<boolean>
) => Promise<Result<boolean>> = (request, loading) => {
return post('/chat_user/current/reset_password', request, undefined, loading)
}
export default {
open,
chat,
@ -221,4 +239,6 @@ export default {
vote,
pageChat,
pageChatRecord,
logout,
resetCurrentPassword
}

View File

@ -52,6 +52,14 @@ import useStore from '@/stores'
import { useRouter } from 'vue-router'
import { t } from '@/locales'
const props = defineProps<{
emitConfirm?: boolean; //
}>()
const emit = defineEmits<{
(e: 'confirm', value: ResetCurrentUserPasswordRequest): void;
}>();
const router = useRouter()
const { login } = useStore()
@ -133,10 +141,14 @@ const open = () => {
}
const resetPassword = () => {
resetPasswordFormRef1.value?.validate().then(() => {
return UserApi.resetCurrentPassword(resetPasswordForm.value).then(() => {
login.logout()
router.push({ name: 'login' })
})
if(props.emitConfirm) {
emit('confirm', resetPasswordForm.value)
} else {
return UserApi.resetCurrentPassword(resetPasswordForm.value).then(() => {
login.logout()
router.push({ name: 'login' })
})
}
})
}
const close = () => {

View File

@ -92,6 +92,14 @@ const useChatUserStore = defineStore('chat-user', {
return this.token
})
},
logout() {
return ChatAPI.logout().then(() => {
sessionStorage.removeItem(`${this.accessToken}-accessToken`)
localStorage.removeItem(`${this.accessToken}-accessToken`)
this.token = undefined
return true
})
},
},
})

View File

@ -20,28 +20,28 @@
:size="32"
style="background: none"
>
<img :src="applicationDetail?.icon" alt="" />
</el-avatar>
<LogoIcon v-else height="28px" style="width: 28px; height: 28px; display: block" />
<img :src="applicationDetail?.icon" alt="" />
</el-avatar>
<LogoIcon v-else height="28px" style="width: 28px; height: 28px; display: block" />
</div>
<h4 v-show="!isPcCollapse">{{ applicationDetail?.name }}</h4>
</div>
<h4 v-show="!isPcCollapse">{{ applicationDetail?.name }}</h4>
</div>
<el-button v-show="!isPcCollapse" class="add-button w-full primary" @click="newChat">
<AppIcon iconName="app-create-chat"></AppIcon>
<span class="ml-4">{{ $t('chat.createChat') }}</span>
</el-button>
<p v-show="!isPcCollapse" class="mt-20 mb-8">{{ $t('chat.history') }}</p>
<el-button v-show="!isPcCollapse" class="add-button w-full primary" @click="newChat">
<AppIcon iconName="app-create-chat"></AppIcon>
<span class="ml-4">{{ $t('chat.createChat') }}</span>
</el-button>
<p v-show="!isPcCollapse" class="mt-20 mb-8">{{ $t('chat.history') }}</p>
</div>
<div v-show="!isPcCollapse" class="left-height pt-0">
<el-scrollbar>
<div class="p-8 pt-0">
<common-list
:style="{
'--el-color-primary': applicationDetail?.custom_theme?.theme_color,
'--el-color-primary-light-9': hexToRgba(
applicationDetail?.custom_theme?.theme_color,
0.1,
),
'--el-color-primary': applicationDetail?.custom_theme?.theme_color,
'--el-color-primary-light-9': hexToRgba(
applicationDetail?.custom_theme?.theme_color,
0.1,
),
}"
:data="chatLogData"
class="mt-8"
@ -135,10 +135,46 @@
<el-text type="info">{{ $t('chat.noHistory') }}</el-text>
</div>
</el-sub-menu>
<el-dropdown trigger="click" type="primary" class="w-full">
<div class="flex align-center user-info">
<el-avatar :size="32">
<img src="@/assets/user-icon.svg" style="width: 54%" alt="" />
</el-avatar>
<!-- TODO -->
<span v-show="!isPcCollapse" class="ml-8 color-text-primary">{{ 222 }}</span>
</div>
<template #dropdown>
<el-dropdown-menu class="avatar-dropdown">
<div class="flex align-center" style="padding: 12px;">
<div class="mr-8 flex align-center">
<el-avatar :size="40">
<img src="@/assets/user-icon.svg" style="width: 54%" alt="" />
</el-avatar>
</div>
<div>
<!-- TODO -->
<h4 class="medium mb-4">{{ 111 }}</h4>
<div class="color-secondary">{{ `${t('common.username')}: 222` }}</div>
</div>
</div>
<el-dropdown-item class="border-t" style="padding-top: 8px; padding-bottom: 8px;" @click="openResetPassword">
<AppIcon iconName="app-export" />
{{ $t('views.login.resetPassword') }}
</el-dropdown-item>
<el-dropdown-item class="border-t" @click="logout">
<AppIcon iconName="app-export" />
{{ $t('layout.logout') }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</el-menu>
<el-button v-if="!common.isMobile()" class="pc-collapse" circle size="small" @click="isPcCollapse = !isPcCollapse">
<el-button v-if="!common.isMobile()" class="pc-collapse" circle size="small"
@click="isPcCollapse = !isPcCollapse">
<el-icon>
<component :is=" isPcCollapse ? 'Fold' : 'Expand'" />
<component :is="isPcCollapse ? 'Fold' : 'Expand'" />
</el-icon>
</el-button>
</div>
@ -200,6 +236,7 @@
</div>
<EditTitleDialog ref="EditTitleDialogRef" @refresh="refreshFieldTitle" />
<ResetPassword ref="resetPasswordRef" emitConfirm @confirm="handleResetPassword"></ResetPassword>
</div>
</template>
@ -213,21 +250,43 @@ import useStore from '@/stores'
import useResize from '@/layout/hooks/useResize'
import { hexToRgba } from '@/utils/theme'
import EditTitleDialog from './EditTitleDialog.vue'
import { useRouter } from 'vue-router'
import ResetPassword from '@/layout/layout-header/avatar/ResetPassword.vue'
import { t } from '@/locales'
import type { ResetCurrentUserPasswordRequest } from '@/api/type/user'
useResize()
const { user, chatLog, common } = useStore()
const { user, chatLog, common, chatUser } = useStore()
const router = useRouter()
const EditTitleDialogRef = ref()
const isCollapse = ref(false)
const isPcCollapse = ref(false)
watch(()=> common.device, () => {
if(common.isMobile()) {
watch(() => common.device, () => {
if (common.isMobile()) {
isPcCollapse.value = false
}
})
const logout = () => {
chatUser.logout().then(() => {
router.push({ name: 'login' })
})
}
const resetPasswordRef = ref<InstanceType<typeof ResetPassword>>()
const openResetPassword = () => {
resetPasswordRef.value?.open()
}
const handleResetPassword = (param: ResetCurrentUserPasswordRequest) => {
chatAPI.resetCurrentPassword(param).then(() => {
logout()
})
}
const customStyle = computed(() => {
return {
background: applicationDetail.value?.custom_theme?.theme_color,
@ -472,6 +531,8 @@ onMounted(() => {
position: relative;
.el-menu {
display: flex;
flex-direction: column;
background:
linear-gradient(187.61deg, rgba(235, 241, 255, 0.5) 39.6%, rgba(231, 249, 255, 0.5) 94.3%),
#eef1f4;
@ -484,12 +545,31 @@ onMounted(() => {
background: transparent;
}
.el-dropdown {
margin-top: auto;
.user-info {
width: 100%;
cursor: pointer;
border-radius: 6px;
padding: 4px 8px;
margin: 16px;
box-sizing: border-box;
&:hover {
background-color: #1F23291A;
}
}
}
&.el-menu--collapse {
.el-menu-item,.el-menu-tooltip__trigger,.el-sub-menu__title {
.el-menu-item,
.el-menu-tooltip__trigger,
.el-sub-menu__title {
padding: 0;
}
.el-menu-item .el-menu-tooltip__trigger,.el-sub-menu__title {
.el-menu-item .el-menu-tooltip__trigger,
.el-sub-menu__title {
position: static;
width: 40px;
height: 40px;
@ -498,9 +578,15 @@ onMounted(() => {
justify-content: center;
margin: 0 auto;
}
.el-menu-item:hover .el-menu-tooltip__trigger,.el-sub-menu__title:hover {
.el-menu-item:hover .el-menu-tooltip__trigger,
.el-sub-menu__title:hover {
background-color: #1F23291A;
}
.user-info {
margin: 16px 8px;
}
}
}
@ -509,7 +595,7 @@ onMounted(() => {
}
.left-height {
height: calc(100vh - 140px);
height: calc(100vh - 212px);
}
.pc-collapse {
@ -633,3 +719,9 @@ onMounted(() => {
}
}
</style>
<style lang="scss" scoped>
.avatar-dropdown {
min-width: 240px;
}
</style>