Просмотр исходного кода

Merge branch 'qhse_file' of shuzhihua/pms-iot-vue into master

yanghao 1 месяц назад
Родитель
Сommit
ef76e53a37

+ 154 - 189
src/components/UploadFile/src/FileUpload.vue

@@ -7,18 +7,16 @@
         :loading="uploading"
         @click="handleFileUploadClick"
         class="upload-btn"
-        style="margin-right: 10px"
-      >
+        style="margin-right: 10px">
         <el-icon><Document /></el-icon>
         {{ uploading ? '文件上传中...' : '选择文件' }}
       </el-button>
       <el-button
-        v-if="showFolderButton"
+        v-if="canShowFolderButton"
         type="success"
         :loading="uploading"
         @click="handleFolderUploadClick"
-        class="upload-btn"
-      >
+        class="upload-btn">
         <el-icon><Folder /></el-icon>
         {{ uploading ? '文件夹上传中...' : '选择文件夹' }}
       </el-button>
@@ -30,24 +28,15 @@
         accept="*"
         :webkitdirectory="isFolderMode"
         :directory="isFolderMode"
-        @change="handleFileChange"
-      />
+        @change="handleFileChange" />
       <p class="upload-hint">{{ uploadHintText }}</p>
     </div>
 
     <!-- 上传列表与进度 - 保持不变 -->
     <el-card v-if="uploadList.length > 0" class="upload-list-card">
-      <el-table
-        :data="uploadList"
-        border
-        style="width: 100%; margin-bottom: 0px;"
-      >
+      <el-table :data="uploadList" border style="width: 100%; margin-bottom: 0px">
         <!-- 表格内容保持不变 -->
-        <el-table-column
-          prop="name"
-          label="文件名/文件夹名"
-          width="300"
-        >
+        <el-table-column prop="name" label="文件名/文件夹名" width="300">
           <template #default="scope">
             <div class="file-name">
               <el-icon v-if="scope.row.type === 'folder'"><Folder /></el-icon>
@@ -56,55 +45,37 @@
             </div>
           </template>
         </el-table-column>
-        <el-table-column
-          prop="size"
-          label="大小"
-          width="120"
-          :formatter="formatSize"
-        />
-        <el-table-column
-          prop="type"
-          label="类型"
-          width="120"
-        >
+        <el-table-column prop="size" label="大小" width="120" :formatter="formatSize" />
+        <el-table-column prop="type" label="类型" width="120">
           <template #default="scope">
             <el-tag size="small" :type="scope.row.type === 'folder' ? 'info' : 'primary'">
               {{ scope.row.type || '文件夹' }}
             </el-tag>
           </template>
         </el-table-column>
-        <el-table-column
-          prop="progress"
-          label="进度"
-          min-width="200"
-        >
+        <el-table-column prop="progress" label="进度" min-width="200">
           <template #default="scope">
             <el-progress
               :percentage="scope.row.progress"
               :status="getProgressStatus(scope.row.status)"
-              stroke-width="6"
-            />
+              stroke-width="6" />
           </template>
         </el-table-column>
-        <el-table-column
-          label="操作"
-          width="120"
-        >
+        <el-table-column label="操作" width="120">
           <template #default="scope">
             <el-button
               v-if="scope.row.status === 'uploading'"
               size="small"
               type="text"
               @click.stop="handleCancelUpload(scope.row)"
-              text
-            >
+              text>
               取消
             </el-button>
             <el-icon v-if="scope.row.status === 'success'" color="#10b981">
-<!--              <CheckCircle />-->
+              <!--              <CheckCircle />-->
             </el-icon>
             <el-icon v-if="scope.row.status === 'error'" color="#ef4444">
-<!--              <CloseCircle />-->
+              <!--              <CloseCircle />-->
             </el-icon>
             <span v-if="scope.row.status === 'cancelled'">已取消</span>
           </template>
@@ -113,11 +84,7 @@
     </el-card>
 
     <!-- 错误提示 - 保持不变 -->
-    <el-dialog
-      v-model="showErrorModal"
-      title="上传错误"
-      @close="showErrorModal = false"
-    >
+    <el-dialog v-model="showErrorModal" title="上传错误" @close="showErrorModal = false">
       <p>{{ errorMessage }}</p>
       <template #footer>
         <el-button @click="showErrorModal = false">确定</el-button>
@@ -127,17 +94,13 @@
 </template>
 
 <script setup>
-import { ref, computed, onMounted } from 'vue';
-import {
-  Upload,
-  Folder,
-  Document,
-} from '@element-plus/icons-vue';
+import { ref, computed, onMounted } from 'vue'
+import { Upload, Folder, Document } from '@element-plus/icons-vue'
 
 // 组件参数 - 保持不变
 const props = defineProps({
-  deviceId:{
-    type: String,
+  deviceId: {
+    type: String
   },
   allowFolderUpload: {
     type: Boolean,
@@ -154,139 +117,142 @@ const props = defineProps({
   },
   uploadUrl: {
     type: String,
-    default: import.meta.env.VITE_BASE_URL+'/admin-api/rq/file/upload'
+    default: import.meta.env.VITE_BASE_URL + '/admin-api/rq/file/upload'
   }
-});
+})
 
 // 组件事件 - 保持不变
-const emit = defineEmits(['uploadSuccess', 'uploadError', 'uploadComplete']);
+const emit = defineEmits(['uploadSuccess', 'uploadError', 'uploadComplete'])
 
 // 新增:用于区分当前是文件还是文件夹上传模式
-const isFolderMode = ref();
+const isFolderMode = ref()
 
 // 状态管理 - 保持不变
-const fileInput = ref(null);
-const uploading = ref(false);
-const uploadList = ref([]);
-const showErrorModal = ref(false);
-const errorMessage = ref('');
-const uploadTasks = ref(new Map());
+const fileInput = ref(null)
+const uploading = ref(false)
+const uploadList = ref([])
+const showErrorModal = ref(false)
+const errorMessage = ref('')
+const uploadTasks = ref(new Map())
 
 // 判断父组件是否传递了 showFolderButton 属性(而非判断其值)
-const hasShowFolderButton = Object.prototype.hasOwnProperty.call(props, 'showFolderButton');
+const hasShowFolderButton = Object.prototype.hasOwnProperty.call(props, 'showFolderButton')
+
+const canShowFolderButton = computed(() => {
+  return props.allowFolderUpload && props.showFolderButton
+})
 
 // 根据是否传递属性决定显示的提示文本
 const uploadHintText = computed(() => {
-  return hasShowFolderButton
-    ? '文件大小不能超过50M'
-    : '单个文件夹总大小不能超过500M';
-});
+  return hasShowFolderButton ? '文件大小不能超过50M' : '单个文件夹总大小不能超过500M'
+})
 
 // 获取进度条状态 - 保持不变
 const getProgressStatus = (status) => {
-  switch(status) {
+  switch (status) {
     case 'success':
-      return 'success';
+      return 'success'
     case 'error':
-      return 'exception';
+      return 'exception'
     case 'uploading':
-      return 'processing';
+      return 'processing'
     default:
-      return '';
+      return ''
   }
-};
+}
 
 // 格式化文件大小 - 保持不变
 const formatSize = (row, column, bytes) => {
-  if (bytes === 0) return '0 Bytes';
-  const k = 1024;
-  const sizes = ['Bytes', 'KB', 'MB', 'GB'];
-  const i = Math.floor(Math.log(bytes) / Math.log(k));
-  return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
-};
+  if (bytes === 0) return '0 Bytes'
+  const k = 1024
+  const sizes = ['Bytes', 'KB', 'MB', 'GB']
+  const i = Math.floor(Math.log(bytes) / Math.log(k))
+  return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
+}
 
 // 计算文件夹总大小 - 保持不变
 const calculateFolderSize = (files) => {
-  let totalSize = 0;
-  files.forEach(file => {
-    totalSize += file.size;
-  });
-  return totalSize;
-};
+  let totalSize = 0
+  files.forEach((file) => {
+    totalSize += file.size
+  })
+  return totalSize
+}
 
 // 新增:文件上传按钮点击事件
 const handleFileUploadClick = () => {
-  if (uploading.value) return;
+  if (uploading.value) return
   if (fileInput.value) {
-    fileInput.value.value = '';
+    fileInput.value.value = ''
   }
-  isFolderMode.value = false; // 切换到文件模式
+  isFolderMode.value = false // 切换到文件模式
   // 强制DOM更新后再触发点击
   nextTick(() => {
-    fileInput.value.click();
-  });
-};
+    fileInput.value.click()
+  })
+}
 
 // 新增:文件夹上传按钮点击事件
 const handleFolderUploadClick = () => {
-  if (uploading.value) return;
+  if (uploading.value) return
   if (fileInput.value) {
-    fileInput.value.value = '';
+    fileInput.value.value = ''
   }
-  isFolderMode.value = true; // 切换到文件夹模式
+  isFolderMode.value = true // 切换到文件夹模式
   if (!props.allowFolderUpload) {
-    errorMessage.value = '不允许上传文件夹';
-    showErrorModal.value = true;
-    return;
+    errorMessage.value = '不允许上传文件夹'
+    showErrorModal.value = true
+    return
   }
   // 强制DOM更新后再触发点击
   nextTick(() => {
-    fileInput.value.click();
-  });
-};
+    fileInput.value.click()
+  })
+}
 
 // 处理文件选择 - 保持不变
 const handleFileChange = async (e) => {
-  const files = Array.from(e.target.files);
-  if (files.length === 0) return;
+  const files = Array.from(e.target.files)
+  if (files.length === 0) return
 
   // 清空input值,允许重复选择同一文件
-  e.target.value = '';
+  e.target.value = ''
 
   // 检查是否包含文件夹(根据当前模式判断)
-  const hasFolders = isFolderMode.value && files.some(file => file.webkitRelativePath.includes('/'));
+  const hasFolders =
+    isFolderMode.value && files.some((file) => file.webkitRelativePath.includes('/'))
 
   if (hasFolders) {
     // 按文件夹分组
-    const folderMap = new Map();
+    const folderMap = new Map()
 
-    files.forEach(file => {
+    files.forEach((file) => {
       // 提取文件夹路径
-      const relativePath = file.webkitRelativePath;
-      const folderPath = relativePath.substring(0, relativePath.lastIndexOf('/') + 1);
+      const relativePath = file.webkitRelativePath
+      const folderPath = relativePath.substring(0, relativePath.lastIndexOf('/') + 1)
 
       if (!folderMap.has(folderPath)) {
-        folderMap.set(folderPath, []);
+        folderMap.set(folderPath, [])
       }
-      folderMap.get(folderPath).push(file);
-    });
+      folderMap.get(folderPath).push(file)
+    })
 
     // 检查每个文件夹大小
     for (const [folderPath, folderFiles] of folderMap) {
-      const folderSize = calculateFolderSize(folderFiles);
-      const folderSizeMB = folderSize / (1024 * 1024);
+      const folderSize = calculateFolderSize(folderFiles)
+      const folderSizeMB = folderSize / (1024 * 1024)
 
       if (folderSizeMB > props.maxFolderSize) {
-        errorMessage.value = `文件夹 "${folderPath}" 大小超过${props.maxFolderSize}M限制,无法上传`;
-        showErrorModal.value = true;
-        return;
+        errorMessage.value = `文件夹 "${folderPath}" 大小超过${props.maxFolderSize}M限制,无法上传`
+        showErrorModal.value = true
+        return
       }
     }
 
     // 添加文件夹到上传列表
     folderMap.forEach((folderFiles, folderPath) => {
-      const folderSize = calculateFolderSize(folderFiles);
-      const uid = Date.now() + Math.random().toString(36).substr(2, 9);
+      const folderSize = calculateFolderSize(folderFiles)
+      const uid = Date.now() + Math.random().toString(36).substr(2, 9)
 
       uploadList.value.push({
         uid,
@@ -296,11 +262,11 @@ const handleFileChange = async (e) => {
         progress: 0,
         status: 'wait',
         files: folderFiles
-      });
-    });
+      })
+    })
   } else {
     // 单个或多个文件
-    files.forEach(file => {
+    files.forEach((file) => {
       uploadList.value.push({
         uid: Date.now() + Math.random().toString(36).substr(2, 9),
         name: file.name,
@@ -309,156 +275,155 @@ const handleFileChange = async (e) => {
         progress: 0,
         status: 'wait',
         files: [file]
-      });
-    });
+      })
+    })
   }
 
   // 开始上传
-  await startUpload();
-};
+  await startUpload()
+}
 
 // 开始上传 - 保持不变
 const startUpload = async () => {
-  uploading.value = true;
+  uploading.value = true
 
   // 过滤出等待上传的文件/文件夹
-  const pendingFiles = uploadList.value.filter(item => item.status === 'wait');
+  const pendingFiles = uploadList.value.filter((item) => item.status === 'wait')
 
   for (const fileItem of pendingFiles) {
     try {
       // 创建FormData
-      const formData = new FormData();
+      const formData = new FormData()
 
       // 添加文件
-      fileItem.files.forEach(file => {
-        formData.append('files', file, file.webkitRelativePath || file.name);
-      });
+      fileItem.files.forEach((file) => {
+        formData.append('files', file, file.webkitRelativePath || file.name)
+      })
 
       // 添加文件夹标识和路径
       if (fileItem.type === 'folder') {
-        formData.append('isFolder', 'true');
-        formData.append('folderPath', fileItem.name);
+        formData.append('isFolder', 'true')
+        formData.append('folderPath', fileItem.name)
       }
 
       // 创建上传任务
-      const xhr = new XMLHttpRequest();
-      uploadTasks.value.set(fileItem.uid, xhr);
+      const xhr = new XMLHttpRequest()
+      uploadTasks.value.set(fileItem.uid, xhr)
 
       // 监听进度
       xhr.upload.addEventListener('progress', (e) => {
         if (e.lengthComputable) {
-          const percent = Math.round((e.loaded / e.total) * 100);
-          const index = uploadList.value.findIndex(item => item.uid === fileItem.uid);
+          const percent = Math.round((e.loaded / e.total) * 100)
+          const index = uploadList.value.findIndex((item) => item.uid === fileItem.uid)
           if (index !== -1) {
-            uploadList.value[index].progress = percent;
-            uploadList.value[index].status = 'uploading';
+            uploadList.value[index].progress = percent
+            uploadList.value[index].status = 'uploading'
           }
         }
-      });
+      })
 
       // 监听完成
       xhr.addEventListener('load', () => {
-        const index = uploadList.value.findIndex(item => item.uid === fileItem.uid);
+        const index = uploadList.value.findIndex((item) => item.uid === fileItem.uid)
         if (index !== -1) {
           if (xhr.status >= 200 && xhr.status < 300) {
-            uploadList.value[index].status = 'success';
-            uploadList.value[index].progress = 100;
+            uploadList.value[index].status = 'success'
+            uploadList.value[index].progress = 100
             emit('uploadSuccess', {
               uid: fileItem.uid,
               name: fileItem.name,
               response: JSON.parse(xhr.responseText)
-            });
+            })
           } else {
-            uploadList.value[index].status = 'error';
+            uploadList.value[index].status = 'error'
             emit('uploadError', {
               uid: fileItem.uid,
               name: fileItem.name,
               error: new Error(`上传失败: ${xhr.statusText}`)
-            });
+            })
           }
         }
-        uploadTasks.value.delete(fileItem.uid);
-      });
+        uploadTasks.value.delete(fileItem.uid)
+      })
 
       // 监听错误
       xhr.addEventListener('error', () => {
-        const index = uploadList.value.findIndex(item => item.uid === fileItem.uid);
+        const index = uploadList.value.findIndex((item) => item.uid === fileItem.uid)
         if (index !== -1) {
-          uploadList.value[index].status = 'error';
+          uploadList.value[index].status = 'error'
           emit('uploadError', {
             uid: fileItem.uid,
             name: fileItem.name,
             error: new Error('网络错误,上传失败')
-          });
+          })
         }
-        uploadTasks.value.delete(fileItem.uid);
-      });
+        uploadTasks.value.delete(fileItem.uid)
+      })
 
       // 发送请求
-      xhr.open('POST', props.uploadUrl, true);
-      xhr.setRequestHeader('tenant-id', 1);
-      xhr.setRequestHeader('device-id', props.deviceId);
-      xhr.send(formData);
-
+      xhr.open('POST', props.uploadUrl, true)
+      xhr.setRequestHeader('tenant-id', 1)
+      xhr.setRequestHeader('device-id', props.deviceId)
+      xhr.send(formData)
     } catch (error) {
-      console.error('上传失败', error);
-      const index = uploadList.value.findIndex(item => item.uid === fileItem.uid);
+      console.error('上传失败', error)
+      const index = uploadList.value.findIndex((item) => item.uid === fileItem.uid)
       if (index !== -1) {
-        uploadList.value[index].status = 'error';
+        uploadList.value[index].status = 'error'
       }
       emit('uploadError', {
         uid: fileItem.uid,
         name: fileItem.name,
         error
-      });
+      })
     }
   }
 
   // 检查是否所有上传都已完成
   const checkUploadComplete = setInterval(() => {
-    const allCompleted = uploadList.value.every(item =>
-      item.status === 'success' || item.status === 'error' || item.status === 'cancelled'
-    );
+    const allCompleted = uploadList.value.every(
+      (item) => item.status === 'success' || item.status === 'error' || item.status === 'cancelled'
+    )
 
     if (allCompleted) {
-      clearInterval(checkUploadComplete);
-      uploading.value = false;
-      emit('uploadComplete');
+      clearInterval(checkUploadComplete)
+      uploading.value = false
+      emit('uploadComplete')
     }
-  }, 1000);
-};
+  }, 1000)
+}
 
 // 取消上传 - 保持不变
 const handleCancelUpload = (fileItem) => {
-  const xhr = uploadTasks.value.get(fileItem.uid);
+  const xhr = uploadTasks.value.get(fileItem.uid)
   if (xhr) {
-    xhr.abort();
-    uploadTasks.value.delete(fileItem.uid);
+    xhr.abort()
+    uploadTasks.value.delete(fileItem.uid)
   }
 
-  const index = uploadList.value.findIndex(item => item.uid === fileItem.uid);
+  const index = uploadList.value.findIndex((item) => item.uid === fileItem.uid)
   if (index !== -1) {
-    uploadList.value[index].status = 'cancelled';
+    uploadList.value[index].status = 'cancelled'
   }
 
   // 检查是否还有正在上传的任务
-  const hasUploading = uploadList.value.some(item => item.status === 'uploading');
+  const hasUploading = uploadList.value.some((item) => item.status === 'uploading')
   if (!hasUploading) {
-    uploading.value = false;
+    uploading.value = false
   }
-};
+}
 
 // 组件挂载时设置样式 - 保持不变
 onMounted(() => {
-  const container = document.querySelector('.file-upload-container');
+  const container = document.querySelector('.file-upload-container')
   if (container) {
-    container.style.height = '8vh';
-    container.style.display = 'flex';
-    container.style.flexDirection = 'column';
-    container.style.padding = '20px';
-    container.style.boxSizing = 'border-box';
+    container.style.height = '8vh'
+    container.style.display = 'flex'
+    container.style.flexDirection = 'column'
+    container.style.padding = '20px'
+    container.style.boxSizing = 'border-box'
   }
-});
+})
 </script>
 
 <style scoped>

+ 1 - 1
src/views/pms/device/monitor/index.vue

@@ -47,7 +47,7 @@
                 class="!w-240px">
                 <el-option label="全部" value="all" />
                 <el-option label="网关设备" value="gateway" />
-                <el-option label="中行北斗" value="zhbd" />
+                <el-option label="中航北斗" value="zhbd" />
               </el-select>
             </el-form-item>
             <el-form-item class="float-right !mr-0 !mb-0">

+ 1 - 0
src/views/pms/qhse/data/IotInfoFormTree.vue

@@ -12,6 +12,7 @@
       <el-form-item :label="t('fileInfo.appendix')" prop="filePath">
         <file-upload
           style="height: 300px"
+          :allowFolderUpload="false"
           @uploadSuccess="handleUploadSuccess"
           @uploadError="handleUploadError"
           @uploadComplete="handleUploadComplete"

+ 126 - 11
src/views/pms/qhse/data/index.vue

@@ -15,6 +15,22 @@
       :inline="true"
       label-width="68px">
       <div class="flex items-center gap-8">
+        <div class="mode-tabs flex items-center rounded-lg bg-[#f3f4f6] p-1">
+          <button
+            type="button"
+            class="mode-tab"
+            :class="{ active: fileViewMode === 'folder' }"
+            @click="switchViewMode('folder')">
+            文件夹列表
+          </button>
+          <button
+            type="button"
+            class="mode-tab"
+            :class="{ active: fileViewMode === 'file' }"
+            @click="switchViewMode('file')">
+            文件列表
+          </button>
+        </div>
         <el-form-item :label="t('file.name')" prop="filename">
           <el-input
             v-model="queryParams.filename"
@@ -83,6 +99,7 @@
         :loading="formLoading"
         :data="list"
         :stripe="true"
+        height="calc(100% - 40px)"
         :show-overflow-tooltip="true"
         settings-cache-key="system-tree-file-list"
         @row-dblclick="inContent"
@@ -184,6 +201,17 @@
           </template>
         </zm-table-column>
       </zm-table>
+
+      <div v-if="fileViewMode === 'file'" class="mt-4 flex justify-end">
+        <el-pagination
+          :current-page="queryParams.pageNo"
+          :page-size="queryParams.pageSize"
+          :total="total"
+          background
+          layout="total, sizes, prev, pager, next, jumper"
+          @size-change="handlePageSizeChange"
+          @current-change="handlePageChange" />
+      </div>
     </div>
   </div>
 
@@ -251,11 +279,68 @@ interface IotInfoFormTreeExpose {
 }
 
 const uploadLoading = ref(false)
+const fileViewMode = ref<'folder' | 'file'>('folder')
 const currentTreeOriginId = ref<TreeKey | undefined>(undefined)
 const rootDeptOriginId = ref<TreeKey | undefined>(undefined)
 const rootNodeName = ref('')
 const treeOriginIdMap = ref<Record<string, TreeKey | undefined>>({})
 
+const total = ref(0)
+
+const normalizeListData = (data: any) => {
+  if (fileViewMode.value !== 'file') {
+    total.value = 0
+    return Array.isArray(data) ? data : []
+  }
+
+  if (Array.isArray(data)) {
+    total.value = data.length
+    return data
+  }
+
+  if (Array.isArray(data?.list)) {
+    total.value = Number(data.total ?? data.list.length ?? 0)
+    return data.list
+  }
+
+  total.value = 0
+  return []
+}
+
+const getCurrentListApi = () => {
+  return fileViewMode.value === 'folder'
+    ? DataLibraryApi.DataLibraryApi.getChildContentFile
+    : DataLibraryApi.DataLibraryApi.getAllChildContentFile
+}
+
+const switchViewMode = async (mode: 'folder' | 'file') => {
+  if (fileViewMode.value === mode) return
+
+  fileViewMode.value = mode
+  queryParams.filename = ''
+  queryParams.allName = null
+  if (mode === 'file') {
+    queryParams.pageNo = 1
+    queryParams.pageSize = 10
+  }
+  if (mode === 'file' && queryParams.classId == null && topNodeId.value != null) {
+    queryParams.classId = topNodeId.value
+  }
+
+  await getList()
+}
+
+const handlePageSizeChange = async (size: number) => {
+  queryParams.pageSize = size
+  queryParams.pageNo = 1
+  await getList()
+}
+
+const handlePageChange = async (page: number) => {
+  queryParams.pageNo = page
+  await getList()
+}
+
 const syncSelectedTreeOriginId = () => {
   queryParams.deptId = currentTreeOriginId.value
 }
@@ -278,11 +363,14 @@ const loadTreeOriginIdMap = async () => {
 
 const searchFolderAndFile = async () => {
   formLoading.value = true
-  syncSelectedTreeOriginId()
-  const data = await DataLibraryApi.DataLibraryApi.getAllChildContentFile(queryParams)
-  list.value = data
-  formLoading.value = false
-  queryParams.filename = ''
+  try {
+    syncSelectedTreeOriginId()
+    const data = await DataLibraryApi.DataLibraryApi.getAllChildContentFile(queryParams)
+    list.value = normalizeListData(data)
+  } finally {
+    formLoading.value = false
+    queryParams.filename = ''
+  }
 }
 const topNodeId = ref<TreeKey | null>(null)
 const successList = async (id?: TreeKey) => {
@@ -396,12 +484,15 @@ const loadRootBreadcrumb = async () => {
 
   try {
     const currentDept = await getUserProfile()
+    const rootDisplayName =
+      currentDept.dept.name === '科瑞石油技术' ? 'QHSE体系文件' : currentDept.dept.name
+
     rootDeptOriginId.value = currentDept.dept.id
     currentTreeOriginId.value = currentDept.dept.id
     deptId.value = String(currentDept.dept.id)
-    rootNodeName.value = currentDept.dept.name
-    nodeName.value = currentDept.dept.name
-    breadcrumbs.value[0].name = currentDept.dept.name
+    rootNodeName.value = rootDisplayName
+    nodeName.value = rootDisplayName
+    breadcrumbs.value[0].name = rootDisplayName
   } catch {}
 }
 
@@ -462,7 +553,8 @@ const handleFileNodeClick = async (row: FileRow) => {
 
   queryParams.classId = row.id
   currentTreeOriginId.value = row.deptId ?? row.originId
-  deptId.value = row.deptId != null ? String(row.deptId) : row.originId != null ? String(row.originId) : ''
+  deptId.value =
+    row.deptId != null ? String(row.deptId) : row.originId != null ? String(row.originId) : ''
   nodeName.value = row.name || row.filename || ''
   if (row.type == 'device') {
     id.value = Number(row.originId)
@@ -532,8 +624,9 @@ const getList = async () => {
   formLoading.value = true
   try {
     syncSelectedTreeOriginId()
-    const data = await DataLibraryApi.DataLibraryApi.getChildContentFile(queryParams)
-    list.value = data
+    const api = getCurrentListApi()
+    const data = await api(queryParams)
+    list.value = normalizeListData(data)
   } finally {
     formLoading.value = false
   }
@@ -588,6 +681,28 @@ onMounted(async () => {
   min-width: 0;
 }
 
+.mode-tabs {
+  border: 1px solid #e5e7eb;
+}
+
+.mode-tab {
+  min-width: 96px;
+  height: 32px;
+  padding: 0 14px;
+  color: #606266;
+  cursor: pointer;
+  background: transparent;
+  border: none;
+  border-radius: 8px;
+  transition: all 0.2s ease;
+}
+
+.mode-tab.active {
+  color: #fff;
+  background: linear-gradient(135deg, #409eff, #3b82f6);
+  box-shadow: 0 4px 10px rgb(59 130 246 / 25%);
+}
+
 :deep(.breadcrumb-container) {
   min-width: 0;
   padding: 0;