记录从企业微信拖拽不同类型消息到AI对话区域时的完整数据结构,为功能实现提供准确的参考。
/**
* 详细打印拖拽数据结构
*/
private logDragDataStructure(event: DragEvent, context: string): void {
console.log(`\n========== [${context}] 拖拽数据结构分析 ==========`);
const dt = event.dataTransfer;
if (!dt) {
console.log('❌ dataTransfer 为空');
return;
}
// 1. 基本信息
console.log('\n📋 基本信息:');
console.log(' dropEffect:', dt.dropEffect);
console.log(' effectAllowed:', dt.effectAllowed);
console.log(' types:', Array.from(dt.types));
// 2. Files
console.log('\n📁 Files 对象:');
console.log(' files.length:', dt.files?.length || 0);
if (dt.files && dt.files.length > 0) {
for (let i = 0; i < dt.files.length; i++) {
const file = dt.files[i];
console.log(` [${i}] File对象:`, {
name: file.name,
size: file.size,
type: file.type,
lastModified: new Date(file.lastModified).toLocaleString(),
webkitRelativePath: (file as any).webkitRelativePath || ''
});
}
}
// 3. Items
console.log('\n📦 Items 对象:');
console.log(' items.length:', dt.items?.length || 0);
if (dt.items && dt.items.length > 0) {
for (let i = 0; i < dt.items.length; i++) {
const item = dt.items[i];
console.log(` [${i}] DataTransferItem:`, {
kind: item.kind,
type: item.type
});
// 尝试获取item的内容
if (item.kind === 'string') {
item.getAsString((str) => {
console.log(` → 字符串内容 (${item.type}):`, str.substring(0, 200));
});
} else if (item.kind === 'file') {
const file = item.getAsFile();
console.log(` → 文件对象:`, file);
}
}
}
// 4. 各种数据类型
console.log('\n📝 getData() 测试:');
const commonTypes = [
'text/plain',
'text/html',
'text/uri-list',
'text/rtf',
'application/json',
'Files'
];
for (const type of commonTypes) {
try {
const data = dt.getData(type);
if (data) {
console.log(` ${type}:`, data.length > 200 ? data.substring(0, 200) + '...' : data);
}
} catch (e) {
// 某些类型可能不可访问
}
}
// 5. 自定义数据类型
console.log('\n🔍 自定义数据类型:');
if (dt.types) {
for (const type of dt.types) {
if (!commonTypes.includes(type)) {
try {
const data = dt.getData(type);
console.log(` ${type}:`, data);
} catch (e) {
console.log(` ${type}: [无法读取]`);
}
}
}
}
console.log('\n========== 数据结构分析结束 ==========\n');
}
操作: 从企业微信群聊拖拽一张JPG图片
预期数据结构:
{
基本信息: {
dropEffect: "none",
effectAllowed: "all",
types: ["Files", "text/html", "text/plain"]
},
Files对象: {
length: 1,
[0]: {
name: "image.jpg",
size: 1234567, // 字节
type: "image/jpeg",
lastModified: "2025-12-03 10:30:00",
webkitRelativePath: ""
}
},
Items对象: {
length: 3,
[0]: { kind: "file", type: "" },
[1]: { kind: "string", type: "text/html" },
[2]: { kind: "string", type: "text/plain" }
},
getData测试: {
"text/plain": "[图片]",
"text/html": "<img src='...' />",
"text/uri-list": ""
}
}
操作: 从企业微信群聊选择3张图片一起拖拽
预期数据结构:
{
基本信息: {
types: ["Files", "text/html", "text/plain"]
},
Files对象: {
length: 3,
[0]: { name: "image1.jpg", size: 1234567, type: "image/jpeg" },
[1]: { name: "image2.png", size: 2345678, type: "image/png" },
[2]: { name: "image3.jpg", size: 3456789, type: "image/jpeg" }
},
Items对象: {
length: 5, // Files + HTML + Plain
[0]: { kind: "file", type: "" },
[1]: { kind: "file", type: "" },
[2]: { kind: "file", type: "" },
[3]: { kind: "string", type: "text/html" },
[4]: { kind: "string", type: "text/plain" }
},
getData测试: {
"text/plain": "[图片] [图片] [图片]",
"text/html": "<img src='...' /><img src='...' /><img src='...' />"
}
}
操作: 从企业微信群聊拖拽一条文字消息
预期数据结构:
{
基本信息: {
types: ["text/html", "text/plain"]
},
Files对象: {
length: 0 // 没有文件
},
Items对象: {
length: 2,
[0]: { kind: "string", type: "text/html" },
[1]: { kind: "string", type: "text/plain" }
},
getData测试: {
"text/plain": "客户要求:现代简约风格,采光要好,预算20万",
"text/html": "<div class='message'>客户要求:现代简约风格,采光要好,预算20万</div>"
}
}
操作: 从企业微信群聊同时选择图片和文字消息拖拽
预期数据结构:
{
基本信息: {
types: ["Files", "text/html", "text/plain"]
},
Files对象: {
length: 2,
[0]: { name: "image1.jpg", size: 1234567, type: "image/jpeg" },
[1]: { name: "image2.jpg", size: 2345678, type: "image/jpeg" }
},
Items对象: {
length: 4,
[0]: { kind: "file", type: "" },
[1]: { kind: "file", type: "" },
[2]: { kind: "string", type: "text/html" },
[3]: { kind: "string", type: "text/plain" }
},
getData测试: {
"text/plain": "[图片] [图片] 客户要求:现代简约风格...",
"text/html": "<img src='...' /><img src='...' /><div>客户要求:...</div>"
}
}
操作: 从企业微信拖拽图片的URL链接
预期数据结构:
{
基本信息: {
types: ["text/uri-list", "text/html", "text/plain"]
},
Files对象: {
length: 0
},
Items对象: {
length: 3,
[0]: { kind: "string", type: "text/uri-list" },
[1]: { kind: "string", type: "text/html" },
[2]: { kind: "string", type: "text/plain" }
},
getData测试: {
"text/uri-list": "https://file-cloud.fmode.cn/path/to/image.jpg",
"text/plain": "https://file-cloud.fmode.cn/path/to/image.jpg",
"text/html": "<a href='https://file-cloud.fmode.cn/path/to/image.jpg'>...</a>"
}
}
操作: 从企业微信群聊拖拽PDF或CAD文件
预期数据结构:
{
基本信息: {
types: ["Files", "text/html", "text/plain"]
},
Files对象: {
length: 1,
[0]: {
name: "design.pdf",
size: 5678901,
type: "application/pdf",
lastModified: "2025-12-03 10:30:00"
}
},
Items对象: {
length: 3,
[0]: { kind: "file", type: "application/pdf" },
[1]: { kind: "string", type: "text/html" },
[2]: { kind: "string", type: "text/plain" }
},
getData测试: {
"text/plain": "[文件] design.pdf",
"text/html": "<a href='...' download='design.pdf'>design.pdf</a>"
}
}
// 企业微信拖拽通常包含的types
types: [
"Files", // 有文件时出现
"text/html", // 几乎总是存在
"text/plain", // 几乎总是存在
"text/uri-list" // 有URL链接时出现
]
<img>标签,文字包含格式图片拖拽:
文字拖拽:
混合拖拽:
function detectDragContentType(event: DragEvent): string {
const dt = event.dataTransfer;
if (!dt) return 'empty';
const types = Array.from(dt.types || []);
// 1. 有文件 - 可能是图片、PDF、CAD
if (types.includes('Files') && dt.files && dt.files.length > 0) {
const hasImage = Array.from(dt.files).some(f => f.type.startsWith('image/'));
const hasPDF = Array.from(dt.files).some(f => f.type === 'application/pdf');
const hasCAD = Array.from(dt.files).some(f =>
f.name.toLowerCase().endsWith('.dwg') ||
f.name.toLowerCase().endsWith('.dxf')
);
if (hasImage) return 'images';
if (hasPDF) return 'pdf';
if (hasCAD) return 'cad';
return 'files';
}
// 2. 有URL链接
if (types.includes('text/uri-list')) {
const uriList = dt.getData('text/uri-list');
if (uriList && uriList.startsWith('http')) {
return 'url';
}
}
// 3. 纯文字
if (types.includes('text/plain')) {
return 'text';
}
return 'unknown';
}
function extractAllDragContent(event: DragEvent): {
files: File[];
images: File[];
text: string;
html: string;
urls: string[];
} {
const dt = event.dataTransfer;
if (!dt) return { files: [], images: [], text: '', html: '', urls: [] };
// 提取文件
const files: File[] = dt.files ? Array.from(dt.files) : [];
const images = files.filter(f => f.type.startsWith('image/'));
// 提取文字
const text = dt.getData('text/plain') || '';
const html = dt.getData('text/html') || '';
// 提取URL
const uriList = dt.getData('text/uri-list') || '';
const urls = uriList.split('\n')
.map(url => url.trim())
.filter(url => url && !url.startsWith('#'));
return { files, images, text, html, urls };
}
/**
* 在AI对话拖拽区域添加测试
*/
onAIChat DragDrop(event: DragEvent) {
event.preventDefault();
event.stopPropagation();
// 🔍 详细打印数据结构
this.logDragDataStructure(event, 'AI对话区域');
// 📊 提取并打印内容
const content = this.extractAllDragContent(event);
console.log('📊 提取的内容:', content);
// 🎯 检测内容类型
const contentType = this.detectDragContentType(event);
console.log('🎯 内容类型:', contentType);
// 继续处理拖拽内容...
}
// 1. 在AI对话输入区域添加拖拽监听
<div class="ai-chat-input-wrapper"
(drop)="onAIChatDrop($event)"
(dragover)="onAIChatDragOver($event)"
(dragleave)="onAIChatDragLeave($event)"
[class.drag-over]="aiChatDragOver">
<!-- AI对话输入框 -->
</div>
// 2. 实现拖拽处理方法
async onAIChatDrop(event: DragEvent) {
// 打印数据结构(开发测试用)
this.logDragDataStructure(event, 'AI对话');
// 提取内容
const content = this.extractAllDragContent(event);
// 处理图片
if (content.images.length > 0) {
await this.addImagesToAIChat(content.images);
}
// 处理文字
if (content.text && !content.text.includes('[图片]')) {
this.appendTextToAIInput(content.text);
}
// 处理URL
if (content.urls.length > 0) {
await this.downloadAndAddToAIChat(content.urls);
}
}
文档版本: 1.0.0
创建时间: 2025-12-03
用途: 数据结构分析、功能实现参考、问题排查