diff --git a/src/views/admin/customer/index.vue b/src/views/admin/customer/index.vue index 6a427a2..1c505ea 100644 --- a/src/views/admin/customer/index.vue +++ b/src/views/admin/customer/index.vue @@ -40,7 +40,7 @@ {{ item1.name }} - + @@ -64,7 +64,7 @@ {{ item1.name }} - + diff --git a/src/views/admin/inquiry_system/index.vue b/src/views/admin/inquiry_system/index.vue index 963f6f7..3804990 100644 --- a/src/views/admin/inquiry_system/index.vue +++ b/src/views/admin/inquiry_system/index.vue @@ -91,7 +91,10 @@ {{scope.row.countryInfo? scope.row.countryInfo.cn_addr : ''}} - {{ scope.row.countryInfo.steering }} + + {{ scope.row.countryInfo.steering }} + {{ scope.row.countryInfo.steering }} + diff --git a/src/views/admin/user/index.vue b/src/views/admin/user/index.vue index ee47432..119288e 100644 --- a/src/views/admin/user/index.vue +++ b/src/views/admin/user/index.vue @@ -30,22 +30,23 @@ lass="customer-table" > - + - - + + diff --git a/src/views/work/myOrder/index.vue b/src/views/work/myOrder/index.vue index 3b2fd0a..112f024 100644 --- a/src/views/work/myOrder/index.vue +++ b/src/views/work/myOrder/index.vue @@ -183,18 +183,96 @@ - + - {{ message.user_id }} + {{ message.user_name }} {{ message.updated_at }} {{ message.content }} + + + + + + + + + + + + + {{ attachment.original_name }} + + 下载 + + + + + + + + + + + 添加附件 + + + + + + + + + + + + {{ file.name }} + + + + + + @@ -226,7 +304,7 @@ - + @@ -247,6 +325,7 @@ import { useAuth } from "@/composables/useAuth"; import { useUserStore } from '@/store/modules/user' import { useUploadHeaders } from '@/composables/useUploadHeaders' + import { Paperclip, Document, Close } from '@element-plus/icons-vue' const { uploadHeaders } = useUploadHeaders() const { info } = useUserStore() const userid = info.user.id @@ -279,6 +358,10 @@ } const openChat = (row: any) => { isDrawerVisible.value = true + // 清空数据 + messageText.value = '' + messages.value = [] + chatFileList.value = [] getConversationList(row.id) } @@ -380,14 +463,28 @@ const sendMessage = async () => { const text = messageText.value.trim() const ids = ticket_id.value - if (!text) return + if (!text && chatFileList.value.length === 0) return + + // 准备附件数据 + const attachments = chatFileList.value.map(file => { + const data = file.response?.data + if (data) { + return { + original_name: data.original_name, + storage_path: data.storage_path || data.file_name + } + } + return null + }).filter(Boolean) const { message } = await WorkService.conversationAdd({ content: text, ticket_id: ids, + attachment: attachments }) ElMessage.success(message) messageText.value = '' + chatFileList.value = [] // 清空附件列表 getConversationList(ids) scrollToBottom() } @@ -523,6 +620,8 @@ const imageViewerVisible = ref(false) const imageViewerIndex = ref(0) const imageList = computed(() => fileList.value.filter(file => /\.(jpg|jpeg|png|gif|webp|bmp)$/i.test(file.name)).map(file => file.file_url?file.file_url:file.response.data.file_url)) + // 聊天图片预览列表 + const chatImageList = ref([]) function handleImageClick(idx: number) { imageViewerIndex.value = idx imageViewerVisible.value = true @@ -533,6 +632,52 @@ function handleVideoClick(url: string) { currentVideoUrl.value = url videoDialogVisible.value = true + } + + // 聊天相关 + const chatFileList = ref([]) + const handleChatUploadSuccess = (response: any, uploadFile: any, uploadFiles: any[]) => { + chatFileList.value = uploadFiles.filter(item => item.status === 'success').map(item => { + const data = item.response?.data + if (data) { + return { + name: data.original_name, + file_url: data.storage_path_full, + status: 'success', + response: { data: data } + } + } + return null + }).filter(Boolean) + } + const handleChatExceed = () => { + ElMessage.warning('最多上传5个文件') + } + const removeChatFile = (index: number) => { + chatFileList.value.splice(index, 1) + } + const handleChatImageClick = (url: string) => { + // 设置聊天图片预览列表 + chatImageList.value = [url] + imageViewerIndex.value = 0 + imageViewerVisible.value = true + } + + const closeImageViewer = () => { + imageViewerVisible.value = false + chatImageList.value = [] // 清空聊天图片列表 + } + const handleChatVideoClick = (url: string) => { + currentVideoUrl.value = url + videoDialogVisible.value = true + } + const downloadAttachment = (attachment: any) => { + const link = document.createElement('a') + link.href = attachment.storage_path_full + link.download = attachment.original_name + document.body.appendChild(link) + link.click() + document.body.removeChild(link) } @@ -618,6 +763,7 @@ .message-info { flex-direction: row; + align-items: center; } .message-text { @@ -634,6 +780,7 @@ .message-info { flex-direction: row-reverse; + align-items: center; } .message-text { @@ -757,4 +904,101 @@ margin-top: 2px; } } + + // 聊天附件样式 + .message-right .message-attachments{ + text-align: right; + } + .message-left .message-attachments{ + text-align: left; + } + .message-attachments { + margin-top: 8px; + + .attachment-item { + margin-bottom: 8px; + + .attachment-thumb { + max-width: 200px; + max-height: 150px; + border-radius: 6px; + cursor: pointer; + border: 1px solid var(--el-border-color-lighter); + transition: transform 0.2s; + + &:hover { + transform: scale(1.05); + } + } + + .file-attachment { + display: flex; + align-items: center; + gap: 8px; + padding: 8px 12px; + background: var(--el-fill-color-light); + border-radius: 6px; + border: 1px solid var(--el-border-color-lighter); + + .file-icon { + font-size: 16px; + color: var(--el-text-color-regular); + } + + .file-name { + flex: 1; + font-size: 14px; + color: var(--el-text-color-regular); + word-break: break-all; + } + } + } + } + + .chat-attachment-upload { + margin-bottom: 12px; + + .chat-upload { + margin-bottom: 8px; + } + + .uploaded-attachments { + display: flex; + flex-wrap: wrap; + gap: 8px; + + .attachment-preview-item { + display: flex; + align-items: center; + gap: 6px; + padding: 6px 10px; + background: var(--el-fill-color-light); + border-radius: 6px; + border: 1px solid var(--el-border-color-lighter); + max-width: 200px; + + .preview-thumb { + width: 40px; + height: 40px; + border-radius: 4px; + object-fit: cover; + } + + .file-icon { + font-size: 16px; + color: var(--el-text-color-regular); + } + + .file-name { + flex: 1; + font-size: 12px; + color: var(--el-text-color-regular); + word-break: break-all; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + } + } + } diff --git a/src/views/work/order/index.vue b/src/views/work/order/index.vue index 764cc1b..faadffb 100644 --- a/src/views/work/order/index.vue +++ b/src/views/work/order/index.vue @@ -123,22 +123,101 @@ String(message.user_id) === String(userid) ? 'message-right' : 'message-left' ]" > + - {{ message.user_id }} + {{ message.user_name }} {{ message.updated_at }} {{ message.content }} + + + + + + + + + + + + + {{ attachment.original_name }} + + 下载 + + + + + + + + + + + 添加附件 + + + + + + + + + + + + {{ file.name }} + + + + + + - - + + @@ -197,6 +276,7 @@ import { ElImageViewer } from 'element-plus' import { useAuth } from "@/composables/useAuth"; import { useUserStore } from '@/store/modules/user' import { useUploadHeaders } from '@/composables/useUploadHeaders' +import { Paperclip, Document, Close } from '@element-plus/icons-vue' const { uploadHeaders } = useUploadHeaders() const { hasAuth } = useAuth(); defineOptions({ name: 'Order' }) @@ -232,6 +312,10 @@ const scrollToBottom = () => { } const openChat = (row: any) => { isDrawerVisible.value = true + // 清空数据 + messageText.value = '' + messages.value = [] + chatFileList.value = [] getConversationList(row.id) } @@ -333,14 +417,28 @@ const getConversationList = async (id:any) => { const sendMessage = async () => { const text = messageText.value.trim() const ids = ticket_id.value - if (!text) return + if (!text && chatFileList.value.length === 0) return + + // 准备附件数据 + const attachments = chatFileList.value.map(file => { + const data = file.response?.data + if (data) { + return { + original_name: data.original_name, + storage_path: data.storage_path || data.file_name + } + } + return null + }).filter(Boolean) const { message } = await WorkService.conversationAdd({ content: text, ticket_id: ids, + attachment: attachments }) ElMessage.success(message) messageText.value = '' + chatFileList.value = [] // 清空附件列表 getConversationList(ids) scrollToBottom() } @@ -473,6 +571,8 @@ const statusSubmit = async () => { const imageViewerVisible = ref(false) const imageViewerIndex = ref(0) const imageList = computed(() => fileList.value.filter(file => /\.(jpg|jpeg|png|gif|webp|bmp)$/i.test(file.name)).map(file => file.file_url?file.file_url:file.response.data.file_url)) +// 聊天图片预览列表 +const chatImageList = ref([]) function handleImageClick(idx: number) { imageViewerIndex.value = idx imageViewerVisible.value = true @@ -484,6 +584,60 @@ function handleVideoClick(url: string) { currentVideoUrl.value = url videoDialogVisible.value = true } + +// 聊天相关 +const chatFileList = ref([]) +const handleChatUploadSuccess = (response: any, uploadFile: any, uploadFiles: any[]) => { + chatFileList.value = uploadFiles.filter(item => item.status === 'success').map(item => { + const data = item.response?.data + if (data) { + return { + name: data.original_name, + file_url: data.storage_path_full, + status: 'success', + response: { data: data } + } + } + return null + }).filter(Boolean) +} +const handleChatExceed = () => { + ElMessage.warning('最多上传5个文件') +} +const removeChatFile = (index: number) => { + chatFileList.value.splice(index, 1) +} +const handleChatImageClick = (url: string) => { + // 设置聊天图片预览列表 + chatImageList.value = [url] + imageViewerIndex.value = 0 + imageViewerVisible.value = true +} + +const closeImageViewer = () => { + imageViewerVisible.value = false + chatImageList.value = [] // 清空聊天图片列表 +} +const handleChatVideoClick = (url: string) => { + currentVideoUrl.value = url + videoDialogVisible.value = true +} +const downloadAttachment = (attachment: any) => { + try { + const fileName = attachment.original_name || 'download' + const link = document.createElement('a') + link.href = attachment.storage_path_full + link.download = fileName + link.target = '_blank' + document.body.appendChild(link) + link.click() + document.body.removeChild(link) + ElMessage.success('开始下载') + } catch (error) { + ElMessage.error('下载失败') + console.error('下载失败:', error) + } +} \ No newline at end of file