|
|
@@ -1,250 +1,154 @@
|
|
|
<template>
|
|
|
- <el-dialog v-model="dialogVisible" :title="title" width="1200px" top="5vh" append-to-body destroy-on-close class="audit-dialog">
|
|
|
+ <el-dialog v-model="visible" :title="title" width="1200px" @close="handleClose">
|
|
|
<div class="audit-container">
|
|
|
- <!-- 左侧:文档编辑区域 -->
|
|
|
+ <!-- 左侧:PDF 预览 -->
|
|
|
<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 || t('document.document.documentAudit.untitledDocument') }}</span>
|
|
|
- </div>
|
|
|
- <div class="header-actions">
|
|
|
- <el-tooltip :content="t('document.document.documentAudit.viewVersionsTooltip')" placement="bottom">
|
|
|
- <el-button size="small" @click="handleViewVersions" :loading="loadingVersions">
|
|
|
- <el-icon><Clock /></el-icon>
|
|
|
- {{ t('document.document.documentAudit.viewVersions') }}
|
|
|
- </el-button>
|
|
|
- </el-tooltip>
|
|
|
- <el-tooltip :content="t('document.document.documentAudit.cleanCommentsTooltip')" placement="bottom">
|
|
|
- <el-button size="small" @click="handleCleanComments" :loading="cleaningComments">
|
|
|
- <el-icon><Delete /></el-icon>
|
|
|
- {{ t('document.document.documentAudit.cleanComments') }}
|
|
|
- </el-button>
|
|
|
- </el-tooltip>
|
|
|
- <el-tooltip :content="t('document.document.documentAudit.copySignatureTooltip')" placement="bottom">
|
|
|
- <el-button type="primary" size="small" @click="handleCopyAvatar" class="copy-avatar-btn" :loading="copyingAvatar">
|
|
|
- <el-icon><Picture /></el-icon>
|
|
|
- {{ t('document.document.documentAudit.copySignature') }}
|
|
|
- </el-button>
|
|
|
- </el-tooltip>
|
|
|
- </div>
|
|
|
+ <div v-if="previewLoading" v-loading="true" class="preview-loading">
|
|
|
+ {{ t('document.document.auditForm.loading') }}
|
|
|
</div>
|
|
|
- <div class="preview-container">
|
|
|
- <!-- WPS 编辑器容器 -->
|
|
|
- <div
|
|
|
- v-if="document?.ossId"
|
|
|
- ref="wpsContainerRef"
|
|
|
- class="wps-container"
|
|
|
- @dragenter="handleDragEnter"
|
|
|
- @dragleave="handleDragLeave"
|
|
|
- @dragover.prevent="handleDragOver"
|
|
|
- @drop.prevent="handleDrop"
|
|
|
- >
|
|
|
- <!-- 拖拽提示层 -->
|
|
|
- <div v-if="isDragging" class="drag-overlay">
|
|
|
- <div class="drag-hint">
|
|
|
- <el-icon class="drag-icon"><Upload /></el-icon>
|
|
|
- <p>{{ t('document.document.documentAudit.dropImageHint') }}</p>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- 降级方案: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>{{ t('document.document.documentAudit.loadingEditor') }}</p>
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- 错误状态 -->
|
|
|
- <div v-if="wpsError && !document?.url" class="error-state">
|
|
|
- <el-result icon="error" :title="t('document.document.documentAudit.loadFailed')" :sub-title="wpsError">
|
|
|
- <template #extra>
|
|
|
- <el-button type="primary" @click="() => initWpsEditor(false)">{{ t('document.document.documentAudit.reload') }}</el-button>
|
|
|
- </template>
|
|
|
- </el-result>
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- 空状态 -->
|
|
|
- <el-empty v-if="!document?.ossId" :description="t('document.document.documentAudit.noDocument')" :image-size="100" />
|
|
|
+ <div v-else-if="previewError" class="preview-error">
|
|
|
+ <el-alert :title="previewError" type="error" :closable="false" />
|
|
|
+ </div>
|
|
|
+ <div v-else-if="pdfBlob" class="preview-content">
|
|
|
+ <VueOfficePdf :src="pdfBlob" @rendered="handleRendered" @error="handlePreviewError" />
|
|
|
+ </div>
|
|
|
+ <div v-else class="preview-empty">
|
|
|
+ <el-empty :description="t('document.document.auditForm.noDocument')" />
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
- <!-- 右侧:审核表单区域 -->
|
|
|
- <div class="audit-section">
|
|
|
- <div class="section-header">
|
|
|
- <el-icon><Edit /></el-icon>
|
|
|
- <span>{{ t('document.document.documentAudit.auditInfo') }}</span>
|
|
|
+ <!-- 右侧:审核表单 -->
|
|
|
+ <div class="form-section">
|
|
|
+ <div class="form-header">
|
|
|
+ <div class="header-icon">
|
|
|
+ <el-icon :size="24"><DocumentChecked /></el-icon>
|
|
|
+ </div>
|
|
|
+ <div class="header-content">
|
|
|
+ <h3 class="header-title">{{ t('document.document.auditForm.title') }}</h3>
|
|
|
+ <p class="header-subtitle">{{ t('document.document.auditForm.subtitle') }}</p>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
|
|
|
- <el-form ref="auditFormRef" :model="auditForm" :rules="auditRules" label-position="top" class="audit-form">
|
|
|
- <!-- 文档信息卡片 -->
|
|
|
- <el-card shadow="never" class="info-card">
|
|
|
- <template #header>
|
|
|
- <div class="card-header">
|
|
|
- <el-icon><InfoFilled /></el-icon>
|
|
|
- <span>{{ t('document.document.documentAudit.documentInfo') }}</span>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- <div class="info-item">
|
|
|
- <span class="info-label">{{ t('document.document.documentAudit.documentName') }}:</span>
|
|
|
- <span class="info-value">{{ document?.fileName || '-' }}</span>
|
|
|
- </div>
|
|
|
- <div class="info-item">
|
|
|
- <span class="info-label">{{ t('document.document.documentAudit.documentId') }}:</span>
|
|
|
- <span class="info-value">{{ document?.id || '-' }}</span>
|
|
|
- </div>
|
|
|
- </el-card>
|
|
|
-
|
|
|
- <!-- 审核结果 -->
|
|
|
- <el-form-item :label="t('document.document.auditForm.result')" prop="result">
|
|
|
- <el-radio-group v-model="auditForm.result" class="result-radio-group">
|
|
|
- <el-radio label="3" border>
|
|
|
- <div class="radio-content">
|
|
|
- <el-icon class="radio-icon success"><CircleCheck /></el-icon>
|
|
|
- <div class="radio-text">
|
|
|
- <div class="radio-title">{{ t('document.document.auditForm.pass') }}</div>
|
|
|
- <div class="radio-desc">{{ t('document.document.documentAudit.passDesc') }}</div>
|
|
|
+ <div class="form-body">
|
|
|
+ <el-form ref="formRef" :model="form" :rules="rules" label-position="top">
|
|
|
+ <el-form-item :label="t('document.document.auditForm.result')" prop="result" class="result-item">
|
|
|
+ <el-radio-group v-model="form.result" class="result-radio-group">
|
|
|
+ <el-radio :value="3" class="result-radio result-radio-pass" border>
|
|
|
+ <div class="radio-content">
|
|
|
+ <el-icon :size="20"><CircleCheck /></el-icon>
|
|
|
+ <span>{{ t('document.document.auditForm.pass') }}</span>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- </el-radio>
|
|
|
- <el-radio label="2" border>
|
|
|
- <div class="radio-content">
|
|
|
- <el-icon class="radio-icon danger"><CircleClose /></el-icon>
|
|
|
- <div class="radio-text">
|
|
|
- <div class="radio-title">{{ t('document.document.auditForm.reject') }}</div>
|
|
|
- <div class="radio-desc">{{ t('document.document.documentAudit.rejectDesc') }}</div>
|
|
|
+ </el-radio>
|
|
|
+ <el-radio :value="2" class="result-radio result-radio-reject" border>
|
|
|
+ <div class="radio-content">
|
|
|
+ <el-icon :size="20"><CircleClose /></el-icon>
|
|
|
+ <span>{{ t('document.document.auditForm.reject') }}</span>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- </el-radio>
|
|
|
- </el-radio-group>
|
|
|
- </el-form-item>
|
|
|
-
|
|
|
- <!-- 驳回原因 -->
|
|
|
- <el-form-item v-if="auditForm.result === '2'" :label="t('document.document.auditForm.reason')" prop="reason">
|
|
|
- <el-input
|
|
|
- v-model="auditForm.reason"
|
|
|
- type="textarea"
|
|
|
- :rows="6"
|
|
|
- :placeholder="t('document.document.auditForm.reasonPlaceholder')"
|
|
|
- maxlength="500"
|
|
|
- show-word-limit
|
|
|
- />
|
|
|
- </el-form-item>
|
|
|
-
|
|
|
- <!-- 审核提示 -->
|
|
|
- <el-alert v-if="auditForm.result === '3'" :title="t('document.document.documentAudit.passAlert')" type="success" :closable="false" show-icon />
|
|
|
- <el-alert v-if="auditForm.result === '2'" :title="t('document.document.documentAudit.rejectAlert')" type="warning" :closable="false" show-icon />
|
|
|
- </el-form>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
+ </el-radio>
|
|
|
+ </el-radio-group>
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <transition name="el-fade-in">
|
|
|
+ <el-form-item
|
|
|
+ v-if="form.result === 2"
|
|
|
+ :label="t('document.document.auditForm.reason')"
|
|
|
+ prop="rejectReason"
|
|
|
+ class="reason-item"
|
|
|
+ >
|
|
|
+ <el-input
|
|
|
+ v-model="form.rejectReason"
|
|
|
+ type="textarea"
|
|
|
+ :rows="6"
|
|
|
+ :placeholder="t('document.document.auditForm.reasonPlaceholder')"
|
|
|
+ maxlength="500"
|
|
|
+ show-word-limit
|
|
|
+ class="reason-textarea"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </transition>
|
|
|
+ </el-form>
|
|
|
+ </div>
|
|
|
|
|
|
- <template #footer>
|
|
|
- <div class="dialog-footer">
|
|
|
- <el-button @click="handleCancel" size="large">
|
|
|
- <el-icon><Close /></el-icon>
|
|
|
- {{ t('document.document.button.cancel') }}
|
|
|
- </el-button>
|
|
|
- <el-button type="primary" @click="submitForm" :loading="loading" size="large">
|
|
|
- <el-icon><Check /></el-icon>
|
|
|
- {{ t('document.document.button.submit') }}
|
|
|
- </el-button>
|
|
|
+ <div class="form-footer">
|
|
|
+ <el-button size="large" @click="handleClose" class="footer-btn">
|
|
|
+ <el-icon><Close /></el-icon>
|
|
|
+ <span>{{ t('document.document.button.cancel') }}</span>
|
|
|
+ </el-button>
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ size="large"
|
|
|
+ :loading="loading"
|
|
|
+ @click="handleSubmit"
|
|
|
+ class="footer-btn footer-btn-primary"
|
|
|
+ >
|
|
|
+ <el-icon v-if="!loading"><Check /></el-icon>
|
|
|
+ <span>{{ t('document.document.button.confirm') }}</span>
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- </template>
|
|
|
- </el-dialog>
|
|
|
-
|
|
|
- <!-- 历史版本对话框 -->
|
|
|
- <el-dialog v-model="showVersionDialog" :title="t('document.document.documentAudit.historyVersions')" width="800px" append-to-body destroy-on-close">
|
|
|
- <el-table :data="versionList" v-loading="loadingVersions" stripe>
|
|
|
- <el-table-column prop="version" :label="t('document.document.documentAudit.versionNumber')" width="150" align="center" />
|
|
|
- <el-table-column prop="createTime" :label="t('document.document.documentAudit.createTime')" min-width="180" align="center" />
|
|
|
- <el-table-column prop="updateTime" :label="t('document.document.documentAudit.updateTime')" min-width="180" align="center" />
|
|
|
- <el-table-column :label="t('document.document.documentAudit.action')" width="120" align="center" fixed="right">
|
|
|
- <template #default="{ row }">
|
|
|
- <el-button type="primary" size="small" @click="handleSelectVersion(row.version)"> {{ t('document.document.documentAudit.select') }} </el-button>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- </el-table>
|
|
|
-
|
|
|
- <template #footer>
|
|
|
- <el-button @click="showVersionDialog = false">{{ t('document.document.button.cancel') }}</el-button>
|
|
|
- </template>
|
|
|
+ </div>
|
|
|
</el-dialog>
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
-import { ref, reactive, watch, nextTick, onBeforeUnmount } from 'vue';
|
|
|
+import { ref, reactive, computed, nextTick, watch } from 'vue';
|
|
|
import { useI18n } from 'vue-i18n';
|
|
|
+import { DocumentChecked, CircleCheck, CircleClose, Check, Close } from '@element-plus/icons-vue';
|
|
|
import type { FormInstance } from 'element-plus';
|
|
|
-import { ElMessage, ElMessageBox } from 'element-plus';
|
|
|
-import { Document, Edit, InfoFilled, CircleCheck, CircleClose, Close, Check, Loading, Upload, Picture, Delete, Clock } from '@element-plus/icons-vue';
|
|
|
-import { useUserStore } from '@/store/modules/user';
|
|
|
-import { cleanDocumentComments, getFileVersionList, getFinalFile, initWpsDocument, cancelWpsDocument, type FileVersion } from '@/api/wps/save';
|
|
|
-
|
|
|
-interface Document {
|
|
|
- id: number | string;
|
|
|
- name?: string;
|
|
|
- ossId?: number | string;
|
|
|
- fileName?: string;
|
|
|
- url?: string;
|
|
|
+import VueOfficePdf from '@vue-office/pdf';
|
|
|
+import axios from 'axios';
|
|
|
+import { globalHeaders } from '@/utils/request';
|
|
|
+
|
|
|
+const baseURL = import.meta.env.VITE_APP_BASE_API;
|
|
|
+
|
|
|
+interface DocumentInfo {
|
|
|
+ id: string | number;
|
|
|
+ actualDocument?: string | number;
|
|
|
+ [key: string]: any;
|
|
|
}
|
|
|
|
|
|
-interface AuditData {
|
|
|
- documentId: number | string;
|
|
|
+interface AuditFormData {
|
|
|
+ documentId: string | number;
|
|
|
result: number;
|
|
|
rejectReason?: string;
|
|
|
- ossId?: number | string; // 最终的 ossId
|
|
|
}
|
|
|
|
|
|
interface Props {
|
|
|
modelValue: boolean;
|
|
|
- document?: Document | null;
|
|
|
+ document: DocumentInfo | null;
|
|
|
title?: string;
|
|
|
}
|
|
|
|
|
|
-interface Emits {
|
|
|
- (e: 'update:modelValue', value: boolean): void;
|
|
|
- (e: 'submit', data: AuditData): void; // 提交审核数据
|
|
|
-}
|
|
|
+const props = withDefaults(defineProps<Props>(), {
|
|
|
+ title: ''
|
|
|
+});
|
|
|
|
|
|
-const props = defineProps<Props>();
|
|
|
-const emit = defineEmits<Emits>();
|
|
|
+const emit = defineEmits<{
|
|
|
+ 'update:modelValue': [value: boolean];
|
|
|
+ submit: [data: AuditFormData];
|
|
|
+}>();
|
|
|
|
|
|
const { t } = useI18n();
|
|
|
-const userStore = useUserStore();
|
|
|
-
|
|
|
-const dialogVisible = ref(false);
|
|
|
+const formRef = ref<FormInstance>();
|
|
|
const loading = ref(false);
|
|
|
-const auditFormRef = ref<FormInstance>();
|
|
|
-
|
|
|
-// WPS 编辑器相关
|
|
|
-const wpsContainerRef = ref<HTMLDivElement>();
|
|
|
-const wpsLoading = ref(false);
|
|
|
-const wpsError = ref('');
|
|
|
-const isDragging = ref(false);
|
|
|
-const copyingAvatar = ref(false);
|
|
|
-const cleaningComments = ref(false);
|
|
|
-const currentVersion = ref(1); // 当前文档版本号
|
|
|
-const showVersionDialog = ref(false); // 显示历史版本对话框
|
|
|
-const versionList = ref<FileVersion[]>([]); // 历史版本列表
|
|
|
-const loadingVersions = ref(false); // 加载历史版本中
|
|
|
-let wpsInstance: any = null;
|
|
|
-let dragCounter = 0; // 用于跟踪拖拽进入/离开次数
|
|
|
-
|
|
|
-// WPS 配置
|
|
|
-const WPS_APP_ID = 'SX20260105YMMIXV';
|
|
|
-
|
|
|
-// 审核表单数据
|
|
|
-const auditForm = ref({
|
|
|
- id: 0 as number | string,
|
|
|
- result: '3',
|
|
|
- reason: ''
|
|
|
+const previewLoading = ref(false);
|
|
|
+const previewError = ref('');
|
|
|
+const pdfBlob = ref<ArrayBuffer | null>(null);
|
|
|
+
|
|
|
+const visible = computed({
|
|
|
+ get: () => props.modelValue,
|
|
|
+ set: (val) => emit('update:modelValue', val)
|
|
|
+});
|
|
|
+
|
|
|
+const title = computed(() => props.title || t('document.document.dialog.auditDocument'));
|
|
|
+
|
|
|
+const form = ref<AuditFormData>({
|
|
|
+ documentId: 0,
|
|
|
+ result: 3,
|
|
|
+ rejectReason: ''
|
|
|
});
|
|
|
|
|
|
-// 审核表单验证规则
|
|
|
-const auditRules = reactive({
|
|
|
+const rules = reactive({
|
|
|
result: [
|
|
|
{
|
|
|
required: true,
|
|
|
@@ -252,7 +156,7 @@ const auditRules = reactive({
|
|
|
trigger: 'change'
|
|
|
}
|
|
|
],
|
|
|
- reason: [
|
|
|
+ rejectReason: [
|
|
|
{
|
|
|
required: true,
|
|
|
message: t('document.document.auditRule.reasonRequired'),
|
|
|
@@ -261,1023 +165,395 @@ const auditRules = reactive({
|
|
|
]
|
|
|
});
|
|
|
|
|
|
-// 获取文件类型
|
|
|
-const getFileType = (fileName: 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 编辑器
|
|
|
-// shouldCallInitApi: 是否需要调用后端初始化接口(只在 dialog 首次打开时为 true)
|
|
|
-const initWpsEditor = async (shouldCallInitApi = false) => {
|
|
|
- if (!wpsContainerRef.value || !props.document?.ossId) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- wpsLoading.value = true;
|
|
|
- wpsError.value = '';
|
|
|
-
|
|
|
- // 检查 WPS SDK
|
|
|
- if (!(window as any).WebOfficeSDK) {
|
|
|
- wpsError.value = 'WPS SDK 未加载';
|
|
|
- wpsLoading.value = false;
|
|
|
- return;
|
|
|
- }
|
|
|
+// 监听对话框打开和 document 变化,初始化表单和加载预览
|
|
|
+watch(
|
|
|
+ [() => props.modelValue, () => props.document],
|
|
|
+ ([isVisible, newDoc]) => {
|
|
|
+ // 只有在对话框打开且有文档时才加载
|
|
|
+ if (isVisible && newDoc) {
|
|
|
+ form.value = {
|
|
|
+ documentId: newDoc.id as number,
|
|
|
+ result: 3,
|
|
|
+ rejectReason: ''
|
|
|
+ };
|
|
|
+ nextTick(() => {
|
|
|
+ formRef.value?.clearValidate();
|
|
|
+ });
|
|
|
|
|
|
- const WebOfficeSDK = (window as any).WebOfficeSDK;
|
|
|
-
|
|
|
- // 获取文件类型
|
|
|
- const officeType = getFileType(props.document.fileName || '', props.document.url);
|
|
|
-
|
|
|
- // 只在 dialog 首次打开时调用后端接口初始化文档
|
|
|
- if (shouldCallInitApi) {
|
|
|
- console.log('[WPS] 调用后端初始化接口,ossId:', props.document.ossId);
|
|
|
- try {
|
|
|
- const initRes = await initWpsDocument(props.document.ossId);
|
|
|
- const backendVersion = initRes.data; // data 直接是版本号
|
|
|
- currentVersion.value = backendVersion;
|
|
|
- console.log('[WPS] 后端返回版本号:', backendVersion);
|
|
|
- } catch (err: any) {
|
|
|
- console.error('[WPS] 调用后端初始化接口失败:', err);
|
|
|
- wpsLoading.value = false;
|
|
|
- wpsError.value = '初始化文档失败';
|
|
|
-
|
|
|
- // 显示错误提示
|
|
|
- ElMessage.error('初始化文档失败: ' + (err.message || '未知错误'));
|
|
|
-
|
|
|
- // 关闭对话框
|
|
|
- setTimeout(() => {
|
|
|
- dialogVisible.value = false;
|
|
|
- }, 1500);
|
|
|
-
|
|
|
- return; // 终止初始化流程
|
|
|
- }
|
|
|
- } else {
|
|
|
- console.log('[WPS] 使用当前版本号重新初始化编辑器,版本:', currentVersion.value);
|
|
|
+ // 加载 PDF 预览
|
|
|
+ loadPdfPreview(newDoc);
|
|
|
+ } else if (!isVisible) {
|
|
|
+ // 对话框关闭时清理状态
|
|
|
+ pdfBlob.value = null;
|
|
|
+ previewError.value = '';
|
|
|
+ previewLoading.value = false;
|
|
|
}
|
|
|
+ },
|
|
|
+ { immediate: true }
|
|
|
+);
|
|
|
|
|
|
- // 使用 ossId + 当前版本号组成 fileId
|
|
|
- const fileId = `${props.document.ossId}_${currentVersion.value}`;
|
|
|
-
|
|
|
- console.log('[WPS] 初始化配置:', {
|
|
|
- appId: WPS_APP_ID,
|
|
|
- officeType: officeType,
|
|
|
- fileId: fileId,
|
|
|
- ossId: props.document.ossId,
|
|
|
- version: currentVersion.value,
|
|
|
- fileName: props.document.fileName,
|
|
|
- fileUrl: props.document.url
|
|
|
- });
|
|
|
-
|
|
|
- // 标准初始化配置(按官方文档)
|
|
|
- const config = {
|
|
|
- // 必需参数
|
|
|
- appId: WPS_APP_ID,
|
|
|
- officeType: officeType,
|
|
|
- fileId: fileId,
|
|
|
-
|
|
|
- // 可选参数
|
|
|
- mount: wpsContainerRef.value,
|
|
|
- // 指定当前用户ID为编辑者ID
|
|
|
- userId: String(userStore.userId)
|
|
|
- };
|
|
|
-
|
|
|
- // 初始化 WPS 编辑器
|
|
|
- wpsInstance = WebOfficeSDK.init(config);
|
|
|
-
|
|
|
- wpsLoading.value = false;
|
|
|
- console.log('[WPS] 编辑器初始化成功');
|
|
|
- } catch (err: any) {
|
|
|
- console.error('[WPS] 初始化失败:', err);
|
|
|
- wpsError.value = err.message || '初始化失败';
|
|
|
- wpsLoading.value = false;
|
|
|
- }
|
|
|
-};
|
|
|
+// 加载 PDF 预览
|
|
|
+const loadPdfPreview = async (doc: DocumentInfo) => {
|
|
|
+ previewLoading.value = true;
|
|
|
+ previewError.value = '';
|
|
|
+ pdfBlob.value = null;
|
|
|
|
|
|
-// 销毁 WPS 编辑器
|
|
|
-const destroyWpsEditor = () => {
|
|
|
- if (wpsInstance) {
|
|
|
- try {
|
|
|
- if (wpsInstance.destroy) {
|
|
|
- wpsInstance.destroy();
|
|
|
- }
|
|
|
- wpsInstance = null;
|
|
|
- console.log('[WPS] 编辑器已销毁');
|
|
|
- } catch (err) {
|
|
|
- console.error('[WPS] 销毁编辑器失败:', err);
|
|
|
- }
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-// 保存文档
|
|
|
-const saveWpsDocument = async () => {
|
|
|
- if (!wpsInstance) {
|
|
|
- return null;
|
|
|
+ if (!doc.actualDocument) {
|
|
|
+ previewError.value = t('document.document.auditForm.documentNotExist');
|
|
|
+ previewLoading.value = false;
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
- console.log('[WPS] 开始保存文档');
|
|
|
- const result = await wpsInstance.save();
|
|
|
- console.log('[WPS] 保存成功:', result);
|
|
|
- return result;
|
|
|
- } catch (err: any) {
|
|
|
- console.error('[WPS] 保存失败:', err);
|
|
|
- throw err;
|
|
|
- }
|
|
|
-};
|
|
|
+ // 使用统一的文件下载接口获取 PDF 文件
|
|
|
+ const response = await axios({
|
|
|
+ method: 'post',
|
|
|
+ url: `${baseURL}/document/document/download/${doc.actualDocument}`,
|
|
|
+ responseType: 'arraybuffer',
|
|
|
+ headers: globalHeaders()
|
|
|
+ });
|
|
|
|
|
|
-// 拖拽进入
|
|
|
-const handleDragEnter = (e: DragEvent) => {
|
|
|
- dragCounter++;
|
|
|
- if (dragCounter === 1) {
|
|
|
- isDragging.value = true;
|
|
|
+ pdfBlob.value = response.data;
|
|
|
+ previewLoading.value = false;
|
|
|
+ } catch (error) {
|
|
|
+ console.error('下载PDF文件失败:', error);
|
|
|
+ previewError.value = t('document.document.auditForm.loadFailed');
|
|
|
+ previewLoading.value = false;
|
|
|
}
|
|
|
};
|
|
|
|
|
|
-// 拖拽离开
|
|
|
-const handleDragLeave = (e: DragEvent) => {
|
|
|
- dragCounter--;
|
|
|
- if (dragCounter === 0) {
|
|
|
- isDragging.value = false;
|
|
|
- }
|
|
|
+// PDF 渲染完成
|
|
|
+const handleRendered = () => {
|
|
|
+ previewLoading.value = false;
|
|
|
};
|
|
|
|
|
|
-// 拖拽悬停
|
|
|
-const handleDragOver = (e: DragEvent) => {
|
|
|
- e.preventDefault();
|
|
|
- e.stopPropagation();
|
|
|
+// PDF 预览错误
|
|
|
+const handlePreviewError = (error: any) => {
|
|
|
+ previewLoading.value = false;
|
|
|
+ previewError.value = t('document.document.auditForm.previewError');
|
|
|
+ console.error('PDF预览错误:', error);
|
|
|
};
|
|
|
|
|
|
-// 处理拖放
|
|
|
-const handleDrop = async (e: DragEvent) => {
|
|
|
- e.preventDefault();
|
|
|
- e.stopPropagation();
|
|
|
-
|
|
|
- isDragging.value = false;
|
|
|
- dragCounter = 0;
|
|
|
-
|
|
|
- if (!wpsInstance) {
|
|
|
- ElMessage.warning(t('document.document.documentAudit.wpsNotInitialized'));
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // 处理图片拖放
|
|
|
- const files = e.dataTransfer?.files;
|
|
|
- if (!files || files.length === 0) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // 只处理第一个文件
|
|
|
- const file = files[0];
|
|
|
-
|
|
|
- // 检查是否是图片
|
|
|
- if (!file.type.startsWith('image/')) {
|
|
|
- ElMessage.warning(t('document.document.documentAudit.onlyImageSupported'));
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- console.log('[WPS] 开始插入图片:', file.name);
|
|
|
-
|
|
|
- // 读取图片为 base64
|
|
|
- const reader = new FileReader();
|
|
|
- reader.onload = async (event) => {
|
|
|
- const base64 = event.target?.result as string;
|
|
|
-
|
|
|
- try {
|
|
|
- // 获取 WPS Application 对象
|
|
|
- const app = await wpsInstance.Application;
|
|
|
-
|
|
|
- if (!app) {
|
|
|
- ElMessage.error('无法获取 WPS Application 对象');
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // 根据文件类型插入图片
|
|
|
- const officeType = getFileType(props.document?.fileName || '');
|
|
|
-
|
|
|
- if (officeType === 'w') {
|
|
|
- // Word 文档:插入图片到光标位置
|
|
|
- const selection = await app.ActiveDocument.Application.Selection;
|
|
|
- await selection.InlineShapes.AddPicture(base64);
|
|
|
- ElMessage.success(t('document.document.documentAudit.imageInserted'));
|
|
|
- } else if (officeType === 's') {
|
|
|
- // Excel 表格:插入图片到当前单元格
|
|
|
- const activeSheet = await app.ActiveSheet;
|
|
|
- const activeCell = await app.ActiveCell;
|
|
|
- const row = await activeCell.Row;
|
|
|
- const col = await activeCell.Column;
|
|
|
- await activeSheet.Shapes.AddPicture(base64, false, true, col * 64, row * 20, 200, 150);
|
|
|
- ElMessage.success(t('document.document.documentAudit.imageInserted'));
|
|
|
- } else if (officeType === 'p') {
|
|
|
- // PowerPoint:插入图片到当前幻灯片
|
|
|
- const activeSlide = await app.ActivePresentation.Slides.Item(await app.ActiveWindow.Selection.SlideRange.SlideIndex);
|
|
|
- await activeSlide.Shapes.AddPicture(base64, false, true, 100, 100, 200, 150);
|
|
|
- ElMessage.success(t('document.document.documentAudit.imageInserted'));
|
|
|
- } else {
|
|
|
- ElMessage.warning(t('document.document.documentAudit.currentTypeNotSupported'));
|
|
|
- }
|
|
|
-
|
|
|
- console.log('[WPS] 图片插入成功');
|
|
|
- } catch (err: any) {
|
|
|
- console.error('[WPS] 插入图片失败:', err);
|
|
|
- ElMessage.error(t('document.document.documentAudit.insertImageFailed') + ': ' + err.message);
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- reader.onerror = () => {
|
|
|
- ElMessage.error(t('document.document.documentAudit.readImageFailed'));
|
|
|
- };
|
|
|
-
|
|
|
- reader.readAsDataURL(file);
|
|
|
- } catch (err: any) {
|
|
|
- console.error('[WPS] 处理拖放失败:', err);
|
|
|
- ElMessage.error(t('document.document.documentAudit.dropHandleFailed'));
|
|
|
- }
|
|
|
+const handleClose = () => {
|
|
|
+ visible.value = false;
|
|
|
+ formRef.value?.resetFields();
|
|
|
+ form.value = {
|
|
|
+ documentId: 0,
|
|
|
+ result: 3,
|
|
|
+ rejectReason: ''
|
|
|
+ };
|
|
|
+ pdfBlob.value = null;
|
|
|
+ previewError.value = '';
|
|
|
};
|
|
|
|
|
|
-// 复制头像到剪贴板
|
|
|
-const handleCopyAvatar = async () => {
|
|
|
- try {
|
|
|
- // 先让用户选择审核结果
|
|
|
- let reviewResult: string;
|
|
|
+const handleSubmit = async () => {
|
|
|
+ if (!formRef.value) return;
|
|
|
|
|
|
+ // 如果选择驳回,需要验证驳回理由
|
|
|
+ if (form.value.result === 2) {
|
|
|
try {
|
|
|
- await ElMessageBox.confirm(t('document.document.copySignature.selectResultMessage'), t('document.document.copySignature.selectResult'), {
|
|
|
- confirmButtonText: t('document.document.copySignature.pass'),
|
|
|
- cancelButtonText: t('document.document.copySignature.reject'),
|
|
|
- distinguishCancelAndClose: true,
|
|
|
- closeOnClickModal: false,
|
|
|
- closeOnPressEscape: false,
|
|
|
- type: 'info'
|
|
|
- });
|
|
|
- // 点击确认按钮 = 通过
|
|
|
- reviewResult = 'pass';
|
|
|
- } catch (action) {
|
|
|
- if (action === 'cancel') {
|
|
|
- // 点击取消按钮 = 驳回
|
|
|
- reviewResult = 'reject';
|
|
|
- } else {
|
|
|
- // 点击关闭或按 ESC = 取消操作
|
|
|
- console.log('[签名] 用户取消选择');
|
|
|
- return;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- copyingAvatar.value = true;
|
|
|
- console.log('[签名] 开始生成审核信息图片,审核结果:', reviewResult);
|
|
|
-
|
|
|
- // 获取当前用户昵称
|
|
|
- const reviewerName = userStore.nickname || userStore.name || t('document.document.copySignature.unknown');
|
|
|
-
|
|
|
- // 获取当前时间
|
|
|
- const now = new Date();
|
|
|
- const year = now.getFullYear();
|
|
|
- const month = now.getMonth() + 1;
|
|
|
- const day = now.getDate();
|
|
|
- const hour = now.getHours();
|
|
|
- const minute = now.getMinutes();
|
|
|
- const reviewTime = t('document.document.copySignature.timeFormat', { year, month, day, hour, minute });
|
|
|
-
|
|
|
- // 审核结果文本
|
|
|
- const resultText = reviewResult === 'pass' ? t('document.document.copySignature.passText') : t('document.document.copySignature.rejectText');
|
|
|
-
|
|
|
- // 根据审核结果确定颜色
|
|
|
- const color = reviewResult === 'pass' ? '#00aa00' : '#ff0000';
|
|
|
-
|
|
|
- console.log('[签名] 审核结果:', reviewResult, '颜色:', color, '结果文本:', resultText);
|
|
|
-
|
|
|
- // 创建 canvas
|
|
|
- const canvas = document.createElement('canvas');
|
|
|
- const ctx = canvas.getContext('2d');
|
|
|
-
|
|
|
- if (!ctx) {
|
|
|
- ElMessage.error(t('document.document.copySignature.canvasNotSupported'));
|
|
|
- copyingAvatar.value = false;
|
|
|
+ await formRef.value.validate();
|
|
|
+ } catch (error) {
|
|
|
return;
|
|
|
}
|
|
|
-
|
|
|
- // 设置 canvas 尺寸
|
|
|
- const width = 300;
|
|
|
- const height = 100;
|
|
|
- canvas.width = width;
|
|
|
- canvas.height = height;
|
|
|
-
|
|
|
- // 不填充背景,保持透明
|
|
|
-
|
|
|
- // 绘制边框(5px 粗,根据审核结果变色)
|
|
|
- ctx.strokeStyle = color;
|
|
|
- ctx.lineWidth = 5;
|
|
|
- ctx.strokeRect(2.5, 2.5, width - 5, height - 5);
|
|
|
-
|
|
|
- // 设置字体样式(根据审核结果变色)
|
|
|
- ctx.fillStyle = color;
|
|
|
- ctx.textBaseline = 'middle';
|
|
|
-
|
|
|
- // 第一行:审核人(左边)和审核结果(右边)
|
|
|
- ctx.font = 'bold 18px Arial, "Microsoft YaHei", sans-serif';
|
|
|
- const reviewerText = t('document.document.copySignature.reviewer', { name: reviewerName });
|
|
|
- ctx.fillText(reviewerText, 20, height / 3);
|
|
|
-
|
|
|
- // 审核结果靠右显示
|
|
|
- const resultWidth = ctx.measureText(resultText).width;
|
|
|
- ctx.fillText(resultText, width - resultWidth - 20, height / 3);
|
|
|
-
|
|
|
- // 第二行:审核时间(居左对齐,加粗)
|
|
|
- ctx.font = 'bold 16px Arial, "Microsoft YaHei", sans-serif';
|
|
|
- const line2 = t('document.document.copySignature.reviewTime', { time: reviewTime });
|
|
|
- ctx.fillText(line2, 20, (height * 2) / 3);
|
|
|
-
|
|
|
- console.log('[签名] Canvas 绘制完成');
|
|
|
-
|
|
|
- // 将 canvas 转换为 Blob
|
|
|
- canvas.toBlob(async (blob) => {
|
|
|
- if (!blob) {
|
|
|
- ElMessage.error(t('document.document.copySignature.generateFailed'));
|
|
|
- copyingAvatar.value = false;
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- console.log('[签名] 图片生成成功,大小:', blob.size);
|
|
|
-
|
|
|
- try {
|
|
|
- // 复制到剪贴板
|
|
|
- await navigator.clipboard.write([
|
|
|
- new ClipboardItem({
|
|
|
- 'image/png': blob
|
|
|
- })
|
|
|
- ]);
|
|
|
-
|
|
|
- ElMessage({
|
|
|
- type: 'success',
|
|
|
- message: t('document.document.copySignature.copySuccess'),
|
|
|
- duration: 3000
|
|
|
- });
|
|
|
-
|
|
|
- // 显示使用提示
|
|
|
- setTimeout(() => {
|
|
|
- ElMessage({
|
|
|
- type: 'info',
|
|
|
- dangerouslyUseHTMLString: true,
|
|
|
- message: t('document.document.copySignature.usageHint'),
|
|
|
- duration: 6000,
|
|
|
- showClose: true
|
|
|
- });
|
|
|
- }, 500);
|
|
|
-
|
|
|
- console.log('[签名] 复制成功');
|
|
|
- } catch (err: any) {
|
|
|
- console.error('[签名] 复制失败:', err);
|
|
|
-
|
|
|
- // 根据错误类型显示不同提示
|
|
|
- if (err.message?.includes('clipboard') || err.message?.includes('Clipboard')) {
|
|
|
- ElMessage({
|
|
|
- type: 'warning',
|
|
|
- dangerouslyUseHTMLString: true,
|
|
|
- message: t('document.document.copySignature.browserNotSupported'),
|
|
|
- duration: 6000,
|
|
|
- showClose: true
|
|
|
- });
|
|
|
- } else {
|
|
|
- ElMessage({
|
|
|
- type: 'error',
|
|
|
- message: t('document.document.copySignature.copyFailed', { error: err.message || t('document.document.copySignature.unknownError') }),
|
|
|
- duration: 5000
|
|
|
- });
|
|
|
- }
|
|
|
- } finally {
|
|
|
- copyingAvatar.value = false;
|
|
|
- }
|
|
|
- }, 'image/png');
|
|
|
- } catch (err: any) {
|
|
|
- console.error('[签名] 生成图片失败:', err);
|
|
|
- ElMessage.error(t('document.document.copySignature.generateFailed') + ': ' + (err.message || t('document.document.copySignature.unknownError')));
|
|
|
- copyingAvatar.value = false;
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-// 清空批注
|
|
|
-const handleCleanComments = async () => {
|
|
|
- if (!props.document?.id || !props.document?.ossId) {
|
|
|
- ElMessage.warning(t('document.document.documentAudit.documentInfoIncomplete'));
|
|
|
- return;
|
|
|
}
|
|
|
|
|
|
+ loading.value = true;
|
|
|
try {
|
|
|
- await ElMessageBox.confirm(t('document.document.documentAudit.cleanCommentsConfirm'), t('document.document.documentAudit.cleanCommentsTitle'), {
|
|
|
- confirmButtonText: t('document.document.message.confirmButton'),
|
|
|
- cancelButtonText: t('document.document.message.cancelButton'),
|
|
|
- type: 'warning'
|
|
|
- });
|
|
|
-
|
|
|
- cleaningComments.value = true;
|
|
|
- console.log('[清空批注] 开始清空文档批注,文档ID:', props.document.id, 'ossId:', props.document.ossId, '当前版本:', currentVersion.value);
|
|
|
-
|
|
|
- // 调用后端接口清空批注,获取新版本号
|
|
|
- const res = await cleanDocumentComments(props.document.ossId);
|
|
|
- const newVersion = res.data; // 后端返回的新版本号
|
|
|
- currentVersion.value = newVersion;
|
|
|
-
|
|
|
- ElMessage.success(t('document.document.documentAudit.cleanCommentsSuccess'));
|
|
|
- console.log('[清空批注] 批注清空成功,新版本号:', newVersion);
|
|
|
-
|
|
|
- // 销毁当前 WPS 编辑器
|
|
|
- destroyWpsEditor();
|
|
|
-
|
|
|
- // 等待 DOM 更新
|
|
|
- await nextTick();
|
|
|
-
|
|
|
- // 使用新版本号重新初始化 WPS 编辑器(不调用后端初始化接口)
|
|
|
- await initWpsEditor(false);
|
|
|
-
|
|
|
- console.log('[清空批注] WPS 编辑器已使用新版本重新初始化,fileId:', `${props.document.ossId}_${currentVersion.value}`);
|
|
|
- } catch (err: any) {
|
|
|
- if (err === 'cancel') {
|
|
|
- console.log('[清空批注] 用户取消操作');
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- console.error('[清空批注] 清空失败:', err);
|
|
|
- ElMessage.error(t('document.document.documentAudit.cleanCommentsFailed') + ': ' + (err.message || t('document.document.copySignature.unknownError')));
|
|
|
- } finally {
|
|
|
- cleaningComments.value = false;
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-// 查看历史版本
|
|
|
-const handleViewVersions = async () => {
|
|
|
- if (!props.document?.ossId) {
|
|
|
- ElMessage.warning(t('document.document.documentAudit.documentInfoIncomplete'));
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- loadingVersions.value = true;
|
|
|
- showVersionDialog.value = true;
|
|
|
-
|
|
|
- console.log('[历史版本] 获取历史版本列表,ossId:', props.document.ossId);
|
|
|
-
|
|
|
- const res = await getFileVersionList(props.document.ossId);
|
|
|
- versionList.value = res.data || [];
|
|
|
+ const submitData: AuditFormData = {
|
|
|
+ documentId: form.value.documentId,
|
|
|
+ result: form.value.result,
|
|
|
+ rejectReason: form.value.result === 2 ? form.value.rejectReason : undefined
|
|
|
+ };
|
|
|
|
|
|
- console.log('[历史版本] 获取成功,版本数量:', versionList.value.length);
|
|
|
- } catch (err: any) {
|
|
|
- console.error('[历史版本] 获取失败:', err);
|
|
|
- ElMessage.error(t('document.document.documentAudit.getVersionsFailed') + ': ' + (err.message || t('document.document.copySignature.unknownError')));
|
|
|
- showVersionDialog.value = false;
|
|
|
+ // 触发提交事件,由父组件处理实际的审核请求
|
|
|
+ emit('submit', submitData);
|
|
|
} finally {
|
|
|
- loadingVersions.value = false;
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-// 选择历史版本
|
|
|
-const handleSelectVersion = async (version: number) => {
|
|
|
- if (!props.document?.ossId) {
|
|
|
- ElMessage.warning(t('document.document.documentAudit.documentInfoIncomplete'));
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- console.log('[历史版本] 选择版本:', version, 'ossId:', props.document.ossId);
|
|
|
-
|
|
|
- // 更新当前版本号
|
|
|
- currentVersion.value = version;
|
|
|
-
|
|
|
- // 关闭历史版本对话框
|
|
|
- showVersionDialog.value = false;
|
|
|
-
|
|
|
- // 销毁当前 WPS 编辑器
|
|
|
- destroyWpsEditor();
|
|
|
-
|
|
|
- // 等待 DOM 更新
|
|
|
- await nextTick();
|
|
|
-
|
|
|
- // 使用选择的版本号重新初始化 WPS 编辑器(不调用后端初始化接口)
|
|
|
- await initWpsEditor(false);
|
|
|
-
|
|
|
- ElMessage.success(t('document.document.documentAudit.switchVersionSuccess', { version }));
|
|
|
- console.log('[历史版本] WPS 编辑器已切换到版本:', version, 'fileId:', `${props.document.ossId}_${version}`);
|
|
|
- } catch (err: any) {
|
|
|
- console.error('[历史版本] 切换版本失败:', err);
|
|
|
- ElMessage.error(t('document.document.documentAudit.switchVersionFailed') + ': ' + (err.message || t('document.document.copySignature.unknownError')));
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-// 监听 modelValue 变化
|
|
|
-watch(
|
|
|
- () => props.modelValue,
|
|
|
- (val) => {
|
|
|
- dialogVisible.value = val;
|
|
|
- if (val && props.document) {
|
|
|
- // 重置版本号为 1
|
|
|
- currentVersion.value = 1;
|
|
|
-
|
|
|
- auditForm.value = {
|
|
|
- id: props.document.id,
|
|
|
- result: '3',
|
|
|
- reason: ''
|
|
|
- };
|
|
|
- nextTick(() => {
|
|
|
- auditFormRef.value?.clearValidate();
|
|
|
- // 自动初始化 WPS 编辑器,使用版本号 1(调用后端初始化接口)
|
|
|
- if (props.document?.ossId) {
|
|
|
- console.log('[WPS] 对话框打开,初始化编辑器,版本号:', currentVersion.value);
|
|
|
- initWpsEditor(true);
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
+ loading.value = false;
|
|
|
}
|
|
|
-);
|
|
|
-
|
|
|
-// 监听dialogVisible变化
|
|
|
-watch(dialogVisible, async (val) => {
|
|
|
- emit('update:modelValue', val);
|
|
|
- if (!val) {
|
|
|
- // 对话框关闭时,调用取消接口
|
|
|
- if (props.document?.ossId) {
|
|
|
- try {
|
|
|
- console.log('[WPS] 对话框关闭,调用取消接口,ossId:', props.document.ossId);
|
|
|
- await cancelWpsDocument(props.document.ossId);
|
|
|
- console.log('[WPS] 取消接口调用成功');
|
|
|
- } catch (err) {
|
|
|
- console.error('[WPS] 取消接口调用失败:', err);
|
|
|
- // 取消接口失败不影响关闭流程
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- auditForm.value = {
|
|
|
- id: 0,
|
|
|
- result: '3',
|
|
|
- reason: ''
|
|
|
- };
|
|
|
- // 关闭对话框时重置版本号
|
|
|
- currentVersion.value = 1;
|
|
|
- destroyWpsEditor();
|
|
|
- }
|
|
|
-});
|
|
|
-
|
|
|
-// 取消操作
|
|
|
-const handleCancel = () => {
|
|
|
- dialogVisible.value = false;
|
|
|
};
|
|
|
|
|
|
-// 提交表单
|
|
|
-const submitForm = () => {
|
|
|
- auditFormRef.value?.validate(async (valid: boolean) => {
|
|
|
- if (valid) {
|
|
|
- loading.value = true;
|
|
|
- try {
|
|
|
- // 如果使用了 WPS 编辑器,先保存文档
|
|
|
- let savedFileInfo = null;
|
|
|
- if (wpsInstance) {
|
|
|
- try {
|
|
|
- savedFileInfo = await saveWpsDocument();
|
|
|
- if (savedFileInfo) {
|
|
|
- console.log('[审核提交] 文档保存成功:', savedFileInfo);
|
|
|
- ElMessage.success(t('document.document.documentAudit.documentSaved'));
|
|
|
- }
|
|
|
- } catch (err) {
|
|
|
- console.error('[审核提交] 保存文档失败:', err);
|
|
|
- ElMessage.warning(t('document.document.documentAudit.documentSaveFailed'));
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 获取当前 fileId
|
|
|
- const currentFileId = `${props.document?.ossId}_${currentVersion.value}`;
|
|
|
- console.log('[审核提交] 当前 fileId:', currentFileId);
|
|
|
-
|
|
|
- // 调用接口获取最终文档信息
|
|
|
- let finalOssId = props.document?.ossId;
|
|
|
- try {
|
|
|
- console.log('[审核提交] 获取最终文档信息...');
|
|
|
- const finalFileRes = await getFinalFile(currentFileId);
|
|
|
- finalOssId = finalFileRes.data.ossId;
|
|
|
- console.log('[审核提交] 获取到最终 ossId:', finalOssId);
|
|
|
- } catch (err) {
|
|
|
- console.error('[审核提交] 获取最终文档信息失败:', err);
|
|
|
- ElMessage.warning(t('document.document.documentAudit.getFinalFileFailed'));
|
|
|
- }
|
|
|
-
|
|
|
- // 构建审核数据
|
|
|
- const auditData: AuditData = {
|
|
|
- documentId: auditForm.value.id,
|
|
|
- result: parseInt(auditForm.value.result),
|
|
|
- rejectReason: auditForm.value.reason,
|
|
|
- ossId: finalOssId // 使用最终的 ossId
|
|
|
- };
|
|
|
-
|
|
|
- console.log('[审核提交] 提交审核数据到父组件:', auditData);
|
|
|
-
|
|
|
- // 关闭对话框
|
|
|
- dialogVisible.value = false;
|
|
|
-
|
|
|
- // 通过 emit 将审核数据传递给父组件
|
|
|
- emit('submit', auditData);
|
|
|
- } catch (error) {
|
|
|
- console.error('[审核提交] 处理失败:', error);
|
|
|
- ElMessage.error(t('document.document.documentAudit.processFailed') + ': ' + (error as any).message || t('document.document.copySignature.unknownError'));
|
|
|
- } finally {
|
|
|
- loading.value = false;
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
+// 暴露方法供父组件调用
|
|
|
+const closeDialog = () => {
|
|
|
+ handleClose();
|
|
|
};
|
|
|
|
|
|
-// 组件卸载前清理
|
|
|
-onBeforeUnmount(() => {
|
|
|
- destroyWpsEditor();
|
|
|
+defineExpose({
|
|
|
+ closeDialog
|
|
|
});
|
|
|
</script>
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
-.audit-dialog {
|
|
|
- :deep(.el-dialog__header) {
|
|
|
- padding: 20px 24px;
|
|
|
- border-bottom: 1px solid #e4e7ed;
|
|
|
- margin: 0;
|
|
|
-
|
|
|
- .el-dialog__title {
|
|
|
- font-size: 18px;
|
|
|
- font-weight: 600;
|
|
|
- color: #303133;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- :deep(.el-dialog__body) {
|
|
|
- padding: 0;
|
|
|
- height: calc(80vh - 140px);
|
|
|
- overflow: hidden;
|
|
|
- }
|
|
|
-
|
|
|
- :deep(.el-dialog__footer) {
|
|
|
- padding: 16px 24px;
|
|
|
- border-top: 1px solid #e4e7ed;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
.audit-container {
|
|
|
display: flex;
|
|
|
- height: 100%;
|
|
|
- gap: 1px;
|
|
|
- background: #e4e7ed;
|
|
|
-}
|
|
|
-
|
|
|
-.preview-section {
|
|
|
- flex: 1;
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- background: #fff;
|
|
|
- min-width: 0;
|
|
|
- position: relative;
|
|
|
-}
|
|
|
-
|
|
|
-.preview-header {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: space-between;
|
|
|
- padding: 16px 20px;
|
|
|
- background: #fff;
|
|
|
- border-bottom: 1px solid #e4e7ed;
|
|
|
- flex-shrink: 0;
|
|
|
-
|
|
|
- .file-info {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- gap: 12px;
|
|
|
-
|
|
|
- .file-icon {
|
|
|
- font-size: 24px;
|
|
|
- color: #409eff;
|
|
|
- }
|
|
|
+ gap: 20px;
|
|
|
+ height: 70vh;
|
|
|
+ min-height: 500px;
|
|
|
|
|
|
- .file-name {
|
|
|
- font-size: 16px;
|
|
|
- font-weight: 500;
|
|
|
- color: #303133;
|
|
|
- max-width: 600px;
|
|
|
- overflow: hidden;
|
|
|
- text-overflow: ellipsis;
|
|
|
- white-space: nowrap;
|
|
|
+ .preview-section {
|
|
|
+ flex: 1;
|
|
|
+ border: 1px solid #dcdfe6;
|
|
|
+ border-radius: 8px;
|
|
|
+ overflow: hidden;
|
|
|
+ background-color: #f5f7fa;
|
|
|
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
|
|
+
|
|
|
+ .preview-loading,
|
|
|
+ .preview-error,
|
|
|
+ .preview-empty {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ height: 100%;
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- .header-actions {
|
|
|
- display: flex;
|
|
|
- gap: 8px;
|
|
|
+ .preview-content {
|
|
|
+ height: 100%;
|
|
|
+ overflow: auto;
|
|
|
+ background-color: #fff;
|
|
|
|
|
|
- .copy-avatar-btn {
|
|
|
- .el-icon {
|
|
|
- margin-right: 4px;
|
|
|
+ :deep(.vue-office-pdf) {
|
|
|
+ height: 100%;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-}
|
|
|
-
|
|
|
-.preview-container {
|
|
|
- position: absolute;
|
|
|
- top: 68px;
|
|
|
- left: 0;
|
|
|
- right: 0;
|
|
|
- bottom: 0;
|
|
|
- overflow: hidden;
|
|
|
-
|
|
|
- .wps-container {
|
|
|
- width: 100%;
|
|
|
- height: 100%;
|
|
|
- position: relative;
|
|
|
- }
|
|
|
-
|
|
|
- .document-iframe {
|
|
|
- width: 100%;
|
|
|
- height: 100%;
|
|
|
- border: none;
|
|
|
- }
|
|
|
|
|
|
- .drag-overlay {
|
|
|
- position: absolute;
|
|
|
- top: 0;
|
|
|
- left: 0;
|
|
|
- right: 0;
|
|
|
- bottom: 0;
|
|
|
- background: rgba(64, 158, 255, 0.1);
|
|
|
- border: 2px dashed #409eff;
|
|
|
+ .form-section {
|
|
|
+ width: 420px;
|
|
|
display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- z-index: 9999;
|
|
|
- pointer-events: none;
|
|
|
-
|
|
|
- .drag-hint {
|
|
|
- text-align: center;
|
|
|
-
|
|
|
- .drag-icon {
|
|
|
- font-size: 64px;
|
|
|
- color: #409eff;
|
|
|
- margin-bottom: 16px;
|
|
|
- }
|
|
|
+ flex-direction: column;
|
|
|
+ background: linear-gradient(to bottom, #ffffff 0%, #fafbfc 100%);
|
|
|
+ border-radius: 8px;
|
|
|
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
|
|
+ overflow: hidden;
|
|
|
|
|
|
- p {
|
|
|
- font-size: 18px;
|
|
|
- font-weight: 500;
|
|
|
- color: #409eff;
|
|
|
+ .form-header {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 16px;
|
|
|
+ padding: 24px;
|
|
|
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
|
+ color: #fff;
|
|
|
+
|
|
|
+ .header-icon {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ width: 48px;
|
|
|
+ height: 48px;
|
|
|
+ background: rgba(255, 255, 255, 0.2);
|
|
|
+ border-radius: 12px;
|
|
|
+ backdrop-filter: blur(10px);
|
|
|
}
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- .loading-state {
|
|
|
- position: absolute;
|
|
|
- top: 50%;
|
|
|
- left: 50%;
|
|
|
- transform: translate(-50%, -50%);
|
|
|
- text-align: center;
|
|
|
-
|
|
|
- .el-icon {
|
|
|
- font-size: 48px;
|
|
|
- color: #409eff;
|
|
|
- margin-bottom: 16px;
|
|
|
- }
|
|
|
-
|
|
|
- p {
|
|
|
- font-size: 14px;
|
|
|
- color: #909399;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- :deep(.el-result),
|
|
|
- :deep(.el-empty) {
|
|
|
- position: absolute;
|
|
|
- top: 50%;
|
|
|
- left: 50%;
|
|
|
- transform: translate(-50%, -50%);
|
|
|
- width: 100%;
|
|
|
- }
|
|
|
-}
|
|
|
|
|
|
-.audit-section {
|
|
|
- width: 400px;
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- background: #fff;
|
|
|
- flex-shrink: 0;
|
|
|
-}
|
|
|
+ .header-content {
|
|
|
+ flex: 1;
|
|
|
|
|
|
-.section-header {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- gap: 8px;
|
|
|
- padding: 16px 20px;
|
|
|
- background: #f5f7fa;
|
|
|
- border-bottom: 1px solid #e4e7ed;
|
|
|
- font-size: 15px;
|
|
|
- font-weight: 500;
|
|
|
- color: #303133;
|
|
|
-
|
|
|
- .el-icon {
|
|
|
- font-size: 18px;
|
|
|
- color: #409eff;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-.audit-form {
|
|
|
- flex: 1;
|
|
|
- padding: 20px;
|
|
|
- overflow-y: auto;
|
|
|
-
|
|
|
- &::-webkit-scrollbar {
|
|
|
- width: 6px;
|
|
|
- }
|
|
|
-
|
|
|
- &::-webkit-scrollbar-thumb {
|
|
|
- background: #dcdfe6;
|
|
|
- border-radius: 3px;
|
|
|
+ .header-title {
|
|
|
+ margin: 0;
|
|
|
+ font-size: 18px;
|
|
|
+ font-weight: 600;
|
|
|
+ line-height: 1.4;
|
|
|
+ }
|
|
|
|
|
|
- &:hover {
|
|
|
- background: #c0c4cc;
|
|
|
+ .header-subtitle {
|
|
|
+ margin: 4px 0 0;
|
|
|
+ font-size: 13px;
|
|
|
+ opacity: 0.9;
|
|
|
+ line-height: 1.4;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
-}
|
|
|
|
|
|
-.info-card {
|
|
|
- margin-bottom: 20px;
|
|
|
+ .form-body {
|
|
|
+ flex: 1;
|
|
|
+ padding: 24px;
|
|
|
+ overflow-y: auto;
|
|
|
|
|
|
- :deep(.el-card__header) {
|
|
|
- padding: 12px 16px;
|
|
|
- background: #f5f7fa;
|
|
|
- }
|
|
|
+ .el-form {
|
|
|
+ height: 100%;
|
|
|
|
|
|
- .card-header {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- gap: 8px;
|
|
|
- font-size: 14px;
|
|
|
- font-weight: 500;
|
|
|
- color: #303133;
|
|
|
-
|
|
|
- .el-icon {
|
|
|
- font-size: 16px;
|
|
|
- color: #409eff;
|
|
|
- }
|
|
|
- }
|
|
|
+ .result-item {
|
|
|
+ margin-bottom: 24px;
|
|
|
|
|
|
- :deep(.el-card__body) {
|
|
|
- padding: 16px;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-.info-item {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- padding: 8px 0;
|
|
|
- font-size: 14px;
|
|
|
-
|
|
|
- &:not(:last-child) {
|
|
|
- border-bottom: 1px solid #f0f2f5;
|
|
|
- }
|
|
|
+ :deep(.el-form-item__label) {
|
|
|
+ font-weight: 600;
|
|
|
+ font-size: 14px;
|
|
|
+ color: #303133;
|
|
|
+ margin-bottom: 12px;
|
|
|
+ }
|
|
|
|
|
|
- .info-label {
|
|
|
- color: #909399;
|
|
|
- min-width: 80px;
|
|
|
- }
|
|
|
+ .result-radio-group {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 12px;
|
|
|
+ width: 100%;
|
|
|
+
|
|
|
+ .result-radio {
|
|
|
+ width: 100%;
|
|
|
+ margin: 0;
|
|
|
+ padding: 0;
|
|
|
+ border-radius: 8px;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+
|
|
|
+ :deep(.el-radio__input) {
|
|
|
+ display: none;
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep(.el-radio__label) {
|
|
|
+ width: 100%;
|
|
|
+ padding: 16px 20px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ }
|
|
|
+
|
|
|
+ .radio-content {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 10px;
|
|
|
+ font-size: 15px;
|
|
|
+ font-weight: 500;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.result-radio-pass {
|
|
|
+ border-color: #e8f4ea;
|
|
|
+ background-color: #f0f9f1;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ border-color: #67c23a;
|
|
|
+ background-color: #e8f4ea;
|
|
|
+ transform: translateY(-2px);
|
|
|
+ box-shadow: 0 4px 12px rgba(103, 194, 58, 0.2);
|
|
|
+ }
|
|
|
+
|
|
|
+ &.is-checked {
|
|
|
+ border-color: #67c23a;
|
|
|
+ background: linear-gradient(135deg, #67c23a 0%, #85ce61 100%);
|
|
|
+ box-shadow: 0 4px 12px rgba(103, 194, 58, 0.3);
|
|
|
+
|
|
|
+ :deep(.el-radio__label) {
|
|
|
+ color: #fff;
|
|
|
+ }
|
|
|
+
|
|
|
+ .radio-content {
|
|
|
+ color: #fff;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ &.result-radio-reject {
|
|
|
+ border-color: #fde8e8;
|
|
|
+ background-color: #fef0f0;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ border-color: #f56c6c;
|
|
|
+ background-color: #fde8e8;
|
|
|
+ transform: translateY(-2px);
|
|
|
+ box-shadow: 0 4px 12px rgba(245, 108, 108, 0.2);
|
|
|
+ }
|
|
|
+
|
|
|
+ &.is-checked {
|
|
|
+ border-color: #f56c6c;
|
|
|
+ background: linear-gradient(135deg, #f56c6c 0%, #f78989 100%);
|
|
|
+ box-shadow: 0 4px 12px rgba(245, 108, 108, 0.3);
|
|
|
+
|
|
|
+ :deep(.el-radio__label) {
|
|
|
+ color: #fff;
|
|
|
+ }
|
|
|
+
|
|
|
+ .radio-content {
|
|
|
+ color: #fff;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- .info-value {
|
|
|
- color: #303133;
|
|
|
- flex: 1;
|
|
|
- word-break: break-all;
|
|
|
- }
|
|
|
-}
|
|
|
+ .reason-item {
|
|
|
+ :deep(.el-form-item__label) {
|
|
|
+ font-weight: 600;
|
|
|
+ font-size: 14px;
|
|
|
+ color: #303133;
|
|
|
+ margin-bottom: 12px;
|
|
|
+ }
|
|
|
|
|
|
-.result-radio-group {
|
|
|
- width: 100%;
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- gap: 12px;
|
|
|
-
|
|
|
- :deep(.el-radio) {
|
|
|
- margin: 0;
|
|
|
- padding: 0;
|
|
|
- height: auto;
|
|
|
-
|
|
|
- &.is-bordered {
|
|
|
- padding: 16px;
|
|
|
- border-radius: 8px;
|
|
|
- border: 2px solid #dcdfe6;
|
|
|
- transition: all 0.3s;
|
|
|
-
|
|
|
- &:hover {
|
|
|
- border-color: #409eff;
|
|
|
- }
|
|
|
+ .reason-textarea {
|
|
|
+ :deep(.el-textarea__inner) {
|
|
|
+ border-radius: 8px;
|
|
|
+ border: 2px solid #e4e7ed;
|
|
|
+ padding: 12px;
|
|
|
+ font-size: 14px;
|
|
|
+ line-height: 1.6;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+
|
|
|
+ &:focus {
|
|
|
+ border-color: #667eea;
|
|
|
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- &.is-checked {
|
|
|
- border-color: #409eff;
|
|
|
- background: #ecf5ff;
|
|
|
+ :deep(.el-input__count) {
|
|
|
+ background-color: transparent;
|
|
|
+ font-size: 12px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- .el-radio__input {
|
|
|
- display: none;
|
|
|
- }
|
|
|
-
|
|
|
- .el-radio__label {
|
|
|
- padding: 0;
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-.radio-content {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- gap: 12px;
|
|
|
- width: 100%;
|
|
|
-
|
|
|
- .radio-icon {
|
|
|
- font-size: 32px;
|
|
|
- flex-shrink: 0;
|
|
|
-
|
|
|
- &.success {
|
|
|
- color: #67c23a;
|
|
|
- }
|
|
|
+ .form-footer {
|
|
|
+ display: flex;
|
|
|
+ gap: 12px;
|
|
|
+ padding: 20px 24px;
|
|
|
+ background-color: #fff;
|
|
|
+ border-top: 1px solid #e4e7ed;
|
|
|
+ box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.04);
|
|
|
+
|
|
|
+ .footer-btn {
|
|
|
+ flex: 1;
|
|
|
+ height: 44px;
|
|
|
+ font-size: 15px;
|
|
|
+ font-weight: 500;
|
|
|
+ border-radius: 8px;
|
|
|
+ transition: all 0.3s ease;
|
|
|
|
|
|
- &.danger {
|
|
|
- color: #f56c6c;
|
|
|
- }
|
|
|
- }
|
|
|
+ .el-icon {
|
|
|
+ margin-right: 6px;
|
|
|
+ }
|
|
|
|
|
|
- .radio-text {
|
|
|
- flex: 1;
|
|
|
+ &:hover {
|
|
|
+ transform: translateY(-2px);
|
|
|
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
|
+ }
|
|
|
|
|
|
- .radio-title {
|
|
|
- font-size: 15px;
|
|
|
- font-weight: 500;
|
|
|
- color: #303133;
|
|
|
- margin-bottom: 4px;
|
|
|
- }
|
|
|
+ &.footer-btn-primary {
|
|
|
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
|
+ border: none;
|
|
|
|
|
|
- .radio-desc {
|
|
|
- font-size: 13px;
|
|
|
- color: #909399;
|
|
|
+ &:hover {
|
|
|
+ background: linear-gradient(135deg, #5568d3 0%, #6a3f8f 100%);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-:deep(.el-form-item) {
|
|
|
- margin-bottom: 20px;
|
|
|
-
|
|
|
- .el-form-item__label {
|
|
|
- font-weight: 500;
|
|
|
- color: #303133;
|
|
|
- margin-bottom: 8px;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-:deep(.el-alert) {
|
|
|
- margin-top: 12px;
|
|
|
- border-radius: 6px;
|
|
|
+// 过渡动画
|
|
|
+.el-fade-in-enter-active,
|
|
|
+.el-fade-in-leave-active {
|
|
|
+ transition: all 0.3s ease;
|
|
|
}
|
|
|
|
|
|
-.dialog-footer {
|
|
|
- display: flex;
|
|
|
- justify-content: flex-end;
|
|
|
- gap: 12px;
|
|
|
-
|
|
|
- .el-button {
|
|
|
- min-width: 100px;
|
|
|
-
|
|
|
- .el-icon {
|
|
|
- margin-right: 4px;
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-// 响应式设计
|
|
|
-@media (max-width: 1400px) {
|
|
|
- .audit-dialog {
|
|
|
- :deep(.el-dialog) {
|
|
|
- width: 95% !important;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- .audit-section {
|
|
|
- width: 350px;
|
|
|
- }
|
|
|
+.el-fade-in-enter-from {
|
|
|
+ opacity: 0;
|
|
|
+ transform: translateY(-10px);
|
|
|
}
|
|
|
|
|
|
-@media (max-width: 1200px) {
|
|
|
- .audit-container {
|
|
|
- flex-direction: column;
|
|
|
- }
|
|
|
-
|
|
|
- .preview-section {
|
|
|
- height: 50%;
|
|
|
- }
|
|
|
-
|
|
|
- .audit-section {
|
|
|
- width: 100%;
|
|
|
- height: 50%;
|
|
|
- }
|
|
|
+.el-fade-in-leave-to {
|
|
|
+ opacity: 0;
|
|
|
+ transform: translateY(-10px);
|
|
|
}
|
|
|
</style>
|