<template>
|
<div class="main-box">
|
<TreeFilter
|
label="name"
|
:title="t('ThreeMap.companyList')"
|
:request-api="getUserDepartment"
|
:labeltext="t('SearchForm.inputPlaceholder')"
|
:zhankai="t('treeFilter.expandAll')"
|
:zhedie="t('treeFilter.collapseAll')"
|
@change="changeTreeFilter"
|
/>
|
<div class="table-box">
|
<ProTable
|
ref="proTable"
|
:columns="columns"
|
:request-api="getThreeModel"
|
:init-param="initParam"
|
:search-col="{ xs: 1, sm: 1, md: 2, lg: 3, xl: 3 }"
|
>
|
<template #tableHeader>
|
<el-button type="primary" v-if="AUTH.add" plain :icon="CirclePlus" @click="openDrawer(t('Config.add'))">
|
{{ t("ThreeModel.addModelFolder") }}
|
</el-button>
|
</template>
|
<!-- 表格操作 -->
|
<template #operation="scope">
|
<el-button type="primary" link :icon="View" @click="openDrawer(t('Config.view'), scope.row)">{{
|
t("Config.view")
|
}}</el-button>
|
<el-button type="primary" v-if="AUTH.edit" link :icon="EditPen" @click="openDrawer(t('Config.update'), scope.row)">
|
{{ t("Config.update") }}
|
</el-button>
|
<el-button type="primary" v-if="AUTH.add" link :icon="CirclePlus" @click="openUploadDialog(scope.row)">
|
{{ t("ThreeModel.addModel") }}
|
</el-button>
|
</template>
|
</ProTable>
|
<ThreeMapDrawer ref="drawerRef" />
|
<ImportExcel ref="dialogRef" />
|
|
<!-- 上传模型对话框 -->
|
<el-dialog
|
v-model="uploadDialogVisible"
|
:title="t('ThreeModel.uploadModel')"
|
width="600px"
|
:close-on-click-modal="!uploading"
|
:show-close="!uploading"
|
:destroy-on-close="true"
|
>
|
<div class="upload-section">
|
<el-alert type="info" show-icon :closable="false" :title="t('ThreeModel.uploadTip')" />
|
<div style="margin-top: 16px">
|
<input ref="fileInputRef" type="file" multiple @change="onFilesSelected" />
|
</div>
|
<div v-if="selectedFiles.length" style="margin-top: 12px">
|
<div
|
style="
|
max-height: 180px;
|
padding: 8px;
|
overflow: auto;
|
border: 1px solid var(--el-border-color);
|
border-radius: 4px;
|
"
|
>
|
<div v-for="(f, idx) in selectedFiles" :key="idx" style="display: flex; gap: 8px; justify-content: space-between">
|
<span style="flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap">{{ f.name }}</span>
|
<span style="color: var(--el-text-color-secondary)">{{ (f.size / 1024 / 1024).toFixed(2) }} MB</span>
|
</div>
|
</div>
|
</div>
|
<div v-if="uploading" style="margin-top: 16px">
|
<el-progress :percentage="uploadProgress" :stroke-width="18" :indeterminate="true" />
|
</div>
|
</div>
|
<template #footer>
|
<span class="dialog-footer">
|
<el-button @click="handleUploadCancel" :disabled="uploading">{{ t("ThreeModel.cancel") }}</el-button>
|
<el-button type="primary" @click="submitUpload" :disabled="!selectedFiles.length || uploading">{{
|
t("ThreeModel.submit")
|
}}</el-button>
|
</span>
|
</template>
|
</el-dialog>
|
</div>
|
</div>
|
</template>
|
|
<script setup lang="tsx" name="useTreeFilter">
|
import { ref, reactive } from "vue";
|
import { User } from "@/api/interface";
|
import { useI18n } from "vue-i18n";
|
import ProTable from "@/components/ProTable/index.vue";
|
import TreeFilter from "@/components/TreeFilter/index.vue";
|
import ImportExcel from "@/components/ImportExcel/index.vue";
|
import ThreeMapDrawer from "@/views/proTable/components/hxzk/map/ThreeModel.vue";
|
import { ProTableInstance, ColumnProps } from "@/components/ProTable/interface";
|
import { EditPen, View, CirclePlus } from "@element-plus/icons-vue";
|
import { getUserDepartment } from "@/api/modules/hxzk/user/user";
|
import { getThreeModel, editThreeModel, addThreeModel, uploadThreeModel } from "@/api/modules/hxzk/map/threemap";
|
import { ElMessage } from "element-plus";
|
import { useAuthButtons } from "@/hooks/useAuthButtons";
|
|
const { t } = useI18n({ useScope: "global" });
|
const { BUTTONS } = useAuthButtons();
|
// 简化模板中的访问,避免类型推断问题
|
const AUTH = BUTTONS as unknown as Record<string, boolean>;
|
|
// ProTable 实例
|
const proTable = ref<ProTableInstance>();
|
|
// 如果表格需要初始化请求参数,直接定义传给 ProTable(之后每次请求都会自动带上该参数,此参数更改之后也会一直带上,改变此参数会自动刷新表格数据)
|
const initParam = reactive({ departmentId: "" });
|
|
// 树形筛选切换
|
const changeTreeFilter = (val: string) => {
|
proTable.value!.pageable.pageNum = 1;
|
initParam.departmentId = val;
|
};
|
|
// 表格配置项
|
const columns = reactive<ColumnProps<User.ResUserList>[]>([
|
{ type: "index", label: "", width: 80 },
|
{ prop: "filename", label: t("ThreeModel.companyModelFolder") },
|
{ prop: "lng", label: t("ThreeModel.longitude") },
|
{ prop: "lat", label: t("ThreeModel.latitude") },
|
{ prop: "alt", label: t("ThreeModel.height") },
|
{ prop: "operation", label: t("Config.operation"), width: 260 }
|
]);
|
|
// 批量添加用户
|
const dialogRef = ref<InstanceType<typeof ImportExcel> | null>(null);
|
|
// 打开 drawer(新增、查看、编辑)
|
const drawerRef = ref<InstanceType<typeof ThreeMapDrawer> | null>(null);
|
const openDrawer = (title: string, row: Partial<User.ResUserList> = {}) => {
|
const params = {
|
title,
|
initParam,
|
isView: title === t("Config.view"),
|
row: { ...row },
|
api: title === t("Config.add") ? addThreeModel : title === t("Config.update") ? editThreeModel : undefined,
|
getTableList: proTable.value?.getTableList
|
};
|
// 该抽屉组件的类型定义将 initParam 标注为 Partial<User.ResUserList>
|
// 这里传入的是包含 departmentId 的对象,运行无影响,做一次类型断言规避不必要的校验
|
drawerRef.value?.acceptParams(params as any);
|
};
|
|
// 上传模型:对话框与逻辑
|
const uploadDialogVisible = ref(false);
|
const selectedFiles = ref<File[]>([]);
|
const uploading = ref(false);
|
const uploadProgress = ref(0);
|
const fileInputRef = ref<HTMLInputElement | null>(null);
|
const currentRow = ref<Partial<User.ResUserList> | null>(null);
|
const addFileName = ref();
|
const openUploadDialog = (row?: Partial<User.ResUserList>) => {
|
currentRow.value = row || null;
|
selectedFiles.value = [];
|
uploadProgress.value = 0;
|
uploading.value = false;
|
uploadDialogVisible.value = true;
|
addFileName.value = currentRow.value.company + "_" + currentRow.value.filename;
|
console.log(addFileName.value);
|
// reset native input value so selecting same files again still triggers change
|
requestAnimationFrame(() => {
|
if (fileInputRef.value) fileInputRef.value.value = "";
|
});
|
};
|
|
const onFilesSelected = (e: Event) => {
|
const input = e.target as HTMLInputElement;
|
if (!input.files) return;
|
selectedFiles.value = Array.from(input.files);
|
};
|
|
const handleUploadCancel = () => {
|
if (uploading.value) return;
|
uploadDialogVisible.value = false;
|
};
|
|
const submitUpload = async () => {
|
if (!selectedFiles.value.length || uploading.value) return;
|
const form = new FormData();
|
// 如果后端需要公司/模型目录等信息,可在此一并附带
|
if (initParam.departmentId) form.append("departmentId", initParam.departmentId);
|
if (addFileName.value) {
|
form.append("filename", addFileName.value);
|
}
|
if (currentRow.value && (currentRow.value as any).id) form.append("id", String((currentRow.value as any).id));
|
selectedFiles.value.forEach(file => form.append("files", file));
|
|
uploading.value = true;
|
uploadProgress.value = 0;
|
try {
|
await uploadThreeModel(form, (evt: ProgressEvent) => {
|
if (evt.total) {
|
uploadProgress.value = Math.min(99, Math.round((evt.loaded / evt.total) * 500));
|
}
|
});
|
uploadProgress.value = 100;
|
ElMessage.success(t("ThreeModel.uploadSuccess"));
|
uploadDialogVisible.value = false;
|
// 刷新表格
|
proTable.value?.getTableList?.();
|
} catch (err) {
|
ElMessage.error(t("ThreeModel.uploadFailed"));
|
} finally {
|
uploading.value = false;
|
}
|
};
|
</script>
|