二维码池批量上传与认证图片预览修复

- 后端新增批量创建二维码接口 POST /admin/chats/qrcodes/batch
- 前端二维码管理页支持多图拖拽批量上传
- 注册 qrcode 图片压缩场景(maxSide=2560, quality=0.95)
- 后端 normalizeScene 注册 qrcode scene
- 增强 AuthImage 组件支持 el-image 大图预览(previewSrcList)
- 修复 WithdrawalDetailDialog 提现凭证图片 401 问题
- 删除重复的 components/AuthImage.vue,统一使用共享组件
- 提交3剩余: 会话列表群类型图标、发布成功跳群、管理端路由等
This commit is contained in:
yml
2026-06-17 17:57:05 +08:00
parent 54f550b8be
commit 12f589f0df
20 changed files with 715 additions and 33 deletions
+1 -1
View File
@@ -40,7 +40,7 @@ export interface ChatMessage {
sender_avatar: string
is_self: boolean
is_read: boolean
content_type: 'text' | 'system'
content_type: 'text' | 'image' | 'system'
content: string
attachment_urls: string[]
created_at: string
@@ -296,6 +296,9 @@ function handleKeydown(e: KeyboardEvent) {
>
<template v-if="item.sender_type === 'system'">
<span class="system-message">{{ item.content }}</span>
<div v-if="item.attachment_urls.length > 0" class="system-attachments">
<ChatAttachmentImage v-for="url in item.attachment_urls" :key="url" :source="url" />
</div>
</template>
<template v-else>
<div class="avatar" :class="{ self: item.is_self }">
@@ -570,6 +573,11 @@ function handleKeydown(e: KeyboardEvent) {
text-align: center;
}
.system-attachments {
margin-top: 6px;
max-width: 200px;
}
.composer {
flex-shrink: 0;
padding: 12px 16px;
@@ -75,6 +75,13 @@ function roleLabel(role: string) {
return map[role] || '成员'
}
// 按会话类型返回图标前缀与标签:发布群 / 订单群 / 平台客服
function conversationTypeMeta(item: ChatConversation) {
if (item.type === 'listing_group') return { icon: '📢', label: '账号群' }
if (item.type === 'order_group' || item.order_id) return { icon: '📦', label: '订单' }
return { icon: '🎧', label: '平台客服' }
}
function previewText(item: ChatConversation) {
return (
item.last_message_preview ||
@@ -124,16 +131,17 @@ function previewText(item: ChatConversation) {
</div>
<div class="conversation-body">
<div class="conversation-head">
<h2>{{ item.title }}</h2>
<h2>
<span class="conv-type-icon">{{ conversationTypeMeta(item).icon }}</span>
{{ item.title }}
</h2>
<span class="conversation-time">{{
formatDateMinute(item.last_message_at || item.created_at)
}}</span>
</div>
<div class="conversation-meta">
<span class="role-chip">{{ roleLabel(item.role) }}</span>
<span class="order-id">{{
item.order_id ? `订单 #${item.order_id}` : '平台客服'
}}</span>
<span class="order-id">{{ conversationTypeMeta(item).label }}</span>
</div>
<p class="conversation-preview">{{ previewText(item) }}</p>
</div>
@@ -263,6 +271,10 @@ function previewText(item: ChatConversation) {
white-space: nowrap;
}
.conv-type-icon {
margin-right: 4px;
}
.conversation-time {
flex: none;
color: #9ca3af;
@@ -277,6 +277,9 @@ function senderLabel(message: ChatMessage) {
>
<template v-if="item.sender_type === 'system'">
<span class="system-message">{{ item.content }}</span>
<div v-if="item.attachment_urls.length > 0" class="system-attachments">
<ChatAttachmentImage v-for="url in item.attachment_urls" :key="url" :source="url" />
</div>
</template>
<template v-else>
<div class="avatar">{{ roleLabel(item.sender_role).slice(0, 1) }}</div>
@@ -531,6 +534,10 @@ function senderLabel(message: ChatMessage) {
text-align: center;
}
.system-attachments {
margin-top: 6px;
max-width: 180px;
}
.composer {
display: grid;
grid-template-columns: 40px minmax(0, 1fr) 42px;