公文使用通用的附件预览

This commit is contained in:
ywyonui 2025-09-26 10:17:14 +08:00
parent 33ae8f8115
commit 4a4573e51c
5 changed files with 472 additions and 918 deletions

View File

@ -52,11 +52,53 @@
<view v-else class="no-files"> <view v-else class="no-files">
<text>暂无附件</text> <text>暂无附件</text>
</view> </view>
<!-- 下载路径选择弹窗 -->
<u-popup :show="showDownloadModal" @close="showDownloadModal = false" mode="center">
<view class="download-modal">
<view class="download-header">
<text class="download-title">文件下载</text>
</view>
<view class="download-content">
<view class="file-info-section">
<text class="file-label">文件名</text>
<text class="file-name">{{ downloadFileName }}</text>
</view>
<view class="path-info-section">
<text class="path-label">下载路径</text>
<text class="path-text">将下载到默认下载目录</text>
</view>
<view class="download-tips">
<text class="tips-text">点击确认后将开始下载文件</text>
</view>
<!-- 下载中状态 -->
<view class="download-progress" v-if="isDownloading">
<view class="progress-spinner"></view>
<text class="progress-text">正在下载文件请稍候...</text>
</view>
</view>
<view class="download-actions" v-if="!isDownloading">
<u-button
text="取消"
type="default"
size="large"
@click="cancelDownload"
style="margin-right: 10px;"
/>
<u-button
text="确认下载"
type="primary"
size="large"
@click="confirmDownload"
/>
</view>
</view>
</u-popup>
</view> </view>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue'; import { computed, ref } from 'vue';
import { import {
isVideo, isVideo,
isImage, isImage,
@ -100,6 +142,13 @@ const emit = defineEmits<{
(e: 'download', file: FileInfo): void; (e: 'download', file: FileInfo): void;
}>(); }>();
//
const showDownloadModal = ref(false);
const downloadUrl = ref('');
const downloadFileName = ref('');
const isDownloading = ref(false);
const currentDownloadFile = ref<FileInfo | null>(null);
// //
const hasAttachments = computed(() => { const hasAttachments = computed(() => {
const attachments = []; const attachments = [];
@ -315,21 +364,8 @@ const downloadFileAction = (file: FileInfo) => {
// //
const originalFileName = getOriginalFileName(file); const originalFileName = getOriginalFileName(file);
// //
downloadFileUtil(finalUrl, originalFileName) showDownloadPathModal(finalUrl, originalFileName, file);
.then(() => {
uni.showToast({
title: "下载成功",
icon: "success",
});
})
.catch((error) => {
console.error('下载失败:', error);
uni.showToast({
title: "下载失败",
icon: "error",
});
});
}; };
// //
@ -358,6 +394,288 @@ const getOriginalFileName = (file: FileInfo) => {
return fileName; return fileName;
}; };
//
const showDownloadPathModal = (url: string, fileName: string, file: FileInfo) => {
downloadUrl.value = url;
downloadFileName.value = fileName;
currentDownloadFile.value = file;
showDownloadModal.value = true;
};
//
const confirmDownload = () => {
//
isDownloading.value = true;
//
const systemInfo = uni.getSystemInfoSync();
const {platform} = systemInfo;
//
const isH5 = platform === 'web' || typeof window !== 'undefined';
console.log('平台检测:', {platform, systemInfo, isH5});
if (isH5) {
console.log('使用H5下载方式');
// H5使
downloadForH5(downloadUrl.value, downloadFileName.value);
} else {
console.log('使用原生下载方式');
// 使
downloadFileUtil(downloadUrl.value, downloadFileName.value)
.then(() => {
//
isDownloading.value = false;
showDownloadModal.value = false;
uni.showToast({
title: "下载成功",
icon: "success",
});
})
.catch((error) => {
console.error('下载失败:', error);
//
isDownloading.value = false;
showDownloadModal.value = false;
uni.showToast({
title: "下载失败",
icon: "error",
});
});
}
};
// H5
const downloadForH5 = (url: string, fileName: string) => {
try {
console.log('H5下载开始:', {url, fileName});
// 使
forceDownload(url, fileName);
} catch (error) {
console.error('H5下载失败:', error);
// 退
fallbackDownload(url, fileName);
}
};
//
const forceDownload = (url: string, fileName: string) => {
try {
console.log('尝试强制下载:', {url, fileName});
// 1: 使fetchblob
fetch(url, {
method: 'GET',
mode: 'cors',
cache: 'no-cache'
})
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.blob();
})
.then(blob => {
console.log('文件下载成功创建blob下载');
// blob URL
const blobUrl = window.URL.createObjectURL(blob);
//
const link = document.createElement('a');
link.href = blobUrl;
link.download = fileName;
link.style.display = 'none';
// DOM
document.body.appendChild(link);
link.click();
// DOMblob URL
document.body.removeChild(link);
window.URL.revokeObjectURL(blobUrl);
//
isDownloading.value = false;
showDownloadModal.value = false;
uni.showToast({
title: "下载成功",
icon: "success",
});
})
.catch(error => {
console.error('Fetch下载失败尝试其他方法:', error);
// fetch
tryAlternativeDownload(url, fileName);
});
} catch (error) {
console.error('强制下载失败,尝试其他方法:', error);
tryAlternativeDownload(url, fileName);
}
};
//
const tryAlternativeDownload = (url: string, fileName: string) => {
console.log('尝试替代下载方法');
// 1: 使XMLHttpRequest
try {
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'blob';
xhr.onload = function () {
if (xhr.status === 200) {
const blob = xhr.response;
const blobUrl = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = blobUrl;
link.download = fileName;
link.style.display = 'none';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(blobUrl);
//
isDownloading.value = false;
showDownloadModal.value = false;
uni.showToast({
title: "下载成功",
icon: "success",
});
console.log('XMLHttpRequest下载成功');
} else {
throw new Error(`HTTP error! status: ${xhr.status}`);
}
};
xhr.onerror = function () {
console.error('XMLHttpRequest下载失败');
console.log('调用手动下载提示');
showManualDownloadModal(url, fileName);
};
xhr.send();
} catch (error) {
console.error('XMLHttpRequest失败:', error);
console.log('调用手动下载提示catch');
showManualDownloadModal(url, fileName);
}
};
//
const showManualDownloadModal = (url: string, fileName: string) => {
console.log('尝试原生下载');
// 使
try {
const link = document.createElement('a');
link.href = url;
link.download = fileName;
link.target = '_blank';
link.style.display = 'none';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
console.log('原生下载已触发');
//
setTimeout(() => {
isDownloading.value = false;
showDownloadModal.value = false;
}, 1000);
uni.showToast({
title: "开始下载",
icon: "success",
});
} catch (error) {
console.error('原生下载失败:', error);
//
isDownloading.value = false;
showDownloadModal.value = false;
uni.showToast({
title: "下载失败",
icon: "error",
});
}
};
//
const fallbackDownload = (url: string, fileName: string) => {
try {
console.log('使用备用下载方法');
// 使
directDownload(url, fileName);
} catch (error) {
console.error('备用下载也失败:', error);
//
uni.showModal({
title: '下载提示',
content: `由于浏览器限制,无法自动下载文件。请长按链接手动保存:\n${url}`,
showCancel: false,
confirmText: '知道了'
});
}
};
//
const directDownload = (url: string, fileName: string) => {
try {
// URL
const downloadUrl = url.includes('?')
? `${url}&download=1&attachment=1`
: `${url}?download=1&attachment=1`;
const link = document.createElement('a');
link.href = downloadUrl;
link.download = fileName;
link.target = '_self'; // 使_self_blank
link.style.display = 'none';
// DOM
document.body.appendChild(link);
link.click();
// DOM
document.body.removeChild(link);
uni.showToast({
title: "开始下载",
icon: "success",
});
} catch (error) {
console.error('直接下载失败:', error);
//
uni.showModal({
title: '下载提示',
content: `由于浏览器限制,无法自动下载文件。请长按链接手动保存:\n${url}`,
showCancel: false,
confirmText: '知道了'
});
}
};
//
const cancelDownload = () => {
showDownloadModal.value = false;
};
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -455,4 +773,125 @@ const getOriginalFileName = (file: FileInfo) => {
color: #999; color: #999;
font-size: 14px; font-size: 14px;
} }
.download-modal {
width: 85vw;
max-width: 450px;
padding: 20px;
background: #fff;
border-radius: 12px;
.download-header {
text-align: center;
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 1px solid #eee;
.download-title {
font-size: 18px;
font-weight: bold;
color: #333;
}
}
.download-content {
margin-bottom: 25px;
.file-info-section {
display: flex;
align-items: flex-start;
margin-bottom: 15px;
.file-label {
font-weight: 500;
color: #666;
margin-right: 10px;
flex-shrink: 0;
width: 60px;
}
.file-name {
color: #333;
font-size: 14px;
word-break: break-all;
flex: 1;
background: #f8f9fa;
padding: 8px 12px;
border-radius: 6px;
border: 1px solid #e9ecef;
}
}
.path-info-section {
display: flex;
align-items: center;
margin-bottom: 15px;
.path-label {
font-weight: 500;
color: #666;
margin-right: 10px;
flex-shrink: 0;
width: 60px;
}
.path-text {
color: #007aff;
font-size: 14px;
flex: 1;
}
}
.download-tips {
text-align: center;
.tips-text {
color: #999;
font-size: 12px;
}
}
}
.download-actions {
display: flex;
justify-content: center;
gap: 10px;
.u-button {
flex: 1;
max-width: 120px;
}
}
.download-progress {
display: flex;
flex-direction: column;
align-items: center;
padding: 20px 0;
.progress-spinner {
width: 24px;
height: 24px;
border: 2px solid #f3f3f3;
border-top: 2px solid #007aff;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-bottom: 10px;
}
.progress-text {
color: #666;
font-size: 14px;
}
}
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style> </style>

View File

@ -1,6 +1,6 @@
<template> <template>
<BasicLayout> <BasicLayout>
<view class="px-15 pb-15"> <view class="p-15">
<!-- 公文基本信息 --> <!-- 公文基本信息 -->
<view class="gw-info-section"> <view class="gw-info-section">
<view class="section-title">公文信息</view> <view class="section-title">公文信息</view>
@ -26,131 +26,18 @@
</view> </view>
</view> </view>
<!-- 文件信息 --> <!-- 附件预览 -->
<view class="file-section"> <BasicFilePreview
<view class="section-title">附件</view> v-if="gwInfo.fileUrl"
<view class="file-list" v-if="hasAttachments"> :file-url="gwInfo.fileUrl"
<!-- 显示所有解析后的附件 --> :file-name="gwInfo.fileName"
<view :file-format="gwInfo.fileFormat"
v-for="(file, index) in parsedAttachments" class="mb-0"
:key="(file as any).id || index" />
class="file-item"
>
<view class="file-icon">
<image
:src="getFileIcon(getFileSuffix(file))"
class="icon-image"
mode="aspectFit"
/>
</view>
<view class="file-info">
<text class="file-name">{{ getFileName(file) }}</text>
</view>
<view class="file-actions">
<u-button
v-if="canPreview(getFileSuffix(file)) && !isVideo(getFileSuffix(file))"
text="预览"
size="mini"
type="primary"
@click="previewFile(file)"
/>
<u-button
v-if="isVideo(getFileSuffix(file))"
text="播放"
size="mini"
type="success"
@click="previewFile(file)"
/>
<u-button
v-if="isImage(getFileSuffix(file))"
text="查看"
size="mini"
type="warning"
@click="previewFile(file)"
/>
<u-button
text="下载"
size="mini"
type="info"
@click="downloadFileAction(file)"
/>
</view>
</view>
</view>
<view v-else class="no-files">
<text>暂无附件</text>
</view>
</view>
</view> </view>
<!-- 审批流程 --> <!-- 审批流程 -->
<LcglSp :yw-id="gwId" yw-type="GW"/> <LcglSp :yw-id="gwId" yw-type="GW"/>
<!-- 操作记录详情弹窗 -->
<u-popup :show="showLogDetailModal" @close="showLogDetailModal = false" mode="center">
<view class="detail-modal">
<view class="detail-header">
<text class="detail-title">操作详情</text>
<u-button text="关闭" @click="showLogDetailModal = false"/>
</view>
<view class="detail-content">
<view class="detail-item" v-if="currentLog.beforeChange">
<text class="detail-label">变更前</text>
<text class="detail-value">{{ currentLog.beforeChange }}</text>
</view>
<view class="detail-item" v-if="currentLog.afterChange">
<text class="detail-label">变更后</text>
<text class="detail-value">{{ currentLog.afterChange }}</text>
</view>
<view class="detail-item" v-if="currentLog.remark">
<text class="detail-label">备注</text>
<text class="detail-value">{{ currentLog.remark }}</text>
</view>
</view>
</view>
</u-popup>
<!-- 下载路径选择弹窗 -->
<u-popup :show="showDownloadModal" @close="showDownloadModal = false" mode="center">
<view class="download-modal">
<view class="download-header">
<text class="download-title">文件下载</text>
</view>
<view class="download-content">
<view class="file-info-section">
<text class="file-label">文件名</text>
<text class="file-name">{{ downloadFileName }}</text>
</view>
<view class="path-info-section">
<text class="path-label">下载路径</text>
<text class="path-text">将下载到默认下载目录</text>
</view>
<view class="download-tips">
<text class="tips-text">点击确认后将开始下载文件</text>
</view>
<!-- 下载中状态 -->
<view class="download-progress" v-if="isDownloading">
<view class="progress-spinner"></view>
<text class="progress-text">正在下载文件请稍候...</text>
</view>
</view>
<view class="download-actions" v-if="!isDownloading">
<u-button
text="取消"
type="default"
size="large"
@click="cancelDownload"
style="margin-right: 10px;"
/>
<u-button
text="确认下载"
type="primary"
size="large"
@click="confirmDownload"
/>
</view>
</view>
</u-popup>
<template #bottom> <template #bottom>
<YwConfirm v-if="showButton" <YwConfirm v-if="showButton"
:spApi="gwSpApi" :spApi="gwSpApi"
@ -172,22 +59,11 @@
import { onLoad } from "@dcloudio/uni-app"; import { onLoad } from "@dcloudio/uni-app";
import {ref, onMounted, computed} from "vue"; import {ref, onMounted, computed} from "vue";
import BasicLayout from "@/components/BasicLayout/Layout.vue"; import BasicLayout from "@/components/BasicLayout/Layout.vue";
import {navigateTo} from "@/utils/uniapp";
import {getGwFlowByIdApi, gwSpApi, gwTransferApi, gwStopApi} from "@/api/routine/gw"; import {getGwFlowByIdApi, gwSpApi, gwTransferApi, gwStopApi} from "@/api/routine/gw";
import dayjs from "dayjs"; import dayjs from "dayjs";
import {useUserStore} from "@/store/modules/user";
import {imagUrl} from "@/utils";
import {
isVideo,
isImage,
canPreview,
previewFile as previewFileUtil,
previewVideo as previewVideoUtil,
previewImage as previewImageUtil,
downloadFile
} from "@/utils/filePreview";
import LcglSp from "@/components/LcglSp/index.vue"; import LcglSp from "@/components/LcglSp/index.vue";
import YwConfirm from "@/pages/components/YwConfirm/index.vue"; import YwConfirm from "@/pages/components/YwConfirm/index.vue";
import BasicFilePreview from "@/components/BasicFile/preview.vue";
import {useDataStore} from "@/store/modules/data"; import {useDataStore} from "@/store/modules/data";
import { GwPageUtils } from "@/utils/gwPageUtils"; import { GwPageUtils } from "@/utils/gwPageUtils";
const { setGwData, getXxts } = useDataStore(); const { setGwData, getXxts } = useDataStore();
@ -264,7 +140,6 @@ interface OperationLog {
remark?: string; remark?: string;
} }
// //
const gwId = ref(""); const gwId = ref("");
@ -275,9 +150,6 @@ const downloadUrl = ref('');
const downloadFileName = ref(''); const downloadFileName = ref('');
const isDownloading = ref(false); const isDownloading = ref(false);
// store
const {getUser, getJs} = useUserStore();
// //
const gwInfo = ref<GwInfo>({} as GwInfo); const gwInfo = ref<GwInfo>({} as GwInfo);
const approvers = ref<Approver[]>([]); const approvers = ref<Approver[]>([]);
@ -291,101 +163,6 @@ const ccExpanded = ref(false);
// //
const logExpanded = ref(false); const logExpanded = ref(false);
//
const parsedAttachments = computed(() => {
const attachments = [];
// files
if (gwInfo.value.files && gwInfo.value.files.length > 0) {
attachments.push(...gwInfo.value.files);
}
// fileUrl
else if (gwInfo.value.fileUrl) {
const fileUrls = gwInfo.value.fileUrl.split(',').map(url => url.trim()).filter(url => url);
// fileName
let fileNames: string[] = [];
if (gwInfo.value.fileName) {
fileNames = gwInfo.value.fileName.split(',').map(name => name.trim()).filter(name => name);
}
// URL
fileUrls.forEach((url, index) => {
// 使URL
let displayName = fileNames[index] || url.split('/').pop() || `文件${index + 1}`;
const fileFormat = url.split('.').pop() || 'unknown';
// resourName
let resourName = displayName;
if (displayName.includes('.' + fileFormat)) {
resourName = displayName.replace('.' + fileFormat, '');
}
attachments.push({
id: `file_${index}`,
name: displayName, //
url: url,
resourUrl: url,
resourName: resourName, //
resSuf: fileFormat,
size: 0 // URL
});
});
}
return attachments;
});
//
const hasAttachments = computed(() => {
return parsedAttachments.value.length > 0;
});
//
const displayedCcUsers = computed(() => {
if (ccExpanded.value || ccUsers.value.length <= 2) {
return ccUsers.value;
}
return ccUsers.value.slice(0, 2);
});
//
const displayedOperationLogs = computed(() => {
if (logExpanded.value || operationLogs.value.length <= 2) {
return operationLogs.value;
}
return operationLogs.value.slice(0, 2);
});
// /
const canCurrentUserOperate = computed(() => {
const userStore = useUserStore();
const getUser = userStore.getUser;
const getJs = userStore.getJs;
const currentUserId = getJs?.id;
if (!currentUserId || !approvers.value || approvers.value.length === 0) {
return false;
}
//
const currentUserApprover = approvers.value.find(approver => {
return approver.userId === currentUserId || approver.id === currentUserId;
});
if (!currentUserApprover) {
return false;
}
// approvedrejected
const approveStatus = currentUserApprover.approveStatus;
if (approveStatus === 'approved' || approveStatus === 'rejected') {
return false;
}
return true;
});
// //
const getGwInfo = async () => { const getGwInfo = async () => {
try { try {
@ -418,361 +195,11 @@ const getGwInfo = async () => {
} }
}; };
//
const showLogDetail = (log: OperationLog) => {
currentLog.value = log;
showLogDetailModal.value = true;
};
//
const toggleCcExpanded = () => {
ccExpanded.value = !ccExpanded.value;
};
//
const toggleLogExpanded = () => {
logExpanded.value = !logExpanded.value;
};
//
const handleReject = () => {
uni.showModal({
title: "驳回公文",
content: "确定要驳回这个公文吗?",
success: async (res) => {
if (res.confirm) {
try {
// API
console.log("驳回公文:", gwId.value);
uni.showToast({
title: "驳回成功",
icon: "success",
});
} catch (error) {
console.error("驳回失败:", error);
uni.showToast({
title: "驳回失败",
icon: "error",
});
}
}
},
});
};
//
const handleTransfer = () => {
//
const params = {
id: gwId.value,
title: encodeURIComponent(gwInfo.value.title || ''),
xxtsInfo: encodeURIComponent(JSON.stringify({
id: gwId.value, // xxtsInfoID
// xxtsInfo
})),
gwInfo: encodeURIComponent(JSON.stringify(gwInfo.value)),
approvers: encodeURIComponent(JSON.stringify(approvers.value)),
ccUsers: encodeURIComponent(JSON.stringify(ccUsers.value)) //
};
//
uni.setStorageSync('transferData', {
xxtsInfo: {id: gwId.value},
gwInfo: gwInfo.value,
approvers: approvers.value,
ccUsers: ccUsers.value //
});
//
navigateTo(`/pages/view/routine/gwlz/gwTransfer?id=${params.id}&title=${params.title}&xxtsInfo=${params.xxtsInfo}&gwInfo=${params.gwInfo}&approvers=${params.approvers}&ccUsers=${params.ccUsers}`);
};
// gwInfo
const previewSingleFile = (event?: Event) => {
//
if (event) {
event.stopPropagation();
event.preventDefault();
}
if (!gwInfo.value.fileUrl) {
return;
}
const fileUrl = imagUrl(gwInfo.value.fileUrl);
const fileName = gwInfo.value.fileName || '未知文件';
const fileFormat = gwInfo.value.fileFormat || '';
//
if (isVideo(fileFormat)) {
handlePreviewVideoSingle(fileUrl, fileName);
} else if (isImage(fileFormat)) {
handlePreviewImageSingle(fileUrl);
} else if (canPreview(fileFormat)) {
handlePreviewDocumentSingle(fileUrl, fileName, fileFormat);
} else {
//
downloadSingleFile();
}
};
//
const downloadSingleFile = (event?: Event) => {
//
if (event) {
event.stopPropagation();
event.preventDefault();
}
if (!gwInfo.value.fileUrl) {
return;
}
const finalUrl = imagUrl(gwInfo.value.fileUrl);
const fileName = gwInfo.value.fileName || '未知文件';
const fileFormat = gwInfo.value.fileFormat || '';
const fullFileName = fileFormat ? `${fileName}.${fileFormat}` : fileName;
//
showDownloadPathModal(finalUrl, fullFileName);
};
//
const previewFile = (file: FileInfo, event?: Event) => {
//
if (event) {
event.stopPropagation();
event.preventDefault();
}
// URL
const fileUrl = file.resourUrl ? imagUrl(file.resourUrl) : file.url;
const fileName = file.resourName ? `${file.resourName}.${file.resSuf}` : file.name;
const fileSuf = file.resSuf || file.name.split('.').pop() || '';
//
if (isVideo(fileSuf)) {
handlePreviewVideo(file);
} else if (isImage(fileSuf)) {
handlePreviewImage(file);
} else if (canPreview(fileSuf)) {
handlePreviewDocument(file);
} else {
//
downloadFileAction(file);
}
};
//
const handlePreviewDocument = (file: FileInfo) => {
const fileUrl = file.resourUrl ? imagUrl(file.resourUrl) : file.url;
const fileName = file.resourName ? `${file.resourName}.${file.resSuf}` : file.name;
const fileSuf = file.resSuf || file.name.split('.').pop() || '';
previewFileUtil(fileUrl, fileName, fileSuf)
.catch((error: any) => {
//
downloadFileAction(file);
});
};
//
const handlePreviewDocumentSingle = (fileUrl: string, fileName: string, fileFormat: string) => {
const fullFileName = fileFormat ? `${fileName}.${fileFormat}` : fileName;
previewFileUtil(fileUrl, fullFileName, fileFormat)
.catch((error: any) => {
//
downloadSingleFile();
});
};
//
const handlePreviewVideo = (file: FileInfo) => {
const videoUrl = file.resourUrl ? imagUrl(file.resourUrl) : file.url;
const videoName = file.resourName || file.name;
previewVideoUtil(videoUrl, videoName)
.catch((error: any) => {
//
});
};
//
const handlePreviewVideoSingle = (videoUrl: string, videoName: string) => {
previewVideoUtil(videoUrl, videoName)
.catch((error: any) => {
//
});
};
//
const handlePreviewImage = (file: FileInfo) => {
const imageUrl = file.resourUrl ? imagUrl(file.resourUrl) : file.url;
previewImageUtil(imageUrl)
.catch((error: any) => {
//
});
};
//
const handlePreviewImageSingle = (imageUrl: string) => {
previewImageUtil(imageUrl)
.catch((error: any) => {
//
});
};
// -
const downloadFileAction = (file: FileInfo, event?: Event) => {
//
if (event) {
event.stopPropagation();
event.preventDefault();
}
const finalUrl = file.resourUrl ? imagUrl(file.resourUrl) : imagUrl(file.url);
//
const originalFileName = getOriginalFileName(file);
console.log('下载文件:', {finalUrl, originalFileName, file});
//
showDownloadPathModal(finalUrl, originalFileName);
};
// //
const formatTime = (time: any) => { const formatTime = (time: any) => {
return dayjs(time).format("YYYY-MM-DD HH:mm:ss"); return dayjs(time).format("YYYY-MM-DD HH:mm:ss");
}; };
//
const formatFileSize = (size: any) => {
if (!size) return "0B";
if (size < 1024) return size + "B";
if (size < 1024 * 1024) return (size / 1024).toFixed(2) + "KB";
return (size / (1024 * 1024)).toFixed(2) + "MB";
};
//
const getFileName = (file: FileInfo) => {
if (file.resourName) {
return file.resourName;
}
if (file.name) {
// name
const lastDotIndex = file.name.lastIndexOf('.');
if (lastDotIndex > 0) {
return file.name.substring(0, lastDotIndex);
}
return file.name;
}
return '未知文件';
};
//
const getFileSuffix = (file: FileInfo) => {
if (file.resSuf) {
return file.resSuf;
}
if (file.name) {
const lastDotIndex = file.name.lastIndexOf('.');
if (lastDotIndex > 0) {
return file.name.substring(lastDotIndex + 1);
}
}
return 'unknown';
};
//
const getFileIcon = (fileType: string) => {
const type = fileType.toLowerCase();
switch (type) {
case 'pdf':
return '/static/base/view/pdf.png';
case 'doc':
case 'docx':
return '/static/base/view/word.png';
case 'xls':
case 'xlsx':
return '/static/base/view/excel.png';
case 'ppt':
case 'pptx':
return '/static/base/view/ppt.png';
case 'zip':
case 'rar':
case '7z':
return '/static/base/view/zip.png';
default:
return '/static/base/view/more.png';
}
};
//
const getStatusClass = (status: any) => {
const statusMap: Record<string, string> = {
draft: "status-draft",
pending: "status-pending",
approved: "status-approved",
rejected: "status-rejected",
};
return statusMap[status] || "status-default";
};
//
const getStatusText = (status: any) => {
const statusMap: Record<string, string> = {
draft: "草稿",
pending: "待审批",
approved: "已通过",
rejected: "已驳回",
};
return statusMap[status] || "未知";
};
//
const getApproverStatusClass = (status: any) => {
const statusMap: Record<string, string> = {
pending: "status-pending",
approved: "status-approved",
rejected: "status-rejected",
skipped: "status-skipped",
};
return statusMap[status] || "status-default";
};
//
const getApproverStatusText = (status: any) => {
const statusMap: Record<string, string> = {
pending: "待审批",
approved: "已同意",
rejected: "已驳回",
skipped: "已跳过",
};
return statusMap[status] || "未知";
};
//
const getCCStatusClass = (status: any) => {
const statusMap: Record<string, string> = {
unread: "status-unread",
read: "status-read",
};
return statusMap[status] || "status-default";
};
//
const getCCStatusText = (status: any) => {
const statusMap: Record<string, string> = {
unread: "未读",
read: "已读",
};
return statusMap[status] || "未知";
};
// //
const getUrgencyClass = (level: string) => { const getUrgencyClass = (level: string) => {
const levelMap: Record<string, string> = { const levelMap: Record<string, string> = {
@ -799,314 +226,6 @@ const getUrgencyText = (level: string) => {
return levelMap[level] || "未知"; return levelMap[level] || "未知";
}; };
//
const getOriginalFileName = (file: FileInfo) => {
// 使resourName使name
let fileName = '';
if (file.resourName) {
// resourName使
if (file.resSuf && !file.resourName.includes('.' + file.resSuf)) {
fileName = `${file.resourName}.${file.resSuf}`;
} else {
fileName = file.resourName;
}
} else if (file.name) {
// name使
if (file.resSuf && !file.name.includes('.' + file.resSuf)) {
fileName = `${file.name}.${file.resSuf}`;
} else {
fileName = file.name;
}
} else {
// URL
const urlFileName = file.url.split('/').pop() || '未知文件';
fileName = urlFileName;
}
return fileName;
};
//
const showDownloadPathModal = (url: string, fileName: string) => {
downloadUrl.value = url;
downloadFileName.value = fileName;
showDownloadModal.value = true;
};
//
const confirmDownload = () => {
//
isDownloading.value = true;
//
const systemInfo = uni.getSystemInfoSync();
const {platform} = systemInfo;
//
const isH5 = platform === 'web' || typeof window !== 'undefined';
console.log('平台检测:', {platform, systemInfo, isH5});
if (isH5) {
console.log('使用H5下载方式');
// H5使
downloadForH5(downloadUrl.value, downloadFileName.value);
} else {
console.log('使用原生下载方式');
// 使
downloadFile(downloadUrl.value, downloadFileName.value)
.then(() => {
//
isDownloading.value = false;
showDownloadModal.value = false;
uni.showToast({
title: "下载成功",
icon: "success",
});
})
.catch((error) => {
console.error('下载失败:', error);
//
isDownloading.value = false;
showDownloadModal.value = false;
uni.showToast({
title: "下载失败",
icon: "error",
});
});
}
};
// H5
const downloadForH5 = (url: string, fileName: string) => {
try {
console.log('H5下载开始:', {url, fileName});
// 使
forceDownload(url, fileName);
} catch (error) {
console.error('H5下载失败:', error);
// 退
fallbackDownload(url, fileName);
}
};
//
const forceDownload = (url: string, fileName: string) => {
try {
console.log('尝试强制下载:', {url, fileName});
// 1: 使fetchblob
fetch(url, {
method: 'GET',
mode: 'cors',
cache: 'no-cache'
})
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.blob();
})
.then(blob => {
console.log('文件下载成功创建blob下载');
// blob URL
const blobUrl = window.URL.createObjectURL(blob);
//
const link = document.createElement('a');
link.href = blobUrl;
link.download = fileName;
link.style.display = 'none';
// DOM
document.body.appendChild(link);
link.click();
// DOMblob URL
document.body.removeChild(link);
window.URL.revokeObjectURL(blobUrl);
//
isDownloading.value = false;
showDownloadModal.value = false;
uni.showToast({
title: "下载成功",
icon: "success",
});
})
.catch(error => {
console.error('Fetch下载失败尝试其他方法:', error);
// fetch
tryAlternativeDownload(url, fileName);
});
} catch (error) {
console.error('强制下载失败,尝试其他方法:', error);
tryAlternativeDownload(url, fileName);
}
};
//
const tryAlternativeDownload = (url: string, fileName: string) => {
console.log('尝试替代下载方法');
// 1: 使XMLHttpRequest
try {
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'blob';
xhr.onload = function () {
if (xhr.status === 200) {
const blob = xhr.response;
const blobUrl = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = blobUrl;
link.download = fileName;
link.style.display = 'none';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(blobUrl);
//
isDownloading.value = false;
showDownloadModal.value = false;
uni.showToast({
title: "下载成功",
icon: "success",
});
console.log('XMLHttpRequest下载成功');
} else {
throw new Error(`HTTP error! status: ${xhr.status}`);
}
};
xhr.onerror = function () {
console.error('XMLHttpRequest下载失败');
console.log('调用手动下载提示');
showManualDownloadModal(url, fileName);
};
xhr.send();
} catch (error) {
console.error('XMLHttpRequest失败:', error);
console.log('调用手动下载提示catch');
showManualDownloadModal(url, fileName);
}
};
//
const showManualDownloadModal = (url: string, fileName: string) => {
console.log('尝试原生下载');
// 使
try {
const link = document.createElement('a');
link.href = url;
link.download = fileName;
link.target = '_blank';
link.style.display = 'none';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
console.log('原生下载已触发');
//
setTimeout(() => {
isDownloading.value = false;
showDownloadModal.value = false;
}, 1000);
uni.showToast({
title: "开始下载",
icon: "success",
});
} catch (error) {
console.error('原生下载失败:', error);
//
isDownloading.value = false;
showDownloadModal.value = false;
uni.showToast({
title: "下载失败",
icon: "error",
});
}
};
//
const directDownload = (url: string, fileName: string) => {
try {
// URL
const downloadUrl = url.includes('?')
? `${url}&download=1&attachment=1`
: `${url}?download=1&attachment=1`;
const link = document.createElement('a');
link.href = downloadUrl;
link.download = fileName;
link.target = '_self'; // 使_self_blank
link.style.display = 'none';
// DOM
document.body.appendChild(link);
link.click();
// DOM
document.body.removeChild(link);
uni.showToast({
title: "开始下载",
icon: "success",
});
} catch (error) {
console.error('直接下载失败:', error);
//
uni.showModal({
title: '下载提示',
content: `由于浏览器限制,无法自动下载文件。请长按链接手动保存:\n${url}`,
showCancel: false,
confirmText: '知道了'
});
}
};
//
const fallbackDownload = (url: string, fileName: string) => {
try {
console.log('使用备用下载方法');
// 使
directDownload(url, fileName);
} catch (error) {
console.error('备用下载也失败:', error);
//
uni.showModal({
title: '下载提示',
content: `由于浏览器限制,无法自动下载文件。请长按链接手动保存:\n${url}`,
showCancel: false,
confirmText: '知道了'
});
}
};
//
const cancelDownload = () => {
showDownloadModal.value = false;
};
// //
const getCreatorName = (tjrId: string) => { const getCreatorName = (tjrId: string) => {
if (!tjrId) return "未知"; if (!tjrId) return "未知";
@ -1158,11 +277,7 @@ onLoad(async (data?: any) => {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.gw-info-section, .gw-info-section {
.file-section,
.approver-section,
.cc-section,
.log-section {
margin-bottom: 20px; margin-bottom: 20px;
padding: 15px; padding: 15px;
background: #fff; background: #fff;
@ -1170,10 +285,6 @@ onLoad(async (data?: any) => {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
} }
.log-section {
margin-bottom: 40px; //
}
.section-title { .section-title {
font-size: 16px; font-size: 16px;
font-weight: bold; font-weight: bold;

View File

@ -8,6 +8,7 @@ const { getGwData, setXxts, setGwData, getXxts } = useDataStore();
export const GwPageUtils = { export const GwPageUtils = {
// 初始化校验 // 初始化校验
async init(data?: any) { async init(data?: any) {
setXxts({});
let ret = { let ret = {
success: true, success: true,
dbFlag: false, dbFlag: false,

View File

@ -32,6 +32,7 @@ const qrStatus: any = {
}; };
const init = async (data?: any) => { const init = async (data?: any) => {
setXxts({});
let ret = { let ret = {
success: true, success: true,
dbFlag: false, dbFlag: false,

View File

@ -8,6 +8,7 @@ const { getData, setXxts, setData, getXxts } = useDataStore();
export const XkTfPageUtils = { export const XkTfPageUtils = {
// 初始化校验 // 初始化校验
async init(data?: any) { async init(data?: any) {
setXxts({});
let ret = { let ret = {
success: true, success: true,
dbFlag: false, dbFlag: false,
@ -31,6 +32,8 @@ export const XkTfPageUtils = {
const xxtsRes = await xxtsFindByIdApi({ id: data.id }); const xxtsRes = await xxtsFindByIdApi({ id: data.id });
if (xxtsRes && xxtsRes.result) { if (xxtsRes && xxtsRes.result) {
const xxts = xxtsRes.result; const xxts = xxtsRes.result;
setXxts(xxts);
ret.xkTfId = xxts.xxzbId;
// 检查待办状态 // 检查待办状态
if (xxts.dbZt === "B") { if (xxts.dbZt === "B") {
setData({ id: xxts.xxzbId }); setData({ id: xxts.xxzbId });
@ -38,8 +41,7 @@ export const XkTfPageUtils = {
uni.reLaunch({ url }); uni.reLaunch({ url });
ret.success = false; ret.success = false;
} else { } else {
setXxts(xxts); ret.success = true;
ret.xkTfId = xxts.xxzbId;
} }
} else { } else {
uni.showToast({ uni.showToast({