前端职责:
后端职责:
删除文件:
src/utils/wpsCallback.ts - 不需要前端拦截请求原因:
标准初始化方式:
// src/components/DocumentWpsAuditDialog/index.vue
const initWpsEditor = async () => {
if (!wpsContainerRef.value || !props.document?.ossId) {
return;
}
try {
wpsLoading.value = true;
wpsError.value = '';
// 检查 SDK
if (!(window as any).WebOfficeSDK) {
wpsError.value = 'WPS SDK 未加载';
wpsLoading.value = false;
return;
}
const WebOfficeSDK = (window as any).WebOfficeSDK;
// 获取文件类型
const officeType = getFileType(props.document.fileName || '');
// 生成文件 ID
const fileId = `${props.document.id}`;
// 标准初始化配置(按官方文档)
const config = {
// 必需参数
appId: 'SX20251229FLIAPDAPP',
officeType: officeType,
fileId: fileId,
// 可选参数
mount: wpsContainerRef.value,
// 自定义参数(会传递到后端回调接口)
customArgs: {
documentId: props.document.id,
fileName: props.document.fileName,
fileUrl: props.document.url,
userId: userStore.userId,
userName: userStore.userName
}
};
console.log('[WPS] 初始化配置:', config);
// 初始化编辑器
wpsInstance = WebOfficeSDK.init(config);
wpsLoading.value = false;
console.log('[WPS] 编辑器初始化成功');
} catch (err: any) {
console.error('[WPS] 初始化失败:', err);
wpsError.value = err.message || '初始化失败';
wpsLoading.value = false;
}
};
// 获取 WPS 文件类型
const getFileType = (fileName: string): string => {
const name = fileName.toLowerCase();
// Word 文档
if (name.endsWith('.docx') || name.endsWith('.doc')) {
return 'w';
}
// Excel 表格
if (name.endsWith('.xlsx') || name.endsWith('.xls')) {
return 's';
}
// PowerPoint 演示
if (name.endsWith('.pptx') || name.endsWith('.ppt')) {
return 'p';
}
// PDF 文档
if (name.endsWith('.pdf')) {
return 'f';
}
// 默认为 Word
return 'w';
};
// 手动保存文档
const saveWpsDocument = async () => {
if (!wpsInstance) {
ElMessage.warning('编辑器未初始化');
return null;
}
try {
console.log('[WPS] 开始保存文档');
// 调用 SDK 的 save 方法
// WPS SDK 会自动调用后端的三阶段保存接口
const result = await wpsInstance.save();
console.log('[WPS] 保存成功:', result);
ElMessage.success('文档已保存');
return result;
} catch (err: any) {
console.error('[WPS] 保存失败:', err);
ElMessage.error('保存失败: ' + err.message);
throw err;
}
};
// 销毁 WPS 编辑器
const destroyWpsEditor = () => {
if (wpsInstance) {
try {
if (wpsInstance.destroy) {
wpsInstance.destroy();
}
wpsInstance = null;
console.log('[WPS] 编辑器已销毁');
} catch (err) {
console.error('[WPS] 销毁编辑器失败:', err);
}
}
};
// 组件卸载时清理
onBeforeUnmount(() => {
destroyWpsEditor();
});
// 降级到 iframe 预览
const fallbackToIframe = () => {
wpsError.value = 'WPS 编辑器加载失败,已切换到预览模式';
// 模板中已有 iframe 降级方案
};
// 初始化时的错误处理
const initWpsEditor = async () => {
try {
// ... 初始化代码
} catch (err: any) {
console.error('[WPS] 初始化失败:', err);
wpsError.value = err.message || '初始化失败';
wpsLoading.value = false;
// 如果有 URL,降级到 iframe
if (props.document?.url) {
fallbackToIframe();
}
}
};
<template>
<el-dialog v-model="dialogVisible" title="文档审核" width="1200px">
<div class="audit-container">
<!-- 左侧:WPS 编辑器 -->
<div class="preview-section">
<div class="preview-header">
<div class="file-info">
<el-icon class="file-icon"><Document /></el-icon>
<span class="file-name">{{ document?.fileName }}</span>
</div>
</div>
<div class="preview-container">
<!-- WPS 编辑器容器 -->
<div v-if="document?.ossId" ref="wpsContainerRef" class="wps-container">
<!-- 降级方案:iframe 预览 -->
<iframe
v-if="wpsError && document?.url"
:src="document.url"
class="document-iframe"
frameborder="0"
></iframe>
</div>
<!-- 加载状态 -->
<div v-if="wpsLoading" class="loading-state">
<el-icon class="is-loading"><Loading /></el-icon>
<p>正在加载编辑器...</p>
</div>
<!-- 错误状态 -->
<div v-if="wpsError && !document?.url" class="error-state">
<el-result icon="error" title="加载失败" :sub-title="wpsError">
<template #extra>
<el-button type="primary" @click="initWpsEditor">重新加载</el-button>
</template>
</el-result>
</div>
<!-- 空状态 -->
<el-empty v-if="!document?.ossId" description="暂无文档" />
</div>
</div>
<!-- 右侧:审核表单 -->
<div class="audit-section">
<!-- 审核表单内容 -->
</div>
</div>
<template #footer>
<el-button @click="handleCancel">取消</el-button>
<el-button type="primary" @click="submitForm" :loading="loading">
提交审核
</el-button>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { ref, watch, nextTick, onBeforeUnmount } from 'vue';
import { ElMessage } from 'element-plus';
import { useUserStore } from '@/store/modules/user';
// Props 和 Emits
interface Props {
modelValue: boolean;
document?: Document | null;
auditApi: (data: AuditData) => Promise<any>;
}
const props = defineProps<Props>();
const emit = defineEmits<Emits>();
// 状态
const userStore = useUserStore();
const dialogVisible = ref(false);
const wpsContainerRef = ref<HTMLDivElement>();
const wpsLoading = ref(false);
const wpsError = ref('');
let wpsInstance: any = null;
// WPS 配置
const WPS_APP_ID = 'SX20251229FLIAPDAPP';
// 获取文件类型
const getFileType = (fileName: string): string => {
const name = fileName.toLowerCase();
if (name.endsWith('.docx') || name.endsWith('.doc')) return 'w';
if (name.endsWith('.xlsx') || name.endsWith('.xls')) return 's';
if (name.endsWith('.pptx') || name.endsWith('.ppt')) return 'p';
if (name.endsWith('.pdf')) return 'f';
return 'w';
};
// 初始化 WPS 编辑器
const initWpsEditor = async () => {
if (!wpsContainerRef.value || !props.document?.ossId) return;
try {
wpsLoading.value = true;
wpsError.value = '';
if (!(window as any).WebOfficeSDK) {
wpsError.value = 'WPS SDK 未加载';
wpsLoading.value = false;
return;
}
const WebOfficeSDK = (window as any).WebOfficeSDK;
const officeType = getFileType(props.document.fileName || '');
const fileId = `${props.document.id}`;
// 标准配置
const config = {
appId: WPS_APP_ID,
officeType: officeType,
fileId: fileId,
mount: wpsContainerRef.value,
customArgs: {
documentId: props.document.id,
fileName: props.document.fileName,
fileUrl: props.document.url,
userId: userStore.userId,
userName: userStore.userName
}
};
console.log('[WPS] 初始化配置:', config);
wpsInstance = WebOfficeSDK.init(config);
wpsLoading.value = false;
console.log('[WPS] 编辑器初始化成功');
} catch (err: any) {
console.error('[WPS] 初始化失败:', err);
wpsError.value = err.message || '初始化失败';
wpsLoading.value = false;
}
};
// 保存文档
const saveWpsDocument = async () => {
if (!wpsInstance) return null;
try {
console.log('[WPS] 开始保存文档');
const result = await wpsInstance.save();
console.log('[WPS] 保存成功:', result);
ElMessage.success('文档已保存');
return result;
} catch (err: any) {
console.error('[WPS] 保存失败:', err);
ElMessage.error('保存失败');
throw err;
}
};
// 销毁编辑器
const destroyWpsEditor = () => {
if (wpsInstance) {
try {
if (wpsInstance.destroy) {
wpsInstance.destroy();
}
wpsInstance = null;
console.log('[WPS] 编辑器已销毁');
} catch (err) {
console.error('[WPS] 销毁编辑器失败:', err);
}
}
};
// 监听对话框打开
watch(() => props.modelValue, (val) => {
dialogVisible.value = val;
if (val && props.document?.ossId) {
nextTick(() => {
initWpsEditor();
});
}
});
// 监听对话框关闭
watch(dialogVisible, (val) => {
emit('update:modelValue', val);
if (!val) {
destroyWpsEditor();
}
});
// 提交审核
const submitForm = async () => {
// 可选:提交前保存文档
if (wpsInstance) {
try {
await saveWpsDocument();
} catch (err) {
console.error('保存文档失败:', err);
// 继续提交审核
}
}
// 提交审核逻辑...
};
// 组件卸载
onBeforeUnmount(() => {
destroyWpsEditor();
});
</script>
# 删除这个文件
src/utils/wpsCallback.ts
// 删除这些导入
import { setWpsFileInfo, clearWpsFileInfo } from '@/utils/wpsCallback';
// 删除这些调用
setWpsFileInfo(fileId, { ... });
clearWpsFileInfo(fileId);
GET /v1/3rd/file/info?_w_appid={appId}&_w_fileid={fileId}
GET /v3/3rd/files/:file_id/upload/prepare
POST /v3/3rd/files/:file_id/upload/address
POST /v3/3rd/files/:file_id/upload/complete
GET /v1/3rd/file/version # 文件版本列表
POST /v1/3rd/file/rename # 文件重命名
POST /v1/3rd/file/copy # 文件复制
customArgs: {
// 这些参数会通过 X-User-Query 请求头传递到后端
documentId: props.document.id, // 文档ID
fileName: props.document.fileName, // 文件名
fileUrl: props.document.url, // 文件URL
userId: userStore.userId, // 用户ID
userName: userStore.userName // 用户名
}
注意:
type, version, mode, history_id, share_iddoc_id, doc_name// 简单方式:使用文档ID
const fileId = `${props.document.id}`;
// 或者:使用文档ID + 时间戳(确保唯一性)
const fileId = `${props.document.id}_${Date.now()}`;
// 或者:使用 UUID
import { v4 as uuidv4 } from 'uuid';
const fileId = uuidv4();
建议:使用文档ID,便于后端关联
const FILE_TYPE_MAP: Record<string, string> = {
'doc': 'w', 'docx': 'w', // Word
'xls': 's', 'xlsx': 's', // Excel
'ppt': 'p', 'pptx': 'p', // PowerPoint
'pdf': 'f' // PDF
};
原因:后端接口未实现或配置错误
解决:等待后端实现回调接口
原因:SDK 文件未加载或网络问题
解决:检查 public/web-office-sdk.js 是否存在
原因:后端三阶段保存接口未实现
解决:等待后端实现保存接口
原因:fileId 或 officeType 不正确
解决:检查参数配置和文件类型映射
前端实现非常简单:
WebOfficeSDK.init() 初始化所有复杂的逻辑都由后端处理!