2025-04-12 18:50:34 +03:30
|
|
|
|
<template>
|
2025-05-04 01:07:39 +03:30
|
|
|
|
<v-toolbar color="toolbar" title="هوش مصنوعی حسابیکس">
|
2025-07-18 02:30:04 +03:30
|
|
|
|
<v-spacer></v-spacer>
|
2025-07-18 06:29:39 +03:30
|
|
|
|
|
2025-07-18 02:30:04 +03:30
|
|
|
|
<!-- نمایش وضعیت سرویس -->
|
2025-07-18 06:29:39 +03:30
|
|
|
|
<v-tooltip location="bottom">
|
|
|
|
|
<template v-slot:activator="{ props }">
|
|
|
|
|
<div v-bind="props" class="d-flex align-center mr-3">
|
|
|
|
|
<v-chip
|
|
|
|
|
:color="aiSettings.aiEnabled ? 'success' : 'error'"
|
|
|
|
|
size="small"
|
|
|
|
|
variant="flat"
|
|
|
|
|
class="status-chip"
|
|
|
|
|
>
|
|
|
|
|
<v-icon start size="16">
|
|
|
|
|
{{ aiSettings.aiEnabled ? 'mdi-robot' : 'mdi-robot-off' }}
|
|
|
|
|
</v-icon>
|
|
|
|
|
{{ aiSettings.aiEnabled ? 'فعال' : 'غیرفعال' }}
|
|
|
|
|
</v-chip>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
<span>وضعیت سرویس هوش مصنوعی</span>
|
|
|
|
|
</v-tooltip>
|
|
|
|
|
|
|
|
|
|
<!-- نمایش اعتبار کاربر -->
|
|
|
|
|
<v-tooltip location="bottom">
|
|
|
|
|
<template v-slot:activator="{ props }">
|
|
|
|
|
<div v-bind="props" class="d-flex align-center mr-3">
|
|
|
|
|
<v-chip
|
|
|
|
|
:color="userBalance < 1000 ? 'warning' : 'success'"
|
|
|
|
|
size="small"
|
|
|
|
|
variant="flat"
|
|
|
|
|
class="balance-chip"
|
|
|
|
|
>
|
|
|
|
|
<v-icon start size="16">mdi-wallet</v-icon>
|
|
|
|
|
{{ formatBalance(userBalance) }}
|
|
|
|
|
</v-chip>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
<span>اعتبار باقیمانده حساب شما</span>
|
|
|
|
|
</v-tooltip>
|
|
|
|
|
|
2025-07-18 02:30:04 +03:30
|
|
|
|
<!-- نمایش وضعیت اتصال -->
|
2025-07-18 06:29:39 +03:30
|
|
|
|
<v-tooltip location="bottom">
|
|
|
|
|
<template v-slot:activator="{ props }">
|
|
|
|
|
<div v-bind="props" class="d-flex align-center mr-3">
|
|
|
|
|
<v-chip
|
|
|
|
|
:color="connectionStatus.color"
|
|
|
|
|
size="small"
|
|
|
|
|
variant="flat"
|
|
|
|
|
class="connection-chip"
|
|
|
|
|
>
|
|
|
|
|
<v-icon start size="16">
|
|
|
|
|
{{ connectionStatus.icon }}
|
|
|
|
|
</v-icon>
|
|
|
|
|
{{ connectionStatus.text }}
|
|
|
|
|
</v-chip>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
<span>وضعیت اتصال به سرور</span>
|
|
|
|
|
</v-tooltip>
|
|
|
|
|
|
|
|
|
|
<!-- دکمه آرشیو گفتگوها -->
|
|
|
|
|
<v-tooltip location="bottom">
|
|
|
|
|
<template v-slot:activator="{ props }">
|
|
|
|
|
<v-btn
|
|
|
|
|
v-bind="props"
|
|
|
|
|
icon
|
|
|
|
|
@click="showConversationsDialog = true"
|
|
|
|
|
class="mr-2 archive-btn"
|
|
|
|
|
color="primary"
|
|
|
|
|
variant="tonal"
|
|
|
|
|
>
|
|
|
|
|
<v-icon>mdi-archive</v-icon>
|
|
|
|
|
</v-btn>
|
|
|
|
|
</template>
|
|
|
|
|
<span>مشاهده آرشیو گفتگوها</span>
|
|
|
|
|
</v-tooltip>
|
2025-07-18 19:59:35 +03:30
|
|
|
|
|
|
|
|
|
<!-- دکمه راهنمای عملیات -->
|
|
|
|
|
<v-tooltip location="bottom">
|
|
|
|
|
<template v-slot:activator="{ props }">
|
|
|
|
|
<v-btn
|
|
|
|
|
v-bind="props"
|
|
|
|
|
icon
|
|
|
|
|
@click="showOperationsGuide = true"
|
|
|
|
|
class="mr-2 guide-btn"
|
|
|
|
|
color="info"
|
|
|
|
|
variant="tonal"
|
|
|
|
|
>
|
|
|
|
|
<v-icon>mdi-help-circle</v-icon>
|
|
|
|
|
</v-btn>
|
|
|
|
|
</template>
|
|
|
|
|
<span>راهنمای عملیات مدیریت اشخاص</span>
|
|
|
|
|
</v-tooltip>
|
2025-05-04 01:07:39 +03:30
|
|
|
|
</v-toolbar>
|
|
|
|
|
<div class="page-container">
|
2025-07-18 06:29:39 +03:30
|
|
|
|
<!-- کارت اطلاعات قیمتگذاری و آمار استفاده -->
|
|
|
|
|
<div class="info-cards" v-if="aiSettings.aiEnabled">
|
|
|
|
|
<v-card variant="outlined" class="ma-4 info-card" elevation="0">
|
|
|
|
|
<v-card-text class="pa-3">
|
|
|
|
|
<div class="d-flex align-center justify-space-between flex-wrap">
|
|
|
|
|
<!-- بخش قیمتگذاری -->
|
|
|
|
|
<div class="pricing-section d-flex align-center">
|
|
|
|
|
<v-icon color="info" size="20" class="mr-2">mdi-currency-usd</v-icon>
|
|
|
|
|
<div class="d-flex align-center gap-3">
|
|
|
|
|
<div class="text-center">
|
|
|
|
|
<div class="text-caption text-medium-emphasis">ورودی</div>
|
|
|
|
|
<div class="text-body-2 font-weight-medium">{{ aiSettings.inputTokenPrice.toLocaleString('fa-IR') }}</div>
|
|
|
|
|
</div>
|
|
|
|
|
<v-divider vertical></v-divider>
|
|
|
|
|
<div class="text-center">
|
|
|
|
|
<div class="text-caption text-medium-emphasis">خروجی</div>
|
|
|
|
|
<div class="text-body-2 font-weight-medium">{{ aiSettings.outputTokenPrice.toLocaleString('fa-IR') }}</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="text-caption text-medium-emphasis">ریال/1000 توکن</div>
|
2025-07-18 02:30:04 +03:30
|
|
|
|
</div>
|
2025-07-18 06:29:39 +03:30
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- خط جداکننده -->
|
|
|
|
|
<v-divider vertical class="mx-4" v-if="userMessages.length > 0"></v-divider>
|
|
|
|
|
|
|
|
|
|
<!-- بخش آمار استفاده -->
|
|
|
|
|
<div class="stats-section d-flex align-center" v-if="userMessages.length > 0">
|
|
|
|
|
<v-icon color="success" size="20" class="mr-2">mdi-chart-line</v-icon>
|
|
|
|
|
<div class="d-flex align-center gap-3">
|
|
|
|
|
<div class="text-center">
|
|
|
|
|
<div class="text-caption text-medium-emphasis">پیامها</div>
|
|
|
|
|
<div class="text-body-2 font-weight-medium">{{ userMessages.length }}</div>
|
|
|
|
|
</div>
|
|
|
|
|
<v-divider vertical></v-divider>
|
|
|
|
|
<div class="text-center">
|
|
|
|
|
<div class="text-caption text-medium-emphasis">توکنها</div>
|
|
|
|
|
<div class="text-body-2 font-weight-medium">{{ usageStats.totalTokens.toLocaleString('fa-IR') }}</div>
|
|
|
|
|
</div>
|
|
|
|
|
<v-divider vertical></v-divider>
|
|
|
|
|
<div class="text-center">
|
|
|
|
|
<div class="text-caption text-medium-emphasis">هزینه</div>
|
|
|
|
|
<div class="text-body-2 font-weight-medium">{{ usageStats.totalCost.toLocaleString('fa-IR') }} ریال</div>
|
|
|
|
|
</div>
|
2025-07-18 02:30:04 +03:30
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</v-card-text>
|
|
|
|
|
</v-card>
|
|
|
|
|
</div>
|
|
|
|
|
|
2025-07-18 06:29:39 +03:30
|
|
|
|
<div class="content-container">
|
|
|
|
|
<v-card class="chat-container" elevation="0">
|
|
|
|
|
<!-- هدر گفتگو -->
|
|
|
|
|
<div class="chat-header" v-if="currentConversation">
|
|
|
|
|
<div class="d-flex align-center justify-space-between pa-4">
|
2025-07-18 02:30:04 +03:30
|
|
|
|
<div class="d-flex align-center">
|
2025-07-18 06:29:39 +03:30
|
|
|
|
<v-icon color="primary" class="mr-2">mdi-chat</v-icon>
|
|
|
|
|
<span class="text-subtitle-1 font-weight-medium">{{ currentConversation.title }}</span>
|
|
|
|
|
<v-chip size="small" variant="outlined" class="ml-2">{{ currentConversation.category }}</v-chip>
|
2025-07-18 02:30:04 +03:30
|
|
|
|
</div>
|
2025-07-18 06:29:39 +03:30
|
|
|
|
<div class="d-flex align-center">
|
|
|
|
|
<v-btn
|
|
|
|
|
size="small"
|
|
|
|
|
variant="text"
|
|
|
|
|
color="grey"
|
|
|
|
|
@click="newConversation"
|
|
|
|
|
>
|
|
|
|
|
<v-icon start size="16">mdi-plus</v-icon>
|
|
|
|
|
گفتگوی جدید
|
|
|
|
|
</v-btn>
|
2025-07-18 02:30:04 +03:30
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2025-07-18 06:29:39 +03:30
|
|
|
|
</div>
|
|
|
|
|
|
2025-05-04 01:07:39 +03:30
|
|
|
|
<div class="chat-box">
|
|
|
|
|
<div class="messages-container" ref="messagesContainer">
|
|
|
|
|
<!-- پیام هوش مصنوعی -->
|
|
|
|
|
<div class="message ai-message" v-if="displayWelcome">
|
|
|
|
|
<div class="message-content">
|
2025-07-18 06:29:39 +03:30
|
|
|
|
<div class="message-text typing-text" v-html="renderMarkdown(displayWelcome)"></div>
|
2025-05-04 01:07:39 +03:30
|
|
|
|
</div>
|
2025-07-18 06:29:39 +03:30
|
|
|
|
<v-avatar color="#1a237e" size="36" class="ml-2">
|
|
|
|
|
<v-icon color="white" size="20">mdi-robot</v-icon>
|
|
|
|
|
</v-avatar>
|
2025-05-04 01:07:39 +03:30
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- پیامهای کاربر و پاسخهای هوش مصنوعی -->
|
|
|
|
|
<template v-for="(message, index) in userMessages" :key="index">
|
|
|
|
|
<!-- پیام کاربر -->
|
|
|
|
|
<div class="message user-message" v-if="typeof message === 'string'">
|
2025-07-18 06:29:39 +03:30
|
|
|
|
<v-avatar color="grey lighten-2" size="36" class="mr-2">
|
|
|
|
|
<v-icon color="grey darken-1" size="20">mdi-account</v-icon>
|
|
|
|
|
</v-avatar>
|
2025-05-04 01:07:39 +03:30
|
|
|
|
<div class="message-content">
|
|
|
|
|
<div class="message-text">{{ message }}</div>
|
2025-07-18 02:30:04 +03:30
|
|
|
|
<div class="message-time">{{ formatTime(new Date()) }}</div>
|
2025-05-04 01:07:39 +03:30
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- پیام هوش مصنوعی -->
|
|
|
|
|
<div class="message ai-message" v-else-if="message && message.isAI">
|
2025-07-18 06:29:39 +03:30
|
|
|
|
<div class="message-content" :class="{
|
|
|
|
|
'details-message': message.isDetails,
|
|
|
|
|
'error-message': message.text && message.text.startsWith('خطا:')
|
|
|
|
|
}">
|
|
|
|
|
<div class="message-text" v-html="renderMarkdown(message.text)"></div>
|
|
|
|
|
|
|
|
|
|
<!-- دکمه شارژ برای پیامهای خطای اعتبار -->
|
|
|
|
|
<div v-if="message.showChargeButton" class="charge-button-container mt-3">
|
|
|
|
|
<v-btn
|
|
|
|
|
color="success"
|
|
|
|
|
variant="elevated"
|
|
|
|
|
size="small"
|
|
|
|
|
@click="goToChargePage"
|
|
|
|
|
prepend-icon="mdi-credit-card"
|
|
|
|
|
class="charge-btn"
|
|
|
|
|
>
|
|
|
|
|
شارژ حساب
|
|
|
|
|
</v-btn>
|
|
|
|
|
</div>
|
|
|
|
|
|
2025-07-18 02:30:04 +03:30
|
|
|
|
<div class="message-time">{{ formatTime(new Date()) }}</div>
|
2025-05-04 01:07:39 +03:30
|
|
|
|
</div>
|
2025-07-18 06:29:39 +03:30
|
|
|
|
<v-avatar :color="message.text && message.text.startsWith('خطا:') ? '#f44336' : '#1a237e'" size="36" class="ml-2">
|
|
|
|
|
<v-icon color="white" size="20">{{ message.text && message.text.startsWith('خطا:') ? 'mdi-alert-circle' : 'mdi-robot' }}</v-icon>
|
|
|
|
|
</v-avatar>
|
2025-05-04 01:07:39 +03:30
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<!-- نشانگر تایپ -->
|
|
|
|
|
<div class="message ai-message" v-if="isTyping">
|
|
|
|
|
<div class="message-content">
|
|
|
|
|
<div class="message-text">
|
|
|
|
|
<span class="typing-indicator">
|
|
|
|
|
<span></span>
|
|
|
|
|
<span></span>
|
|
|
|
|
<span></span>
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2025-07-18 06:29:39 +03:30
|
|
|
|
<v-avatar color="#1a237e" size="36" class="ml-2">
|
|
|
|
|
<v-icon color="white" size="20">mdi-robot</v-icon>
|
|
|
|
|
</v-avatar>
|
2025-05-04 01:07:39 +03:30
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
2025-07-18 02:30:04 +03:30
|
|
|
|
<!-- پیشنهادات سوالات -->
|
2025-07-18 07:15:33 +03:30
|
|
|
|
<div class="suggestions-container" v-if="userMessages.length === 0 && aiSettings.aiEnabled">
|
2025-07-18 06:29:39 +03:30
|
|
|
|
<div class="d-flex flex-wrap gap-1">
|
2025-07-18 02:30:04 +03:30
|
|
|
|
<v-chip
|
|
|
|
|
v-for="suggestion in suggestions"
|
|
|
|
|
:key="suggestion"
|
2025-07-18 06:29:39 +03:30
|
|
|
|
size="x-small"
|
|
|
|
|
variant="tonal"
|
2025-07-18 02:30:04 +03:30
|
|
|
|
@click="useSuggestion(suggestion)"
|
|
|
|
|
class="suggestion-chip"
|
2025-07-18 06:29:39 +03:30
|
|
|
|
color="primary"
|
2025-07-18 02:30:04 +03:30
|
|
|
|
>
|
|
|
|
|
{{ suggestion }}
|
|
|
|
|
</v-chip>
|
|
|
|
|
</div>
|
2025-05-04 01:07:39 +03:30
|
|
|
|
</div>
|
2025-07-18 06:29:39 +03:30
|
|
|
|
|
|
|
|
|
<!-- بخش ورودی پیام -->
|
|
|
|
|
<div class="input-container d-flex align-center flex-row-reverse">
|
|
|
|
|
<v-btn
|
|
|
|
|
color="primary"
|
|
|
|
|
class="send-button ml-2"
|
2025-07-18 07:15:33 +03:30
|
|
|
|
:disabled="isLoading || !userMessage.trim() || !aiSettings.aiEnabled"
|
2025-07-18 06:29:39 +03:30
|
|
|
|
@click="sendMessage"
|
|
|
|
|
icon
|
|
|
|
|
>
|
|
|
|
|
<v-icon class="send-icon-rotate">mdi-send</v-icon>
|
|
|
|
|
</v-btn>
|
|
|
|
|
<v-text-field
|
|
|
|
|
v-model="userMessage"
|
|
|
|
|
class="flex-grow-1 message-input"
|
2025-07-18 07:15:33 +03:30
|
|
|
|
:placeholder="aiSettings.aiEnabled ? 'پیام خود را بنویسید...' : 'سرویس هوش مصنوعی غیرفعال است'"
|
2025-07-18 06:29:39 +03:30
|
|
|
|
hide-details="auto"
|
|
|
|
|
variant="outlined"
|
|
|
|
|
@keyup.enter="sendMessage"
|
2025-07-18 07:15:33 +03:30
|
|
|
|
:disabled="isLoading || !aiSettings.aiEnabled"
|
2025-07-18 06:29:39 +03:30
|
|
|
|
></v-text-field>
|
|
|
|
|
</div>
|
2025-05-04 01:07:39 +03:30
|
|
|
|
</div>
|
2025-04-12 18:50:34 +03:30
|
|
|
|
</v-card>
|
2025-05-04 01:07:39 +03:30
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2025-07-18 06:29:39 +03:30
|
|
|
|
|
|
|
|
|
<!-- دیالوگ آرشیو گفتگوها -->
|
|
|
|
|
<v-dialog v-model="showConversationsDialog" max-width="800px">
|
|
|
|
|
<v-card>
|
|
|
|
|
<v-card-title class="d-flex align-center justify-space-between">
|
|
|
|
|
<span>آرشیو گفتگوها</span>
|
|
|
|
|
<v-btn icon @click="showConversationsDialog = false">
|
|
|
|
|
<v-icon>mdi-close</v-icon>
|
|
|
|
|
</v-btn>
|
|
|
|
|
</v-card-title>
|
|
|
|
|
|
|
|
|
|
<v-card-text>
|
|
|
|
|
<!-- فیلترها -->
|
|
|
|
|
<div class="d-flex align-center gap-4 mb-4">
|
|
|
|
|
<v-text-field
|
|
|
|
|
v-model="searchTerm"
|
|
|
|
|
placeholder="جستجو در گفتگوها..."
|
|
|
|
|
prepend-inner-icon="mdi-magnify"
|
|
|
|
|
variant="outlined"
|
|
|
|
|
density="compact"
|
|
|
|
|
hide-details
|
|
|
|
|
class="flex-grow-1"
|
|
|
|
|
@input="searchConversations"
|
|
|
|
|
></v-text-field>
|
|
|
|
|
<v-select
|
|
|
|
|
v-model="selectedCategory"
|
|
|
|
|
:items="categories"
|
|
|
|
|
placeholder="دستهبندی"
|
|
|
|
|
variant="outlined"
|
|
|
|
|
density="compact"
|
|
|
|
|
hide-details
|
|
|
|
|
class="flex-grow-1"
|
|
|
|
|
@update:model-value="filterByCategory"
|
|
|
|
|
></v-select>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- لیست گفتگوها -->
|
|
|
|
|
<div class="conversations-list">
|
|
|
|
|
<v-list>
|
|
|
|
|
<v-list-item
|
|
|
|
|
v-for="conversation in filteredConversations"
|
|
|
|
|
:key="conversation.id"
|
|
|
|
|
class="conversation-item"
|
|
|
|
|
>
|
|
|
|
|
<template #prepend>
|
|
|
|
|
<v-avatar color="primary" size="40" @click="loadConversation(conversation)" style="cursor: pointer;">
|
|
|
|
|
<v-icon color="white">mdi-chat</v-icon>
|
|
|
|
|
</v-avatar>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<v-list-item-title @click="loadConversation(conversation)" style="cursor: pointer;">{{ conversation.title }}</v-list-item-title>
|
|
|
|
|
<v-list-item-subtitle>
|
|
|
|
|
<div class="d-flex align-center justify-space-between">
|
|
|
|
|
<span>{{ conversation.category }}</span>
|
|
|
|
|
<span>{{ conversation.updatedAt }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="text-caption text-medium-emphasis">
|
|
|
|
|
{{ conversation.messageCount || 0 }} پیام • {{ (conversation.stats?.totalTokens || 0).toLocaleString('fa-IR') }} توکن • {{ (conversation.stats?.totalCost || 0).toLocaleString('fa-IR') }} ریال
|
|
|
|
|
</div>
|
|
|
|
|
</v-list-item-subtitle>
|
|
|
|
|
|
|
|
|
|
<template #append>
|
|
|
|
|
<v-btn
|
|
|
|
|
variant="text"
|
|
|
|
|
size="small"
|
|
|
|
|
color="error"
|
|
|
|
|
@click.stop="deleteConversation(conversation)"
|
|
|
|
|
class="delete-btn"
|
|
|
|
|
icon
|
|
|
|
|
>
|
|
|
|
|
<v-icon size="18">mdi-delete</v-icon>
|
|
|
|
|
<v-tooltip activator="parent" location="top">حذف گفتگو</v-tooltip>
|
|
|
|
|
</v-btn>
|
|
|
|
|
</template>
|
|
|
|
|
</v-list-item>
|
|
|
|
|
</v-list>
|
|
|
|
|
</div>
|
|
|
|
|
</v-card-text>
|
|
|
|
|
|
|
|
|
|
<v-card-actions>
|
|
|
|
|
<v-spacer></v-spacer>
|
|
|
|
|
<v-btn
|
|
|
|
|
color="primary"
|
|
|
|
|
@click="newConversation"
|
|
|
|
|
prepend-icon="mdi-plus"
|
|
|
|
|
>
|
|
|
|
|
گفتگوی جدید
|
|
|
|
|
</v-btn>
|
|
|
|
|
</v-card-actions>
|
|
|
|
|
</v-card>
|
|
|
|
|
</v-dialog>
|
|
|
|
|
|
|
|
|
|
<!-- دیالوگ تأیید حذف گفتگو -->
|
|
|
|
|
<v-dialog v-model="showDeleteDialog" max-width="400px">
|
|
|
|
|
<v-card>
|
|
|
|
|
<v-card-title class="d-flex align-center">
|
|
|
|
|
<v-icon color="error" class="mr-2">mdi-delete</v-icon>
|
|
|
|
|
حذف گفتگو
|
|
|
|
|
</v-card-title>
|
|
|
|
|
|
|
|
|
|
<v-card-text>
|
|
|
|
|
<p class="text-body-1">
|
|
|
|
|
آیا مطمئن هستید که میخواهید گفتگوی
|
|
|
|
|
<strong>"{{ conversationToDelete?.title }}"</strong>
|
|
|
|
|
را حذف کنید؟
|
|
|
|
|
</p>
|
|
|
|
|
<p class="text-caption text-medium-emphasis mt-2">
|
|
|
|
|
این عملیات قابل بازگشت نیست و گفتگو از لیست شما حذف خواهد شد.
|
|
|
|
|
</p>
|
|
|
|
|
</v-card-text>
|
|
|
|
|
|
|
|
|
|
<v-card-actions>
|
|
|
|
|
<v-spacer></v-spacer>
|
|
|
|
|
<v-btn
|
|
|
|
|
variant="text"
|
|
|
|
|
@click="showDeleteDialog = false"
|
|
|
|
|
color="grey"
|
|
|
|
|
>
|
|
|
|
|
انصراف
|
|
|
|
|
</v-btn>
|
|
|
|
|
<v-btn
|
|
|
|
|
color="error"
|
|
|
|
|
@click="confirmDeleteConversation"
|
|
|
|
|
:loading="isLoading"
|
|
|
|
|
prepend-icon="mdi-delete"
|
|
|
|
|
>
|
|
|
|
|
حذف
|
|
|
|
|
</v-btn>
|
|
|
|
|
</v-card-actions>
|
|
|
|
|
</v-card>
|
|
|
|
|
</v-dialog>
|
2025-07-18 19:59:35 +03:30
|
|
|
|
|
|
|
|
|
<!-- دیالوگ نمایش اطلاعات اشخاص -->
|
|
|
|
|
<v-dialog v-model="showPersonInfo" max-width="900px">
|
|
|
|
|
<v-card>
|
|
|
|
|
<v-card-title class="d-flex align-center justify-space-between">
|
|
|
|
|
<span>اطلاعات شخص</span>
|
|
|
|
|
<v-btn icon @click="showPersonInfo = false">
|
|
|
|
|
<v-icon>mdi-close</v-icon>
|
|
|
|
|
</v-btn>
|
|
|
|
|
</v-card-title>
|
|
|
|
|
|
|
|
|
|
<v-card-text>
|
|
|
|
|
<PersonInfo
|
|
|
|
|
:person="selectedPerson"
|
|
|
|
|
:transactions="personTransactions"
|
|
|
|
|
/>
|
|
|
|
|
</v-card-text>
|
|
|
|
|
</v-card>
|
|
|
|
|
</v-dialog>
|
|
|
|
|
|
|
|
|
|
<!-- دیالوگ راهنمای عملیات -->
|
|
|
|
|
<v-dialog v-model="showOperationsGuide" max-width="800px">
|
|
|
|
|
<v-card>
|
|
|
|
|
<v-card-title class="d-flex align-center justify-space-between">
|
|
|
|
|
<span>راهنمای عملیات مدیریت اشخاص</span>
|
|
|
|
|
<v-btn icon @click="showOperationsGuide = false">
|
|
|
|
|
<v-icon>mdi-close</v-icon>
|
|
|
|
|
</v-btn>
|
|
|
|
|
</v-card-title>
|
|
|
|
|
|
|
|
|
|
<v-card-text>
|
|
|
|
|
<div v-if="operationsGuide" v-html="renderMarkdown(operationsGuide)"></div>
|
|
|
|
|
<div v-else class="text-center pa-4">
|
|
|
|
|
<v-progress-circular indeterminate color="primary"></v-progress-circular>
|
|
|
|
|
</div>
|
|
|
|
|
</v-card-text>
|
|
|
|
|
|
|
|
|
|
<v-card-actions>
|
|
|
|
|
<v-spacer></v-spacer>
|
|
|
|
|
<v-btn color="primary" @click="showOperationsGuide = false">
|
|
|
|
|
بستن
|
|
|
|
|
</v-btn>
|
|
|
|
|
</v-card-actions>
|
|
|
|
|
</v-card>
|
|
|
|
|
</v-dialog>
|
2025-04-12 18:50:34 +03:30
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script>
|
2025-07-18 06:29:39 +03:30
|
|
|
|
import { marked } from 'marked';
|
|
|
|
|
import DOMPurify from 'dompurify';
|
|
|
|
|
import axios from 'axios';
|
2025-07-18 19:59:35 +03:30
|
|
|
|
import PersonInfo from '@/components/PersonInfo.vue';
|
2025-07-18 02:30:04 +03:30
|
|
|
|
|
2025-04-12 18:50:34 +03:30
|
|
|
|
export default {
|
|
|
|
|
name: 'WizardHome',
|
2025-07-18 19:59:35 +03:30
|
|
|
|
components: {
|
|
|
|
|
PersonInfo
|
|
|
|
|
},
|
2025-04-12 18:50:34 +03:30
|
|
|
|
data() {
|
|
|
|
|
return {
|
2025-05-04 01:07:39 +03:30
|
|
|
|
userMessage: '',
|
|
|
|
|
userMessages: [],
|
2025-07-18 06:29:39 +03:30
|
|
|
|
isLoading: false,
|
|
|
|
|
isTyping: false,
|
2025-07-18 02:30:04 +03:30
|
|
|
|
aiSettings: {
|
|
|
|
|
aiEnabled: false,
|
2025-07-18 06:29:39 +03:30
|
|
|
|
aiAgentSource: '',
|
|
|
|
|
aiModel: '',
|
2025-07-18 02:30:04 +03:30
|
|
|
|
inputTokenPrice: 0,
|
|
|
|
|
outputTokenPrice: 0,
|
|
|
|
|
aiPrompt: ''
|
|
|
|
|
},
|
2025-07-18 06:29:39 +03:30
|
|
|
|
connectionStatus: {
|
|
|
|
|
color: 'grey',
|
|
|
|
|
icon: 'mdi-help-circle',
|
|
|
|
|
text: 'در حال بررسی...'
|
|
|
|
|
},
|
2025-05-04 01:07:39 +03:30
|
|
|
|
displayWelcome: '',
|
2025-07-18 02:30:04 +03:30
|
|
|
|
suggestions: [
|
2025-07-18 06:29:39 +03:30
|
|
|
|
'چگونه سند حسابداری ثبت کنم؟',
|
|
|
|
|
'راهنمای انبارداری',
|
|
|
|
|
'ثبت خرید و فروش',
|
|
|
|
|
'گزارشهای مالی',
|
2025-07-18 19:59:35 +03:30
|
|
|
|
'تنظیمات کاربران',
|
|
|
|
|
'اطلاعات اشخاص',
|
|
|
|
|
'جستجو در اشخاص',
|
|
|
|
|
'موجودی اشخاص',
|
|
|
|
|
'شخص علی اضافه کن',
|
|
|
|
|
'تلفن 09123456789 را برای محسن اضافه کن',
|
|
|
|
|
'شخص احمد را حذف کن'
|
2025-07-18 02:30:04 +03:30
|
|
|
|
],
|
2025-07-18 06:29:39 +03:30
|
|
|
|
// متغیرهای جدید برای گفتگوها
|
|
|
|
|
currentConversation: null,
|
|
|
|
|
showConversationsDialog: false,
|
|
|
|
|
conversations: [],
|
|
|
|
|
filteredConversations: [],
|
|
|
|
|
searchTerm: '',
|
|
|
|
|
selectedCategory: '',
|
|
|
|
|
categories: [],
|
|
|
|
|
// متغیر اعتبار کاربر
|
|
|
|
|
userBalance: 0,
|
|
|
|
|
// متغیرهای دیالوگ حذف
|
|
|
|
|
showDeleteDialog: false,
|
2025-07-18 19:59:35 +03:30
|
|
|
|
conversationToDelete: null,
|
|
|
|
|
// متغیرهای اطلاعات اشخاص
|
|
|
|
|
selectedPerson: null,
|
|
|
|
|
personTransactions: [],
|
|
|
|
|
showPersonInfo: false,
|
|
|
|
|
// متغیرهای راهنمای عملیات
|
|
|
|
|
showOperationsGuide: false,
|
|
|
|
|
operationsGuide: null
|
2025-07-18 06:29:39 +03:30
|
|
|
|
};
|
2025-07-18 02:30:04 +03:30
|
|
|
|
},
|
|
|
|
|
computed: {
|
|
|
|
|
aiMessageCount() {
|
2025-07-18 06:29:39 +03:30
|
|
|
|
return this.userMessages.filter(msg => msg && msg.isAI).length;
|
2025-07-18 02:30:04 +03:30
|
|
|
|
},
|
|
|
|
|
userMessageCount() {
|
2025-07-18 06:29:39 +03:30
|
|
|
|
return this.userMessages.filter(msg => typeof msg === 'string').length;
|
|
|
|
|
},
|
|
|
|
|
usageStats() {
|
|
|
|
|
let totalTokens = 0;
|
|
|
|
|
let totalCost = 0;
|
|
|
|
|
|
|
|
|
|
this.userMessages.forEach(msg => {
|
|
|
|
|
if (msg && msg.isAI && msg.usage) {
|
|
|
|
|
totalTokens += (msg.usage.prompt_tokens || 0) + (msg.usage.completion_tokens || 0);
|
|
|
|
|
totalCost += msg.cost?.total_cost || 0;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
totalTokens,
|
|
|
|
|
totalCost
|
|
|
|
|
};
|
2025-05-04 01:07:39 +03:30
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
watch: {
|
2025-07-18 06:29:39 +03:30
|
|
|
|
searchTerm() {
|
|
|
|
|
this.searchConversations();
|
2025-05-04 01:07:39 +03:30
|
|
|
|
},
|
2025-07-18 06:29:39 +03:30
|
|
|
|
selectedCategory() {
|
|
|
|
|
this.filterByCategory();
|
2025-04-12 18:50:34 +03:30
|
|
|
|
}
|
|
|
|
|
},
|
2025-07-18 06:29:39 +03:30
|
|
|
|
async mounted() {
|
|
|
|
|
await this.loadSettings();
|
|
|
|
|
await this.checkConnection();
|
|
|
|
|
await this.loadConversations();
|
|
|
|
|
await this.loadBalance();
|
2025-07-18 19:59:35 +03:30
|
|
|
|
await this.loadOperationsGuide(); // Add this line
|
2025-07-18 06:29:39 +03:30
|
|
|
|
this.setWelcomeMessage();
|
|
|
|
|
},
|
2025-04-12 18:50:34 +03:30
|
|
|
|
methods: {
|
2025-07-18 06:29:39 +03:30
|
|
|
|
async loadSettings() {
|
2025-07-18 02:30:04 +03:30
|
|
|
|
try {
|
2025-07-18 06:29:39 +03:30
|
|
|
|
const response = await axios.get('/api/wizard/settings');
|
2025-07-18 02:30:04 +03:30
|
|
|
|
if (response.data.success) {
|
2025-07-18 06:29:39 +03:30
|
|
|
|
this.aiSettings = response.data.settings;
|
2025-07-18 02:30:04 +03:30
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
2025-07-18 06:29:39 +03:30
|
|
|
|
console.error('خطا در بارگذاری تنظیمات:', error);
|
2025-07-18 02:30:04 +03:30
|
|
|
|
}
|
|
|
|
|
},
|
2025-07-18 06:29:39 +03:30
|
|
|
|
|
|
|
|
|
async checkConnection() {
|
2025-07-18 02:30:04 +03:30
|
|
|
|
try {
|
2025-07-18 06:29:39 +03:30
|
|
|
|
const response = await axios.get('/api/wizard/status');
|
2025-07-18 02:30:04 +03:30
|
|
|
|
if (response.data.success) {
|
2025-07-18 06:29:39 +03:30
|
|
|
|
const status = response.data.status;
|
2025-07-18 02:30:04 +03:30
|
|
|
|
|
2025-07-18 06:29:39 +03:30
|
|
|
|
if (status === 'available') {
|
|
|
|
|
this.connectionStatus = {
|
|
|
|
|
color: 'success',
|
|
|
|
|
icon: 'mdi-check-circle',
|
|
|
|
|
text: 'متصل'
|
|
|
|
|
};
|
|
|
|
|
} else if (status === 'disabled') {
|
|
|
|
|
this.connectionStatus = {
|
|
|
|
|
color: 'error',
|
|
|
|
|
icon: 'mdi-robot-off',
|
|
|
|
|
text: 'غیرفعال'
|
|
|
|
|
};
|
|
|
|
|
} else if (status === 'no_api_key') {
|
|
|
|
|
this.connectionStatus = {
|
|
|
|
|
color: 'warning',
|
|
|
|
|
icon: 'mdi-key-remove',
|
|
|
|
|
text: 'بدون کلید API'
|
|
|
|
|
};
|
|
|
|
|
} else {
|
|
|
|
|
this.connectionStatus = {
|
|
|
|
|
color: 'error',
|
|
|
|
|
icon: 'mdi-alert-circle',
|
|
|
|
|
text: 'خطا در اتصال'
|
|
|
|
|
};
|
2025-07-18 02:30:04 +03:30
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
this.connectionStatus = {
|
|
|
|
|
color: 'error',
|
|
|
|
|
icon: 'mdi-alert-circle',
|
2025-07-18 06:29:39 +03:30
|
|
|
|
text: 'خطا در اتصال'
|
|
|
|
|
};
|
|
|
|
|
console.error('خطا در بررسی وضعیت:', error);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
async loadConversations() {
|
|
|
|
|
try {
|
|
|
|
|
const response = await axios.post('/api/ai/conversations/list');
|
2025-07-18 19:59:35 +03:30
|
|
|
|
this.conversations = Array.isArray(response.data) ? response.data : [];
|
|
|
|
|
this.filteredConversations = Array.isArray(this.conversations) ? [...this.conversations] : [];
|
2025-07-18 06:29:39 +03:30
|
|
|
|
|
|
|
|
|
// استخراج دستهبندیها
|
|
|
|
|
const categorySet = new Set();
|
2025-07-18 19:59:35 +03:30
|
|
|
|
if (Array.isArray(this.conversations)) {
|
|
|
|
|
this.conversations.forEach(conv => {
|
|
|
|
|
if (conv.category) {
|
|
|
|
|
categorySet.add(conv.category);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
2025-07-18 06:29:39 +03:30
|
|
|
|
this.categories = Array.from(categorySet);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('خطا در بارگذاری گفتگوها:', error);
|
2025-07-18 19:59:35 +03:30
|
|
|
|
this.conversations = [];
|
|
|
|
|
this.filteredConversations = [];
|
|
|
|
|
this.categories = [];
|
2025-07-18 06:29:39 +03:30
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
async loadBalance() {
|
|
|
|
|
try {
|
|
|
|
|
const response = await axios.get('/api/wizard/balance');
|
|
|
|
|
if (response.data.success) {
|
|
|
|
|
this.userBalance = response.data.balance;
|
2025-07-18 02:30:04 +03:30
|
|
|
|
}
|
2025-07-18 06:29:39 +03:30
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('خطا در بارگذاری اعتبار:', error);
|
2025-07-18 02:30:04 +03:30
|
|
|
|
}
|
2025-05-04 01:07:39 +03:30
|
|
|
|
},
|
|
|
|
|
|
2025-07-18 19:59:35 +03:30
|
|
|
|
async loadOperationsGuide() {
|
|
|
|
|
try {
|
|
|
|
|
const response = await axios.get('/api/wizard/persons/guide');
|
|
|
|
|
if (response.data.success) {
|
|
|
|
|
this.operationsGuide = response.data.guide;
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('خطا در بارگذاری راهنما:', error);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
2025-07-18 06:29:39 +03:30
|
|
|
|
formatBalance(balance) {
|
|
|
|
|
// تبدیل به عدد صحیح
|
|
|
|
|
const intBalance = Math.round(balance);
|
|
|
|
|
if (intBalance < 1000) {
|
|
|
|
|
return `${intBalance.toLocaleString('fa-IR')} ریال`;
|
|
|
|
|
} else {
|
|
|
|
|
return `${(intBalance / 1000).toFixed(1)} هزار ریال`;
|
|
|
|
|
}
|
2025-05-04 01:07:39 +03:30
|
|
|
|
},
|
2025-07-18 06:29:39 +03:30
|
|
|
|
|
|
|
|
|
async loadConversation(conversation) {
|
|
|
|
|
try {
|
|
|
|
|
const response = await axios.post(`/api/ai/conversations/${conversation.id}/messages`);
|
|
|
|
|
this.currentConversation = conversation;
|
|
|
|
|
this.userMessages = [];
|
|
|
|
|
|
|
|
|
|
// تبدیل پیامهای دیتابیس به فرمت نمایش
|
2025-07-18 19:59:35 +03:30
|
|
|
|
const messages = Array.isArray(response.data) ? response.data : [];
|
|
|
|
|
messages.forEach(msg => {
|
2025-07-18 06:29:39 +03:30
|
|
|
|
if (msg.role === 'user') {
|
|
|
|
|
this.userMessages.push(msg.content);
|
|
|
|
|
} else if (msg.role === 'assistant') {
|
|
|
|
|
this.userMessages.push({
|
|
|
|
|
isAI: true,
|
|
|
|
|
text: msg.content,
|
|
|
|
|
usage: {
|
|
|
|
|
prompt_tokens: msg.inputTokens,
|
|
|
|
|
completion_tokens: msg.outputTokens
|
|
|
|
|
},
|
|
|
|
|
cost: {
|
|
|
|
|
total_cost: msg.totalCost
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
this.showConversationsDialog = false;
|
|
|
|
|
this.$nextTick(() => {
|
|
|
|
|
this.scrollToBottom();
|
|
|
|
|
});
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('خطا در بارگذاری گفتگو:', error);
|
2025-05-04 01:07:39 +03:30
|
|
|
|
}
|
|
|
|
|
},
|
2025-07-18 06:29:39 +03:30
|
|
|
|
|
|
|
|
|
deleteConversation(conversation) {
|
|
|
|
|
this.conversationToDelete = conversation;
|
|
|
|
|
this.showDeleteDialog = true;
|
2025-05-04 01:07:39 +03:30
|
|
|
|
},
|
2025-07-18 06:29:39 +03:30
|
|
|
|
|
|
|
|
|
async confirmDeleteConversation() {
|
|
|
|
|
if (!this.conversationToDelete) return;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
this.isLoading = true;
|
|
|
|
|
const response = await axios.post(`/api/ai/conversations/${this.conversationToDelete.id}/delete`);
|
|
|
|
|
|
|
|
|
|
if (response.data.success) {
|
|
|
|
|
// اگر گفتگوی حذف شده، گفتگوی فعلی است، گفتگوی جدید ایجاد کن
|
|
|
|
|
if (this.currentConversation && this.currentConversation.id === this.conversationToDelete.id) {
|
|
|
|
|
this.newConversation();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// بهروزرسانی کامل لیست گفتگوها
|
|
|
|
|
await this.loadConversations();
|
|
|
|
|
|
|
|
|
|
// نمایش پیام موفقیت
|
|
|
|
|
this.$emit('show-snackbar', {
|
|
|
|
|
text: 'گفتگو با موفقیت حذف شد',
|
|
|
|
|
color: 'success'
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
this.$emit('show-snackbar', {
|
|
|
|
|
text: response.data.error || 'خطا در حذف گفتگو',
|
|
|
|
|
color: 'error'
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('خطا در حذف گفتگو:', error);
|
|
|
|
|
this.$emit('show-snackbar', {
|
|
|
|
|
text: 'خطا در حذف گفتگو',
|
|
|
|
|
color: 'error'
|
|
|
|
|
});
|
|
|
|
|
} finally {
|
|
|
|
|
this.isLoading = false;
|
|
|
|
|
this.showDeleteDialog = false;
|
|
|
|
|
this.conversationToDelete = null;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
async newConversation() {
|
|
|
|
|
this.currentConversation = null;
|
|
|
|
|
this.userMessages = [];
|
|
|
|
|
this.showConversationsDialog = false;
|
|
|
|
|
this.setWelcomeMessage();
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
searchConversations() {
|
|
|
|
|
if (!this.searchTerm) {
|
2025-07-18 19:59:35 +03:30
|
|
|
|
this.filteredConversations = Array.isArray(this.conversations) ? [...this.conversations] : [];
|
2025-07-18 06:29:39 +03:30
|
|
|
|
} else {
|
2025-07-18 19:59:35 +03:30
|
|
|
|
this.filteredConversations = Array.isArray(this.conversations) ? this.conversations.filter(conv =>
|
|
|
|
|
conv.title && conv.title.toLowerCase().includes(this.searchTerm.toLowerCase())
|
|
|
|
|
) : [];
|
2025-07-18 06:29:39 +03:30
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
filterByCategory() {
|
|
|
|
|
if (!this.selectedCategory) {
|
2025-07-18 19:59:35 +03:30
|
|
|
|
this.filteredConversations = Array.isArray(this.conversations) ? [...this.conversations] : [];
|
2025-07-18 06:29:39 +03:30
|
|
|
|
} else {
|
2025-07-18 19:59:35 +03:30
|
|
|
|
this.filteredConversations = Array.isArray(this.conversations) ? this.conversations.filter(conv =>
|
2025-07-18 06:29:39 +03:30
|
|
|
|
conv.category === this.selectedCategory
|
2025-07-18 19:59:35 +03:30
|
|
|
|
) : [];
|
2025-07-18 06:29:39 +03:30
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
setWelcomeMessage() {
|
2025-07-18 07:15:33 +03:30
|
|
|
|
if (!this.aiSettings.aiEnabled) {
|
|
|
|
|
this.displayWelcome = 'سرویس هوش مصنوعی در حال حاضر غیرفعال است. لطفاً با مدیر سیستم تماس بگیرید تا این سرویس را فعال کند.';
|
|
|
|
|
} else {
|
|
|
|
|
this.displayWelcome = 'سلام! من دستیار هوشمند حسابیکس هستم. چطور میتوانم به شما کمک کنم؟';
|
|
|
|
|
}
|
2025-07-18 06:29:39 +03:30
|
|
|
|
},
|
|
|
|
|
|
2025-05-04 01:07:39 +03:30
|
|
|
|
async sendMessage() {
|
2025-07-18 06:29:39 +03:30
|
|
|
|
if (!this.userMessage.trim() || this.isLoading) return;
|
2025-05-04 01:07:39 +03:30
|
|
|
|
|
2025-07-18 07:15:33 +03:30
|
|
|
|
// بررسی فعال بودن هوش مصنوعی
|
|
|
|
|
if (!this.aiSettings.aiEnabled) {
|
|
|
|
|
this.userMessages.push({
|
|
|
|
|
isAI: true,
|
|
|
|
|
text: 'سرویس هوش مصنوعی در حال حاضر غیرفعال است. لطفاً با مدیر سیستم تماس بگیرید تا این سرویس را فعال کند.',
|
|
|
|
|
isError: true
|
|
|
|
|
});
|
|
|
|
|
this.userMessage = '';
|
|
|
|
|
this.$nextTick(() => {
|
|
|
|
|
this.scrollToBottom();
|
|
|
|
|
});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-18 06:29:39 +03:30
|
|
|
|
// بررسی اعتبار قبل از ارسال
|
|
|
|
|
if (this.userBalance < 100) {
|
2025-07-18 19:59:35 +03:30
|
|
|
|
this.userMessages.push({
|
|
|
|
|
isAI: true,
|
2025-07-18 06:29:39 +03:30
|
|
|
|
text: `خطا: اعتبار شما کافی نیست (${this.formatBalance(this.userBalance)}). برای شارژ حساب خود روی دکمه زیر کلیک کنید:`,
|
|
|
|
|
isError: true,
|
|
|
|
|
showChargeButton: true
|
|
|
|
|
});
|
|
|
|
|
this.userMessage = '';
|
|
|
|
|
this.$nextTick(() => {
|
|
|
|
|
this.scrollToBottom();
|
|
|
|
|
});
|
|
|
|
|
return;
|
2025-07-18 02:30:04 +03:30
|
|
|
|
}
|
|
|
|
|
|
2025-07-18 06:29:39 +03:30
|
|
|
|
const message = this.userMessage;
|
|
|
|
|
this.userMessage = '';
|
|
|
|
|
this.userMessages.push(message);
|
|
|
|
|
this.isLoading = true;
|
|
|
|
|
this.isTyping = true;
|
|
|
|
|
|
|
|
|
|
this.$nextTick(() => {
|
|
|
|
|
this.scrollToBottom();
|
|
|
|
|
});
|
2025-05-04 01:07:39 +03:30
|
|
|
|
|
2025-07-18 19:59:35 +03:30
|
|
|
|
let response = null;
|
2025-07-18 02:30:04 +03:30
|
|
|
|
try {
|
2025-07-18 19:59:35 +03:30
|
|
|
|
// بررسی درخواستهای مربوط به اشخاص
|
|
|
|
|
const personKeywords = ['شخص', 'مشتری', 'تامینکننده', 'کارمند', 'موجودی', 'تراکنش'];
|
|
|
|
|
const isPersonRequest = personKeywords.some(keyword =>
|
|
|
|
|
message.toLowerCase().includes(keyword.toLowerCase())
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (isPersonRequest) {
|
|
|
|
|
// اگر درخواست مربوط به اشخاص است، ابتدا اطلاعات شخص را نمایش دهیم
|
|
|
|
|
await this.showPersonDetails(message);
|
|
|
|
|
}
|
|
|
|
|
// در هر صورت درخواست را به سرور ارسال کن
|
|
|
|
|
response = await axios.post('/api/wizard/talk', {
|
2025-07-18 02:30:04 +03:30
|
|
|
|
message: message,
|
2025-07-18 06:29:39 +03:30
|
|
|
|
conversationId: this.currentConversation?.id || null
|
|
|
|
|
});
|
2025-05-04 01:07:39 +03:30
|
|
|
|
|
2025-07-18 19:59:35 +03:30
|
|
|
|
if (response && response.data && response.data.success) {
|
2025-07-18 02:30:04 +03:30
|
|
|
|
this.userMessages.push({
|
2025-07-18 06:29:39 +03:30
|
|
|
|
isAI: true,
|
2025-07-18 02:30:04 +03:30
|
|
|
|
text: response.data.response,
|
2025-07-18 06:29:39 +03:30
|
|
|
|
usage: response.data.usage,
|
|
|
|
|
cost: response.data.cost
|
|
|
|
|
});
|
|
|
|
|
// بهروزرسانی اعتبار کاربر
|
|
|
|
|
await this.loadBalance();
|
|
|
|
|
// بهروزرسانی لیست گفتگوها
|
|
|
|
|
await this.loadConversations();
|
2025-07-18 19:59:35 +03:30
|
|
|
|
} else if (response && response.data) {
|
2025-07-18 06:29:39 +03:30
|
|
|
|
// بررسی خطای اعتبار
|
|
|
|
|
if (response.data.error && response.data.error.includes('اعتبار')) {
|
|
|
|
|
this.userMessages.push({
|
|
|
|
|
isAI: true,
|
|
|
|
|
text: `خطا: ${response.data.error}`,
|
|
|
|
|
isError: true,
|
|
|
|
|
showChargeButton: response.data.showChargeButton || false
|
|
|
|
|
});
|
|
|
|
|
await this.loadBalance();
|
|
|
|
|
} else {
|
|
|
|
|
this.userMessages.push({
|
|
|
|
|
isAI: true,
|
|
|
|
|
text: `خطا: ${response.data.error}`,
|
|
|
|
|
isError: true
|
|
|
|
|
});
|
|
|
|
|
}
|
2025-07-18 19:59:35 +03:30
|
|
|
|
} else {
|
|
|
|
|
// اگر هیچ پاسخی از سرور دریافت نشد
|
|
|
|
|
this.userMessages.push({
|
|
|
|
|
isAI: true,
|
|
|
|
|
text: 'خطا: خطا در ارتباط با سرور',
|
|
|
|
|
isError: true
|
|
|
|
|
});
|
2025-07-18 02:30:04 +03:30
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
2025-07-18 06:29:39 +03:30
|
|
|
|
console.error('خطا در ارسال پیام:', error);
|
2025-07-18 02:30:04 +03:30
|
|
|
|
this.userMessages.push({
|
|
|
|
|
isAI: true,
|
2025-07-18 06:29:39 +03:30
|
|
|
|
text: 'خطا: خطا در ارتباط با سرور',
|
|
|
|
|
isError: true
|
|
|
|
|
});
|
|
|
|
|
} finally {
|
|
|
|
|
this.isLoading = false;
|
|
|
|
|
this.isTyping = false;
|
|
|
|
|
this.$nextTick(() => {
|
|
|
|
|
this.scrollToBottom();
|
|
|
|
|
});
|
2025-07-18 02:30:04 +03:30
|
|
|
|
}
|
2025-07-18 06:29:39 +03:30
|
|
|
|
},
|
2025-05-04 01:07:39 +03:30
|
|
|
|
|
2025-07-18 06:29:39 +03:30
|
|
|
|
useSuggestion(suggestion) {
|
|
|
|
|
this.userMessage = suggestion;
|
2025-05-04 01:07:39 +03:30
|
|
|
|
},
|
2025-07-18 06:29:39 +03:30
|
|
|
|
|
|
|
|
|
clearChat() {
|
|
|
|
|
this.userMessages = [];
|
|
|
|
|
this.currentConversation = null;
|
|
|
|
|
this.setWelcomeMessage();
|
|
|
|
|
},
|
|
|
|
|
|
2025-05-04 01:07:39 +03:30
|
|
|
|
scrollToBottom() {
|
2025-07-18 06:29:39 +03:30
|
|
|
|
if (this.$refs.messagesContainer) {
|
|
|
|
|
this.$refs.messagesContainer.scrollTop = this.$refs.messagesContainer.scrollHeight;
|
2025-05-04 01:07:39 +03:30
|
|
|
|
}
|
2025-07-18 02:30:04 +03:30
|
|
|
|
},
|
2025-07-18 06:29:39 +03:30
|
|
|
|
|
|
|
|
|
renderMarkdown(text) {
|
|
|
|
|
if (!text) return '';
|
|
|
|
|
const html = marked(text);
|
|
|
|
|
return DOMPurify.sanitize(html);
|
2025-07-18 02:30:04 +03:30
|
|
|
|
},
|
2025-07-18 06:29:39 +03:30
|
|
|
|
|
2025-07-18 02:30:04 +03:30
|
|
|
|
formatTime(date) {
|
2025-07-18 06:29:39 +03:30
|
|
|
|
return date.toLocaleTimeString('fa-IR', {
|
2025-07-18 02:30:04 +03:30
|
|
|
|
hour: '2-digit',
|
2025-07-18 06:29:39 +03:30
|
|
|
|
minute: '2-digit'
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
goToChargePage() {
|
|
|
|
|
// هدایت به صفحه شارژ با vue-router
|
|
|
|
|
this.$router.push('/acc/sms/panel');
|
2025-07-18 19:59:35 +03:30
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
async searchPersons(searchTerm) {
|
|
|
|
|
try {
|
|
|
|
|
const response = await axios.post('/api/wizard/persons/search', {
|
|
|
|
|
search: searchTerm
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (response.data.success) {
|
|
|
|
|
return response.data.persons;
|
|
|
|
|
} else {
|
|
|
|
|
console.error('خطا در جستجوی اشخاص:', response.data.error);
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('خطا در جستجوی اشخاص:', error);
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
async getPersonDetails(personId) {
|
|
|
|
|
try {
|
|
|
|
|
const response = await axios.get(`/api/wizard/persons/${personId}`);
|
|
|
|
|
|
|
|
|
|
if (response.data.success) {
|
|
|
|
|
return response.data.person;
|
|
|
|
|
} else {
|
|
|
|
|
console.error('خطا در دریافت اطلاعات شخص:', response.data.error);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('خطا در دریافت اطلاعات شخص:', error);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
async getPersonTransactions(personId, limit = 10) {
|
|
|
|
|
try {
|
|
|
|
|
const response = await axios.get(`/api/wizard/persons/${personId}/transactions?limit=${limit}`);
|
|
|
|
|
|
|
|
|
|
if (response.data.success) {
|
|
|
|
|
return response.data.transactions;
|
|
|
|
|
} else {
|
|
|
|
|
console.error('خطا در دریافت تراکنشهای شخص:', response.data.error);
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('خطا در دریافت تراکنشهای شخص:', error);
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
async showPersonDetails(searchTerm) {
|
|
|
|
|
try {
|
|
|
|
|
// جستجوی شخص
|
|
|
|
|
const persons = await this.searchPersons(searchTerm);
|
|
|
|
|
|
|
|
|
|
if (persons.length === 0) {
|
|
|
|
|
this.$emit('show-snackbar', {
|
|
|
|
|
text: 'شخصی با این نام یافت نشد',
|
|
|
|
|
color: 'warning'
|
|
|
|
|
});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// انتخاب اولین شخص (یا میتوانیم لیست انتخاب نمایش دهیم)
|
|
|
|
|
const person = persons[0];
|
|
|
|
|
|
|
|
|
|
// دریافت اطلاعات کامل شخص
|
|
|
|
|
const personDetails = await this.getPersonDetails(person.id);
|
|
|
|
|
if (!personDetails) {
|
|
|
|
|
this.$emit('show-snackbar', {
|
|
|
|
|
text: 'خطا در دریافت اطلاعات شخص',
|
|
|
|
|
color: 'error'
|
|
|
|
|
});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// دریافت تراکنشهای شخص
|
|
|
|
|
const transactions = await this.getPersonTransactions(person.id, 10);
|
|
|
|
|
|
|
|
|
|
// نمایش اطلاعات
|
|
|
|
|
this.selectedPerson = personDetails;
|
|
|
|
|
this.personTransactions = transactions;
|
|
|
|
|
this.showPersonInfo = true;
|
|
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('خطا در نمایش اطلاعات شخص:', error);
|
|
|
|
|
this.$emit('show-snackbar', {
|
|
|
|
|
text: 'خطا در نمایش اطلاعات شخص',
|
|
|
|
|
color: 'error'
|
|
|
|
|
});
|
|
|
|
|
}
|
2025-05-04 01:07:39 +03:30
|
|
|
|
}
|
2025-04-12 18:50:34 +03:30
|
|
|
|
}
|
2025-07-18 06:29:39 +03:30
|
|
|
|
};
|
2025-04-12 18:50:34 +03:30
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
2025-05-04 01:07:39 +03:30
|
|
|
|
.page-container {
|
2025-07-18 06:29:39 +03:30
|
|
|
|
height: calc(100vh - 64px);
|
2025-05-04 01:07:39 +03:30
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-18 06:29:39 +03:30
|
|
|
|
.info-cards {
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.info-card {
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
|
|
|
|
|
border: 1px solid rgba(0, 0, 0, 0.08);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.pricing-section, .stats-section {
|
|
|
|
|
flex: 1;
|
|
|
|
|
min-width: 200px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@media (max-width: 768px) {
|
|
|
|
|
.pricing-section, .stats-section {
|
|
|
|
|
min-width: 100%;
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.info-card .v-card-text {
|
|
|
|
|
padding: 12px;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-04 01:07:39 +03:30
|
|
|
|
.content-container {
|
|
|
|
|
flex: 1;
|
2025-07-18 06:29:39 +03:30
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
overflow: hidden;
|
2025-05-04 01:07:39 +03:30
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-container {
|
2025-07-18 06:29:39 +03:30
|
|
|
|
flex: 1;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
margin: 16px;
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-header {
|
|
|
|
|
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
|
|
|
|
|
background: rgba(0, 0, 0, 0.02);
|
2025-05-04 01:07:39 +03:30
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chat-box {
|
2025-07-18 06:29:39 +03:30
|
|
|
|
flex: 1;
|
2025-05-04 01:07:39 +03:30
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
2025-07-18 06:29:39 +03:30
|
|
|
|
overflow: hidden;
|
2025-05-04 01:07:39 +03:30
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.messages-container {
|
|
|
|
|
flex: 1;
|
|
|
|
|
overflow-y: auto;
|
2025-07-18 06:29:39 +03:30
|
|
|
|
padding: 16px;
|
2025-05-04 01:07:39 +03:30
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 16px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.message {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: flex-start;
|
2025-07-18 06:29:39 +03:30
|
|
|
|
gap: 8px;
|
2025-05-04 01:07:39 +03:30
|
|
|
|
max-width: 80%;
|
2025-04-12 18:50:34 +03:30
|
|
|
|
}
|
|
|
|
|
|
2025-05-04 01:07:39 +03:30
|
|
|
|
.user-message {
|
|
|
|
|
align-self: flex-end;
|
|
|
|
|
flex-direction: row-reverse;
|
2025-04-12 18:50:34 +03:30
|
|
|
|
}
|
|
|
|
|
|
2025-07-18 06:29:39 +03:30
|
|
|
|
.ai-message {
|
|
|
|
|
align-self: flex-start;
|
2025-05-04 01:07:39 +03:30
|
|
|
|
}
|
|
|
|
|
|
2025-07-18 06:29:39 +03:30
|
|
|
|
.message-content {
|
|
|
|
|
background: #f5f5f5;
|
|
|
|
|
padding: 12px 16px;
|
|
|
|
|
border-radius: 18px;
|
|
|
|
|
border-top-left-radius: 4px;
|
|
|
|
|
position: relative;
|
2025-05-04 01:07:39 +03:30
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.user-message .message-content {
|
2025-07-18 06:29:39 +03:30
|
|
|
|
background: #1a237e;
|
2025-05-04 01:07:39 +03:30
|
|
|
|
color: white;
|
2025-07-18 06:29:39 +03:30
|
|
|
|
border-top-left-radius: 18px;
|
|
|
|
|
border-top-right-radius: 4px;
|
2025-04-12 18:50:34 +03:30
|
|
|
|
}
|
|
|
|
|
|
2025-05-04 01:07:39 +03:30
|
|
|
|
.message-text {
|
|
|
|
|
line-height: 1.5;
|
2025-07-18 06:29:39 +03:30
|
|
|
|
white-space: pre-wrap;
|
2025-04-12 18:50:34 +03:30
|
|
|
|
}
|
|
|
|
|
|
2025-07-18 02:30:04 +03:30
|
|
|
|
.message-time {
|
|
|
|
|
font-size: 0.75rem;
|
2025-07-18 06:29:39 +03:30
|
|
|
|
opacity: 0.7;
|
2025-07-18 02:30:04 +03:30
|
|
|
|
margin-top: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-18 06:29:39 +03:30
|
|
|
|
.input-container {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: row-reverse;
|
|
|
|
|
align-items: center;
|
|
|
|
|
padding: 16px;
|
|
|
|
|
border-top: 1px solid rgba(0, 0, 0, 0.12);
|
|
|
|
|
background: white;
|
2025-07-18 02:30:04 +03:30
|
|
|
|
}
|
|
|
|
|
|
2025-07-18 06:29:39 +03:30
|
|
|
|
.message-input {
|
|
|
|
|
flex-grow: 1;
|
|
|
|
|
margin-left: 12px;
|
|
|
|
|
margin-right: 0;
|
2025-07-18 02:30:04 +03:30
|
|
|
|
}
|
2025-07-18 06:29:39 +03:30
|
|
|
|
.send-button {
|
|
|
|
|
min-width: 48px;
|
|
|
|
|
min-height: 48px;
|
2025-07-18 02:30:04 +03:30
|
|
|
|
}
|
|
|
|
|
|
2025-07-18 06:29:39 +03:30
|
|
|
|
.suggestions-container {
|
|
|
|
|
padding: 8px 16px;
|
|
|
|
|
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
|
|
|
|
|
background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
|
2025-04-12 18:50:34 +03:30
|
|
|
|
}
|
|
|
|
|
|
2025-07-18 06:29:39 +03:30
|
|
|
|
.suggestion-chip {
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
transition: all 0.2s ease;
|
|
|
|
|
font-size: 0.75rem;
|
2025-05-04 01:07:39 +03:30
|
|
|
|
}
|
|
|
|
|
|
2025-07-18 06:29:39 +03:30
|
|
|
|
.suggestion-chip:hover {
|
|
|
|
|
transform: translateY(-1px);
|
|
|
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
2025-05-04 01:07:39 +03:30
|
|
|
|
}
|
|
|
|
|
|
2025-07-18 06:29:39 +03:30
|
|
|
|
.delete-btn {
|
|
|
|
|
opacity: 0.7;
|
|
|
|
|
transition: all 0.2s ease;
|
2025-05-04 01:07:39 +03:30
|
|
|
|
}
|
|
|
|
|
|
2025-07-18 06:29:39 +03:30
|
|
|
|
.delete-btn:hover {
|
|
|
|
|
opacity: 1;
|
2025-04-12 18:50:34 +03:30
|
|
|
|
transform: scale(1.1);
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-18 06:29:39 +03:30
|
|
|
|
.conversation-item:hover .delete-btn {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-04 01:07:39 +03:30
|
|
|
|
.typing-indicator {
|
2025-07-18 06:29:39 +03:30
|
|
|
|
display: inline-flex;
|
2025-05-04 01:07:39 +03:30
|
|
|
|
gap: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.typing-indicator span {
|
|
|
|
|
width: 8px;
|
|
|
|
|
height: 8px;
|
|
|
|
|
border-radius: 50%;
|
2025-07-18 06:29:39 +03:30
|
|
|
|
background: #666;
|
|
|
|
|
animation: typing 1.4s infinite ease-in-out;
|
2025-05-04 01:07:39 +03:30
|
|
|
|
}
|
|
|
|
|
|
2025-07-18 06:29:39 +03:30
|
|
|
|
.typing-indicator span:nth-child(1) { animation-delay: -0.32s; }
|
|
|
|
|
.typing-indicator span:nth-child(2) { animation-delay: -0.16s; }
|
2025-05-04 01:07:39 +03:30
|
|
|
|
|
|
|
|
|
@keyframes typing {
|
2025-07-18 06:29:39 +03:30
|
|
|
|
0%, 80%, 100% {
|
|
|
|
|
transform: scale(0.8);
|
|
|
|
|
opacity: 0.5;
|
|
|
|
|
}
|
|
|
|
|
40% {
|
|
|
|
|
transform: scale(1);
|
|
|
|
|
opacity: 1;
|
|
|
|
|
}
|
2025-04-12 18:50:34 +03:30
|
|
|
|
}
|
|
|
|
|
|
2025-07-18 06:29:39 +03:30
|
|
|
|
.error-message {
|
|
|
|
|
background: #ffebee !important;
|
|
|
|
|
border: 1px solid #ffcdd2;
|
2025-04-12 18:50:34 +03:30
|
|
|
|
}
|
2025-07-18 02:30:04 +03:30
|
|
|
|
|
2025-07-18 06:29:39 +03:30
|
|
|
|
.details-message {
|
|
|
|
|
background: #e8f5e8 !important;
|
|
|
|
|
border: 1px solid #c8e6c9;
|
2025-07-18 02:30:04 +03:30
|
|
|
|
}
|
|
|
|
|
|
2025-07-18 06:29:39 +03:30
|
|
|
|
/* استایلهای آرشیو گفتگوها */
|
|
|
|
|
.conversations-list {
|
|
|
|
|
max-height: 400px;
|
|
|
|
|
overflow-y: auto;
|
2025-07-18 02:30:04 +03:30
|
|
|
|
}
|
|
|
|
|
|
2025-07-18 06:29:39 +03:30
|
|
|
|
/* استایلهای بهبود یافته تولبار */
|
|
|
|
|
.status-chip, .balance-chip, .connection-chip {
|
2025-07-18 02:30:04 +03:30
|
|
|
|
transition: all 0.3s ease;
|
2025-07-18 06:29:39 +03:30
|
|
|
|
cursor: pointer;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
font-weight: 500;
|
2025-07-18 02:30:04 +03:30
|
|
|
|
}
|
|
|
|
|
|
2025-07-18 06:29:39 +03:30
|
|
|
|
.status-chip:hover, .balance-chip:hover, .connection-chip:hover {
|
|
|
|
|
transform: translateY(-1px);
|
|
|
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
2025-07-18 02:30:04 +03:30
|
|
|
|
}
|
|
|
|
|
|
2025-07-18 06:29:39 +03:30
|
|
|
|
.archive-btn {
|
|
|
|
|
transition: all 0.3s ease;
|
|
|
|
|
border-radius: 8px;
|
2025-07-18 02:30:04 +03:30
|
|
|
|
}
|
|
|
|
|
|
2025-07-18 06:29:39 +03:30
|
|
|
|
.archive-btn:hover {
|
|
|
|
|
transform: translateY(-1px);
|
|
|
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
2025-07-18 02:30:04 +03:30
|
|
|
|
}
|
|
|
|
|
|
2025-07-18 06:29:39 +03:30
|
|
|
|
/* بهبود tooltip */
|
|
|
|
|
.v-tooltip {
|
|
|
|
|
font-size: 0.875rem;
|
|
|
|
|
font-weight: 500;
|
2025-07-18 02:30:04 +03:30
|
|
|
|
}
|
|
|
|
|
|
2025-07-18 06:29:39 +03:30
|
|
|
|
/* استایل دکمه شارژ */
|
|
|
|
|
.charge-button-container {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: center;
|
2025-07-18 02:30:04 +03:30
|
|
|
|
}
|
|
|
|
|
|
2025-07-18 06:29:39 +03:30
|
|
|
|
.charge-btn {
|
2025-07-18 02:30:04 +03:30
|
|
|
|
transition: all 0.3s ease;
|
2025-07-18 06:29:39 +03:30
|
|
|
|
border-radius: 8px;
|
|
|
|
|
font-weight: 500;
|
2025-07-18 02:30:04 +03:30
|
|
|
|
}
|
|
|
|
|
|
2025-07-18 06:29:39 +03:30
|
|
|
|
.charge-btn:hover {
|
2025-07-18 02:30:04 +03:30
|
|
|
|
transform: translateY(-1px);
|
2025-07-18 06:29:39 +03:30
|
|
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
2025-07-18 02:30:04 +03:30
|
|
|
|
}
|
|
|
|
|
|
2025-07-18 06:29:39 +03:30
|
|
|
|
.conversation-item {
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
transition: background-color 0.2s ease;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
margin-bottom: 4px;
|
2025-07-18 02:30:04 +03:30
|
|
|
|
}
|
|
|
|
|
|
2025-07-18 06:29:39 +03:30
|
|
|
|
.conversation-item:hover {
|
|
|
|
|
background-color: rgba(26, 35, 126, 0.1);
|
2025-07-18 02:30:04 +03:30
|
|
|
|
}
|
|
|
|
|
|
2025-07-18 06:29:39 +03:30
|
|
|
|
.conversation-item:active {
|
|
|
|
|
background-color: rgba(26, 35, 126, 0.2);
|
2025-07-18 02:30:04 +03:30
|
|
|
|
}
|
|
|
|
|
|
2025-07-18 06:29:39 +03:30
|
|
|
|
.send-icon-rotate {
|
|
|
|
|
transform: rotate(180deg);
|
2025-07-18 02:30:04 +03:30
|
|
|
|
}
|
2025-04-12 18:50:34 +03:30
|
|
|
|
</style>
|