|
|
@@ -9,17 +9,18 @@
|
|
|
<span class="file-name">{{ document?.fileName || '未命名文档' }}</span>
|
|
|
</div>
|
|
|
<div class="header-actions">
|
|
|
- <el-button
|
|
|
- type="primary"
|
|
|
- size="small"
|
|
|
- draggable="true"
|
|
|
- @dragstart="handleUserNameDragStart"
|
|
|
- @dragend="handleUserNameDragEnd"
|
|
|
- class="drag-username-btn"
|
|
|
- >
|
|
|
- <el-icon><User /></el-icon>
|
|
|
- 拖我到文档中
|
|
|
- </el-button>
|
|
|
+ <el-tooltip content="点击复制签名图片到剪贴板" placement="bottom">
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ size="small"
|
|
|
+ @click="handleCopyAvatar"
|
|
|
+ class="copy-avatar-btn"
|
|
|
+ :loading="copyingAvatar"
|
|
|
+ >
|
|
|
+ <el-icon><Picture /></el-icon>
|
|
|
+ 复制签名
|
|
|
+ </el-button>
|
|
|
+ </el-tooltip>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="preview-container">
|
|
|
@@ -153,9 +154,10 @@
|
|
|
import { ref, reactive, watch, nextTick, onBeforeUnmount } from 'vue';
|
|
|
import { useI18n } from 'vue-i18n';
|
|
|
import type { FormInstance } from 'element-plus';
|
|
|
-import { ElMessage } from 'element-plus';
|
|
|
-import { Document, Edit, InfoFilled, CircleCheck, CircleClose, Close, Check, Loading, Upload, User } from '@element-plus/icons-vue';
|
|
|
+import { ElMessage, ElMessageBox } from 'element-plus';
|
|
|
+import { Document, Edit, InfoFilled, CircleCheck, CircleClose, Close, Check, Loading, Upload, Picture } from '@element-plus/icons-vue';
|
|
|
import { useUserStore } from '@/store/modules/user';
|
|
|
+import { getSignature, downloadSignature, updateDocumentVersion } from '@/api/system/signature';
|
|
|
|
|
|
interface Document {
|
|
|
id: number | string;
|
|
|
@@ -198,7 +200,7 @@ const wpsContainerRef = ref<HTMLDivElement>();
|
|
|
const wpsLoading = ref(false);
|
|
|
const wpsError = ref('');
|
|
|
const isDragging = ref(false);
|
|
|
-const isDraggingUserName = ref(false);
|
|
|
+const copyingAvatar = ref(false);
|
|
|
let wpsInstance: any = null;
|
|
|
let dragCounter = 0; // 用于跟踪拖拽进入/离开次数
|
|
|
|
|
|
@@ -262,7 +264,7 @@ const initWpsEditor = async () => {
|
|
|
// 获取文件类型
|
|
|
const officeType = getFileType(props.document.fileName || '');
|
|
|
|
|
|
- // 生成文件 ID
|
|
|
+ // 使用原始文档 ID(不添加时间戳)
|
|
|
const fileId = `${props.document.id}`;
|
|
|
|
|
|
console.log('[WPS] 初始化配置:', {
|
|
|
@@ -363,14 +365,6 @@ const handleDrop = async (e: DragEvent) => {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- // 检查是否是拖拽用户名
|
|
|
- const userName = e.dataTransfer?.getData('text/plain');
|
|
|
- if (isDraggingUserName.value && userName) {
|
|
|
- await insertUserNameToWps(userName);
|
|
|
- isDraggingUserName.value = false;
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
// 处理图片拖放
|
|
|
const files = e.dataTransfer?.files;
|
|
|
if (!files || files.length === 0) {
|
|
|
@@ -447,150 +441,126 @@ const handleDrop = async (e: DragEvent) => {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
-// 开始拖拽用户名
|
|
|
-const handleUserNameDragStart = (e: DragEvent) => {
|
|
|
- const userName = userStore.userName || '当前用户';
|
|
|
- e.dataTransfer!.effectAllowed = 'copy';
|
|
|
- e.dataTransfer!.setData('text/plain', userName);
|
|
|
- isDraggingUserName.value = true;
|
|
|
- console.log('[拖拽] 开始拖拽用户名:', userName);
|
|
|
-};
|
|
|
-
|
|
|
-// 结束拖拽用户名
|
|
|
-const handleUserNameDragEnd = (e: DragEvent) => {
|
|
|
- isDraggingUserName.value = false;
|
|
|
- console.log('[拖拽] 结束拖拽用户名');
|
|
|
-};
|
|
|
-
|
|
|
-// 插入用户名到 WPS
|
|
|
-const insertUserNameToWps = async (userName: string) => {
|
|
|
- if (!wpsInstance) {
|
|
|
- ElMessage.warning('WPS 编辑器未初始化');
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
+// 复制头像到剪贴板
|
|
|
+const handleCopyAvatar = async () => {
|
|
|
try {
|
|
|
- console.log('[WPS] 开始插入用户名:', userName);
|
|
|
+ copyingAvatar.value = true;
|
|
|
+ console.log('[签名] 开始获取签名信息');
|
|
|
|
|
|
- // 获取 WPS Application 对象
|
|
|
- const app = await wpsInstance.Application;
|
|
|
+ // 1. 调用获取签名接口
|
|
|
+ const signatureRes = await getSignature();
|
|
|
+ const ossId = signatureRes.data.id;
|
|
|
|
|
|
- if (!app) {
|
|
|
- ElMessage.error('无法获取 WPS Application 对象');
|
|
|
+ if (!ossId) {
|
|
|
+ ElMessage.warning('未找到签名图片');
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- // 根据文件类型插入文本
|
|
|
- const officeType = getFileType(props.document?.fileName || '');
|
|
|
+ console.log('[签名] 签名 OSS ID:', ossId);
|
|
|
|
|
|
- if (officeType === 'w') {
|
|
|
- // Word 文档:在光标位置插入文本
|
|
|
- const selection = await app.ActiveDocument.Application.Selection;
|
|
|
- await selection.TypeText(userName);
|
|
|
- ElMessage.success(`已插入:${userName}`);
|
|
|
- } else if (officeType === 's') {
|
|
|
- // Excel 表格:在当前单元格插入文本
|
|
|
- const activeCell = await app.ActiveCell;
|
|
|
- await activeCell.put_Value(userName);
|
|
|
- ElMessage.success(`已插入:${userName}`);
|
|
|
- } else if (officeType === 'p') {
|
|
|
- // PowerPoint:在当前文本框插入文本
|
|
|
- try {
|
|
|
- const selection = await app.ActiveWindow.Selection;
|
|
|
- const textRange = await selection.TextRange;
|
|
|
- await textRange.InsertAfter(userName);
|
|
|
- ElMessage.success(`已插入:${userName}`);
|
|
|
- } catch (err) {
|
|
|
- ElMessage.warning('请先选择一个文本框');
|
|
|
- }
|
|
|
- } else if (officeType === 'f') {
|
|
|
- // PDF 文档:添加文本批注
|
|
|
- try {
|
|
|
- const pdfDoc = await app.ActivePDF;
|
|
|
-
|
|
|
- // 方法 1:尝试添加文本批注
|
|
|
- try {
|
|
|
- // 获取当前页面
|
|
|
- const currentPage = await pdfDoc.CurrentPage;
|
|
|
-
|
|
|
- // 在页面中心位置添加文本批注
|
|
|
- const pageWidth = await currentPage.Width;
|
|
|
- const pageHeight = await currentPage.Height;
|
|
|
-
|
|
|
- // 添加文本批注(自由文本批注)
|
|
|
- const annotation = await currentPage.AddAnnotation({
|
|
|
- type: 'FreeText', // 自由文本类型
|
|
|
- rect: {
|
|
|
- left: pageWidth / 2 - 100,
|
|
|
- top: pageHeight / 2 - 20,
|
|
|
- right: pageWidth / 2 + 100,
|
|
|
- bottom: pageHeight / 2 + 20
|
|
|
- },
|
|
|
- contents: userName,
|
|
|
- color: { r: 255, g: 0, b: 0 }, // 红色
|
|
|
- fontSize: 14
|
|
|
- });
|
|
|
-
|
|
|
- ElMessage.success(`已在 PDF 中添加文本:${userName}`);
|
|
|
- } catch (err1) {
|
|
|
- console.error('[WPS] 方法1失败,尝试方法2:', err1);
|
|
|
-
|
|
|
- // 方法 2:使用简单的批注 API
|
|
|
- try {
|
|
|
- await pdfDoc.AddComment({
|
|
|
- text: userName,
|
|
|
- author: userStore.userName || '审核人',
|
|
|
- color: '#FF0000'
|
|
|
- });
|
|
|
- ElMessage.success(`已添加批注:${userName}`);
|
|
|
- } catch (err2) {
|
|
|
- console.error('[WPS] 方法2失败,尝试方法3:', err2);
|
|
|
-
|
|
|
- // 方法 3:使用文本标记
|
|
|
- try {
|
|
|
- const selection = await app.ActivePDF.Selection;
|
|
|
- if (selection) {
|
|
|
- await selection.AddTextMarkup({
|
|
|
- type: 'Highlight',
|
|
|
- text: userName,
|
|
|
- color: '#FFFF00'
|
|
|
- });
|
|
|
- ElMessage.success(`已添加高亮标记:${userName}`);
|
|
|
- } else {
|
|
|
- throw new Error('未选中文本');
|
|
|
- }
|
|
|
- } catch (err3) {
|
|
|
- console.error('[WPS] 方法3失败:', err3);
|
|
|
-
|
|
|
- // 方法 4:使用印章功能
|
|
|
- ElMessage.info('正在尝试添加文本印章...');
|
|
|
- try {
|
|
|
- await pdfDoc.AddStamp({
|
|
|
- text: userName,
|
|
|
- position: 'center',
|
|
|
- color: '#FF0000',
|
|
|
- fontSize: 14
|
|
|
- });
|
|
|
- ElMessage.success(`已添加文本印章:${userName}`);
|
|
|
- } catch (err4) {
|
|
|
- console.error('[WPS] 所有方法都失败了:', err4);
|
|
|
- ElMessage.error('PDF 文本插入失败,请使用 WPS 自带的批注工具');
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- } catch (err: any) {
|
|
|
- console.error('[WPS] PDF 操作失败:', err);
|
|
|
- ElMessage.error('PDF 操作失败: ' + err.message);
|
|
|
+ // 2. 下载签名图片
|
|
|
+ console.log('[签名] 开始下载签名图片');
|
|
|
+ const blob = await downloadSignature(ossId);
|
|
|
+
|
|
|
+ console.log('[签名] 图片下载成功,大小:', blob.size, '原始类型:', blob.type);
|
|
|
+
|
|
|
+ // 3. 检测图片真实类型并转换 Blob
|
|
|
+ let imageBlob = blob;
|
|
|
+ let mimeType = blob.type;
|
|
|
+
|
|
|
+ // 如果是 application/octet-stream,需要检测真实的图片类型
|
|
|
+ if (mimeType === 'application/octet-stream' || !mimeType.startsWith('image/')) {
|
|
|
+ console.log('[签名] 检测到非图片 MIME 类型,尝试转换');
|
|
|
+
|
|
|
+ // 通过读取文件头来判断图片类型
|
|
|
+ const arrayBuffer = await blob.arrayBuffer();
|
|
|
+ const uint8Array = new Uint8Array(arrayBuffer);
|
|
|
+
|
|
|
+ // 检测文件头
|
|
|
+ if (uint8Array[0] === 0xFF && uint8Array[1] === 0xD8 && uint8Array[2] === 0xFF) {
|
|
|
+ mimeType = 'image/jpeg';
|
|
|
+ } else if (uint8Array[0] === 0x89 && uint8Array[1] === 0x50 && uint8Array[2] === 0x4E && uint8Array[3] === 0x47) {
|
|
|
+ mimeType = 'image/png';
|
|
|
+ } else if (uint8Array[0] === 0x47 && uint8Array[1] === 0x49 && uint8Array[2] === 0x46) {
|
|
|
+ mimeType = 'image/gif';
|
|
|
+ } else if (uint8Array[0] === 0x42 && uint8Array[1] === 0x4D) {
|
|
|
+ mimeType = 'image/bmp';
|
|
|
+ } else if (uint8Array[0] === 0x52 && uint8Array[1] === 0x49 && uint8Array[2] === 0x46 && uint8Array[3] === 0x46) {
|
|
|
+ mimeType = 'image/webp';
|
|
|
+ } else {
|
|
|
+ // 默认使用 PNG
|
|
|
+ mimeType = 'image/png';
|
|
|
}
|
|
|
- } else {
|
|
|
- ElMessage.warning('当前文档类型不支持插入文本');
|
|
|
+
|
|
|
+ console.log('[签名] 检测到的图片类型:', mimeType);
|
|
|
+
|
|
|
+ // 创建新的 Blob,使用正确的 MIME 类型
|
|
|
+ imageBlob = new Blob([arrayBuffer], { type: mimeType });
|
|
|
}
|
|
|
|
|
|
- console.log('[WPS] 用户名插入成功');
|
|
|
+ console.log('[签名] 最终 MIME 类型:', imageBlob.type);
|
|
|
+
|
|
|
+ // 4. 复制到剪贴板
|
|
|
+ await navigator.clipboard.write([
|
|
|
+ new ClipboardItem({
|
|
|
+ [imageBlob.type]: imageBlob
|
|
|
+ })
|
|
|
+ ]);
|
|
|
+
|
|
|
+ ElMessage({
|
|
|
+ type: 'success',
|
|
|
+ message: '签名图片已复制到剪贴板',
|
|
|
+ duration: 3000
|
|
|
+ });
|
|
|
+
|
|
|
+ // 显示使用提示
|
|
|
+ setTimeout(() => {
|
|
|
+ ElMessage({
|
|
|
+ type: 'info',
|
|
|
+ dangerouslyUseHTMLString: true,
|
|
|
+ message: `
|
|
|
+ <div style="text-align: left;">
|
|
|
+ <p style="margin: 0 0 8px 0; font-weight: bold;">请按以下步骤操作:</p>
|
|
|
+ <p style="margin: 0 0 4px 0;">• <strong>PDF</strong>:使用批注工具 → 选择图片 → 粘贴</p>
|
|
|
+ <p style="margin: 0 0 4px 0;">• <strong>Word/Excel/PPT</strong>:在文档中按 Ctrl+V 粘贴</p>
|
|
|
+ <p style="margin: 0;">• 或者直接拖拽外部图片到文档中</p>
|
|
|
+ </div>
|
|
|
+ `,
|
|
|
+ duration: 6000,
|
|
|
+ showClose: true
|
|
|
+ });
|
|
|
+ }, 500);
|
|
|
+
|
|
|
+ console.log('[签名] 复制成功');
|
|
|
} catch (err: any) {
|
|
|
- console.error('[WPS] 插入用户名失败:', err);
|
|
|
- ElMessage.error('插入用户名失败: ' + err.message);
|
|
|
+ console.error('[签名] 复制失败:', err);
|
|
|
+
|
|
|
+ // 根据错误类型显示不同提示
|
|
|
+ if (err.message?.includes('clipboard') || err.message?.includes('Clipboard')) {
|
|
|
+ ElMessage({
|
|
|
+ type: 'warning',
|
|
|
+ dangerouslyUseHTMLString: true,
|
|
|
+ message: `
|
|
|
+ <div style="text-align: left;">
|
|
|
+ <p style="margin: 0 0 4px 0;">浏览器不支持复制图片,请尝试以下方法:</p>
|
|
|
+ <p style="margin: 0 0 4px 0;">1. 使用 Chrome 或 Edge 浏览器</p>
|
|
|
+ <p style="margin: 0;">2. 或者使用 WPS 的插入图片功能</p>
|
|
|
+ </div>
|
|
|
+ `,
|
|
|
+ duration: 6000,
|
|
|
+ showClose: true
|
|
|
+ });
|
|
|
+ } else if (err.response?.status === 404) {
|
|
|
+ ElMessage.warning('未找到签名图片,请先设置签名');
|
|
|
+ } else {
|
|
|
+ ElMessage({
|
|
|
+ type: 'error',
|
|
|
+ message: '获取签名失败: ' + (err.message || '未知错误'),
|
|
|
+ duration: 5000
|
|
|
+ });
|
|
|
+ }
|
|
|
+ } finally {
|
|
|
+ copyingAvatar.value = false;
|
|
|
}
|
|
|
};
|
|
|
|
|
|
@@ -763,14 +733,7 @@ onBeforeUnmount(() => {
|
|
|
display: flex;
|
|
|
gap: 8px;
|
|
|
|
|
|
- .drag-username-btn {
|
|
|
- cursor: move;
|
|
|
- user-select: none;
|
|
|
-
|
|
|
- &:active {
|
|
|
- cursor: grabbing;
|
|
|
- }
|
|
|
-
|
|
|
+ .copy-avatar-btn {
|
|
|
.el-icon {
|
|
|
margin-right: 4px;
|
|
|
}
|