模块取出
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { api } from '@/api/index'
|
||||
import type { ResultDomain } from '@/types'
|
||||
import type { TbChat, TbChatMessage, ChatPrepareData, StopChatParams, CommentMessageParams, DifyFileInfo } from '@/types/ai'
|
||||
import type { TbChat, TbChatMessage, ChatPrepareData, StopChatParam, CommentMessageParam, DifyFileInfo } from '@/types/ai'
|
||||
|
||||
/**
|
||||
* @description AI对话相关接口
|
||||
@@ -93,7 +93,7 @@ export const aiChatAPI = {
|
||||
* 停止对话
|
||||
* @param params 停止参数
|
||||
*/
|
||||
async stopChat(params: StopChatParams): Promise<ResultDomain<boolean>> {
|
||||
async stopChat(params: StopChatParam): Promise<ResultDomain<boolean>> {
|
||||
const response = await api.post<boolean>(`${this.baseUrl}/stop`, params)
|
||||
return response.data
|
||||
},
|
||||
@@ -102,7 +102,7 @@ export const aiChatAPI = {
|
||||
* 评价消息
|
||||
* @param params 评价参数
|
||||
*/
|
||||
async commentMessage(params: CommentMessageParams): Promise<ResultDomain<boolean>> {
|
||||
async commentMessage(params: CommentMessageParam): Promise<ResultDomain<boolean>> {
|
||||
const response = await api.post<boolean>(`${this.baseUrl}/comment`, params)
|
||||
return response.data
|
||||
},
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './workcase'
|
||||
export * from './workcaseChat'
|
||||
@@ -1,185 +0,0 @@
|
||||
import { api } from '@/api/index'
|
||||
import type { ResultDomain, PageRequest } from '@/types'
|
||||
import type { TbWorkcaseDTO, TbWorkcaseProcessDTO, TbWorkcaseDeviceDTO } from '@/types/workcase'
|
||||
|
||||
/**
|
||||
* @description 工单管理接口
|
||||
* @filename workcase.ts
|
||||
* @author yslg
|
||||
* @copyright xyzh
|
||||
* @since 2025-12-19
|
||||
*/
|
||||
export const workcaseAPI = {
|
||||
baseUrl: '/urban-lifeline/workcase',
|
||||
|
||||
// ========================= 工单管理 =========================
|
||||
|
||||
/**
|
||||
* 创建工单
|
||||
* @param workcase 工单信息
|
||||
*/
|
||||
async createWorkcase(workcase: TbWorkcaseDTO): Promise<ResultDomain<TbWorkcaseDTO>> {
|
||||
const response = await api.post<TbWorkcaseDTO>(`${this.baseUrl}`, workcase)
|
||||
return response.data
|
||||
},
|
||||
|
||||
/**
|
||||
* 更新工单
|
||||
* @param workcase 工单信息
|
||||
*/
|
||||
async updateWorkcase(workcase: TbWorkcaseDTO): Promise<ResultDomain<TbWorkcaseDTO>> {
|
||||
const response = await api.put<TbWorkcaseDTO>(`${this.baseUrl}`, workcase)
|
||||
return response.data
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除工单
|
||||
* @param workcaseId 工单ID
|
||||
*/
|
||||
async deleteWorkcase(workcaseId: string): Promise<ResultDomain<TbWorkcaseDTO>> {
|
||||
const response = await api.delete<TbWorkcaseDTO>(`${this.baseUrl}/${workcaseId}`)
|
||||
return response.data
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取工单详情
|
||||
* @param workcaseId 工单ID
|
||||
*/
|
||||
async getWorkcaseById(workcaseId: string): Promise<ResultDomain<TbWorkcaseDTO>> {
|
||||
const response = await api.get<TbWorkcaseDTO>(`${this.baseUrl}/${workcaseId}`)
|
||||
return response.data
|
||||
},
|
||||
|
||||
/**
|
||||
* 查询工单列表
|
||||
* @param filter 筛选条件
|
||||
*/
|
||||
async getWorkcaseList(filter?: TbWorkcaseDTO): Promise<ResultDomain<TbWorkcaseDTO>> {
|
||||
const response = await api.post<TbWorkcaseDTO>(`${this.baseUrl}/list`, filter || {})
|
||||
return response.data
|
||||
},
|
||||
|
||||
/**
|
||||
* 分页查询工单
|
||||
* @param pageRequest 分页请求
|
||||
*/
|
||||
async getWorkcasePage(pageRequest: PageRequest<TbWorkcaseDTO>): Promise<ResultDomain<TbWorkcaseDTO>> {
|
||||
const response = await api.post<TbWorkcaseDTO>(`${this.baseUrl}/page`, pageRequest)
|
||||
return response.data
|
||||
},
|
||||
|
||||
// ========================= CRM同步接口 =========================
|
||||
|
||||
/**
|
||||
* 同步工单到CRM
|
||||
* @param workcase 工单信息
|
||||
*/
|
||||
async syncWorkcaseToCrm(workcase: TbWorkcaseDTO): Promise<ResultDomain<void>> {
|
||||
const response = await api.post<void>(`${this.baseUrl}/sync/crm`, workcase)
|
||||
return response.data
|
||||
},
|
||||
|
||||
/**
|
||||
* 接收CRM工单更新(CRM回调)
|
||||
* @param jsonBody JSON字符串
|
||||
*/
|
||||
async receiveWorkcaseFromCrm(jsonBody: string): Promise<ResultDomain<void>> {
|
||||
const response = await api.post<void>(`${this.baseUrl}/receive/crm`, jsonBody)
|
||||
return response.data
|
||||
},
|
||||
|
||||
// ========================= 工单处理过程 =========================
|
||||
|
||||
/**
|
||||
* 创建工单处理过程
|
||||
* @param process 处理过程信息
|
||||
*/
|
||||
async createWorkcaseProcess(process: TbWorkcaseProcessDTO): Promise<ResultDomain<TbWorkcaseProcessDTO>> {
|
||||
const response = await api.post<TbWorkcaseProcessDTO>(`${this.baseUrl}/process`, process)
|
||||
return response.data
|
||||
},
|
||||
|
||||
/**
|
||||
* 更新工单处理过程
|
||||
* @param process 处理过程信息
|
||||
*/
|
||||
async updateWorkcaseProcess(process: TbWorkcaseProcessDTO): Promise<ResultDomain<TbWorkcaseProcessDTO>> {
|
||||
const response = await api.put<TbWorkcaseProcessDTO>(`${this.baseUrl}/process`, process)
|
||||
return response.data
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除工单处理过程
|
||||
* @param processId 处理过程ID
|
||||
*/
|
||||
async deleteWorkcaseProcess(processId: string): Promise<ResultDomain<TbWorkcaseProcessDTO>> {
|
||||
const response = await api.delete<TbWorkcaseProcessDTO>(`${this.baseUrl}/process/${processId}`)
|
||||
return response.data
|
||||
},
|
||||
|
||||
/**
|
||||
* 查询工单处理过程列表
|
||||
* @param filter 筛选条件
|
||||
*/
|
||||
async getWorkcaseProcessList(filter?: TbWorkcaseProcessDTO): Promise<ResultDomain<TbWorkcaseProcessDTO>> {
|
||||
const response = await api.post<TbWorkcaseProcessDTO>(`${this.baseUrl}/process/list`, filter || {})
|
||||
return response.data
|
||||
},
|
||||
|
||||
/**
|
||||
* 分页查询工单处理过程
|
||||
* @param pageRequest 分页请求
|
||||
*/
|
||||
async getWorkcaseProcessPage(pageRequest: PageRequest<TbWorkcaseProcessDTO>): Promise<ResultDomain<TbWorkcaseProcessDTO>> {
|
||||
const response = await api.post<TbWorkcaseProcessDTO>(`${this.baseUrl}/process/page`, pageRequest)
|
||||
return response.data
|
||||
},
|
||||
|
||||
// ========================= 工单设备管理 =========================
|
||||
|
||||
/**
|
||||
* 创建工单设备
|
||||
* @param device 设备信息
|
||||
*/
|
||||
async createWorkcaseDevice(device: TbWorkcaseDeviceDTO): Promise<ResultDomain<TbWorkcaseDeviceDTO>> {
|
||||
const response = await api.post<TbWorkcaseDeviceDTO>(`${this.baseUrl}/device`, device)
|
||||
return response.data
|
||||
},
|
||||
|
||||
/**
|
||||
* 更新工单设备
|
||||
* @param device 设备信息
|
||||
*/
|
||||
async updateWorkcaseDevice(device: TbWorkcaseDeviceDTO): Promise<ResultDomain<TbWorkcaseDeviceDTO>> {
|
||||
const response = await api.put<TbWorkcaseDeviceDTO>(`${this.baseUrl}/device`, device)
|
||||
return response.data
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除工单设备
|
||||
* @param workcaseId 工单ID
|
||||
* @param device 设备名称
|
||||
*/
|
||||
async deleteWorkcaseDevice(workcaseId: string, device: string): Promise<ResultDomain<TbWorkcaseDeviceDTO>> {
|
||||
const response = await api.delete<TbWorkcaseDeviceDTO>(`${this.baseUrl}/device/${workcaseId}/${device}`)
|
||||
return response.data
|
||||
},
|
||||
|
||||
/**
|
||||
* 查询工单设备列表
|
||||
* @param filter 筛选条件
|
||||
*/
|
||||
async getWorkcaseDeviceList(filter?: TbWorkcaseDeviceDTO): Promise<ResultDomain<TbWorkcaseDeviceDTO>> {
|
||||
const response = await api.post<TbWorkcaseDeviceDTO>(`${this.baseUrl}/device/list`, filter || {})
|
||||
return response.data
|
||||
},
|
||||
|
||||
/**
|
||||
* 分页查询工单设备
|
||||
* @param pageRequest 分页请求
|
||||
*/
|
||||
async getWorkcaseDevicePage(pageRequest: PageRequest<TbWorkcaseDeviceDTO>): Promise<ResultDomain<TbWorkcaseDeviceDTO>> {
|
||||
const response = await api.post<TbWorkcaseDeviceDTO>(`${this.baseUrl}/device/page`, pageRequest)
|
||||
return response.data
|
||||
}
|
||||
}
|
||||
@@ -1,301 +0,0 @@
|
||||
import { api } from '@/api/index'
|
||||
import type { ResultDomain, PageRequest } from '@/types'
|
||||
import type { TbChat, TbChatMessage, ChatPrepareData } from '@/types/ai'
|
||||
import type { TbWorkcaseDTO } from '@/types/workcase/workcase'
|
||||
import type {
|
||||
TbChatRoomDTO,
|
||||
TbChatRoomMemberDTO,
|
||||
TbChatRoomMessageDTO,
|
||||
TbCustomerServiceDTO,
|
||||
TbWordCloudDTO,
|
||||
ChatRoomVO,
|
||||
ChatMemberVO,
|
||||
ChatRoomMessageVO,
|
||||
CustomerServiceVO
|
||||
} from '@/types/workcase/chatRoom'
|
||||
|
||||
/**
|
||||
* @description 工单对话相关接口
|
||||
* @filename workcaseChat.ts
|
||||
* @author cascade
|
||||
* @copyright xyzh
|
||||
* @since 2025-12-22
|
||||
*/
|
||||
export const workcaseChatAPI = {
|
||||
baseUrl: '/urban-lifeline/workcase/chat',
|
||||
|
||||
// ====================== AI对话管理 ======================
|
||||
|
||||
/**
|
||||
* 创建对话
|
||||
*/
|
||||
async createChat(chat: TbChat): Promise<ResultDomain<TbChat>> {
|
||||
const response = await api.post<TbChat>(`${this.baseUrl}`, chat)
|
||||
return response.data
|
||||
},
|
||||
|
||||
/**
|
||||
* 更新对话
|
||||
*/
|
||||
async updateChat(chat: TbChat): Promise<ResultDomain<TbChat>> {
|
||||
const response = await api.put<TbChat>(`${this.baseUrl}`, chat)
|
||||
return response.data
|
||||
},
|
||||
|
||||
/**
|
||||
* 查询对话列表
|
||||
*/
|
||||
async getChatList(filter: TbChat): Promise<ResultDomain<TbChat>> {
|
||||
const response = await api.post<TbChat>(`${this.baseUrl}/list`, filter)
|
||||
return response.data
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取对话消息列表
|
||||
*/
|
||||
async getChatMessageList(filter: TbChat): Promise<ResultDomain<TbChatMessage>> {
|
||||
const response = await api.post<TbChatMessage>(`${this.baseUrl}/message/list`, filter)
|
||||
return response.data
|
||||
},
|
||||
|
||||
/**
|
||||
* 准备对话会话
|
||||
*/
|
||||
async prepareChatMessageSession(prepareData: ChatPrepareData): Promise<ResultDomain<string>> {
|
||||
const response = await api.post<string>(`${this.baseUrl}/prepare`, prepareData)
|
||||
return response.data
|
||||
},
|
||||
|
||||
/**
|
||||
* 流式对话(SSE)- 返回EventSource URL
|
||||
*/
|
||||
getStreamUrl(sessionId: string): string {
|
||||
return `${this.baseUrl}/stream/${sessionId}`
|
||||
},
|
||||
|
||||
/**
|
||||
* 停止对话
|
||||
*/
|
||||
async stopChat(filter: TbChat, taskId: string): Promise<ResultDomain<boolean>> {
|
||||
const response = await api.post<boolean>(`${this.baseUrl}/stop/${taskId}`, filter)
|
||||
return response.data
|
||||
},
|
||||
|
||||
/**
|
||||
* 评论对话消息
|
||||
*/
|
||||
async commentChatMessage(filter: TbChat, messageId: string, comment: string): Promise<ResultDomain<boolean>> {
|
||||
const response = await api.post<boolean>(`${this.baseUrl}/comment`, filter, {
|
||||
params: { messageId, comment }
|
||||
})
|
||||
return response.data
|
||||
},
|
||||
|
||||
// ====================== 对话分析 ======================
|
||||
|
||||
/**
|
||||
* 分析对话(AI预填工单信息)
|
||||
*/
|
||||
async analyzeChat(chatId: string): Promise<ResultDomain<TbWorkcaseDTO>> {
|
||||
const response = await api.get<TbWorkcaseDTO>(`${this.baseUrl}/analyze/${chatId}`)
|
||||
return response.data
|
||||
},
|
||||
|
||||
/**
|
||||
* 总结对话
|
||||
*/
|
||||
async summaryChat(chatId: string): Promise<ResultDomain<TbWorkcaseDTO>> {
|
||||
const response = await api.post<TbWorkcaseDTO>(`${this.baseUrl}/summary/${chatId}`)
|
||||
return response.data
|
||||
},
|
||||
|
||||
// ====================== ChatRoom聊天室管理 ======================
|
||||
|
||||
/**
|
||||
* 创建聊天室
|
||||
*/
|
||||
async createChatRoom(chatRoom: TbChatRoomDTO): Promise<ResultDomain<TbChatRoomDTO>> {
|
||||
const response = await api.post<TbChatRoomDTO>(`${this.baseUrl}/room`, chatRoom)
|
||||
return response.data
|
||||
},
|
||||
|
||||
/**
|
||||
* 更新聊天室
|
||||
*/
|
||||
async updateChatRoom(chatRoom: TbChatRoomDTO): Promise<ResultDomain<TbChatRoomDTO>> {
|
||||
const response = await api.put<TbChatRoomDTO>(`${this.baseUrl}/room`, chatRoom)
|
||||
return response.data
|
||||
},
|
||||
|
||||
/**
|
||||
* 关闭聊天室
|
||||
*/
|
||||
async closeChatRoom(roomId: string, closedBy: string): Promise<ResultDomain<boolean>> {
|
||||
const response = await api.post<boolean>(`${this.baseUrl}/room/${roomId}/close`, null, {
|
||||
params: { closedBy }
|
||||
})
|
||||
return response.data
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取聊天室详情
|
||||
*/
|
||||
async getChatRoomById(roomId: string): Promise<ResultDomain<TbChatRoomDTO>> {
|
||||
const response = await api.get<TbChatRoomDTO>(`${this.baseUrl}/room/${roomId}`)
|
||||
return response.data
|
||||
},
|
||||
|
||||
/**
|
||||
* 分页查询聊天室
|
||||
*/
|
||||
async getChatRoomPage(pageRequest: PageRequest<TbChatRoomDTO>): Promise<ResultDomain<ChatRoomVO>> {
|
||||
const response = await api.post<ChatRoomVO>(`${this.baseUrl}/room/page`, pageRequest)
|
||||
return response.data
|
||||
},
|
||||
|
||||
// ====================== ChatRoom成员管理 ======================
|
||||
|
||||
/**
|
||||
* 添加聊天室成员
|
||||
*/
|
||||
async addChatRoomMember(member: TbChatRoomMemberDTO): Promise<ResultDomain<TbChatRoomMemberDTO>> {
|
||||
const response = await api.post<TbChatRoomMemberDTO>(`${this.baseUrl}/room/member`, member)
|
||||
return response.data
|
||||
},
|
||||
|
||||
/**
|
||||
* 移除聊天室成员
|
||||
*/
|
||||
async removeChatRoomMember(memberId: string): Promise<ResultDomain<boolean>> {
|
||||
const response = await api.delete<boolean>(`${this.baseUrl}/room/member/${memberId}`)
|
||||
return response.data
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取聊天室成员列表
|
||||
*/
|
||||
async getChatRoomMemberList(roomId: string): Promise<ResultDomain<ChatMemberVO>> {
|
||||
const response = await api.get<ChatMemberVO>(`${this.baseUrl}/room/${roomId}/members`)
|
||||
return response.data
|
||||
},
|
||||
|
||||
// ====================== ChatRoom消息管理 ======================
|
||||
|
||||
/**
|
||||
* 发送聊天室消息
|
||||
*/
|
||||
async sendMessage(message: TbChatRoomMessageDTO): Promise<ResultDomain<TbChatRoomMessageDTO>> {
|
||||
const response = await api.post<TbChatRoomMessageDTO>(`${this.baseUrl}/room/message`, message)
|
||||
return response.data
|
||||
},
|
||||
|
||||
/**
|
||||
* 分页查询聊天室消息
|
||||
*/
|
||||
async getChatMessagePage(pageRequest: PageRequest<TbChatRoomMessageDTO>): Promise<ResultDomain<ChatRoomMessageVO>> {
|
||||
const response = await api.post<ChatRoomMessageVO>(`${this.baseUrl}/room/message/page`, pageRequest)
|
||||
return response.data
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除聊天室消息
|
||||
*/
|
||||
async deleteMessage(messageId: string): Promise<ResultDomain<boolean>> {
|
||||
const response = await api.delete<boolean>(`${this.baseUrl}/room/message/${messageId}`)
|
||||
return response.data
|
||||
},
|
||||
|
||||
// ====================== 客服人员管理 ======================
|
||||
|
||||
/**
|
||||
* 添加客服人员
|
||||
*/
|
||||
async addCustomerService(customerService: TbCustomerServiceDTO): Promise<ResultDomain<TbCustomerServiceDTO>> {
|
||||
const response = await api.post<TbCustomerServiceDTO>(`${this.baseUrl}/customer-service`, customerService)
|
||||
return response.data
|
||||
},
|
||||
|
||||
/**
|
||||
* 更新客服人员
|
||||
*/
|
||||
async updateCustomerService(customerService: TbCustomerServiceDTO): Promise<ResultDomain<TbCustomerServiceDTO>> {
|
||||
const response = await api.put<TbCustomerServiceDTO>(`${this.baseUrl}/customer-service`, customerService)
|
||||
return response.data
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除客服人员
|
||||
*/
|
||||
async deleteCustomerService(userId: string): Promise<ResultDomain<boolean>> {
|
||||
const response = await api.delete<boolean>(`${this.baseUrl}/customer-service/${userId}`)
|
||||
return response.data
|
||||
},
|
||||
|
||||
/**
|
||||
* 分页查询客服人员
|
||||
*/
|
||||
async getCustomerServicePage(pageRequest: PageRequest<TbCustomerServiceDTO>): Promise<ResultDomain<CustomerServiceVO>> {
|
||||
const response = await api.post<CustomerServiceVO>(`${this.baseUrl}/customer-service/page`, pageRequest)
|
||||
return response.data
|
||||
},
|
||||
|
||||
/**
|
||||
* 更新客服在线状态
|
||||
*/
|
||||
async updateCustomerServiceStatus(userId: string, status: string): Promise<ResultDomain<boolean>> {
|
||||
const response = await api.post<boolean>(`${this.baseUrl}/customer-service/${userId}/status`, null, {
|
||||
params: { status }
|
||||
})
|
||||
return response.data
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取可接待客服列表
|
||||
*/
|
||||
async getAvailableCustomerServices(): Promise<ResultDomain<CustomerServiceVO>> {
|
||||
const response = await api.get<CustomerServiceVO>(`${this.baseUrl}/customer-service/available`)
|
||||
return response.data
|
||||
},
|
||||
|
||||
/**
|
||||
* 自动分配客服
|
||||
*/
|
||||
async assignCustomerService(roomId: string): Promise<ResultDomain<CustomerServiceVO>> {
|
||||
const response = await api.post<CustomerServiceVO>(`${this.baseUrl}/room/${roomId}/assign`)
|
||||
return response.data
|
||||
},
|
||||
|
||||
// ====================== 词云管理 ======================
|
||||
|
||||
/**
|
||||
* 添加词云
|
||||
*/
|
||||
async addWordCloud(wordCloud: TbWordCloudDTO): Promise<ResultDomain<TbWordCloudDTO>> {
|
||||
const response = await api.post<TbWordCloudDTO>(`${this.baseUrl}/wordcloud`, wordCloud)
|
||||
return response.data
|
||||
},
|
||||
|
||||
/**
|
||||
* 更新词云
|
||||
*/
|
||||
async updateWordCloud(wordCloud: TbWordCloudDTO): Promise<ResultDomain<TbWordCloudDTO>> {
|
||||
const response = await api.put<TbWordCloudDTO>(`${this.baseUrl}/wordcloud`, wordCloud)
|
||||
return response.data
|
||||
},
|
||||
|
||||
/**
|
||||
* 查询词云列表
|
||||
*/
|
||||
async getWordCloudList(filter: TbWordCloudDTO): Promise<ResultDomain<TbWordCloudDTO>> {
|
||||
const response = await api.post<TbWordCloudDTO>(`${this.baseUrl}/wordcloud/list`, filter)
|
||||
return response.data
|
||||
},
|
||||
|
||||
/**
|
||||
* 分页查询词云
|
||||
*/
|
||||
async getWordCloudPage(pageRequest: PageRequest<TbWordCloudDTO>): Promise<ResultDomain<TbWordCloudDTO>> {
|
||||
const response = await api.post<TbWordCloudDTO>(`${this.baseUrl}/wordcloud/page`, pageRequest)
|
||||
return response.data
|
||||
}
|
||||
}
|
||||
@@ -1,323 +0,0 @@
|
||||
// 品牌色
|
||||
$brand-color: #0055AA;
|
||||
$brand-color-light: #EBF5FF;
|
||||
$brand-color-hover: #004488;
|
||||
|
||||
.chat-room-main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
// ==================== 聊天室头部 ====================
|
||||
.chat-header {
|
||||
height: 64px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 24px;
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
flex-shrink: 0;
|
||||
|
||||
.header-default {
|
||||
h3 {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 消息容器 ====================
|
||||
.messages-container {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
background: #f8fafc;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
// ==================== Jitsi Meet会议容器 ====================
|
||||
.meeting-container {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
height: 400px;
|
||||
background: #000;
|
||||
border-bottom: 2px solid $brand-color;
|
||||
margin-bottom: 16px;
|
||||
|
||||
iframe {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 消息列表 ====================
|
||||
.messages-list {
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
padding: 24px 16px;
|
||||
|
||||
.message-row {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
margin-bottom: 24px;
|
||||
|
||||
&.is-me {
|
||||
flex-direction: row-reverse;
|
||||
|
||||
.message-bubble {
|
||||
background: $brand-color;
|
||||
color: #fff;
|
||||
border-radius: 16px 16px 4px 16px;
|
||||
|
||||
.message-time {
|
||||
text-align: right;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.other {
|
||||
.message-bubble {
|
||||
background: #fff;
|
||||
border: 1px solid #f1f5f9;
|
||||
border-radius: 16px 16px 16px 4px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.message-avatar {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 50%;
|
||||
flex-shrink: 0;
|
||||
overflow: hidden;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
.message-content-wrapper {
|
||||
max-width: 70%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.message-bubble {
|
||||
padding: 12px 16px;
|
||||
|
||||
.message-text {
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.message-files {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.file-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 12px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
|
||||
&:hover {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.file-icon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 6px;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.file-info {
|
||||
.file-name {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.other .file-item {
|
||||
background: #f8fafc;
|
||||
border-color: #e2e8f0;
|
||||
|
||||
&:hover {
|
||||
background: #f1f5f9;
|
||||
}
|
||||
|
||||
.file-icon {
|
||||
background: $brand-color-light;
|
||||
color: $brand-color;
|
||||
}
|
||||
|
||||
.file-info {
|
||||
color: #374151;
|
||||
}
|
||||
}
|
||||
|
||||
.message-time {
|
||||
font-size: 12px;
|
||||
color: #94a3b8;
|
||||
padding: 0 4px;
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 输入区域 ====================
|
||||
.input-area {
|
||||
padding: 16px 24px 24px;
|
||||
background: #fff;
|
||||
border-top: 1px solid #e2e8f0;
|
||||
flex-shrink: 0;
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 12px;
|
||||
|
||||
.action-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 8px 16px;
|
||||
background: transparent;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
color: #64748b;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
font-size: 14px;
|
||||
|
||||
&:hover {
|
||||
border-color: $brand-color;
|
||||
color: $brand-color;
|
||||
background: $brand-color-light;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.input-wrapper {
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.input-card {
|
||||
background: #f8fafc;
|
||||
border-radius: 12px;
|
||||
border: 1px solid #e2e8f0;
|
||||
overflow: hidden;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:focus-within {
|
||||
border-color: $brand-color;
|
||||
background: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.input-row {
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
.chat-textarea {
|
||||
width: 100%;
|
||||
border: none;
|
||||
outline: none;
|
||||
resize: none;
|
||||
font-size: 14px;
|
||||
color: #374151;
|
||||
background: transparent;
|
||||
line-height: 1.5;
|
||||
min-height: 60px;
|
||||
max-height: 150px;
|
||||
font-family: inherit;
|
||||
|
||||
&::placeholder {
|
||||
color: #94a3b8;
|
||||
}
|
||||
}
|
||||
|
||||
.toolbar-row {
|
||||
padding: 8px 16px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-top: 1px solid #e2e8f0;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.toolbar-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.tool-btn {
|
||||
padding: 8px;
|
||||
color: #94a3b8;
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
&:hover {
|
||||
color: $brand-color;
|
||||
background: $brand-color-light;
|
||||
}
|
||||
}
|
||||
|
||||
.send-btn {
|
||||
padding: 8px 16px;
|
||||
background: #e2e8f0;
|
||||
color: #94a3b8;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
cursor: not-allowed;
|
||||
transition: all 0.2s;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
|
||||
&.active {
|
||||
background: $brand-color;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 2px 8px rgba($brand-color, 0.3);
|
||||
|
||||
&:hover {
|
||||
background: $brand-color-hover;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,241 +0,0 @@
|
||||
<template>
|
||||
<div class="chat-room-main">
|
||||
<!-- 聊天室头部 -->
|
||||
<header class="chat-header">
|
||||
<slot name="header">
|
||||
<div class="header-default">
|
||||
<h3>{{ roomName }}</h3>
|
||||
</div>
|
||||
</slot>
|
||||
</header>
|
||||
|
||||
<!-- 消息容器 -->
|
||||
<div ref="messagesRef" class="messages-container">
|
||||
<!-- Jitsi Meet会议iframe -->
|
||||
<div v-if="showMeeting && meetingUrl" class="meeting-container">
|
||||
<IframeView :src="meetingUrl" />
|
||||
</div>
|
||||
|
||||
<!-- 聊天消息列表 -->
|
||||
<div class="messages-list">
|
||||
<div
|
||||
v-for="message in messages"
|
||||
:key="message.messageId"
|
||||
class="message-row"
|
||||
:class="message.senderId === currentUserId ? 'is-me' : 'other'"
|
||||
>
|
||||
<!-- 头像 -->
|
||||
<div class="message-avatar">
|
||||
<img :src="FILE_DOWNLOAD_URL + message.senderAvatar" />
|
||||
</div>
|
||||
|
||||
<!-- 消息内容 -->
|
||||
<div class="message-content-wrapper">
|
||||
<div class="message-bubble">
|
||||
<p class="message-text">{{ message.content }}</p>
|
||||
|
||||
<!-- 文件列表 -->
|
||||
<div v-if="message.files && message.files.length > 0" class="message-files">
|
||||
<div
|
||||
v-for="file in message.files"
|
||||
:key="file"
|
||||
class="file-item"
|
||||
@click="$emit('download-file', file)"
|
||||
>
|
||||
<div class="file-icon">
|
||||
<FileText :size="16" />
|
||||
</div>
|
||||
<div class="file-info">
|
||||
<div class="file-name">附件</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="message-time">{{ formatTime(message.sendTime) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 输入区域 -->
|
||||
<footer class="input-area">
|
||||
<!-- 操作按钮区域 -->
|
||||
<div class="action-buttons">
|
||||
<!-- 发起会议按钮(始终显示) -->
|
||||
<button class="action-btn" @click="$emit('start-meeting')">
|
||||
<Video :size="18" />
|
||||
发起会议
|
||||
</button>
|
||||
|
||||
<!-- 额外的操作按钮插槽 -->
|
||||
<slot name="action-area"></slot>
|
||||
</div>
|
||||
|
||||
<!-- 输入框 -->
|
||||
<div class="input-wrapper">
|
||||
<div class="input-card">
|
||||
<div class="input-row">
|
||||
<textarea
|
||||
ref="textareaRef"
|
||||
v-model="inputText"
|
||||
@input="adjustHeight"
|
||||
@keydown="handleKeyDown"
|
||||
placeholder="输入消息..."
|
||||
class="chat-textarea"
|
||||
/>
|
||||
</div>
|
||||
<div class="toolbar-row">
|
||||
<div class="toolbar-left">
|
||||
<button class="tool-btn" @click="selectFiles" title="上传文件">
|
||||
<Paperclip :size="18" />
|
||||
</button>
|
||||
<input
|
||||
ref="fileInputRef"
|
||||
type="file"
|
||||
multiple
|
||||
style="display: none"
|
||||
@change="handleFileSelect"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
class="send-btn"
|
||||
:class="{ active: inputText.trim() }"
|
||||
:disabled="!inputText.trim()"
|
||||
@click="sendMessage"
|
||||
>
|
||||
<Send :size="18" />
|
||||
发送
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, nextTick } from 'vue'
|
||||
import { FileText, Video, Paperclip, Send } from 'lucide-vue-next'
|
||||
import IframeView from '@/components/iframe/IframeView.vue'
|
||||
|
||||
interface ChatMessageVO {
|
||||
messageId: string
|
||||
senderId: string
|
||||
senderName: string
|
||||
senderAvatar: string
|
||||
content: string
|
||||
files: string[]
|
||||
sendTime: string
|
||||
}
|
||||
|
||||
interface Props {
|
||||
messages: ChatMessageVO[]
|
||||
currentUserId: string
|
||||
roomName?: string
|
||||
meetingUrl?: string
|
||||
showMeeting?: boolean
|
||||
fileDownloadUrl?: string
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
roomName: '聊天室',
|
||||
showMeeting: false,
|
||||
fileDownloadUrl: ''
|
||||
})
|
||||
|
||||
const FILE_DOWNLOAD_URL = props.fileDownloadUrl
|
||||
|
||||
const emit = defineEmits<{
|
||||
'send-message': [content: string, files: File[]]
|
||||
'start-meeting': []
|
||||
'download-file': [fileId: string]
|
||||
}>()
|
||||
|
||||
defineSlots<{
|
||||
header?: () => any
|
||||
'action-area'?: () => any
|
||||
}>()
|
||||
|
||||
const inputText = ref('')
|
||||
const selectedFiles = ref<File[]>([])
|
||||
const messagesRef = ref<HTMLElement | null>(null)
|
||||
const textareaRef = ref<HTMLTextAreaElement | null>(null)
|
||||
const fileInputRef = ref<HTMLInputElement | null>(null)
|
||||
|
||||
// 发送消息
|
||||
const sendMessage = () => {
|
||||
if (!inputText.value.trim() && selectedFiles.value.length === 0) return
|
||||
|
||||
emit('send-message', inputText.value.trim(), selectedFiles.value)
|
||||
|
||||
inputText.value = ''
|
||||
selectedFiles.value = []
|
||||
|
||||
if (textareaRef.value) {
|
||||
textareaRef.value.style.height = 'auto'
|
||||
}
|
||||
|
||||
scrollToBottom()
|
||||
}
|
||||
|
||||
// 选择文件
|
||||
const selectFiles = () => {
|
||||
fileInputRef.value?.click()
|
||||
}
|
||||
|
||||
// 处理文件选择
|
||||
const handleFileSelect = (e: Event) => {
|
||||
const files = (e.target as HTMLInputElement).files
|
||||
if (!files || files.length === 0) return
|
||||
|
||||
selectedFiles.value = Array.from(files)
|
||||
}
|
||||
|
||||
// 滚动到底部
|
||||
const scrollToBottom = () => {
|
||||
nextTick(() => {
|
||||
if (messagesRef.value) {
|
||||
messagesRef.value.scrollTop = messagesRef.value.scrollHeight
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 自动调整输入框高度
|
||||
const adjustHeight = () => {
|
||||
const el = textareaRef.value
|
||||
if (el) {
|
||||
el.style.height = 'auto'
|
||||
el.style.height = Math.min(el.scrollHeight, 150) + 'px'
|
||||
}
|
||||
}
|
||||
|
||||
// 键盘事件
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault()
|
||||
sendMessage()
|
||||
}
|
||||
}
|
||||
|
||||
// 格式化时间
|
||||
const formatTime = (time: string) => {
|
||||
const date = new Date(time)
|
||||
const now = new Date()
|
||||
const diff = now.getTime() - date.getTime()
|
||||
|
||||
if (diff < 60000) return '刚刚'
|
||||
if (diff < 3600000) return Math.floor(diff / 60000) + '分钟前'
|
||||
if (diff < 86400000) return Math.floor(diff / 3600000) + '小时前'
|
||||
|
||||
return date.toLocaleDateString('zh-CN', { month: '2-digit', day: '2-digit' })
|
||||
}
|
||||
|
||||
// 暴露方法给父组件
|
||||
defineExpose({
|
||||
scrollToBottom
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import url("./ChatRoom.scss");
|
||||
</style>
|
||||
@@ -1 +0,0 @@
|
||||
export { default as ChatRoom } from './chatRoom/ChatRoom.vue';
|
||||
@@ -2,6 +2,5 @@ export * from './base'
|
||||
export * from './dynamicFormItem'
|
||||
export * from './ai'
|
||||
export * from './file'
|
||||
export * from './chatRoom'
|
||||
// 通用视图组件
|
||||
export { default as IframeView } from './iframe/IframeView.vue'
|
||||
@@ -90,22 +90,120 @@ export interface ChatPrepareData {
|
||||
userType?: boolean
|
||||
}
|
||||
|
||||
// ==================== 请求参数类型(必传校验) ====================
|
||||
|
||||
/**
|
||||
* 创建对话参数
|
||||
*/
|
||||
export interface CreateChatParam {
|
||||
/** 智能体ID(必传) */
|
||||
agentId: string
|
||||
/** 用户ID(必传) */
|
||||
userId: string
|
||||
/** 用户类型(必传) */
|
||||
userType: boolean
|
||||
/** 对话标题 */
|
||||
title?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 准备流式对话参数
|
||||
*/
|
||||
export interface PrepareChatParam {
|
||||
/** 对话ID(必传) */
|
||||
chatId: string
|
||||
/** 用户问题(必传) */
|
||||
query: string
|
||||
/** 智能体ID(必传) */
|
||||
agentId: string
|
||||
/** 用户类型(必传) */
|
||||
userType: boolean
|
||||
/** 用户ID */
|
||||
userId?: string
|
||||
/** 文件列表 */
|
||||
files?: DifyFileInfo[]
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止对话参数
|
||||
*/
|
||||
export interface StopChatParams {
|
||||
export interface StopChatParam {
|
||||
/** 任务ID(必传) */
|
||||
taskId: string
|
||||
/** 智能体ID(必传) */
|
||||
agentId: string
|
||||
/** 用户ID(必传) */
|
||||
userId: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 评价消息参数
|
||||
*/
|
||||
export interface CommentMessageParams {
|
||||
export interface CommentMessageParam {
|
||||
/** 智能体ID(必传) */
|
||||
agentId: string
|
||||
/** 对话ID(必传) */
|
||||
chatId: string
|
||||
/** 消息ID(必传) */
|
||||
messageId: string
|
||||
/** 评价内容(必传) */
|
||||
comment: string
|
||||
/** 用户ID(必传) */
|
||||
userId: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询对话列表参数
|
||||
*/
|
||||
export interface ChatListParam {
|
||||
/** 用户ID(必传) */
|
||||
userId: string
|
||||
/** 智能体ID */
|
||||
agentId?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询对话消息列表参数
|
||||
*/
|
||||
export interface ChatMessageListParam {
|
||||
/** 对话ID(必传) */
|
||||
chatId: string
|
||||
}
|
||||
|
||||
// ==================== SSE 流式对话类型 ====================
|
||||
|
||||
/**
|
||||
* SSE 消息事件数据
|
||||
*/
|
||||
export interface SSEMessageData {
|
||||
/** 事件类型 */
|
||||
event?: string
|
||||
/** 回答内容 */
|
||||
answer?: string
|
||||
/** 任务ID */
|
||||
task_id?: string
|
||||
/** 错误消息 */
|
||||
message?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* SSE 回调函数
|
||||
*/
|
||||
export interface SSECallbacks {
|
||||
/** 收到消息 */
|
||||
onMessage?: (data: SSEMessageData) => void
|
||||
/** 消息结束 */
|
||||
onEnd?: (taskId: string) => void
|
||||
/** 发生错误 */
|
||||
onError?: (error: string) => void
|
||||
/** 请求完成(无论成功失败) */
|
||||
onComplete?: () => void
|
||||
}
|
||||
|
||||
/**
|
||||
* SSE 请求任务对象
|
||||
*/
|
||||
export interface SSETask {
|
||||
/** 停止请求 */
|
||||
abort: () => void
|
||||
}
|
||||
@@ -12,4 +12,4 @@ export * from "./message"
|
||||
export * from "./ai"
|
||||
export * from "./crontab"
|
||||
export * from "./bidding"
|
||||
export * from "./workcase"
|
||||
// workcase 类型已移至各自的服务包中(如 workcase_wechat)
|
||||
@@ -1,351 +0,0 @@
|
||||
import { BaseVO } from '../base'
|
||||
import { BaseDTO } from '../base'
|
||||
|
||||
// ==================== DTO ====================
|
||||
|
||||
/**
|
||||
* 聊天室DTO
|
||||
*/
|
||||
export interface TbChatRoomDTO extends BaseDTO {
|
||||
roomId?: string
|
||||
workcaseId?: string
|
||||
roomName?: string
|
||||
roomType?: string
|
||||
status?: string
|
||||
guestId?: string
|
||||
guestName?: string
|
||||
aiSessionId?: string
|
||||
currentAgentId?: string
|
||||
agentCount?: number
|
||||
messageCount?: number
|
||||
unreadCount?: number
|
||||
lastMessageTime?: string
|
||||
lastMessage?: string
|
||||
closedBy?: string
|
||||
closedTime?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 聊天消息DTO
|
||||
*/
|
||||
export interface TbChatRoomMessageDTO extends BaseDTO {
|
||||
messageId?: string
|
||||
roomId?: string
|
||||
senderId?: string
|
||||
senderType?: string
|
||||
senderName?: string
|
||||
messageType?: string
|
||||
content?: string
|
||||
files?: string[]
|
||||
contentExtra?: Record<string, any>
|
||||
replyToMsgId?: string
|
||||
isAiMessage?: boolean
|
||||
aiMessageId?: string
|
||||
status?: string
|
||||
readCount?: number
|
||||
sendTime?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 聊天室成员DTO
|
||||
*/
|
||||
export interface TbChatRoomMemberDTO extends BaseDTO {
|
||||
memberId?: string
|
||||
roomId?: string
|
||||
userId?: string
|
||||
userType?: string
|
||||
userName?: string
|
||||
status?: string
|
||||
unreadCount?: number
|
||||
lastReadTime?: string
|
||||
lastReadMsgId?: string
|
||||
joinTime?: string
|
||||
leaveTime?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 视频会议DTO
|
||||
*/
|
||||
export interface TbVideoMeetingDTO extends BaseDTO {
|
||||
meetingId?: string
|
||||
roomId?: string
|
||||
workcaseId?: string
|
||||
meetingName?: string
|
||||
meetingPassword?: string
|
||||
jwtToken?: string
|
||||
jitsiRoomName?: string
|
||||
jitsiServerUrl?: string
|
||||
status?: string
|
||||
creatorId?: string
|
||||
creatorType?: string
|
||||
creatorName?: string
|
||||
participantCount?: number
|
||||
maxParticipants?: number
|
||||
actualStartTime?: string
|
||||
actualEndTime?: string
|
||||
durationSeconds?: number
|
||||
iframeUrl?: string
|
||||
config?: Record<string, any>
|
||||
}
|
||||
|
||||
/**
|
||||
* 会议参与记录DTO
|
||||
*/
|
||||
export interface TbMeetingParticipantDTO extends BaseDTO {
|
||||
participantId?: string
|
||||
meetingId?: string
|
||||
userId?: string
|
||||
userType?: string
|
||||
userName?: string
|
||||
joinTime?: string
|
||||
leaveTime?: string
|
||||
durationSeconds?: number
|
||||
isModerator?: boolean
|
||||
joinMethod?: string
|
||||
deviceInfo?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 会议转录记录表数据对象DTO
|
||||
*/
|
||||
export interface TbMeetingTranscriptionDTO extends BaseDTO {
|
||||
/** 转录记录ID */
|
||||
transcriptionId?: string
|
||||
/** 会议ID */
|
||||
meetingId?: string
|
||||
/** 说话人ID */
|
||||
speakerId?: string
|
||||
/** 说话人名称 */
|
||||
speakerName?: string
|
||||
/** 说话人类型:guest-来客 agent-客服 */
|
||||
speakerType?: string
|
||||
/** 转录文本内容 */
|
||||
content?: string
|
||||
/** 原始转录结果 */
|
||||
contentRaw?: string
|
||||
/** 语言 */
|
||||
language?: string
|
||||
/** 识别置信度(0-1) */
|
||||
confidence?: number
|
||||
/** 语音开始时间 */
|
||||
speechStartTime?: string
|
||||
/** 语音结束时间 */
|
||||
speechEndTime?: string
|
||||
/** 语音时长(毫秒) */
|
||||
durationMs?: number
|
||||
/** 音频片段URL */
|
||||
audioUrl?: string
|
||||
/** 片段序号 */
|
||||
segmentIndex?: number
|
||||
/** 是否最终结果 */
|
||||
isFinal?: boolean
|
||||
/** 服务提供商 */
|
||||
serviceProvider?: string
|
||||
}
|
||||
|
||||
// ==================== VO ====================
|
||||
|
||||
/**
|
||||
* 聊天室VO
|
||||
* 用于前端展示聊天室信息
|
||||
*/
|
||||
export interface ChatRoomVO extends BaseVO {
|
||||
roomId?: string
|
||||
workcaseId?: string
|
||||
roomName?: string
|
||||
roomType?: string
|
||||
status?: string
|
||||
guestId?: string
|
||||
guestName?: string
|
||||
aiSessionId?: string
|
||||
currentAgentId?: string
|
||||
currentAgentName?: string
|
||||
agentCount?: number
|
||||
messageCount?: number
|
||||
unreadCount?: number
|
||||
lastMessageTime?: string
|
||||
lastMessage?: string
|
||||
closedBy?: string
|
||||
closedByName?: string
|
||||
closedTime?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 聊天消息VO
|
||||
* 用于前端展示聊天消息
|
||||
*/
|
||||
export interface ChatRoomMessageVO extends BaseVO {
|
||||
messageId?: string
|
||||
roomId?: string
|
||||
senderId?: string
|
||||
senderType?: string
|
||||
senderName?: string
|
||||
senderAvatar?: string
|
||||
messageType?: string
|
||||
content?: string
|
||||
files?: string[]
|
||||
fileCount?: number
|
||||
contentExtra?: Record<string, any>
|
||||
replyToMsgId?: string
|
||||
replyToMsgContent?: string
|
||||
isAiMessage?: boolean
|
||||
aiMessageId?: string
|
||||
status?: string
|
||||
readCount?: number
|
||||
sendTime?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 聊天室成员VO
|
||||
* 用于前端展示聊天室成员信息
|
||||
*/
|
||||
export interface ChatMemberVO extends BaseVO {
|
||||
memberId?: string
|
||||
roomId?: string
|
||||
userId?: string
|
||||
userType?: string
|
||||
userName?: string
|
||||
userAvatar?: string
|
||||
status?: string
|
||||
unreadCount?: number
|
||||
lastReadTime?: string
|
||||
lastReadMsgId?: string
|
||||
joinTime?: string
|
||||
leaveTime?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 视频会议VO
|
||||
* 用于前端展示Jitsi Meet会议信息
|
||||
*/
|
||||
export interface VideoMeetingVO extends BaseVO {
|
||||
meetingId?: string
|
||||
roomId?: string
|
||||
workcaseId?: string
|
||||
meetingName?: string
|
||||
meetingPassword?: string
|
||||
jwtToken?: string
|
||||
jitsiRoomName?: string
|
||||
jitsiServerUrl?: string
|
||||
status?: string
|
||||
creatorId?: string
|
||||
creatorType?: string
|
||||
creatorName?: string
|
||||
participantCount?: number
|
||||
maxParticipants?: number
|
||||
startTime?: string
|
||||
endTime?: string
|
||||
durationSeconds?: number
|
||||
durationFormatted?: string
|
||||
iframeUrl?: string
|
||||
config?: Record<string, any>
|
||||
}
|
||||
|
||||
/**
|
||||
* 会议参与记录VO
|
||||
* 用于前端展示会议参与者信息
|
||||
*/
|
||||
export interface MeetingParticipantVO extends BaseVO {
|
||||
participantId?: string
|
||||
meetingId?: string
|
||||
userId?: string
|
||||
userType?: string
|
||||
userName?: string
|
||||
userAvatar?: string
|
||||
joinTime?: string
|
||||
leaveTime?: string
|
||||
durationSeconds?: number
|
||||
durationFormatted?: string
|
||||
isModerator?: boolean
|
||||
joinMethod?: string
|
||||
joinMethodName?: string
|
||||
deviceInfo?: string
|
||||
isOnline?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息参数
|
||||
*/
|
||||
export interface SendMessageParam {
|
||||
roomId: string
|
||||
content: string
|
||||
files?: string[]
|
||||
messageType?: string
|
||||
replyToMsgId?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建会议参数
|
||||
*/
|
||||
export interface CreateMeetingParam {
|
||||
roomId: string
|
||||
workcaseId: string
|
||||
meetingName?: string
|
||||
meetingPassword?: string
|
||||
maxParticipants?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记已读参数
|
||||
*/
|
||||
export interface MarkReadParam {
|
||||
roomId: string
|
||||
messageIds?: string[]
|
||||
}
|
||||
|
||||
// ==================== 客服相关 ====================
|
||||
|
||||
/**
|
||||
* 客服人员DTO
|
||||
*/
|
||||
export interface TbCustomerServiceDTO extends BaseDTO {
|
||||
userId?: string
|
||||
userName?: string
|
||||
status?: string
|
||||
maxConcurrentChats?: number
|
||||
currentChatCount?: number
|
||||
totalServedCount?: number
|
||||
avgResponseTime?: number
|
||||
avgRating?: number
|
||||
skills?: string[]
|
||||
priority?: number
|
||||
lastOnlineTime?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 客服人员VO
|
||||
*/
|
||||
export interface CustomerServiceVO extends BaseVO {
|
||||
userId?: string
|
||||
userName?: string
|
||||
userAvatar?: string
|
||||
status?: string
|
||||
statusName?: string
|
||||
maxConcurrentChats?: number
|
||||
currentChatCount?: number
|
||||
totalServedCount?: number
|
||||
avgResponseTime?: number
|
||||
avgResponseTimeFormatted?: string
|
||||
avgRating?: number
|
||||
skills?: string[]
|
||||
skillNames?: string[]
|
||||
priority?: number
|
||||
lastOnlineTime?: string
|
||||
isAvailable?: boolean
|
||||
}
|
||||
|
||||
// ==================== 词云相关 ====================
|
||||
|
||||
/**
|
||||
* 词云DTO
|
||||
*/
|
||||
export interface TbWordCloudDTO extends BaseDTO {
|
||||
wordCloudId?: string
|
||||
word?: string
|
||||
category?: string
|
||||
weight?: number
|
||||
frequency?: number
|
||||
sentiment?: string
|
||||
source?: string
|
||||
relatedWords?: string[]
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
import { BaseVO } from '../base'
|
||||
|
||||
// ==================== VO ====================
|
||||
|
||||
/**
|
||||
* 会话VO
|
||||
* 用于前端展示会话信息
|
||||
*/
|
||||
export interface ConversationVO extends BaseVO {
|
||||
/** 会话ID */
|
||||
conversationId?: string
|
||||
/** 客户ID */
|
||||
customerId?: string
|
||||
/** 客户姓名 */
|
||||
customerName?: string
|
||||
/** 客户头像 */
|
||||
customerAvatar?: string
|
||||
/** 会话类型 */
|
||||
conversationType?: string
|
||||
/** 会话类型名称 */
|
||||
conversationTypeName?: string
|
||||
/** 渠道 */
|
||||
channel?: string
|
||||
/** 渠道名称 */
|
||||
channelName?: string
|
||||
/** 智能体ID或客服人员ID */
|
||||
agentId?: string
|
||||
/** 座席名称 */
|
||||
agentName?: string
|
||||
/** 座席类型 */
|
||||
agentType?: string
|
||||
/** 座席类型名称 */
|
||||
agentTypeName?: string
|
||||
/** 会话开始时间 */
|
||||
sessionStartTime?: string
|
||||
/** 会话结束时间 */
|
||||
sessionEndTime?: string
|
||||
/** 会话时长(秒) */
|
||||
durationSeconds?: number
|
||||
/** 会话时长格式化显示 */
|
||||
durationFormatted?: string
|
||||
/** 消息数量 */
|
||||
messageCount?: number
|
||||
/** 会话状态 */
|
||||
conversationStatus?: string
|
||||
/** 会话状态名称 */
|
||||
conversationStatusName?: string
|
||||
/** 会话状态颜色 */
|
||||
statusColor?: string
|
||||
/** 满意度评分(1-5星) */
|
||||
satisfactionRating?: number
|
||||
/** 满意度反馈 */
|
||||
satisfactionFeedback?: string
|
||||
/** 会话摘要 */
|
||||
summary?: string
|
||||
/** 会话标签 */
|
||||
tags?: string[]
|
||||
/** 会话元数据 */
|
||||
metadata?: Record<string, any>
|
||||
/** 最后一条消息内容 */
|
||||
lastMessageContent?: string
|
||||
/** 最后一条消息时间 */
|
||||
lastMessageTime?: string
|
||||
/** 创建者姓名 */
|
||||
creatorName?: string
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
import { BaseDTO, BaseVO } from '../base'
|
||||
|
||||
// ==================== DTO ====================
|
||||
|
||||
/**
|
||||
* 客服人员配置表数据对象DTO
|
||||
*/
|
||||
export interface TbCustomerServiceDTO extends BaseDTO {
|
||||
/** 员工ID(关联sys用户ID) */
|
||||
userId?: string
|
||||
/** 员工姓名 */
|
||||
username?: string
|
||||
/** 员工工号 */
|
||||
userCode?: string
|
||||
/** 状态:online-在线 busy-忙碌 offline-离线 */
|
||||
status?: string
|
||||
/** 技能标签 */
|
||||
skillTags?: string[]
|
||||
/** 最大并发接待数 */
|
||||
maxConcurrent?: number
|
||||
/** 当前工作量 */
|
||||
currentWorkload?: number
|
||||
/** 累计服务次数 */
|
||||
totalServed?: number
|
||||
/** 平均响应时间(秒) */
|
||||
avgResponseTime?: number
|
||||
/** 满意度评分(0-5) */
|
||||
satisfactionScore?: number
|
||||
}
|
||||
|
||||
// ==================== VO ====================
|
||||
|
||||
/**
|
||||
* 客服人员配置VO
|
||||
* 用于前端展示客服人员信息
|
||||
*/
|
||||
export interface CustomerServiceVO extends BaseVO {
|
||||
/** 员工ID(关联sys用户ID) */
|
||||
userId?: string
|
||||
/** 员工姓名 */
|
||||
username?: string
|
||||
/** 员工工号 */
|
||||
userCode?: string
|
||||
/** 员工头像 */
|
||||
avatar?: string
|
||||
/** 状态:online-在线 busy-忙碌 offline-离线 */
|
||||
status?: string
|
||||
/** 状态名称 */
|
||||
statusName?: string
|
||||
/** 技能标签 */
|
||||
skillTags?: string[]
|
||||
/** 最大并发接待数 */
|
||||
maxConcurrent?: number
|
||||
/** 当前工作量 */
|
||||
currentWorkload?: number
|
||||
/** 累计服务次数 */
|
||||
totalServed?: number
|
||||
/** 平均响应时间(秒) */
|
||||
avgResponseTime?: number
|
||||
/** 平均响应时间(格式化) */
|
||||
avgResponseTimeFormatted?: string
|
||||
/** 满意度评分(0-5) */
|
||||
satisfactionScore?: number
|
||||
/** 是否可接待(工作量未满) */
|
||||
isAvailable?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* 客户信息VO
|
||||
* 用于前端展示客户信息
|
||||
*/
|
||||
export interface CustomerVO extends BaseVO {
|
||||
/** 客户ID */
|
||||
customerId?: string
|
||||
/** 客户编号 */
|
||||
customerNo?: string
|
||||
/** 客户姓名 */
|
||||
customerName?: string
|
||||
/** 客户类型 */
|
||||
customerType?: string
|
||||
/** 客户类型名称 */
|
||||
customerTypeName?: string
|
||||
/** 公司名称 */
|
||||
companyName?: string
|
||||
/** 电话 */
|
||||
phone?: string
|
||||
/** 邮箱 */
|
||||
email?: string
|
||||
/** 微信OpenID */
|
||||
wechatOpenid?: string
|
||||
/** 头像URL */
|
||||
avatar?: string
|
||||
/** 性别 */
|
||||
gender?: number
|
||||
/** 性别名称 */
|
||||
genderName?: string
|
||||
/** 地址 */
|
||||
address?: string
|
||||
/** 客户等级 */
|
||||
customerLevel?: string
|
||||
/** 客户等级名称 */
|
||||
customerLevelName?: string
|
||||
/** 客户来源 */
|
||||
customerSource?: string
|
||||
/** 客户来源名称 */
|
||||
customerSourceName?: string
|
||||
/** 客户标签 */
|
||||
tags?: string[]
|
||||
/** 备注 */
|
||||
notes?: string
|
||||
/** CRM系统客户ID */
|
||||
crmCustomerId?: string
|
||||
/** 最后联系时间 */
|
||||
lastContactTime?: string
|
||||
/** 咨询总次数 */
|
||||
totalConsultations?: number
|
||||
/** 订单总数 */
|
||||
totalOrders?: number
|
||||
/** 总消费金额 */
|
||||
totalAmount?: number
|
||||
/** 满意度评分 */
|
||||
satisfactionScore?: number
|
||||
/** 状态 */
|
||||
status?: string
|
||||
/** 状态名称 */
|
||||
statusName?: string
|
||||
/** 状态颜色 */
|
||||
statusColor?: string
|
||||
/** 创建者姓名 */
|
||||
creatorName?: string
|
||||
/** 更新者姓名 */
|
||||
updaterName?: string
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
export * from "./workcase"
|
||||
export * from "./chatRoom"
|
||||
export * from "./customer"
|
||||
export * from "./conversation"
|
||||
export * from "./wordCloud"
|
||||
@@ -1,19 +0,0 @@
|
||||
import { BaseDTO } from '../base'
|
||||
|
||||
// ==================== DTO ====================
|
||||
|
||||
/**
|
||||
* 词云表数据对象DTO
|
||||
*/
|
||||
export interface TbWordCloudDTO extends BaseDTO {
|
||||
/** 词条ID */
|
||||
wordId?: string
|
||||
/** 词语 */
|
||||
word?: string
|
||||
/** 词频 */
|
||||
frequency?: string
|
||||
/** 分类 */
|
||||
category?: string
|
||||
/** 统计日期 */
|
||||
statDate?: string
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
import type { BaseDTO } from '@/types/base'
|
||||
|
||||
/**
|
||||
* 工单表对象
|
||||
*/
|
||||
export interface TbWorkcaseDTO extends BaseDTO {
|
||||
/** 工单ID */
|
||||
workcaseId?: string
|
||||
/** 来客ID */
|
||||
userId?: string
|
||||
/** 来客姓名 */
|
||||
username?: string
|
||||
/** 来客电话 */
|
||||
phone?: string
|
||||
/** 故障类型 */
|
||||
type?: string
|
||||
/** 设备名称 */
|
||||
device?: string
|
||||
/** 设备代码 */
|
||||
deviceCode?: string
|
||||
/** 工单图片列表 */
|
||||
imgs?: string[]
|
||||
/** 紧急程度 normal-普通 emergency-紧急 */
|
||||
emergency?: 'normal' | 'emergency'
|
||||
/** 状态 pending-待处理 processing-处理中 done-已完成 */
|
||||
status?: 'pending' | 'processing' | 'done'
|
||||
/** 处理人ID */
|
||||
processor?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 工单过程表DTO
|
||||
*/
|
||||
export interface TbWorkcaseProcessDTO extends BaseDTO {
|
||||
/** 工单ID */
|
||||
workcaseId?: string
|
||||
/** 过程ID */
|
||||
processId?: string
|
||||
/** 动作 info:记录,assign:指派,redeploy:转派,repeal:撤销,finish:完成 */
|
||||
action?: 'info' | 'assign' | 'redeploy' | 'repeal' | 'finish'
|
||||
/** 消息 */
|
||||
message?: string
|
||||
/** 携带文件列表 */
|
||||
files?: string[]
|
||||
/** 处理人(指派、转派专属) */
|
||||
processor?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 工单设备涉及的文件DTO
|
||||
*/
|
||||
export interface TbWorkcaseDeviceDTO extends BaseDTO {
|
||||
/** 工单ID */
|
||||
workcaseId?: string
|
||||
/** 设备名称 */
|
||||
device?: string
|
||||
/** 设备代码 */
|
||||
deviceCode?: string
|
||||
/** 文件ID */
|
||||
fileId?: string
|
||||
/** 文件名 */
|
||||
fileName?: string
|
||||
/** 文件根ID */
|
||||
fileRootId?: string
|
||||
}
|
||||
@@ -40,13 +40,11 @@ export default defineConfig({
|
||||
'./components/iframe/IframeView.vue': './src/components/iframe/IframeView.vue',
|
||||
'./components/ai/knowledge/DocumentSegment.vue': './src/components/ai/knowledge/documentSegment/DocumentSegment.vue',
|
||||
'./components/ai/knowledge/DocumentDetail.vue': './src/components/ai/knowledge/documentDetail/DocumentDetail.vue',
|
||||
'./components/chatRoom/ChatRoom.vue': './src/components/chatRoom/chatRoom/ChatRoom.vue',
|
||||
|
||||
// ========== API 模块 ==========
|
||||
'./api': './src/api/index.ts',
|
||||
'./api/auth': './src/api/auth/auth.ts',
|
||||
'./api/file': './src/api/file/file.ts',
|
||||
'./api/workcase': './src/api/workcase/index.ts',
|
||||
'./api/ai': './src/api/ai/index.ts',
|
||||
|
||||
// ========== Utils 工具模块 ==========
|
||||
@@ -62,7 +60,6 @@ export default defineConfig({
|
||||
'./types/auth': './src/types/auth/index.ts',
|
||||
'./types/file': './src/types/file/index.ts',
|
||||
'./types/sys': './src/types/sys/index.ts',
|
||||
'./types/workcase': './src/types/workcase/index.ts',
|
||||
'./types/ai': './src/types/ai/index.ts',
|
||||
|
||||
// ========== Config 配置模块 ==========
|
||||
|
||||
Reference in New Issue
Block a user