InternalChatMessage — tela de conversa com header azul, área de mensagens
(InternalChatLog) e barra de input. O header expõe ChevronLeft para voltar
à lista e ChevronDown para fechar o sistema inteiro.
Oi Maria! Você pode verificar o pedido #1234?
10:42
MS
Claro, já estou verificando o status dele.
10:43
Obrigado! Cliente está aguardando retorno.
10:44
<!-- InternalChatMessage nunca é usado diretamente em views externas. -->
<InternalChatMessage
:selectedUser="selectedUser"
:upInternalChat="openInternalChat"
@backInternalChat="closeInternalChat()"
@closeListChat="closeListChat"
/>
InternalChatLogItem — alinhamento controlado por chat.type e pela prop type.
Agente renderiza à direita com gradiente azul; cliente à esquerda com fundo branco e borda.
Replies são aninhados via slot padrão.
Agente (direita) — chat.type === 0
Pedido foi confirmado!
09:15
Cliente (esquerda) — chat.type === 1
CL
Preciso verificar o status
09:12
Com reply — slot padrão aninhado
Maria Souza
Preciso verificar o status do pedid...
Sim! Pedido #1234 foi confirmado agora.
09:16
<!-- Bolha do agente (direita) -->
<InternalChatLogItem
:chat="chatMsg"
:index="idx"
type="agent-agent"
/>
<!-- Bolha do cliente (esquerda) -->
<InternalChatLogItem
:chat="chatMsg"
:index="idx"
type="customer-agent"
/>
<!-- Com reply — mensagem citada no slot padrão -->
<InternalChatLogItem
:chat="replyMsg"
:index="idx"
:principalIndex="parentMsgId"
>
<InternalChatLogItem
:chat="quotedMsg"
:index="quotedIdx"
:hiddenReply="true"
/>
</InternalChatLogItem>
Barra sticky no topo do InternalChatLog. Suporta navegação entre múltiplas mensagens fixadas
via ChevronRight. Click navega até a mensagem com scrollIntoView + highlight de 2s.
Texto truncado em 25 caracteres. Botão X desafixa.
Mensagem fixada
Pedido #1234 foi conf...
Pedido #1234 foi confirmado!
08:30
CL
Perfeito, obrigado!
08:32
Mensagem com highlight de 2s após navegação via barra fixada.
<!-- Dentro de InternalChatLog -->
<div
v-if="pinnedMessages.length"
class="pinned-messages-bar"
@click="scrollToPinned"
>
<feather-icon icon="MapPinIcon" class="text-warning" />
<span class="pinned-text">{{ currentPinned.message | truncate(25) }}</span>
<feather-icon
icon="ChevronRightIcon"
class="text-primary"
@click.stop="nextPinned"
/>
<feather-icon
icon="XIcon"
class="text-danger"
@click.stop="unpinMessage(currentPinned.id)"
/>
</div>
Estado de gravação via MediaRecorder API. A barra de input troca para modo de gravação:
XCircleIcon cancela, timer vermelho piscando + contador, CheckCircleIcon envia.
Vou enviar um áudio explicando
14:20
Vermelho = cancelar gravação | Verde = enviar áudio
<!-- Barra de input em modo gravação -->
<div v-if="isRecording" class="audio-recording-bar">
<feather-icon
icon="XCircleIcon"
class="text-danger cursor-pointer"
@click="cancelRecording"
/>
<span class="recording-dot"></span>
<span class="recording-timer">{{ recordingTime }}</span>
<feather-icon
icon="CheckCircleIcon"
class="text-success cursor-pointer"
@click="sendAudio"
/>
</div>
<!-- Barra normal -->
<div v-else class="input-bar">
<feather-icon icon="PaperclipIcon" @click="openAttachment" />
<vs-input v-model="message" placeholder="Digite uma mensagem..." />
<feather-icon icon="MicIcon" @click="startRecording" />
<vs-button @click="sendMessage">
<feather-icon icon="SendIcon" />
</vs-button>
</div>
API
Arquitetura
InternalChatContact
InternalChatMessage
InternalChatLog
InternalChatLogItem
slot: reply (LogItem aninhado)
Sub-componentes
Componente Papel Renderização
InternalChatContactEntry point. Lista de agentes internos. Painel fixo bottom-right. Sempre via v-if="isActive"
InternalChatMessageTela de conversa. Header + InternalChatLog + barra de input. Dentro de InternalChatContact
InternalChatLogÁrea de mensagens scrollável. Barra de pinned. VuePerfectScrollbar. Dentro de InternalChatMessage
InternalChatLogItemBolha individual. Suporta slot para reply aninhado. Loop dentro de InternalChatLog
Props — InternalChatContact
Nome Tipo Default Required Descrição
isActiveBoolean false false Controla visibilidade do painel (v-if)
Eventos — InternalChatContact
Evento Payload Quando
setInternalChatValueBoolean Emitido para fechar o painel. Pai deve setar isActive = false
Props — InternalChatMessage
Nome Tipo Default Required Descrição
upInternalChatBoolean — true Controla visibilidade (v-if)
selectedUserObject|null — true Usuário da conversa: { id, username, image? }
Eventos — InternalChatMessage
Evento Payload Quando
backInternalChatnull Click em ChevronLeft — volta para lista de contatos
closeListChatnull Click em ChevronDown — fecha todo o sistema
Props — InternalChatLog
Nome Tipo Default Required Descrição
upInternalChatBoolean — true Controla carregamento inicial das mensagens
activeChatUserObject|null — true Usuário da conversa: { id }
Props — InternalChatLogItem
Nome Tipo Default Required Descrição
chatObject|null — false Mensagem: { id, message, type, tag_type, created_at, ack, is_pinned, is_temp, document_name }
indexNumber — true Index na lista de mensagens
typeString null false 'agent-agent', 'customer-customer', 'agent-customer', 'customer-agent', 'customer'
hiddenReplyBoolean false false Quando true: oculta botões reply e pin (usado no slot de reply)
principalIndexNumber null false ID da mensagem pai no reply
Slot — InternalChatLogItem
Slot Escopado Descrição
defaultNão Reply aninhado: a mensagem atual envolve o slot que contém a mensagem citada (InternalChatLogItem com :hiddenReply="true")
Lógica de alinhamento
Direção Condição Classe CSS
direita
chat.type === 0 (agente) OU type === 'customer-agent'
flex-row-reverse
esquerda
chat.type === 1 (cliente)
border cinza + bg-white
força esquerda
type === 'customer-customer' OU 'agent-customer'
force-revert-flex
Estilos de bolha
Tipo Classe / Estilo
Agente (normal) bg-primary-gradient text-white
Agente (bot) is-bot-message text-white
Comentário is-comment-message text-white
Cliente border border-solid border-grey-light bg-white
Renderização de mídia
Tipo Renderização
image / sticker <img :src="chat.message"> — max-width 100%
audio <audio controls><source :src></audio>
video <video controls style="width:240px"><source type="video/mp4"></video>
document document_name + DownloadIcon + "Baixar documento". Download via axios blob.
text v-html com stylizeMessage(). white-space: pre-line.
Ícones de ACK (Material Icons)
Valor Ícone Significado
0scheduleEnviando...
1doneEntregue
2done_all (cinza)Lido (cinza)
3done_all (azul)Lido (azul)
-1report_problemErro no envio
Mensagens fixadas
Funcionalidade Detalhe
Quantidade por vez 1 exibida por vez; ChevronRight navega entre múltiplas
Truncamento 25 caracteres
Desafixar Botão X vermelho chama unpinMessage(id)
Navegação Click faz scrollIntoView + highlight da bolha por 2s
Event Bus ($events)
Evento Emitido por Descrição
OpenInternalChatInternalChatContactAbre o chat interno programaticamente de fora do componente
closeInternalChatMessageComponentsInternalChatMessageFecha popup de imagem
Dependências
Tipo Dependência
Vuesax vs-input, vs-avatar, vs-popup (3×), vs-button, vs-dropdown, vs-divider, vs-chip, $vs.loading
Custom Upload (src/components/upload/Upload.vue), VuePerfectScrollbar
Feather Icons ChevronDownIcon, ChevronLeftIcon, PaperclipIcon, XCircleIcon, CheckCircleIcon, DownloadIcon, PinIcon, ChevronRightIcon, XIcon
External moment (formatação de datas), axios (download de documentos)
Vuex Store internalChatMessages — fetch, send, pin, reply, reset
Vuex Store user/fetchInternalPaginated
Event Bus $events
Onde é usado
Arquivo Nota
src/layouts/main/Main.vueLayout principal — monta InternalChatContact globalmente
src/components/button/ButtonFloating.vueBotão flutuante com barra de acesso rápido
src/views/chat-attendance/Index.vueTela de atendimento — integração com chat de agentes