fix support issue 2

This commit is contained in:
2026-08-05 14:01:05 +03:30
parent 4dcc3c7135
commit b759aa9570
6 changed files with 84 additions and 26 deletions
@@ -47,6 +47,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue' import { computed } from 'vue'
import { getNameAvatarInitials } from '@/utils/avatarInitials'
const props = defineProps<{ const props = defineProps<{
title: string title: string
@@ -61,10 +62,7 @@ defineEmits<{
(e: 'toggle-mobile-list'): void (e: 'toggle-mobile-list'): void
}>() }>()
const titleInitial = computed(() => { const titleInitial = computed(() => getNameAvatarInitials(props.title))
const value = String(props.title || '').trim()
return value ? value.charAt(0) : '؟'
})
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
@@ -132,6 +130,9 @@ const titleInitial = computed(() => {
.admin-support-header__avatar-text { .admin-support-header__avatar-text {
line-height: 1; line-height: 1;
font-size: 13px;
font-weight: 800;
letter-spacing: 0.02em;
} }
.admin-support-header__online { .admin-support-header__online {
@@ -75,9 +75,7 @@
:class="message.sender === 'admin' ? 'admin-message-row__inner--out' : ''" :class="message.sender === 'admin' ? 'admin-message-row__inner--out' : ''"
> >
<div v-if="message.sender === 'user'" class="admin-message-avatar"> <div v-if="message.sender === 'user'" class="admin-message-avatar">
<span class="text-xs font-bold flex items-center justify-center"> <span class="admin-message-avatar__text">{{ userAvatarText }}</span>
<BaseIcon icon="vuesax-linear:profile" />
</span>
</div> </div>
<div <div
@@ -231,7 +229,7 @@
<div v-if="supportTyping" class="admin-message-row admin-message-row--in"> <div v-if="supportTyping" class="admin-message-row admin-message-row--in">
<div class="admin-message-row__inner"> <div class="admin-message-row__inner">
<div class="admin-message-avatar"> <div class="admin-message-avatar">
<span class="text-xs font-bold">U</span> <span class="admin-message-avatar__text">{{ userAvatarText }}</span>
</div> </div>
<div class="admin-bubble admin-bubble--in"> <div class="admin-bubble admin-bubble--in">
@@ -252,6 +250,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, nextTick, ref, watch, onBeforeUnmount } from 'vue' import { computed, nextTick, ref, watch, onBeforeUnmount } from 'vue'
import type { SupportMessage, SupportMessageSender } from '@/model/support' import type { SupportMessage, SupportMessageSender } from '@/model/support'
import { getNameAvatarInitials } from '@/utils/avatarInitials'
const props = defineProps<{ const props = defineProps<{
messages: SupportMessage[] messages: SupportMessage[]
@@ -260,6 +259,7 @@ const props = defineProps<{
loadingOlder?: boolean loadingOlder?: boolean
loading?: boolean loading?: boolean
loadingLabel?: string loadingLabel?: string
userName?: string
}>() }>()
const emit = defineEmits<{ const emit = defineEmits<{
@@ -268,6 +268,8 @@ const emit = defineEmits<{
(e: 'bottom-state-change', value: boolean): void (e: 'bottom-state-change', value: boolean): void
}>() }>()
const userAvatarText = computed(() => getNameAvatarInitials(props.userName || ''))
const scrollContainer = ref<HTMLElement | null>(null) const scrollContainer = ref<HTMLElement | null>(null)
const isAtBottom = ref(true) const isAtBottom = ref(true)
@@ -919,8 +921,11 @@ AdminSupport .admin-bubble__image-wrap {
display: flex; display: flex;
justify-content: center; justify-content: center;
pointer-events: none; pointer-events: none;
padding: 4px 0 8px; /* نوار تمام‌عرض مات تا تاریخ بلند زیر «امروز/دیروز» از کنار دیده نشود */
margin-bottom: 4px; width: 100%;
padding: 8px 0;
margin: 0 0 4px;
background: var(--admin-bg-subtle, #fafafa);
} }
.admin-day-chip { .admin-day-chip {
@@ -999,11 +1004,18 @@ AdminSupport .admin-bubble__image-wrap {
justify-content: center; justify-content: center;
border-radius: 999px; border-radius: 999px;
border: 1px solid #e5e7eb; border: 1px solid #e5e7eb;
background: #fff; background: color-mix(in srgb, var(--admin-gold-light, #2563eb) 14%, #fff);
color: #3f3f46; color: var(--admin-gold-light, #2563eb);
box-shadow: 0 4px 12px rgba(15, 23, 42, 0.06); box-shadow: 0 4px 12px rgba(15, 23, 42, 0.06);
} }
.admin-message-avatar__text {
font-size: 12px;
font-weight: 800;
line-height: 1;
letter-spacing: 0.02em;
}
.admin-bubble { .admin-bubble {
min-width: 110px; min-width: 110px;
width: fit-content; width: fit-content;
@@ -1144,26 +1156,35 @@ AdminSupport .admin-bubble__image-wrap {
min-width: 10rem; min-width: 10rem;
width: 100%; width: 100%;
max-width: 100%; max-width: 100%;
margin-bottom: 9px; margin-bottom: 10px;
padding: 8px 10px; padding: 9px 11px;
border-radius: 14px; border-radius: 12px;
overflow: hidden; overflow: hidden;
border: 1px solid transparent;
} }
.admin-bubble--out .admin-message__reply { .admin-bubble--out .admin-message__reply {
background: rgba(255, 255, 255, 0.14); background: rgba(255, 255, 255, 0.22);
border-color: rgba(255, 255, 255, 0.28);
} }
.admin-bubble--in .admin-message__reply { .admin-bubble--in .admin-message__reply {
background: #f4f7ff; background: #f4f4f5;
border-color: #e4e4e7;
} }
.admin-message__reply-line { .admin-message__reply-line {
width: 3px; width: 3px;
flex: 0 0 3px; flex: 0 0 3px;
align-self: stretch;
min-height: 28px;
border-radius: 999px; border-radius: 999px;
background: currentColor; background: var(--colorPrimary, #2563eb);
opacity: 0.75; opacity: 1;
}
.admin-bubble--out .admin-message__reply-line {
background: rgba(255, 255, 255, 0.92);
} }
.admin-message__reply-content { .admin-message__reply-content {
@@ -1189,7 +1210,7 @@ AdminSupport .admin-bubble__image-wrap {
unicode-bidi: plaintext; unicode-bidi: plaintext;
font-size: 12px; font-size: 12px;
line-height: 1.7; line-height: 1.7;
opacity: 0.86; opacity: 1;
} }
.admin-bubble--out .admin-message__reply-title, .admin-bubble--out .admin-message__reply-title,
@@ -1864,6 +1885,10 @@ html.is-dark .admin-messages::-webkit-scrollbar-thumb {
html.is-dark .admin-messages-loading, html.is-dark .admin-messages-loading,
html.is-dark .admin-jump-bottom, html.is-dark .admin-jump-bottom,
html.is-dark .admin-day-sticky {
background: var(--admin-bg-subtle, #18181b);
}
html.is-dark .admin-day-chip { html.is-dark .admin-day-chip {
color: var(--admin-text-muted); color: var(--admin-text-muted);
border-color: var(--admin-border); border-color: var(--admin-border);
@@ -1939,6 +1964,15 @@ html.is-dark .admin-reply-swipe-cue {
html.is-dark .admin-bubble--in .admin-message__reply, html.is-dark .admin-bubble--in .admin-message__reply,
html.is-dark .admin-bubble--out .admin-message__reply { html.is-dark .admin-bubble--out .admin-message__reply {
background: var(--admin-bg-muted) !important; background: var(--admin-bg-muted) !important;
border-color: var(--admin-border) !important;
}
html.is-dark .admin-bubble--in .admin-message__reply-line {
background: var(--admin-gold-light, var(--colorPrimary, #2563eb)) !important;
}
html.is-dark .admin-bubble--out .admin-message__reply-line {
background: rgba(255, 255, 255, 0.85) !important;
} }
html.is-dark .admin-bubble--in .admin-message__reply-title { html.is-dark .admin-bubble--in .admin-message__reply-title {
@@ -140,6 +140,7 @@
import { computed, ref } from 'vue' import { computed, ref } from 'vue'
import type { SupportConversation } from '@/model/support' import type { SupportConversation } from '@/model/support'
import BasicInput from '@/components/BasicInput.vue' import BasicInput from '@/components/BasicInput.vue'
import { getNameAvatarInitials } from '@/utils/avatarInitials'
const props = defineProps<{ const props = defineProps<{
conversations: SupportConversation[] conversations: SupportConversation[]
@@ -180,10 +181,7 @@ const totalUnread = computed(() => {
}, 0) }, 0)
}) })
const getAvatarText = (name: string) => { const getAvatarText = (name: string) => getNameAvatarInitials(name, 'ک')
const value = String(name || 'ک').trim()
return value.slice(0, 1)
}
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
@@ -483,8 +481,10 @@ const getAvatarText = (name: string) => {
} }
.admin-room-card__avatar-text { .admin-room-card__avatar-text {
font-size: 16px; font-size: 14px;
font-weight: 950; font-weight: 950;
line-height: 1;
letter-spacing: 0.02em;
} }
.admin-room-card__online { .admin-room-card__online {
+1
View File
@@ -39,6 +39,7 @@
:loading-older="activeLoadingOlder" :loading-older="activeLoadingOlder"
:loading="activeRoomLoading" :loading="activeRoomLoading"
:loading-label="activeLoadingLabel" :loading-label="activeLoadingLabel"
:user-name="activeConversation.userName"
@load-older="loadOlderMessages" @load-older="loadOlderMessages"
@reply="handleReplyMessage" @reply="handleReplyMessage"
@bottom-state-change="handleBottomStateChange" @bottom-state-change="handleBottomStateChange"
+1 -1
View File
@@ -12,7 +12,7 @@ const support: RouteRecordRaw = {
disabled: false, disabled: false,
hideMenu: false, hideMenu: false,
hideChildren: true, hideChildren: true,
order: 11, order: 7.5,
}, },
children: [ children: [
{ {
+22
View File
@@ -0,0 +1,22 @@
/** دو حرف از اسم و فامیل برای آواتار (اولِ نام + اولِ نام‌خانوادگی) */
export function getNameAvatarInitials(name: string, fallback = '؟'): string {
const parts = String(name || '')
.trim()
.split(/\s+/)
.filter(Boolean)
if (!parts.length) return fallback
const firstChars = (word: string) => Array.from(word)
if (parts.length === 1) {
const chars = firstChars(parts[0])
if (!chars.length) return fallback
return chars.slice(0, 2).join('')
}
const first = firstChars(parts[0])[0] || ''
const last = firstChars(parts[parts.length - 1])[0] || ''
const initials = `${first}${last}`.trim()
return initials || fallback
}