feat(frontend): unify attachment management spec and implement fetch+blob download\n\n- Implement forced download via fetch blob, delayed delete etc in repairOrder module\n- Add Attachment Management Unified Standard doctrine to cursorrules-frontend.md so future pages keep consistency\n- Update change log for download preview fix
This commit is contained in:
1 parent
1540bd1a1b
commit
4ef2208a0c
11 files changed
+1318
-108
No files matched your search
@@ -11,7 +11,7 @@ import {
|
||||
RepairStatisticsVO,
|
||||
ServiceEvaluationForm,
|
||||
ServiceEvaluationVO
|
||||
} from '@/api/hq/hq/pm/types';
|
||||
} from '@/api/hq/pm/types';
|
||||
|
||||
/**
|
||||
* 查询报修工单列表
|
||||
@@ -161,7 +161,7 @@ export const uploadAttachment = (orderId: string | number, uploadStage: string,
|
||||
if (description) {
|
||||
formData.append('description', description);
|
||||
}
|
||||
|
||||
|
||||
return request({
|
||||
url: '/hq/pm/attachment/upload',
|
||||
method: 'post',
|
||||
@@ -183,17 +183,17 @@ export const uploadAttachments = (orderId: string | number, uploadStage: string,
|
||||
const formData = new FormData();
|
||||
formData.append('orderId', orderId.toString());
|
||||
formData.append('uploadStage', uploadStage);
|
||||
|
||||
|
||||
files.forEach((file, index) => {
|
||||
formData.append('files', file);
|
||||
});
|
||||
|
||||
|
||||
if (descriptions) {
|
||||
descriptions.forEach((desc, index) => {
|
||||
formData.append('descriptions', desc);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
return request({
|
||||
url: '/hq/pm/attachment/upload/batch',
|
||||
method: 'post',
|
||||
@@ -293,3 +293,31 @@ export const getServiceEvaluation = (orderId: string | number): AxiosPromise<Ser
|
||||
method: 'get'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 更新附件的工单ID(用于新增工单时关联已上传的附件)
|
||||
* @param attachmentId 附件ID
|
||||
* @param orderId 工单ID
|
||||
*/
|
||||
export const updateAttachmentOrderId = (attachmentId: string | number, orderId: string | number): AxiosPromise<any> => {
|
||||
return request({
|
||||
url: `/hq/pm/attachment/${attachmentId}/order/${orderId}`,
|
||||
method: 'put'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 批量更新附件的工单ID(用于新增工单时关联已上传的附件)
|
||||
* @param attachmentIds 附件ID列表
|
||||
* @param orderId 工单ID
|
||||
*/
|
||||
export const batchUpdateAttachmentOrderId = (attachmentIds: Array<string | number>, orderId: string | number): AxiosPromise<any> => {
|
||||
return request({
|
||||
url: '/hq/pm/attachment/batch-update-order',
|
||||
method: 'put',
|
||||
data: {
|
||||
attachmentIds: attachmentIds,
|
||||
orderId: orderId
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -227,6 +227,7 @@
|
||||
border
|
||||
:data="repairOrderList"
|
||||
@selection-change="handleSelectionChange"
|
||||
@row-dblclick="handleDetail"
|
||||
style="width: 100%"
|
||||
:scroll-x="true"
|
||||
class="scroll-table"
|
||||
@@ -429,7 +430,7 @@
|
||||
</el-card>
|
||||
|
||||
<!-- 添加或修改报修工单对话框 -->
|
||||
<el-dialog :title="dialog.title" v-model="dialog.visible" width="800px" append-to-body>
|
||||
<el-dialog :title="dialog.title" v-model="dialog.visible" width="900px" append-to-body>
|
||||
<el-form ref="repairOrderFormRef" :model="form" :rules="rules" label-width="120px">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
@@ -529,7 +530,7 @@
|
||||
:action="uploadAction"
|
||||
:headers="uploadHeaders"
|
||||
:data="uploadData"
|
||||
:auto-upload="false"
|
||||
:auto-upload="true"
|
||||
:before-upload="handleBeforeUpload"
|
||||
:on-success="handleUploadSuccess"
|
||||
:on-error="handleUploadError"
|
||||
@@ -545,7 +546,8 @@
|
||||
<template #tip>
|
||||
<div class="el-upload__tip">
|
||||
支持图片、视频、PDF、Word文档等格式,单个文件大小不超过10MB,最多上传15个文件。
|
||||
<span class="el-upload__tip" v-if="!form.id" style="color: #ff0000">(新增时文件将在保存工单后上传)</span>
|
||||
<span class="el-upload__tip" v-if="!form.id" style="color: #ff0000">(新增时文件会立即上传,保存工单后自动关联)</span>
|
||||
<span class="el-upload__tip" v-else style="color: #ff0000">(编辑时文件会立即上传并关联到当前工单)</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -569,9 +571,26 @@
|
||||
/>
|
||||
<div v-else class="upload-file-placeholder">
|
||||
<el-icon><Document /></el-icon>
|
||||
<span class="file-ext">{{ file.name }}</span>
|
||||
<span class="file-ext">{{ getFileExt(file.name) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 文件名称 -->
|
||||
<div class="upload-file-name" :title="file.name">
|
||||
{{ file.name }}
|
||||
</div>
|
||||
|
||||
<!-- 操作按钮区域 (与预览界面样式一致) -->
|
||||
<div class="upload-file-card-actions" v-if="file.status && file.status !== 'ready'">
|
||||
<el-button-group size="small">
|
||||
<el-button type="success" @click.stop="handleDownloadAttachment(file)" title="下载" :disabled="file.status !== 'success'">
|
||||
<el-icon><Download /></el-icon>
|
||||
</el-button>
|
||||
<el-button type="danger" @click.stop="handleRemoveAttachmentConfirm(file)" title="删除">
|
||||
<el-icon><Delete /></el-icon>
|
||||
</el-button>
|
||||
</el-button-group>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
@@ -876,6 +895,8 @@
|
||||
<p><strong>报修标题:</strong>{{ currentOrder?.title }}</p>
|
||||
<p><strong>报修地点:</strong>{{ currentOrder?.repairLocation }}</p>
|
||||
<p><strong>维修类型:</strong>{{ getCategoryNameById(currentOrder?.categoryId) }}</p>
|
||||
<p><strong>问题描述:</strong>{{ currentOrder?.description }}</p>
|
||||
<p><strong>备注信息:</strong>{{ currentOrder?.remark }}</p>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="分派维修员" prop="assigneeId">
|
||||
@@ -928,7 +949,7 @@
|
||||
<div class="progress-card">
|
||||
<div class="progress-header">
|
||||
<div class="progress-type-status">
|
||||
<el-tag :type="getProgressTypeTag(progress.progressType) as any" size="small">
|
||||
<el-tag :type="getProgressTypeTag(progress.progressType)" size="small">
|
||||
{{ getProgressTypeText(progress.progressType) }}
|
||||
</el-tag>
|
||||
<span class="progress-status">{{ progress.progressStatus }}</span>
|
||||
@@ -997,14 +1018,14 @@
|
||||
<!-- 进度更新对话框 -->
|
||||
<el-dialog title="进度更新" v-model="updateProgressDialog.visible" width="600px" append-to-body>
|
||||
<el-form ref="progressFormRef" :model="progressForm" :rules="progressRules" label-width="120px">
|
||||
<el-form-item label="工单编号" prop="orderId">
|
||||
<el-input v-model="progressForm.orderId" placeholder="请输入工单编号" />
|
||||
<el-form-item label="工单编号" prop="orderNo">
|
||||
<el-input v-model="progressForm.orderNo" placeholder="请输入工单编号" />
|
||||
</el-form-item>
|
||||
<el-form-item label="进度类型" prop="progressType">
|
||||
<!-- <el-form-item label="进度类型" prop="progressType">
|
||||
<el-select v-model="progressForm.progressType" placeholder="请选择进度类型" style="width: 100%">
|
||||
<el-option v-for="type in progressTypeOptions" :key="type.value" :label="type.label" :value="type.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form-item> -->
|
||||
<el-form-item label="进度状态" prop="progressStatus">
|
||||
<el-input v-model="progressForm.progressStatus" placeholder="请输入进度状态" />
|
||||
</el-form-item>
|
||||
@@ -1041,17 +1062,17 @@
|
||||
<!-- 完成工单对话框 -->
|
||||
<el-dialog title="完成工单" v-model="completeDialog.visible" width="600px" append-to-body>
|
||||
<el-form ref="completeFormRef" :model="completeForm" :rules="completeRules" label-width="120px">
|
||||
<el-form-item label="工单编号" prop="orderId">
|
||||
<el-input v-model="completeForm.orderId" placeholder="请输入工单编号" />
|
||||
<el-form-item label="工单编号" prop="orderNo">
|
||||
<el-input v-model="completeForm.orderNo" placeholder="请输入工单编号" readonly disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="维修结果" prop="repairResult">
|
||||
<el-input v-model="completeForm.repairResult" placeholder="请输入维修结果" />
|
||||
</el-form-item>
|
||||
<el-form-item label="使用材料" prop="usedMaterials">
|
||||
<el-input v-model="completeForm.usedMaterials" placeholder="请输入使用材料" />
|
||||
<el-input v-model="completeForm.usedMaterials" type="textarea" placeholder="请输入使用材料" />
|
||||
</el-form-item>
|
||||
<el-form-item label="后续建议" prop="followSuggestions">
|
||||
<el-input v-model="completeForm.followSuggestions" placeholder="请输入后续建议" />
|
||||
<el-input v-model="completeForm.followSuggestions" type="textarea" placeholder="请输入后续建议" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
@@ -1206,7 +1227,8 @@ import {
|
||||
getRepairAttachments,
|
||||
uploadAttachment,
|
||||
uploadAttachments,
|
||||
deleteAttachment
|
||||
deleteAttachment,
|
||||
batchUpdateAttachmentOrderId
|
||||
} from '@/api/hq/pm/repairOrder';
|
||||
import { getRepairCategoryOptions, getRepairCategoryTree } from '@/api/hq/pm/repairCategory';
|
||||
import {
|
||||
@@ -1224,7 +1246,7 @@ import {
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import { getInfo } from '@/api/login'; // 添加获取用户详细信息的API
|
||||
import { getToken } from '@/utils/auth'; // 修复:添加getToken导入
|
||||
import { vLoading } from 'element-plus';
|
||||
import { vLoading, ElLoading } from 'element-plus';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
|
||||
@@ -1266,7 +1288,7 @@ const workerOptions = ref<Array<{ value: string | number; label: string; userNam
|
||||
|
||||
// 附件相关数据
|
||||
const attachmentList = ref<any[]>([]);
|
||||
const uploadAction = ref('/dev-api/pm/attachment/upload'); // 修复:添加/dev-api前缀
|
||||
const uploadAction = ref('/dev-api/hq/pm/attachment/upload'); // 修复:使用正确的API路径
|
||||
const uploadHeaders = ref({
|
||||
'Authorization': `Bearer ${getToken() || ''}`,
|
||||
'clientid': import.meta.env.VITE_APP_CLIENT_ID || 'e5cd7e4891bf95d1d19206ce24a7b32e'
|
||||
@@ -1276,6 +1298,12 @@ const uploadData = ref<any>({});
|
||||
// 新增:临时文件存储,用于新增时收集文件
|
||||
const tempFileList = ref<File[]>([]);
|
||||
|
||||
// 新增:临时附件ID列表,用于新增时收集已上传附件的ID
|
||||
const tempAttachmentIds = ref<string[]>([]);
|
||||
|
||||
// 待删除的附件ID列表 (保存时统一删除)
|
||||
const pendingDeleteAttachmentIds = ref<string[]>([]);
|
||||
|
||||
// 字典数据
|
||||
const pm_priority_level = ref([
|
||||
{ value: '1', label: '非常紧急' },
|
||||
@@ -1830,24 +1858,53 @@ const submitForm = () => {
|
||||
});
|
||||
};
|
||||
|
||||
/** 处理待上传的附件 */
|
||||
/** 处理待关联的附件 */
|
||||
const handlePendingAttachments = async (orderId: string | number) => {
|
||||
// 处理临时文件列表(新增时的文件)
|
||||
if (tempFileList.value.length > 0) {
|
||||
try {
|
||||
console.log('开始上传临时文件,文件数量:', tempFileList.value.length);
|
||||
for (const file of tempFileList.value) {
|
||||
await uploadAttachment(String(orderId), '1', file, '');
|
||||
console.log('成功上传文件:', file.name);
|
||||
}
|
||||
proxy?.$modal.msgSuccess(`成功上传 ${tempFileList.value.length} 个附件`);
|
||||
let hasOperations = false;
|
||||
let successMessages: string[] = [];
|
||||
let errorMessages: string[] = [];
|
||||
|
||||
// 清空临时文件列表
|
||||
// 处理待删除的附件(编辑模式)
|
||||
if (pendingDeleteAttachmentIds.value.length > 0) {
|
||||
try {
|
||||
console.log('开始删除标记的附件,数量:', pendingDeleteAttachmentIds.value.length);
|
||||
|
||||
// 批量删除附件
|
||||
for (const attachmentId of pendingDeleteAttachmentIds.value) {
|
||||
await deleteAttachment(attachmentId);
|
||||
}
|
||||
|
||||
console.log('成功删除附件IDs:', pendingDeleteAttachmentIds.value);
|
||||
successMessages.push(`成功删除 ${pendingDeleteAttachmentIds.value.length} 个附件`);
|
||||
|
||||
// 清空待删除列表
|
||||
pendingDeleteAttachmentIds.value = [];
|
||||
hasOperations = true;
|
||||
} catch (error) {
|
||||
console.error('附件删除失败:', error);
|
||||
errorMessages.push('部分附件删除失败');
|
||||
}
|
||||
}
|
||||
|
||||
// 处理新增模式下已上传的附件(更新order_id)
|
||||
if (tempAttachmentIds.value.length > 0) {
|
||||
try {
|
||||
console.log('开始关联临时附件到工单,附件数量:', tempAttachmentIds.value.length);
|
||||
|
||||
// 调用API批量更新附件的order_id
|
||||
await batchUpdateAttachmentOrderId(tempAttachmentIds.value, String(orderId));
|
||||
console.log('成功关联附件IDs:', tempAttachmentIds.value, '到工单:', orderId);
|
||||
|
||||
successMessages.push(`成功关联 ${tempAttachmentIds.value.length} 个附件到工单`);
|
||||
|
||||
// 清空临时附件ID列表
|
||||
tempAttachmentIds.value = [];
|
||||
tempFileList.value = [];
|
||||
attachmentList.value = [];
|
||||
hasOperations = true;
|
||||
} catch (error) {
|
||||
console.error('附件上传失败:', error);
|
||||
proxy?.$modal.msgWarning('工单保存成功,但部分附件上传失败');
|
||||
console.error('附件关联失败:', error);
|
||||
errorMessages.push('部分附件关联失败');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1865,13 +1922,21 @@ const handlePendingAttachments = async (orderId: string | number) => {
|
||||
fileItem.status = 'success';
|
||||
}
|
||||
}
|
||||
if (tempFileList.value.length === 0) {
|
||||
// 只有当没有临时文件时才显示这个提示,避免重复提示
|
||||
proxy?.$modal.msgSuccess(`成功上传 ${newFiles.length} 个新附件`);
|
||||
}
|
||||
successMessages.push(`成功上传 ${newFiles.length} 个新附件`);
|
||||
hasOperations = true;
|
||||
} catch (error) {
|
||||
console.error('新附件上传失败:', error);
|
||||
proxy?.$modal.msgWarning('工单保存成功,但部分新附件上传失败');
|
||||
errorMessages.push('部分新附件上传失败');
|
||||
}
|
||||
}
|
||||
|
||||
// 显示操作结果消息
|
||||
if (hasOperations) {
|
||||
if (successMessages.length > 0) {
|
||||
proxy?.$modal.msgSuccess(successMessages.join(','));
|
||||
}
|
||||
if (errorMessages.length > 0) {
|
||||
proxy?.$modal.msgWarning(`工单保存成功,但${errorMessages.join(',')}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -2227,16 +2292,27 @@ const handleBeforeUpload = (file: File) => {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 新增时不直接上传,而是收集文件
|
||||
if (!form.value.id) {
|
||||
return false; // 阻止自动上传
|
||||
// 设置上传参数
|
||||
const uploadParams: any = {
|
||||
uploadStage: '1', // 报修申请阶段
|
||||
description: `${form.value.title || '报修工单'}相关附件` // 添加描述信息
|
||||
};
|
||||
|
||||
// 只有编辑模式时才传递orderId
|
||||
if (form.value.id) {
|
||||
uploadParams.orderId = form.value.id;
|
||||
}
|
||||
|
||||
// 编辑时设置上传参数
|
||||
uploadData.value = {
|
||||
orderId: form.value.id,
|
||||
uploadStage: '1' // 报修申请阶段
|
||||
};
|
||||
uploadData.value = uploadParams;
|
||||
|
||||
console.log('准备上传文件:', {
|
||||
文件名: file.name,
|
||||
文件大小: (file.size / 1024 / 1024).toFixed(2) + 'MB',
|
||||
工单ID: uploadParams.orderId || '待关联',
|
||||
上传阶段: uploadParams.uploadStage,
|
||||
描述: uploadParams.description,
|
||||
模式: form.value.id ? '编辑模式' : '新增模式'
|
||||
});
|
||||
|
||||
return true;
|
||||
};
|
||||
@@ -2244,11 +2320,41 @@ const handleBeforeUpload = (file: File) => {
|
||||
/** 上传成功 */
|
||||
const handleUploadSuccess = (response: any, file: any) => {
|
||||
console.log('上传成功:', response, file);
|
||||
proxy?.$modal.msgSuccess(`文件 ${file.name} 上传成功`);
|
||||
|
||||
// 更新附件列表
|
||||
if (response.code === 200) {
|
||||
// 这里可以刷新附件列表或更新UI
|
||||
proxy?.$modal.msgSuccess(`文件 ${file.name} 上传成功`);
|
||||
|
||||
// 立即记录附件数据
|
||||
const attachmentData = response.data;
|
||||
if (attachmentData && attachmentData.id) {
|
||||
// 如果是新增模式,收集附件ID到临时列表
|
||||
if (!form.value.id) {
|
||||
tempAttachmentIds.value.push(attachmentData.id);
|
||||
console.log('新增模式:已收集附件ID', attachmentData.id, '当前临时附件ID列表:', tempAttachmentIds.value);
|
||||
}
|
||||
|
||||
// 更新文件对象的相关信息
|
||||
file.attachmentId = attachmentData.id;
|
||||
file.status = 'success';
|
||||
file.url = attachmentData.filePath;
|
||||
|
||||
// 如果是编辑模式,附件已经自动关联到工单,直接更新UI
|
||||
if (form.value.id) {
|
||||
console.log('编辑模式:附件已关联到工单', form.value.id, '附件ID:', attachmentData.id);
|
||||
// 可以选择刷新附件列表以确保数据同步
|
||||
// loadAttachments(form.value.id);
|
||||
}
|
||||
|
||||
console.log('附件记录成功:', {
|
||||
附件ID: attachmentData.id,
|
||||
文件名: attachmentData.fileName,
|
||||
文件路径: attachmentData.filePath,
|
||||
工单ID: form.value.id || '待关联',
|
||||
模式: form.value.id ? '编辑' : '新增'
|
||||
});
|
||||
}
|
||||
} else {
|
||||
proxy?.$modal.msgError(`文件 ${file.name} 上传失败: ${response.msg || '未知错误'}`);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2258,25 +2364,108 @@ const handleUploadError = (error: Error, file: any) => {
|
||||
proxy?.$modal.msgError(`文件 ${file.name} 上传失败`);
|
||||
};
|
||||
|
||||
/** 移除附件 */
|
||||
const handleRemoveAttachment = async (file: any) => {
|
||||
// 如果是已上传的文件(编辑模式下的已有附件)
|
||||
if (file.response && file.response.data && file.response.data.id) {
|
||||
try {
|
||||
await deleteAttachment(file.response.data.id);
|
||||
proxy?.$modal.msgSuccess('附件删除成功');
|
||||
/** 下载附件 */
|
||||
const handleDownloadAttachment = async (file: any) => {
|
||||
let downloadUrl = '';
|
||||
let fileName = file.name || 'attachment';
|
||||
|
||||
// 编辑模式下删除附件后,从attachmentList中移除
|
||||
const index = attachmentList.value.findIndex((item) => item.uid === file.uid);
|
||||
if (index > -1) {
|
||||
attachmentList.value.splice(index, 1);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('删除附件失败:', error);
|
||||
proxy?.$modal.msgError('删除附件失败');
|
||||
}
|
||||
// 如果文件已经上传成功,使用服务器返回的路径
|
||||
if (file.response && file.response.data && file.response.data.filePath) {
|
||||
downloadUrl = file.response.data.filePath;
|
||||
fileName = file.response.data.fileName || fileName;
|
||||
}
|
||||
// 如果是临时文件(新增时添加的文件),从临时列表中删除
|
||||
// 如果是已有的附件,使用文件的url
|
||||
else if (file.url) {
|
||||
downloadUrl = file.url;
|
||||
}
|
||||
// 如果是本地文件,直接使用raw文件
|
||||
else if (file.raw) {
|
||||
const link = document.createElement('a');
|
||||
link.href = URL.createObjectURL(file.raw);
|
||||
link.download = fileName;
|
||||
link.style.display = 'none';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(link.href);
|
||||
console.log('下载本地文件:', fileName);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!downloadUrl) {
|
||||
proxy?.$modal.msgError('无法获取文件下载地址');
|
||||
return;
|
||||
}
|
||||
|
||||
// 对于服务器文件,使用fetch获取内容强制下载
|
||||
try {
|
||||
const loading = ElLoading.service({
|
||||
text: '正在下载文件...',
|
||||
target: document.body
|
||||
});
|
||||
|
||||
const response = await fetch(downloadUrl);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const blob = await response.blob();
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = fileName;
|
||||
link.style.display = 'none';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
|
||||
window.URL.revokeObjectURL(url);
|
||||
loading?.close();
|
||||
console.log('下载服务器文件成功:', fileName);
|
||||
} catch (error) {
|
||||
console.error('下载文件失败:', error);
|
||||
proxy?.$modal.msgError('下载文件失败,请稍后重试');
|
||||
}
|
||||
};
|
||||
|
||||
/** 删除附件确认 */
|
||||
const handleRemoveAttachmentConfirm = async (file: any) => {
|
||||
await proxy?.$modal.confirm('确定要删除该附件吗?删除操作将在保存时生效。');
|
||||
handleRemoveAttachment(file);
|
||||
};
|
||||
|
||||
/** 移除附件 (修改为延迟删除) */
|
||||
const handleRemoveAttachment = (file: any) => {
|
||||
// 如果是已上传的文件(编辑模式下的已有附件或新增模式下已上传的附件)
|
||||
if (file.response && file.response.data && file.response.data.id) {
|
||||
// 不立即删除数据库记录,而是标记为待删除
|
||||
const attachmentId = file.response.data.id;
|
||||
|
||||
// 如果是新增模式,从临时附件ID列表中移除
|
||||
if (!form.value.id) {
|
||||
const attachmentIndex = tempAttachmentIds.value.indexOf(attachmentId);
|
||||
if (attachmentIndex > -1) {
|
||||
tempAttachmentIds.value.splice(attachmentIndex, 1);
|
||||
console.log('新增模式:已从临时附件ID列表中移除:', attachmentId, '剩余附件数:', tempAttachmentIds.value.length);
|
||||
}
|
||||
} else {
|
||||
// 编辑模式:添加到待删除列表
|
||||
if (!pendingDeleteAttachmentIds.value.includes(attachmentId)) {
|
||||
pendingDeleteAttachmentIds.value.push(attachmentId);
|
||||
console.log('编辑模式:已标记附件为待删除:', attachmentId, '待删除列表:', pendingDeleteAttachmentIds.value);
|
||||
}
|
||||
}
|
||||
|
||||
// 从attachmentList中移除
|
||||
const index = attachmentList.value.findIndex((item) => item.uid === file.uid);
|
||||
if (index > -1) {
|
||||
attachmentList.value.splice(index, 1);
|
||||
}
|
||||
|
||||
proxy?.$modal.msgSuccess('附件已移除,将在保存时生效');
|
||||
}
|
||||
// 如果是临时文件(新增时添加但未上传的文件),从临时列表中删除
|
||||
else if (file.raw && !form.value.id) {
|
||||
const index = tempFileList.value.findIndex((f) => f.name === file.raw.name && f.size === file.raw.size);
|
||||
if (index > -1) {
|
||||
@@ -2321,7 +2510,9 @@ const reset = async (autoFillUserInfo: boolean = false) => {
|
||||
// 清空附件相关数据
|
||||
attachmentList.value = [];
|
||||
tempFileList.value = [];
|
||||
console.log('已清空附件列表和临时文件列表');
|
||||
tempAttachmentIds.value = [];
|
||||
pendingDeleteAttachmentIds.value = [];
|
||||
console.log('已清空附件列表、临时文件列表、临时附件ID列表和待删除附件列表');
|
||||
|
||||
// 如果是新增操作,自动填充当前用户信息
|
||||
if (autoFillUserInfo) {
|
||||
@@ -2497,8 +2688,46 @@ const cancelEvaluation = () => {
|
||||
};
|
||||
|
||||
/** 下载附件 */
|
||||
const downloadAttachment = (attachment: any) => {
|
||||
window.open(attachment.filePath, '_blank');
|
||||
const downloadAttachment = async (attachment: any) => {
|
||||
if (!attachment.filePath) {
|
||||
proxy?.$modal.msgError('无法获取文件下载地址');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 显示加载提示
|
||||
const loading = ElLoading.service({
|
||||
text: '正在下载文件...',
|
||||
target: document.body
|
||||
});
|
||||
|
||||
// 通过fetch获取文件内容,强制下载
|
||||
const response = await fetch(attachment.filePath);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const blob = await response.blob();
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
|
||||
// 创建下载链接
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = attachment.fileName || '附件';
|
||||
link.style.display = 'none';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
|
||||
// 释放blob URL
|
||||
window.URL.revokeObjectURL(url);
|
||||
|
||||
loading?.close();
|
||||
console.log('下载附件成功:', attachment.fileName || '附件');
|
||||
} catch (error) {
|
||||
console.error('下载附件失败:', error);
|
||||
proxy?.$modal.msgError('下载文件失败,请稍后重试');
|
||||
}
|
||||
};
|
||||
|
||||
/** 预览附件 */
|
||||
@@ -2602,8 +2831,8 @@ const getProgressIconColor = (progressType: string) => {
|
||||
};
|
||||
|
||||
/** 获取进度类型标签类型 */
|
||||
const getProgressTypeTag = (progressType: string) => {
|
||||
const tagMap: Record<string, string> = {
|
||||
const getProgressTypeTag = (progressType: string): 'success' | 'warning' | 'info' | 'primary' | 'danger' => {
|
||||
const tagMap: Record<string, 'success' | 'warning' | 'info' | 'primary' | 'danger'> = {
|
||||
'1': 'primary',
|
||||
'2': 'success',
|
||||
'3': 'warning',
|
||||
@@ -2827,6 +3056,8 @@ const getFileExt = (fileName: string): string => {
|
||||
|
||||
/* 工单信息样式 */
|
||||
.order-info {
|
||||
width: 100%;
|
||||
box-sizing: border-box; // 使内边距计入总宽度,避免超出表单内容区域
|
||||
background-color: #f8f9fa;
|
||||
padding: 16px;
|
||||
border-radius: 8px;
|
||||
@@ -3505,18 +3736,35 @@ const getFileExt = (fileName: string): string => {
|
||||
display: flex !important;
|
||||
}
|
||||
|
||||
/* Element Plus upload 组件样式覆盖 */
|
||||
:deep(.el-upload-list--picture-card) {
|
||||
.el-upload-list__item {
|
||||
width: 166px !important;
|
||||
height: auto !important;
|
||||
min-height: 180px !important;
|
||||
margin: 0 8px 8px 0 !important;
|
||||
border-radius: 6px !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* 卡片整体布局包含文件名 */
|
||||
.upload-file-card {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
min-height: 180px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.upload-file-thumb {
|
||||
width: 100%;
|
||||
aspect-ratio: 1 / 1;
|
||||
height: 120px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
|
||||
.upload-file-thumb .el-image,
|
||||
@@ -3525,13 +3773,61 @@ const getFileExt = (fileName: string): string => {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.upload-file-placeholder {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #fafafa;
|
||||
|
||||
.el-icon {
|
||||
font-size: 24px;
|
||||
color: #c0c4cc;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.file-ext {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
text-align: center;
|
||||
word-break: break-all;
|
||||
padding: 0 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.upload-file-name {
|
||||
width: 100%;
|
||||
margin-top: 4px;
|
||||
padding: 8px 4px 4px 4px;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: #606266;
|
||||
background-color: #fff;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* 上传文件卡片操作按钮样式 (与预览界面保持一致) */
|
||||
.upload-file-card-actions {
|
||||
width: 100%;
|
||||
padding: 6px 8px;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
background-color: #fafafa;
|
||||
margin-top: auto;
|
||||
flex-shrink: 0;
|
||||
|
||||
.el-button-group {
|
||||
.el-button {
|
||||
padding: 4px 6px;
|
||||
font-size: 12px;
|
||||
|
||||
.el-icon {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user