SWX\10484 4 days ago
commit 480c0c6531
  1. 85
      src/utils/requestMinio.js
  2. 147
      src/views/cases/detail.vue

@ -49,8 +49,14 @@ export function getMinioClient() {
*/
export async function uploadFile(bucket, objectName, file, metaData = {}, onProgress) {
return new Promise((resolve, reject) => {
// 检查 MinIO 客户端是否初始化
if (!s3Client) {
return reject(new Error('MinIO client not initialized'))
console.error('❌ MinIO client not initialized, attempting fallback upload...')
// 回退到使用 HTTP 表单上传
return fallbackUpload(bucket, objectName, file, metaData, onProgress)
.then(resolve)
.catch(reject)
}
console.log('=== MinIO Upload ===')
@ -90,9 +96,82 @@ export async function uploadFile(bucket, objectName, file, metaData = {}, onProg
})
})
.catch((err) => {
console.error('❌ Upload failed:', err)
reject(new Error('Upload failed: ' + err.message))
console.error('❌ AWS SDK Upload failed:', err)
console.log('Attempting fallback upload...')
// 尝试回退上传
fallbackUpload(bucket, objectName, file, metaData, onProgress)
.then(resolve)
.catch((fallbackErr) => {
reject(new Error('Upload failed: ' + fallbackErr.message))
})
})
})
}
/**
* 回退上传方法 - 使用 HTTP POST 表单上传
* @param {string} bucket - 存储桶名称
* @param {string} objectName - 对象名称
* @param {File} file - 文件对象
* @param {object} metaData - 元数据
* @param {function} onProgress - 进度回调
* @returns {Promise} - 上传结果
*/
async function fallbackUpload(bucket, objectName, file, metaData, onProgress) {
return new Promise((resolve, reject) => {
const config = window._globalConfig || {}
const uploadUrl = config.MINIO_UPLOAD_URL || '/api/upload/minio'
console.log('=== Fallback Upload ===')
console.log('Upload URL:', uploadUrl)
console.log('Bucket:', bucket)
console.log('Object:', objectName)
const formData = new FormData()
formData.append('bucket', bucket)
formData.append('objectName', objectName)
formData.append('file', file, file.name)
// 添加元数据
for (const key in metaData) {
formData.append('meta_' + key, metaData[key])
}
const xhr = new XMLHttpRequest()
xhr.upload.addEventListener('progress', (event) => {
if (event.lengthComputable && onProgress) {
const percent = Math.round((event.loaded / event.total) * 100)
onProgress(percent)
}
})
xhr.addEventListener('load', () => {
try {
const response = JSON.parse(xhr.responseText)
if (xhr.status >= 200 && xhr.status < 300) {
console.log('✅ Fallback upload successful:', response)
resolve({
bucket,
objectName: response.objectName || objectName,
etag: response.etag,
url: response.url || `${uploadUrl}/${bucket}/${objectName}`
})
} else {
reject(new Error(response.message || 'Upload failed'))
}
} catch (e) {
reject(new Error('Failed to parse response: ' + e.message))
}
})
xhr.addEventListener('error', () => {
reject(new Error('Network error during upload'))
})
xhr.open('POST', uploadUrl, true)
xhr.send(formData)
})
}

@ -283,7 +283,7 @@
v-model="form.text_comment"
type="textarea"
:rows="9"
:disabled="
:readonly="
form.status != 1 &&
form.status != 5 &&
form.status != 15
@ -297,7 +297,7 @@
v-model="form.text_conclusion"
type="textarea"
:rows="4"
:disabled="
:readonly="
form.status != 1 &&
form.status != 5 &&
form.status != 15
@ -319,10 +319,12 @@
v-model="item.user_comment"
type="textarea"
:rows="3"
:disabled="
form.status != 1 &&
:readonly="
(form.status != 1 &&
form.status != 5 &&
form.status != 15
form.status != 15) ||
item.confirm > 0 ||
item.expert_id != userInfo.id
"
/>
<div class="expert-btns">
@ -330,10 +332,13 @@
type="text"
icon="el-icon-document-copy"
:disabled="
form.status != 1 &&
(form.status != 1 &&
form.status != 5 &&
form.status != 15
form.status != 15) ||
!item.user_comment ||
!item.user_comment.trim()
"
@click="handleCopyAllExpertComments(item)"
>
全部复制
</el-button>
@ -345,18 +350,25 @@
form.status != 5 &&
form.status != 15
"
v-if="item.confirm == 0"
v-if="
item.confirm == 0 && item.expert_id == userInfo.id
"
>
一键同意
</el-button>
<el-button
type="text"
icon="el-icon-check"
disabled
v-else-if="item.confirm > 0"
>
专家已确认
</el-button>
<el-button
type="text"
icon="el-icon-warning"
:disabled="
form.status != 1 &&
form.status != 5 &&
form.status != 15
"
disabled
v-else
>
待确认
</el-button>
@ -1484,6 +1496,110 @@ export default {
}
});
},
async handleCopyAllExpertComments(item) {
const text = item.user_comment || "";
if (!text || !text.trim()) {
// this.$modal.msgWarning('');
return;
}
try {
if (navigator.clipboard && window.isSecureContext) {
console.log("复制策略 - 使用 Clipboard API");
await navigator.clipboard.writeText(text);
this.$modal.msgSuccess("复制成功");
} else {
console.log("复制策略 - 使用 execCommand fallback");
const textarea = document.createElement("textarea");
textarea.value = text;
textarea.style.position = "fixed";
textarea.style.left = "-9999px";
document.body.appendChild(textarea);
textarea.select();
const success = document.execCommand("copy");
document.body.removeChild(textarea);
if (success) {
console.log("execCommand 返回 true");
this.$modal.msgSuccess("复制成功");
} else {
console.log("execCommand 返回 false,使用兜底方案");
this.showCopyFallback(text);
}
}
} catch (err) {
console.error("复制失败 - 捕获异常:", err);
this.showCopyFallback(text);
}
},
showCopyFallback(text) {
const dialog = document.createElement("div");
dialog.style.cssText = `
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0,0,0,0.15);
z-index: 9999;
max-width: 80%;
max-height: 80vh;
overflow-y: auto;
`;
const title = document.createElement("h3");
title.textContent = "复制内容";
title.style.margin = "0 0 12px 0";
title.style.fontSize = "16px";
dialog.appendChild(title);
const textarea = document.createElement("textarea");
textarea.value = text;
textarea.style.width = "400px";
textarea.style.height = "200px";
textarea.style.marginBottom = "12px";
textarea.style.padding = "8px";
textarea.style.border = "1px solid #e4e7ed";
textarea.style.borderRadius = "4px";
textarea.addEventListener("focus", () => textarea.select());
dialog.appendChild(textarea);
const btn = document.createElement("button");
btn.textContent = "确定";
btn.style.cssText = `
display: block;
margin: 0 auto;
padding: 8px 24px;
background: #409eff;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
`;
btn.addEventListener("click", () => {
document.body.removeChild(dialog);
document.body.removeChild(mask);
});
dialog.appendChild(btn);
const mask = document.createElement("div");
mask.style.cssText = `
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0,0,0,0.5);
z-index: 9998;
`;
mask.addEventListener("click", () => {
document.body.removeChild(dialog);
document.body.removeChild(mask);
});
document.body.appendChild(mask);
document.body.appendChild(dialog);
textarea.select();
},
},
};
</script>
@ -1613,9 +1729,4 @@ export default {
display: inline-block;
cursor: pointer;
}
//
::v-deep .el-textarea__inner:disabled {
color: #000;
}
</style>
Loading…
Cancel
Save