feat: 添加客服在线状态和聊天粘贴图片功能
**客服在线状态:** - 后端:添加 admin_users.support_status 字段(online/offline/busy) - 接口:PUT /admin/me/support-status 更新状态 - 前端:管理员顶部导航显示状态切换器(仅客服权限可见) - 转接对话框显示客服在线状态和智能排序(在线优先) **聊天粘贴图片:** - 用户端、移动端、管理端聊天输入框支持 Ctrl+V/Cmd+V 直接粘贴图片 - 自动调用图片压缩和上传逻辑,与文件选择上传保持一致 - 最多支持粘贴9张图片 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -20,13 +20,14 @@ import { ElMessage } from 'element-plus'
|
||||
import { computed, ref } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
|
||||
import { logoutAdmin } from '@/features/admin'
|
||||
import { logoutAdmin, updateSupportStatus } from '@/features/admin'
|
||||
import { useAdminSessionStore } from '@/stores/adminSession'
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const adminSession = useAdminSessionStore()
|
||||
const isCollapsed = ref(false)
|
||||
const updatingStatus = ref(false)
|
||||
|
||||
interface NavItem {
|
||||
label: string
|
||||
@@ -60,6 +61,34 @@ const navItems = computed(() => {
|
||||
|
||||
const activeMenu = computed(() => route.path)
|
||||
const adminName = computed(() => adminSession.nickname || adminSession.username || '管理员')
|
||||
const supportStatus = computed(() => adminSession.supportStatus || 'offline')
|
||||
const hasChatPermission = computed(() => adminSession.hasPermission('chat:view'))
|
||||
|
||||
const statusLabels: Record<string, string> = {
|
||||
online: '在线',
|
||||
offline: '离线',
|
||||
busy: '忙碌',
|
||||
}
|
||||
|
||||
const statusColors: Record<string, string> = {
|
||||
online: '#10b981',
|
||||
offline: '#9ca3af',
|
||||
busy: '#f59e0b',
|
||||
}
|
||||
|
||||
async function handleStatusChange(status: 'online' | 'offline' | 'busy') {
|
||||
if (updatingStatus.value) return
|
||||
updatingStatus.value = true
|
||||
try {
|
||||
await updateSupportStatus(status)
|
||||
adminSession.setSupportStatus(status)
|
||||
ElMessage.success(`已切换为${statusLabels[status]}`)
|
||||
} catch {
|
||||
ElMessage.error('状态更新失败')
|
||||
} finally {
|
||||
updatingStatus.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handleLogout() {
|
||||
try {
|
||||
@@ -110,6 +139,28 @@ async function handleLogout() {
|
||||
</el-breadcrumb>
|
||||
</div>
|
||||
<div class="topbar-right">
|
||||
<el-dropdown v-if="hasChatPermission" trigger="click" @command="handleStatusChange" :disabled="updatingStatus">
|
||||
<span class="status-badge" :style="{ borderColor: statusColors[supportStatus] }">
|
||||
<span class="status-dot" :style="{ backgroundColor: statusColors[supportStatus] }"></span>
|
||||
<span class="status-text">{{ statusLabels[supportStatus] }}</span>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item command="online">
|
||||
<span class="status-dot" style="background-color: #10b981;"></span>
|
||||
在线
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="busy">
|
||||
<span class="status-dot" style="background-color: #f59e0b;"></span>
|
||||
忙碌
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="offline">
|
||||
<span class="status-dot" style="background-color: #9ca3af;"></span>
|
||||
离线
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
<el-dropdown trigger="click">
|
||||
<span class="user-info">
|
||||
<el-avatar :size="32" :icon="User" />
|
||||
@@ -256,6 +307,39 @@ async function handleLogout() {
|
||||
.topbar-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 6px 12px;
|
||||
border: 1px solid;
|
||||
border-radius: 16px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.status-badge:hover {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
.status-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.status-text {
|
||||
font-size: 13px;
|
||||
color: #606266;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
:deep(.el-dropdown-menu__item .status-dot) {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
|
||||
Reference in New Issue
Block a user