u
This commit is contained in:
1 parent
b7f2efc7c3
commit
cc88205cba
23 files changed
+1239
-799
No files matched your search
@@ -130,6 +130,9 @@ function handleUpdateDialogClosed() {
|
||||
|
||||
function handleShowUpdateDialog(file) {
|
||||
updateFile.value = JSON.parse(JSON.stringify(file));
|
||||
|
||||
updateFile.value._main = file.mx_cate_id === "0";
|
||||
|
||||
showUpdateDialog.value = true;
|
||||
}
|
||||
|
||||
@@ -162,7 +165,6 @@ async function handleConfirmUpdate() {
|
||||
subid: updateFile.value.subid,
|
||||
mx_filename: updateFile.value.mx_filename,
|
||||
mx_bz: updateFile.value.mx_bz,
|
||||
mx_cate_id: updateFile.value.mx_cate_id,
|
||||
},
|
||||
],
|
||||
deletes: [],
|
||||
@@ -170,7 +172,7 @@ async function handleConfirmUpdate() {
|
||||
];
|
||||
|
||||
// 检查是否设置了箱门照
|
||||
if (updateFile.value.mx_cate_id === "0") {
|
||||
if (updateFile.value._main) {
|
||||
requestData[0].updates[0].mx_xh = "0";
|
||||
|
||||
// 检查数据库表其它是否有文件已经设置了箱门照
|
||||
@@ -178,6 +180,7 @@ async function handleConfirmUpdate() {
|
||||
const res = await loadDataApi("bf_file", searchCondition, "mx_xh", ["subid", "mx_xh"]);
|
||||
|
||||
if (res.success && res.data.length > 0) {
|
||||
// 更新旧数据
|
||||
requestData.push({
|
||||
tableName: "bf_file",
|
||||
keyField: "subid",
|
||||
@@ -185,13 +188,32 @@ async function handleConfirmUpdate() {
|
||||
updates: [
|
||||
{
|
||||
subid: res.data[0].subid,
|
||||
mx_cate_id: "1",
|
||||
mx_cate_id: updateFile.value.mx_cate_id,
|
||||
mx_xh: updateFile.value.mx_xh,
|
||||
},
|
||||
],
|
||||
deletes: [],
|
||||
});
|
||||
|
||||
// 更新新数据
|
||||
requestData[0].updates[0].mx_cate_id = "0";
|
||||
|
||||
// 更新主图
|
||||
requestData.push({
|
||||
tableName: "b_container",
|
||||
keyField: "b_id",
|
||||
inserts: [],
|
||||
updates: [
|
||||
{
|
||||
b_id: props.father,
|
||||
b_container_img: extractFilePath(updateFile.value.mx_mapfilename),
|
||||
},
|
||||
],
|
||||
deletes: [],
|
||||
});
|
||||
} else {
|
||||
requestData[0].updates[0].mx_cate_id = "0";
|
||||
|
||||
requestData.push({
|
||||
tableName: "b_container",
|
||||
keyField: "b_id",
|
||||
@@ -215,7 +237,7 @@ async function handleConfirmUpdate() {
|
||||
|
||||
showUpdateDialog.value = false;
|
||||
|
||||
loadFiles()
|
||||
loadFiles();
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error("更新文件时发生错误");
|
||||
@@ -287,7 +309,7 @@ async function loadFiles() {
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await loadDataApi("bf_file", searchCondition, "mx_cate_id,mx_xh");
|
||||
const res = await loadDataApi("bf_file", searchCondition, "mx_cate_id ASC, mx_xh ASC");
|
||||
|
||||
if (res.success) {
|
||||
let datas = res.data.map((item) => {
|
||||
@@ -385,69 +407,75 @@ function handleDrop(e) {
|
||||
|
||||
// 本地上传方法
|
||||
async function uploadLocalFiles(files) {
|
||||
if (files.length > 0) {
|
||||
const currentLength = fileList.value.length;
|
||||
if (!files.length) return;
|
||||
|
||||
Array.from(files).forEach((file, index) => {
|
||||
const fileInfo = {
|
||||
mx_filename: file.name,
|
||||
mx_filesize: file.size,
|
||||
mx_fileext: getFileExtension(file.name),
|
||||
mx_inputuser_id: userStore.userInfo.userid,
|
||||
mx_inputdatetime: "",
|
||||
mx_mapfilename: "",
|
||||
mx_xh: currentLength + index + 1,
|
||||
_content: null,
|
||||
_status: "pending",
|
||||
};
|
||||
const currentLength = fileList.value.length;
|
||||
|
||||
const reader = new FileReader();
|
||||
Array.from(files).forEach((file, index) => {
|
||||
const fileInfo = {
|
||||
mx_filename: file.name,
|
||||
mx_filesize: file.size,
|
||||
mx_fileext: getFileExtension(file.name),
|
||||
mx_inputuser_id: userStore.userInfo.userid,
|
||||
mx_inputdatetime: "",
|
||||
mx_mapfilename: "",
|
||||
mx_xh: currentLength + index + 1,
|
||||
|
||||
reader.onload = async (event) => {
|
||||
const blob = new Blob([event.target.result], { type: file.type });
|
||||
_content: file, // 默认原始文件
|
||||
_status: "pending",
|
||||
};
|
||||
|
||||
if (file.type.startsWith("image/")) {
|
||||
try {
|
||||
const tempUrl = URL.createObjectURL(blob);
|
||||
// ===== 图片处理 =====
|
||||
if (file.type.startsWith("image/")) {
|
||||
const tempUrl = URL.createObjectURL(file);
|
||||
|
||||
if (file.size > 512000) {
|
||||
const { url, size, blob: compressedBlob } = await compressImage(tempUrl);
|
||||
if (file.size > 512000) {
|
||||
compressImage(tempUrl)
|
||||
.then(({ url, size, blob }) => {
|
||||
fileInfo.mx_mapfilename = url;
|
||||
fileInfo.mx_filesize = size;
|
||||
|
||||
fileInfo.mx_mapfilename = url;
|
||||
fileInfo.mx_filesize = size;
|
||||
fileInfo._content = compressedBlob;
|
||||
} else {
|
||||
fileInfo.mx_mapfilename = tempUrl;
|
||||
fileInfo._content = blob;
|
||||
}
|
||||
} catch (err) {
|
||||
// 用压缩后的 File(推荐稳定结构)
|
||||
fileInfo._content = new File([blob], file.name, {
|
||||
type: file.type,
|
||||
});
|
||||
|
||||
fileList.value.push(fileInfo);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error("图片压缩失败:", err);
|
||||
fileInfo.mx_mapfilename = URL.createObjectURL(blob);
|
||||
}
|
||||
} else {
|
||||
fileInfo._content = blob;
|
||||
}
|
||||
|
||||
fileInfo.mx_mapfilename = tempUrl;
|
||||
fileInfo._content = file;
|
||||
|
||||
fileList.value.push(fileInfo);
|
||||
});
|
||||
} else {
|
||||
fileInfo.mx_mapfilename = tempUrl;
|
||||
fileInfo._content = file;
|
||||
|
||||
fileList.value.push(fileInfo);
|
||||
};
|
||||
}
|
||||
|
||||
reader.readAsArrayBuffer(file);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
nextTick(() => {
|
||||
setTimeout(() => {
|
||||
if (displayMode.value === 0) {
|
||||
if (containerRef.value) {
|
||||
containerRef.value.scrollTop = containerRef.value.scrollHeight;
|
||||
}
|
||||
} else {
|
||||
if (tableRef.value) {
|
||||
tableRef.value.scrollToEndRow();
|
||||
}
|
||||
}
|
||||
}, 50);
|
||||
});
|
||||
}
|
||||
// ===== 非图片:直接使用原文件 =====
|
||||
fileInfo._content = file;
|
||||
fileList.value.push(fileInfo);
|
||||
});
|
||||
|
||||
nextTick(() => {
|
||||
setTimeout(() => {
|
||||
if (displayMode.value === 0) {
|
||||
containerRef.value?.scrollTo?.({
|
||||
top: containerRef.value.scrollHeight,
|
||||
});
|
||||
} else {
|
||||
tableRef.value?.scrollToEndRow?.();
|
||||
}
|
||||
}, 50);
|
||||
});
|
||||
}
|
||||
|
||||
function isImage(fileExtension) {
|
||||
@@ -646,7 +674,13 @@ async function deleteFiles(files) {
|
||||
});
|
||||
}
|
||||
// 保存
|
||||
// 保存
|
||||
async function handleSave() {
|
||||
if (cateId.value !== "1" && cateId.value !== "2") {
|
||||
ElMessage.warning("只允许 装货、发货单 上传");
|
||||
return;
|
||||
}
|
||||
|
||||
loading.value = true;
|
||||
loadingTip.value = "正在保存中 . . .";
|
||||
|
||||
@@ -658,19 +692,19 @@ async function handleSave() {
|
||||
["_content", "_status", "checked"],
|
||||
);
|
||||
|
||||
// 没有变化直接结束
|
||||
if (!hasChanges) {
|
||||
ElMessage.info("没有数据变更");
|
||||
return;
|
||||
}
|
||||
|
||||
const mxXh =
|
||||
fileList.value.length > 0 ? Math.max(...fileList.value.map((item) => item.mx_xh ?? 0)) : 2;
|
||||
// ===============================
|
||||
// 1️⃣ 先处理新增(上传文件)
|
||||
// ===============================
|
||||
const fileInserts = [];
|
||||
|
||||
// 处理新增
|
||||
for (const item of insertdata) {
|
||||
try {
|
||||
const uploadRes = await uploadContainerFileApi(item.mx_mapfilename, {
|
||||
const uploadRes = await uploadContainerFileApi(item._content, {
|
||||
jobId: props.father,
|
||||
fileName: item.mx_filename,
|
||||
containerNo: props.containerNo,
|
||||
@@ -680,31 +714,85 @@ async function handleSave() {
|
||||
throw new Error("上传失败");
|
||||
}
|
||||
|
||||
const insertRes = await insertFileApi({
|
||||
fileInserts.push({
|
||||
subid: uploadRes.data.uid,
|
||||
father: props.father,
|
||||
mx_xh: mxXh + 1,
|
||||
mx_filename: item.mx_filename,
|
||||
mx_filesize: item.mx_filesize,
|
||||
mx_fileext: item.mx_fileext,
|
||||
mx_mapfilename: uploadRes.data.path,
|
||||
mx_moduleid: "b_container",
|
||||
mx_cate_id: "1",
|
||||
mx_cate_id: cateId.value || "1",
|
||||
});
|
||||
|
||||
if (!insertRes.success) {
|
||||
throw new Error("入库失败");
|
||||
}
|
||||
|
||||
console.log(`文件 ${item.mx_filename} 上传成功`);
|
||||
item._status = "";
|
||||
} catch (error) {
|
||||
console.error(`文件 ${item.mx_filename} 处理失败:`, error);
|
||||
ElMessage.error(`文件 ${item.mx_filename} 上传失败,请重试`);
|
||||
}
|
||||
}
|
||||
|
||||
// ===============================
|
||||
// 2️⃣ 合并所有数据(旧 + 新 + 更新)
|
||||
// ===============================
|
||||
const mergedList = [
|
||||
...fileList.value
|
||||
.filter((item) => !insertdata.includes(item)) // 去掉旧 insert 原始对象
|
||||
.map((item) => ({
|
||||
...item,
|
||||
type: "update",
|
||||
})),
|
||||
...fileInserts.map((item) => ({
|
||||
...item,
|
||||
type: "insert",
|
||||
})),
|
||||
];
|
||||
|
||||
// ===============================
|
||||
// 3️⃣ 重新排序 mx_xh(核心)
|
||||
// ===============================
|
||||
const ordered = mergedList.map((item, index) => ({
|
||||
...item,
|
||||
mx_xh: index + 1,
|
||||
}));
|
||||
|
||||
// ===============================
|
||||
// 4️⃣ 拆分成 API 结构
|
||||
// ===============================
|
||||
const fileUpdates = ordered
|
||||
.filter((item) => item.type === "update")
|
||||
.map((item) => ({
|
||||
subid: item.subid,
|
||||
mx_xh: item.mx_xh,
|
||||
}));
|
||||
|
||||
const finalInserts = ordered
|
||||
.filter((item) => item.type === "insert")
|
||||
.map(({ type, ...rest }) => rest);
|
||||
|
||||
const fileDeletes = deletedata.map((item) => ({
|
||||
subid: item.subid,
|
||||
}));
|
||||
|
||||
// ===============================
|
||||
// 5️⃣ 统一提交
|
||||
// ===============================
|
||||
const reqData = [
|
||||
{
|
||||
tableName: "bf_file",
|
||||
keyField: "subid",
|
||||
inserts: finalInserts,
|
||||
updates: fileUpdates,
|
||||
deletes: fileDeletes,
|
||||
},
|
||||
];
|
||||
|
||||
await saveObjectApi(reqData);
|
||||
|
||||
ElMessage.success("保存成功");
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
ElMessage.error("保存失败");
|
||||
} finally {
|
||||
loading.value = false;
|
||||
loadFiles();
|
||||
@@ -745,7 +833,6 @@ function onClosed() {
|
||||
}
|
||||
|
||||
function handleTreeNodeClick(data) {
|
||||
console.log(data);
|
||||
cateId.value = data.cateId || "";
|
||||
|
||||
loadFiles();
|
||||
@@ -760,7 +847,7 @@ function handleTreeNodeClick(data) {
|
||||
<template>
|
||||
<el-dialog
|
||||
title="附件管理"
|
||||
width="900"
|
||||
width="1100"
|
||||
draggable
|
||||
modal-class="g3-file-modal"
|
||||
v-bind="$attrs"
|
||||
@@ -783,272 +870,288 @@ function handleTreeNodeClick(data) {
|
||||
/>
|
||||
|
||||
<div class="g3-container">
|
||||
<div v-if="categories[0].children.length > 0" class="g3-category">
|
||||
<el-tree
|
||||
ref="treeRef"
|
||||
node-key="cateId"
|
||||
:data="categories"
|
||||
:default-expanded-keys="['']"
|
||||
@node-click="handleTreeNodeClick"
|
||||
/>
|
||||
</div>
|
||||
<div class="g3-file-main">
|
||||
<div class="g3-header">
|
||||
<div>
|
||||
<el-checkbox
|
||||
v-model="selectAll"
|
||||
:indeterminate="indeterminate"
|
||||
:disabled="loading"
|
||||
@change="toggleSelectAll"
|
||||
>
|
||||
全选
|
||||
</el-checkbox>
|
||||
|
||||
<el-button
|
||||
size="small"
|
||||
type="primary"
|
||||
:disabled="checkedFiles.length === 0 || loading"
|
||||
style="margin-left: 8px"
|
||||
@click="handleDownloadZip"
|
||||
>
|
||||
<el-icon class="el-icon--left">
|
||||
<G3Icon icon="lucide:download" />
|
||||
</el-icon>
|
||||
打包下载
|
||||
</el-button>
|
||||
|
||||
<el-button
|
||||
v-if="!props.readonly"
|
||||
size="small"
|
||||
type="danger"
|
||||
:disabled="checkedFiles.length === 0 || loading"
|
||||
@click="handleBatchDelete()"
|
||||
>
|
||||
<el-icon class="el-icon--left">
|
||||
<G3Icon icon="lucide:trash-2" />
|
||||
</el-icon>
|
||||
批量删除
|
||||
</el-button>
|
||||
<el-splitter>
|
||||
<el-splitter-panel size="20%" v-if="categories[0].children.length > 0">
|
||||
<div class="g3-category">
|
||||
<el-tree
|
||||
ref="treeRef"
|
||||
node-key="cateId"
|
||||
:data="categories"
|
||||
:default-expanded-keys="['']"
|
||||
@node-click="handleTreeNodeClick"
|
||||
/>
|
||||
</div>
|
||||
</el-splitter-panel>
|
||||
<el-splitter-panel :min="400">
|
||||
<div class="g3-file-main">
|
||||
<div class="g3-header">
|
||||
<div>
|
||||
<el-checkbox
|
||||
v-model="selectAll"
|
||||
:indeterminate="indeterminate"
|
||||
:disabled="loading"
|
||||
@change="toggleSelectAll"
|
||||
>
|
||||
全选
|
||||
</el-checkbox>
|
||||
|
||||
<div>
|
||||
<el-dropdown>
|
||||
<span class="g3-mode">
|
||||
模式:{{ displayMode === 0 ? "卡片视图" : "列表视图" }}
|
||||
<el-icon class="el-icon--right">
|
||||
<G3Icon icon="lucide:chevron-down" />
|
||||
</el-icon>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item @click="handleChangeDisplayMode(0)">
|
||||
卡片视图
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click="handleChangeDisplayMode(1)">
|
||||
列表视图
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
ref="containerRef"
|
||||
v-loading="loading"
|
||||
:element-loading-text="loadingTip"
|
||||
class="g3-main"
|
||||
tabindex="0"
|
||||
@paste="handlePaste"
|
||||
@dragover="handleDragOver"
|
||||
@drop="handleDrop"
|
||||
>
|
||||
<div v-if="fileList.length === 0" class="g3-tip">
|
||||
<el-icon size="64">
|
||||
<G3Icon icon="lucide:cloud-upload" />
|
||||
</el-icon>
|
||||
<div>支持 拖拽文件、粘贴文件、多文件上传</div>
|
||||
</div>
|
||||
|
||||
<div v-else-if="displayMode === 0" class="g3-files">
|
||||
<div v-for="(file, index) in fileList" :key="index" class="g3-file-item">
|
||||
<div class="g3-file-checkbox">
|
||||
<el-checkbox v-model="file._checked" size="large" />
|
||||
</div>
|
||||
<!-- pending:未上传,completed:已上传,failed:上传失败 -->
|
||||
<div v-if="file.mx_cate_id === '0'" class="g3-file-status status-primary">
|
||||
<span>箱门照</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="file._status === 'pending' || file._status === 'failed'"
|
||||
:class="`g3-file-status status-${file._status}`"
|
||||
>
|
||||
<span v-if="file._status === 'pending'">未上传</span>
|
||||
<span v-if="file._status === 'failed'">上传失败</span>
|
||||
</div>
|
||||
<div class="g3-file-preview" @click.stop="handleShowPreview(file)">
|
||||
<template v-if="getFileIcon(file.mx_fileext) === 'image'">
|
||||
<el-image class="g3-file-preview-img" :src="file.mx_mapfilename" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<G3Icon :icon="getFileIcon(file.mx_fileext)" size="88" />
|
||||
</template>
|
||||
</div>
|
||||
<div class="g3-file-info">
|
||||
<el-tooltip :content="file.mx_filename">
|
||||
<div class="g3-file-name">
|
||||
{{ file.mx_filename }}
|
||||
</div>
|
||||
</el-tooltip>
|
||||
<div class="g3-file-details">
|
||||
<div>{{ formatFileSize(file.mx_filesize) }}</div>
|
||||
<div>
|
||||
{{ file.mx_updateuser_id || file.mx_inputuser_id }}
|
||||
<template v-if="file.mx_inputdatetime || file.mx_updatedatetime">
|
||||
|
|
||||
{{ formatTime(file.mx_updatedatetime || file.mx_inputdatetime) }}
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div class="g3-file-actions">
|
||||
<el-button
|
||||
v-if="!props.readonly"
|
||||
type="danger"
|
||||
size="small"
|
||||
circle
|
||||
@click.stop="handleDeleteOne(file)"
|
||||
>
|
||||
<G3Icon icon="lucide:trash-2" />
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
circle
|
||||
@click.stop="handleDownloadFile(file)"
|
||||
>
|
||||
<el-button
|
||||
size="small"
|
||||
type="primary"
|
||||
:disabled="checkedFiles.length === 0 || loading"
|
||||
style="margin-left: 8px"
|
||||
@click="handleDownloadZip"
|
||||
>
|
||||
<el-icon class="el-icon--left">
|
||||
<G3Icon icon="lucide:download" />
|
||||
</el-button>
|
||||
<el-dropdown v-if="!props.readonly" placement="top" trigger="click">
|
||||
<el-button type="info" size="small" circle style="margin-left: 12px">
|
||||
<G3Icon icon="lucide:ellipsis" />
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item @click="handleShowUpdateDialog(file)">
|
||||
修改文件信息
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</el-icon>
|
||||
打包下载
|
||||
</el-button>
|
||||
|
||||
<el-button
|
||||
v-if="!props.readonly"
|
||||
size="small"
|
||||
type="danger"
|
||||
:disabled="checkedFiles.length === 0 || loading"
|
||||
@click="handleBatchDelete()"
|
||||
>
|
||||
<el-icon class="el-icon--left">
|
||||
<G3Icon icon="lucide:trash-2" />
|
||||
</el-icon>
|
||||
批量删除
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<el-dropdown>
|
||||
<span class="g3-mode">
|
||||
模式:{{ displayMode === 0 ? "卡片视图" : "列表视图" }}
|
||||
<el-icon class="el-icon--right">
|
||||
<G3Icon icon="lucide:chevron-down" />
|
||||
</el-icon>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item @click="handleChangeDisplayMode(0)">
|
||||
卡片视图
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click="handleChangeDisplayMode(1)">
|
||||
列表视图
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="g3-table">
|
||||
<div>
|
||||
<vxe-table
|
||||
ref="tableRef"
|
||||
border
|
||||
stripe
|
||||
header-align="center"
|
||||
:data="fileList"
|
||||
height="100%"
|
||||
show-overflow
|
||||
show-header-overflow
|
||||
:sort-config="{
|
||||
trigger: 'cell',
|
||||
}"
|
||||
:filter-config="{
|
||||
showIcon: false,
|
||||
}"
|
||||
size="small"
|
||||
>
|
||||
<vxe-column type="seq" width="50" title=" " align="center" />
|
||||
<vxe-column width="50" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-checkbox v-model="row._checked" size="large" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="mx_filename" title="文件名" sortable align="center">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" link @click.stop="handleShowPreview(row)">
|
||||
{{ row.mx_filename }}
|
||||
</el-button>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="mx_inputdatetime" title="上传日期" sortable align="center">
|
||||
<template #default="{ row }">
|
||||
{{ formatTime(row.mx_inputdatetime) }}
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column
|
||||
field="mx_fileext"
|
||||
title="文件类型"
|
||||
sortable
|
||||
width="100"
|
||||
align="center"
|
||||
/>
|
||||
<vxe-column
|
||||
field="mx_filesize"
|
||||
title="文件大小"
|
||||
sortable
|
||||
width="100"
|
||||
align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
{{ formatFileSize(row.mx_filesize) }}
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column width="180" title="操作">
|
||||
<template #default="{ row }">
|
||||
<div
|
||||
style="
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 28px;
|
||||
"
|
||||
>
|
||||
|
||||
<div
|
||||
ref="containerRef"
|
||||
v-loading="loading"
|
||||
:element-loading-text="loadingTip"
|
||||
class="g3-main"
|
||||
tabindex="0"
|
||||
@paste="handlePaste"
|
||||
@dragover="handleDragOver"
|
||||
@drop="handleDrop"
|
||||
>
|
||||
<div v-if="fileList.length === 0" class="g3-tip">
|
||||
<el-icon size="64">
|
||||
<G3Icon icon="lucide:cloud-upload" />
|
||||
</el-icon>
|
||||
<div>支持 拖拽文件、粘贴文件、多文件上传</div>
|
||||
</div>
|
||||
|
||||
<div v-else-if="displayMode === 0" class="g3-files">
|
||||
<div v-for="(file, index) in fileList" :key="index" class="g3-file-item">
|
||||
<div class="g3-file-checkbox">
|
||||
<el-checkbox v-model="file._checked" size="large" />
|
||||
</div>
|
||||
<!-- pending:未上传,completed:已上传,failed:上传失败 -->
|
||||
<div v-if="file.mx_cate_id === '0'" class="g3-file-status status-primary">
|
||||
<span>箱门照</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="file._status === 'pending' || file._status === 'failed'"
|
||||
:class="`g3-file-status status-${file._status}`"
|
||||
>
|
||||
<span v-if="file._status === 'pending'">未上传</span>
|
||||
<span v-if="file._status === 'failed'">上传失败</span>
|
||||
</div>
|
||||
<div class="g3-file-preview" @click.stop="handleShowPreview(file)">
|
||||
<template v-if="getFileIcon(file.mx_fileext) === 'image'">
|
||||
<el-image class="g3-file-preview-img" :src="file.mx_mapfilename" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<G3Icon :icon="getFileIcon(file.mx_fileext)" size="88" />
|
||||
</template>
|
||||
</div>
|
||||
<div class="g3-file-info">
|
||||
<el-tooltip :content="file.mx_filename">
|
||||
<div class="g3-file-name">
|
||||
{{ file.mx_filename }}
|
||||
</div>
|
||||
</el-tooltip>
|
||||
<div class="g3-file-details">
|
||||
<div>{{ formatFileSize(file.mx_filesize) }}</div>
|
||||
<div>
|
||||
{{ file.mx_updateuser_id || file.mx_inputuser_id }}
|
||||
<template v-if="file.mx_inputdatetime || file.mx_updatedatetime">
|
||||
|
|
||||
{{ formatTime(file.mx_updatedatetime || file.mx_inputdatetime) }}
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div class="g3-file-actions">
|
||||
<el-button
|
||||
v-if="!props.readonly"
|
||||
type="danger"
|
||||
link
|
||||
@click.stop="handleDeleteOne(row)"
|
||||
size="small"
|
||||
circle
|
||||
@click.stop="handleDeleteOne(file)"
|
||||
>
|
||||
删除
|
||||
<G3Icon icon="lucide:trash-2" />
|
||||
</el-button>
|
||||
<el-button type="primary" link @click.stop="handleDownloadFile(row)">
|
||||
下载
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
circle
|
||||
@click.stop="handleDownloadFile(file)"
|
||||
>
|
||||
<G3Icon icon="lucide:download" />
|
||||
</el-button>
|
||||
<el-dropdown v-if="!props.readonly" placement="top" trigger="click">
|
||||
<el-button type="info" link style="margin-left: 12px"> 更多 </el-button>
|
||||
<el-button type="info" size="small" circle style="margin-left: 12px">
|
||||
<G3Icon icon="lucide:ellipsis" />
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item @click="handleShowUpdateDialog(row)">
|
||||
<el-dropdown-item @click="handleShowUpdateDialog(file)">
|
||||
修改文件信息
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</template>
|
||||
</vxe-column>
|
||||
</vxe-table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="g3-table">
|
||||
<div>
|
||||
<vxe-table
|
||||
ref="tableRef"
|
||||
border
|
||||
stripe
|
||||
header-align="center"
|
||||
:data="fileList"
|
||||
height="100%"
|
||||
show-overflow
|
||||
show-header-overflow
|
||||
:sort-config="{
|
||||
trigger: 'cell',
|
||||
}"
|
||||
:filter-config="{
|
||||
showIcon: false,
|
||||
}"
|
||||
size="small"
|
||||
>
|
||||
<vxe-column type="seq" width="50" title=" " align="center" />
|
||||
<vxe-column width="50" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-checkbox v-model="row._checked" size="large" />
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="mx_filename" title="文件名" sortable align="center">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" link @click.stop="handleShowPreview(row)">
|
||||
{{ row.mx_filename }}
|
||||
</el-button>
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column field="mx_inputdatetime" title="上传日期" sortable align="center">
|
||||
<template #default="{ row }">
|
||||
{{ formatTime(row.mx_inputdatetime) }}
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column
|
||||
field="mx_fileext"
|
||||
title="文件类型"
|
||||
sortable
|
||||
width="100"
|
||||
align="center"
|
||||
/>
|
||||
<vxe-column
|
||||
field="mx_filesize"
|
||||
title="文件大小"
|
||||
sortable
|
||||
width="100"
|
||||
align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
{{ formatFileSize(row.mx_filesize) }}
|
||||
</template>
|
||||
</vxe-column>
|
||||
<vxe-column width="180" title="操作">
|
||||
<template #default="{ row }">
|
||||
<div
|
||||
style="
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 28px;
|
||||
"
|
||||
>
|
||||
<el-button
|
||||
v-if="!props.readonly"
|
||||
type="danger"
|
||||
link
|
||||
@click.stop="handleDeleteOne(row)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
<el-button type="primary" link @click.stop="handleDownloadFile(row)">
|
||||
下载
|
||||
</el-button>
|
||||
<el-dropdown v-if="!props.readonly" placement="top" trigger="click">
|
||||
<el-button type="info" link style="margin-left: 12px"> 更多 </el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item @click="handleShowUpdateDialog(row)">
|
||||
修改文件信息
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</template>
|
||||
</vxe-column>
|
||||
</vxe-table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-splitter-panel>
|
||||
</el-splitter>
|
||||
</div>
|
||||
|
||||
<template v-if="!props.readonly" #footer>
|
||||
<el-button type="primary" :disabled="loading" @click="handleClickUpload">
|
||||
<el-button
|
||||
type="primary"
|
||||
:disabled="loading"
|
||||
@click="handleClickUpload"
|
||||
v-if="cateId === '1' || cateId === '2'"
|
||||
>
|
||||
<el-icon class="el-icon--left">
|
||||
<G3Icon icon="lucide:mouse-pointer" />
|
||||
</el-icon>
|
||||
选择
|
||||
</el-button>
|
||||
<el-button type="primary" :disabled="loading" @click="handleSave">
|
||||
<el-button
|
||||
type="primary"
|
||||
:disabled="loading"
|
||||
@click="handleSave"
|
||||
v-if="cateId === '1' || cateId === '2'"
|
||||
>
|
||||
<el-icon class="el-icon--left">
|
||||
<G3Icon icon="lucide:upload" />
|
||||
</el-icon>
|
||||
@@ -1107,7 +1210,7 @@ function handleTreeNodeClick(data) {
|
||||
<el-input v-model="updateFile.mx_bz" />
|
||||
<template v-if="updateFile._status !== 'pending'">
|
||||
<div style="margin: 4px 0px">是否为箱门照:</div>
|
||||
<el-switch v-model="updateFile.mx_cate_id" active-value="0" inactive-value="1" />
|
||||
<el-switch v-model="updateFile._main" :active-value="true" :inactive-value="false" />
|
||||
</template>
|
||||
</div>
|
||||
<template #footer>
|
||||
@@ -1143,7 +1246,8 @@ function handleTreeNodeClick(data) {
|
||||
}
|
||||
|
||||
.g3-category {
|
||||
width: 140px;
|
||||
border-right: 1px solid #ddd;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.g3-file-main {
|
||||
@@ -1151,6 +1255,7 @@ function handleTreeNodeClick(data) {
|
||||
padding: 0px 8px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.g3-main {
|
||||
|
||||
@@ -146,21 +146,12 @@ export async function httpUpload(url, file, extraData = {}, config = {}) {
|
||||
}
|
||||
|
||||
async function appendFile(formData, file) {
|
||||
// ✅ 已经是 File / Blob
|
||||
if (file instanceof File || file instanceof Blob) {
|
||||
formData.append("file", file);
|
||||
return;
|
||||
}
|
||||
|
||||
// ✅ 处理 blob:http://xxx
|
||||
if (typeof file === "string" && file.startsWith("blob:")) {
|
||||
const blob = await fetch(file).then((res) => res.blob());
|
||||
|
||||
formData.append("file", blob);
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Error("不支持的文件类型:" + typeof file);
|
||||
throw new Error("上传只支持 File 或 Blob,不支持字符串");
|
||||
}
|
||||
|
||||
export function reset401Flag() {
|
||||
|
||||
@@ -195,7 +195,7 @@ async function downloadAll() {
|
||||
const containerCountMap = new Map();
|
||||
|
||||
for (const item of records) {
|
||||
const fileRes = await loadDataApi("bf_file", `father='${item.b_id}'`, "mx_xh asc");
|
||||
const fileRes = await loadDataApi("bf_file", `father='${item.b_id}'`, "mx_cate_id asc,mx_xh asc");
|
||||
|
||||
const files = (fileRes.success && fileRes.data) || [];
|
||||
totalFiles += files.length;
|
||||
@@ -320,7 +320,7 @@ async function downloadSlice() {
|
||||
|
||||
containerCountMap.set(containerNo, count + 1);
|
||||
|
||||
const folderPromise = loadDataApi("bf_file", `father='${item.b_id}'`, "mx_xh asc");
|
||||
const folderPromise = loadDataApi("bf_file", `father='${item.b_id}'`, "mx_cate_id asc,mx_xh asc");
|
||||
|
||||
allTasks.push(async () => {
|
||||
const fileRes = await folderPromise;
|
||||
@@ -403,7 +403,7 @@ async function downloadChunk() {
|
||||
|
||||
pageRecords.forEach((item) => {
|
||||
allPageTasks.push(async () => {
|
||||
const fileRes = await loadDataApi("bf_file", `father='${item.b_id}'`, "mx_xh asc");
|
||||
const fileRes = await loadDataApi("bf_file", `father='${item.b_id}'`, "mx_cate_id asc,mx_xh asc");
|
||||
|
||||
if (!fileRes.success) {
|
||||
console.error("获取附件失败:", item.b_container_no);
|
||||
|
||||
@@ -57,9 +57,13 @@ const fileCategories = [
|
||||
cateId: "0",
|
||||
},
|
||||
{
|
||||
label: "其它照片",
|
||||
label: "装货照片",
|
||||
cateId: "1",
|
||||
},
|
||||
{
|
||||
label: "发货单、签收单、过磅单",
|
||||
cateId: "2",
|
||||
},
|
||||
];
|
||||
|
||||
const showPreview = ref(false);
|
||||
@@ -291,7 +295,7 @@ async function handleDownloadOne(data) {
|
||||
});
|
||||
|
||||
try {
|
||||
const fileRes = await loadDataApi("bf_file", `father = ${data.b_id}`, "mx_xh asc");
|
||||
const fileRes = await loadDataApi("bf_file", `father = ${data.b_id}`, "mx_cate_id asc,mx_xh asc");
|
||||
|
||||
if (!fileRes.success) {
|
||||
ElMessage.error("获取附件失败");
|
||||
@@ -365,7 +369,7 @@ async function handleDownloadBatch() {
|
||||
const containerCountMap = new Map();
|
||||
|
||||
for (const item of records) {
|
||||
const fileRes = await loadDataApi("bf_file", `father='${item.b_id}'`, "mx_xh asc");
|
||||
const fileRes = await loadDataApi("bf_file", `father='${item.b_id}'`, "mx_cate_id asc,mx_xh asc");
|
||||
|
||||
const files = (fileRes.success && fileRes.data) || [];
|
||||
totalFiles += files.length;
|
||||
|
||||
Reference in new issue
Block a user