spring
8 天以前 ba01c8bd58bea9acbb98c2097765b939a81b21cd
src/views/fileManagement/document/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,1262 @@
<template>
  <div class="app-container document-view">
    <div class="left">
      <div>
        <el-input
          v-model="search"
          style="width: 210px"
          placeholder="输入关键字进行搜索"
          @change="searchFilter"
          @clear="searchFilter"
          clearable
          prefix-icon="Search"
        />
        <el-button
          type="primary"
          @click="openCategoryDia('addOne')"
          style="margin-left: 10px"
          >新增分类</el-button
        >
      </div>
      <div ref="containerRef">
        <el-tree
          ref="tree"
          v-loading="treeLoad"
          :data="categoryList"
          @node-click="handleNodeClick"
          :expand-on-click-node="false"
          default-expand-all
          :default-expanded-keys="expandedKeys"
          :draggable="true"
          :filter-node-method="filterNode"
          :props="{ children: 'children', label: 'category' }"
          highlight-current
          node-key="id"
          style="
            height: calc(100vh - 190px);
            overflow-y: scroll;
            scrollbar-width: none;
            margin-top: 10px;
          "
        >
          <template #default="{ node, data }">
            <div class="custom-tree-node">
              <span class="tree-node-content">
                <el-icon class="orange-icon">
                  <component :is="data.children && data.children.length > 0
                  ? node.expanded ? 'FolderOpened' : 'Folder' : 'Tickets'" />
                </el-icon>
                {{ data.category }}
              </span>
              <div>
                <el-button
                  type="primary"
                  link
                  @click="openCategoryDia('edit', data)"
                >
                  ç¼–辑
                </el-button>
                <el-button
                  type="primary"
                  link
                  @click="openCategoryDia('addSub', data)"
                  v-if="node.level < 2"
                >
                  æ·»åŠ å­åˆ†ç±»
                </el-button>
                <el-button
                  v-if="!node.childNodes.length"
                  style="margin-left: 4px"
                  type="danger"
                  link
                  @click="removeCategory(node, data)"
                >
                  åˆ é™¤
                </el-button>
              </div>
            </div>
          </template>
        </el-tree>
      </div>
    </div>
    <div class="right">
      <div style="margin-bottom: 10px" v-if="isShowButton">
        <el-button type="primary" @click="openDocumentDia('add')">
          æ–°å¢žæ–‡æ¡£
        </el-button>
        <el-button
          type="danger"
          @click="handleDelete"
          style="margin-left: 10px"
          plain
          :disabled="selectedRows.length === 0"
        >
          åˆ é™¤ ({{ selectedRows.length }})
        </el-button>
      </div>
      <div class="table-container">
        <!-- PIMTable ç»„ä»¶ -->
        <PIMTable
          :table-data="documentList"
          :column="tableColumns"
          :is-selection="true"
          :border="true"
          :table-loading="tableLoading"
          :page="{
            current: pagination.currentPage,
            size: pagination.pageSize,
            total: pagination.total,
            layout: 'total, sizes, prev, pager, next, jumper'
          }"
          @selection-change="handleSelectionChange"
          @pagination="handlePagination"
        />
      </div>
    </div>
    <!-- åˆ†ç±»æ–°å¢ž/修改对话框 -->
    <el-dialog v-model="categoryDia" title="分类" width="400px" @keydown.enter.prevent>
      <el-form
        :model="categoryForm"
        label-width="140px"
        label-position="top"
        :rules="categoryRules"
        ref="categoryFormRef"
      >
        <el-row :gutter="30">
          <el-col :span="24" v-if="categoryOperationType === 'addSub'">
            <el-form-item label="父分类:" prop="parentName">
              <el-input
                v-model="categoryForm.parentName"
                placeholder="父分类名称"
                disabled
              />
            </el-form-item>
          </el-col>
          <el-col :span="24">
            <el-form-item label="分类名称:" prop="category">
              <el-input
                v-model="categoryForm.category"
                placeholder="请输入分类名称"
                clearable
                @keydown.enter.prevent
              />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitCategoryForm">确认</el-button>
          <el-button @click="closeCategoryDia">取消</el-button>
        </div>
      </template>
    </el-dialog>
    <!-- æ–‡æ¡£æ–°å¢ž/修改对话框 -->
    <el-dialog
      v-model="documentDia"
      :title="documentOperationType === 'add' ? '新增文档' : '编辑文档'"
      width="600px"
      @close="closeDocumentDia"
      @keydown.enter.prevent
    >
      <el-form
        :model="documentForm"
        label-width="140px"
        label-position="top"
        :rules="documentRules"
        ref="documentFormRef"
      >
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="文档名称:" prop="docName">
              <el-input v-model="documentForm.docName" placeholder="请输入" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="年度:" prop="year">
              <el-date-picker
                v-model="documentForm.year"
                type="year"
                value-format="YYYY"
                format="YYYY"
                placeholder="选择年度"
                style="width: 100%"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="文档编号:" prop="docNumber">
              <el-input v-model="documentForm.docNumber" placeholder="请输入" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="责任人:" prop="responsiblePerson">
              <el-input v-model="documentForm.responsiblePerson" placeholder="请输入" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="文档分类:" prop="documentClassificationId">
              <el-select v-model="documentForm.documentClassificationId" placeholder="请选择文档分类" style="width: 100%">
                <el-option
                  v-for="item in categoryList"
                  :key="item.id"
                  :label="item.category"
                  :value="item.id"
                />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="文档放置位置:" prop="warehouseGoodsShelvesRowcolId">
              <el-tree-select
                v-model="documentForm.warehouseGoodsShelvesRowcolId"
                :data="locationTree"
                placeholder="请选择文件放置位置"
                clearable
                check-strictly
                :render-after-expand="false"
                :props="{ children: 'children', label: 'label', value: 'value' }"
                style="width: 100%"
                @change="handleLocationChange"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="文档日期:" prop="docData">
              <el-date-picker
                v-model="documentForm.docData"
                type="date"
                value-format="YYYY-MM-DD"
                format="YYYY-MM-DD"
                placeholder="选择日期"
                style="width: 100%"
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="保管期限:" prop="retentionPeriod">
              <el-select v-model="documentForm.retentionPeriod" placeholder="请选择">
                <el-option
                  v-for="item in retention_period"
                  :key="item.value"
                  :label="item.label"
                  :value="item.value"
                />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="保密级别:" prop="securityLevel">
              <el-select v-model="documentForm.securityLevel" placeholder="请选择">
                <el-option
                  v-for="item in confidentiality_level"
                  :key="item.value"
                  :label="item.label"
                  :value="item.value"
                />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="分数:" prop="copyCount">
              <el-input v-model="documentForm.copyCount" placeholder="请输入" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="页数:" prop="pageCount">
              <el-input v-model="documentForm.pageCount" placeholder="请输入" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="文档类别:" prop="docCategory">
              <el-select v-model="documentForm.docCategory" placeholder="请选择">
                <el-option
                  v-for="item in document_type"
                  :key="item.value"
                  :label="item.label"
                  :value="item.value"
                />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="文档种类:" prop="docType">
              <el-select v-model="documentForm.docType" placeholder="请选择">
                <el-option
                  v-for="item in document_categories"
                  :key="item.value"
                  :label="item.label"
                  :value="item.value"
                />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="紧急程度:" prop="urgencyLevel">
              <el-select v-model="documentForm.urgencyLevel" placeholder="请选择">
                <el-option
                  v-for="item in document_urgency"
                  :key="item.value"
                  :label="item.label"
                  :value="item.value"
                />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="文档状态:" prop="docStatus">
              <el-select v-model="documentForm.docStatus" placeholder="请选择">
                <el-option
                  v-for="item in document_status"
                  :key="item.value"
                  :label="item.label"
                  :value="item.value"
                />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="24">
            <el-form-item label="备注:" prop="remark">
              <el-input
                v-model="documentForm.remark"
                type="textarea"
                :rows="3"
                placeholder="请输入备注信息"
              />
            </el-form-item>
          </el-col>
        </el-row>
       </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitDocumentForm">确认</el-button>
          <el-button @click="closeDocumentDia">取消</el-button>
        </div>
             </template>
     </el-dialog>
             <AttachmentManager ref="attachmentManagerRef" />
     </div>
   </template>
<script setup>
import { ref, reactive, onMounted, getCurrentInstance, toRefs, watch } from "vue";
import { ElMessageBox, ElMessage } from "element-plus";
import { ArrowRight, Folder, FolderOpened, Tickets, Document } from '@element-plus/icons-vue';
import PIMTable from '@/components/PIMTable/PIMTable.vue';
import { getToken } from "@/utils/auth";
import { getCategoryTree, addCategory, updateCategory, deleteCategory, getDocumentList, addDocument, updateDocument, deleteDocument, getDocumentDetail, searchDocument, getWarehouseStructure } from '@/api/fileManagement/document'
import { getWarehouseList } from '@/api/fileManagement/bookshelf'
import AttachmentManager from './attachmentManager.vue'
import { useDict } from '@/utils/dict'
const { proxy } = getCurrentInstance();
const tree = ref(null);
const containerRef = ref(null);
// ä½¿ç”¨å­—典数据
const { confidentiality_level, document_urgency, document_status, document_type, document_categories, retention_period } = useDict('confidentiality_level', 'document_urgency', 'document_status', 'document_type', 'document_categories', 'retention_period')
// ç›‘听字典数据变化
watch([confidentiality_level, document_urgency, document_status, document_type, document_categories, retention_period], () => {
  // å­—典数据已更新
}, { immediate: true, deep: true });
const categoryDia = ref(false);
const documentDia = ref(false);
const categoryOperationType = ref("");
const documentOperationType = ref("");
const search = ref("");
const currentId = ref("");
const currentParentId = ref("");
const treeLoad = ref(false);
const categoryList = ref([]);
const expandedKeys = ref([]);
const documentList = ref([]);
const isShowButton = ref(false);
const selectedRows = ref([]);
const selectAll = ref(false);
const isIndeterminate = ref(false);
const tableLoading = ref(false);
const attachmentManagerRef = ref(null);
// æ–‡ä»¶ä¸Šä¼ é…ç½®
const upload = reactive({
  url: import.meta.env.VITE_APP_BASE_API + "/file/upload",
  headers: { Authorization: "Bearer " + getToken() },
});
// ä½ç½®æ ‘数据
const locationTree = ref([]);
// è¡¨æ ¼åˆ—配置
const tableColumns = ref([
  { label: '文档名称', prop: 'docName', width: '200' },
  { label: '文档编号', prop: 'docNumber', width: '120' },
  { label: '年度', prop: 'year', width: '80' },
  { label: '责任人', prop: 'responsiblePerson', width: '100' },
  {
    label: '文档放置位置',
    prop: 'warehouseGoodsShelvesRowcolId',
    width: '150',
    formatData: (params) => {
      if (params === null || params === undefined || params === '') return '-';
      return getLocationName(params);
    }
  },
  { label: '文档日期', prop: 'docData', width: '120' },
  {
    label: '保管期限',
    prop: 'retentionPeriod',
    width: '100',
    dataType: 'tag',
    formatData: (params) => {
      if (params === null || params === undefined || params === '') return '-';
      if (!retention_period.value || retention_period.value.length === 0) {
        return params;
      }
      const item = retention_period.value.find(item => item.value == params);
      return item ? item.label : params;
    },
    formatType: (params) => {
      if (params === null || params === undefined || params === '') return 'info';
      if (!retention_period.value || retention_period.value.length === 0) {
        return 'info';
      }
      const item = retention_period.value.find(item => item.value == params);
      const validTypes = ['success', 'warning', 'danger', 'info'];
      return item && validTypes.includes(item.elTagType) ? item.elTagType : 'info';
    }
  },
  {
    label: '保密级别',
    prop: 'securityLevel',
    width: '80',
    dataType: 'tag',
    formatData: (params) => {
      if (params === null || params === undefined || params === '') return '-';
      if (!confidentiality_level.value || confidentiality_level.value.length === 0) {
        return params;
      }
      const item = confidentiality_level.value.find(item => item.value == params);
      return item ? item.label : params;
    },
    formatType: (params) => {
      if (params === null || params === undefined || params === '') return 'info';
      if (!confidentiality_level.value || confidentiality_level.value.length === 0) {
        return 'info';
      }
      const item = confidentiality_level.value.find(item => item.value == params);
      const validTypes = ['success', 'warning', 'danger', 'info'];
      return item && validTypes.includes(item.elTagType) ? item.elTagType : 'info';
    }
  },
  { label: '分数', prop: 'copyCount', width: '80' },
  { label: '页数', prop: 'pageCount', width: '80' },
  {
    label: '文档类别',
    prop: 'docCategory',
    width: '100',
    dataType: 'tag',
    formatData: (params) => {
      if (params === null || params === undefined || params === '') return '-';
      if (!document_type.value || document_type.value.length === 0) {
        return params;
      }
      const item = document_type.value.find(item => item.value == params);
      return item ? item.label : params;
    },
    formatType: (params) => {
      if (params === null || params === undefined || params === '') return 'info';
      if (!document_type.value || document_type.value.length === 0) {
        return 'info';
      }
      const item = document_type.value.find(item => item.value == params);
      const validTypes = ['success', 'warning', 'danger', 'info'];
      return item && validTypes.includes(item.elTagType) ? item.elTagType : 'info';
    }
  },
  {
    label: '文档种类',
    prop: 'docType',
    width: '100',
    dataType: 'tag',
    formatData: (params) => {
      if (params === null || params === undefined || params === '') return '-';
      if (!document_categories.value || document_categories.value.length === 0) {
        return params;
      }
      const item = document_categories.value.find(item => item.value == params);
      return item ? item.label : params;
    },
    formatType: (params) => {
      if (params === null || params === undefined || params === '') return 'info';
      if (!document_categories.value || document_categories.value.length === 0) {
        return 'info';
      }
      const item = document_categories.value.find(item => item.value == params);
      const validTypes = ['success', 'warning', 'danger', 'info'];
      return item && validTypes.includes(item.elTagType) ? item.elTagType : 'info';
    }
  },
  {
    label: '紧急程度',
    prop: 'urgencyLevel',
    width: '100',
    dataType: 'tag',
    formatData: (params) => {
      if (params === null || params === undefined || params === '') return '-';
      if (!document_urgency.value || document_urgency.value.length === 0) {
        return params;
      }
      const item = document_urgency.value.find(item => item.value == params);
      return item ? item.label : params;
    },
    formatType: (params) => {
      if (params === null || params === undefined || params === '') return 'info';
      if (!document_urgency.value || document_urgency.value.length === 0) {
        return 'info';
      }
      const item = document_urgency.value.find(item => item.value == params);
      const validTypes = ['success', 'warning', 'danger', 'info'];
      return item && validTypes.includes(item.elTagType) ? item.elTagType : 'info';
    }
  },
  {
    label: '文档状态',
    prop: 'docStatus',
    width: '100',
    dataType: 'tag',
    formatData: (params) => {
      if (params === null || params === undefined || params === '') return '-';
      if (!document_status.value || document_status.value.length === 0) {
        return params;
      }
      const item = document_status.value.find(item => item.value == params);
      return item ? item.label : params;
    },
    formatType: (params) => {
      if (params === null || params === undefined || params === '') return 'info';
      if (!document_status.value || document_status.value.length === 0) {
        return 'info';
      }
      const item = document_status.value.find(item => item.value == params);
      const validTypes = ['success', 'warning', 'danger', 'info'];
      return item && validTypes.includes(item.elTagType) ? item.elTagType : 'info';
    }
  },
  {
    dataType: "action",
    label: "操作",
    align: "center",
    fixed: 'right',
    width: '150',
    operation: [
      {
        name: "编辑",
        type: "text",
        clickFun: (row) => {
          openDocumentDia('edit', row)
        },
      },
      {
        name: "附件",
        type: "text",
        clickFun: (row) => {
          openAttachment(row)
        },
      },
    ],
  }
]);
// åˆ†ç±»è¡¨å•
const categoryForm = reactive({
  category: "",
  parentId: "",
  parentName: "",
});
const categoryRules = reactive({
  category: [{ required: true, message: "请输入分类名称", trigger: "blur" }],
});
// æ–‡æ¡£è¡¨å•
const documentForm = reactive({
  id: "",
  documentClassificationId: "",
  docName: "",
  docNumber: "",
  year: "",
  responsiblePerson: "",
  warehouseGoodsShelvesRowcolId: "",
  docData: "",
  retentionPeriod: "",
  securityLevel: "",
  copyCount: "",
  pageCount: "",
  docCategory: "",
  docType: "",
  urgencyLevel: "",
  docStatus: "",
  remark: "",
  attachments: [], // æ–°å¢žé™„件数组
});
const documentRules = reactive({
  docName: [{ required: true, message: "请输入文档名称", trigger: "blur" }],
  docNumber: [{ required: true, message: "请输入文档编号", trigger: "blur" }],
  year: [{ required: true, message: "请选择年度", trigger: "change" }],
  documentClassificationId: [{ required: true, message: "请选择文档分类", trigger: "change" }],
  warehouseGoodsShelvesRowcolId: [{ required: true, message: "请选择文档放置位置", trigger: "change" }],
});
// åˆ†é¡µç›¸å…³
const pagination = reactive({
  currentPage: 1,
  pageSize: 10,
  total: 0,
});
// åˆå§‹åŒ–分类树数据
const initCategoryTree = async() => {
  try {
    treeLoad.value = true;
    const res = await getCategoryTree();
    if (res.code === 200) {
      categoryList.value = res.data || [];
      // è®¾ç½®å±•开的节点
      expandedKeys.value = [];
      categoryList.value.forEach((item) => {
        if (item.id) {
          expandedKeys.value.push(item.id);
        }
      });
    } else {
      ElMessage.error(res.msg || "获取分类树失败");
    }
  } catch (error) {
    ElMessage.error("获取分类树失败,请重试");
  } finally {
    treeLoad.value = false;
  }
};
// åˆå§‹åŒ–仓库位置数据
const initLocationTree = async() => {
  try {
    const res = await getWarehouseList();
    if (res.code === 200) {
      // è½¬æ¢æ•°æ®æ ¼å¼ï¼Œé€‚配el-tree-select组件
      locationTree.value = transformWarehouseData(res.data || []);
    } else {
      ElMessage.error(res.msg || "获取仓库位置失败");
    }
  } catch (error) {
    ElMessage.error("获取仓库位置失败,请重试");
  }
};
// è½¬æ¢ä»“库数据格式
const transformWarehouseData = (data) => {
  return data.map(item => ({
    id: item.id,
    label: item.name || item.warehouseName || item.label,
    value: item.id,
    children: item.children ? transformWarehouseData(item.children) : []
  }));
};
// æ ¹æ®ID获取位置名称
const getLocationName = (locationId) => {
  if (!locationId || !locationTree.value || locationTree.value.length === 0) {
    return locationId || '-';
  }
  const findLocation = (tree, id) => {
    for (let item of tree) {
      if (item.value === locationId || item.id === locationId) {
        return item.label;
      }
      if (item.children && item.children.length > 0) {
        const result = findLocation(item.children, id);
        if (result) return result;
      }
    }
    return null;
  };
  const locationName = findLocation(locationTree.value, locationId);
  return locationName || locationId;
};
// è¿‡æ»¤åˆ†ç±»æ ‘
const searchFilter = () => {
  if (proxy.$refs.tree) {
    proxy.$refs.tree.filter(search.value);
  }
};
// æ‰“开分类弹框
const openCategoryDia = (type, data) => {
  categoryOperationType.value = type;
  categoryDia.value = true;
  categoryForm.category = "";
  categoryForm.parentId ="";
  categoryForm.parentName = "";
  if (type === "edit") {
    categoryForm.category = data.category;
    // ä¿å­˜å½“前编辑的分类ID
    currentId.value = data.id;
  } else if (type === "addSub") {
    categoryForm.parentId = data.id;
    categoryForm.parentName = data.category;
  }
};
// æ‰“开文档弹框
const openDocumentDia = (type, data) => {
  documentOperationType.value = type;
  documentDia.value = true;
  if (type === "edit") {
    // ç¼–辑模式,加载现有数据
    Object.assign(documentForm, data);
    documentForm.retentionPeriod = String(documentForm.retentionPeriod)
    documentForm.securityLevel = String(documentForm.securityLevel)
    documentForm.docCategory = String(documentForm.docCategory)
    documentForm.docType = String(documentForm.docType)
    documentForm.urgencyLevel = String(documentForm.urgencyLevel)
    documentForm.docStatus = String(documentForm.docStatus)
    // åŠ è½½é™„ä»¶ä¿¡æ¯
    if (data.attachments) {
      documentForm.attachments = [...data.attachments];
    } else {
      documentForm.attachments = [];
    }
  } else {
    // æ–°å¢žæ¨¡å¼ï¼Œæ¸…空表单
    Object.keys(documentForm).forEach(key => {
      documentForm[key] = "";
    });
    documentForm.attachments = []; // æ–°å¢žæ¨¡å¼ä¸‹ä¹Ÿæ¸…空附件
    // è®¾ç½®é»˜è®¤å€¼ - ä½¿ç”¨å­—典数据的第一个选项作为默认值
    if (document_status.value && document_status.value.length > 0) {
      documentForm.docStatus = document_status.value[0].value;
    }
    if (document_urgency.value && document_urgency.value.length > 0) {
      documentForm.urgencyLevel = document_urgency.value[0].value;
    }
  }
};
// æäº¤åˆ†ç±»è¡¨å•
const submitCategoryForm = () => {
  proxy.$refs.categoryFormRef.validate(async (valid) => {
    if (valid) {
      try {
        if (categoryOperationType.value === "addSub") {
          // æ·»åŠ å­åˆ†ç±»
          const res = await addCategory({
            category: categoryForm.category,
            parentId: categoryForm.parentId
          });
          if (res.code === 200) {
            ElMessage.success("添加子分类成功");
            // é‡æ–°åŠ è½½åˆ†ç±»æ ‘
            await initCategoryTree();
          } else {
            ElMessage.error(res.msg || "添加子分类失败");
          }
        } else if (categoryOperationType.value === "edit") {
          // ç¼–辑分类
          const res = await updateCategory({
            id: currentId.value,
            category: categoryForm.category
          });
          if (res.code === 200) {
            ElMessage.success("编辑分类成功");
            // é‡æ–°åŠ è½½åˆ†ç±»æ ‘
            await initCategoryTree();
          } else {
            ElMessage.error(res.msg || "编辑分类失败");
          }
        } else {
          // æ–°å¢žé¡¶çº§åˆ†ç±»
          const res = await addCategory({
            category: categoryForm.category,
            parentId: null
          });
          if (res.code === 200) {
            ElMessage.success("新增分类成功");
            // é‡æ–°åŠ è½½åˆ†ç±»æ ‘
            await initCategoryTree();
          } else {
            ElMessage.error(res.msg || "新增分类失败");
          }
        }
        closeCategoryDia();
      } catch (error) {
        ElMessage.error("操作失败,请重试");
      }
    }
  });
};
// å…³é—­åˆ†ç±»å¼¹æ¡†
const closeCategoryDia = () => {
  proxy.$refs.categoryFormRef.resetFields();
  categoryForm.parentId = "";
  categoryForm.parentName = "";
  categoryDia.value = false;
};
// åˆ é™¤åˆ†ç±»
const removeCategory = (node, data) => {
  ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "删除提示", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
    .then(async () => {
      try {
        const res = await deleteCategory([data.id]);
        if (res.code === 200) {
          ElMessage.success("删除成功");
          // é‡æ–°åŠ è½½åˆ†ç±»æ ‘
          await initCategoryTree();
        } else {
          ElMessage.error(res.msg || "删除失败");
        }
      } catch (error) {
        ElMessage.error("删除失败,请重试");
      }
    })
    .catch(() => {
      ElMessage("已取消");
    });
};
// é€‰æ‹©åˆ†ç±»
const handleNodeClick = (val, node, el) => {
  // åˆ¤æ–­æ˜¯å¦ä¸ºå¶å­èŠ‚ç‚¹
  isShowButton.value = true;
  // åªæœ‰å¶å­èŠ‚ç‚¹æ‰æ‰§è¡Œä»¥ä¸‹é€»è¾‘
  currentId.value = val.id;
  currentParentId.value = val.parentId;
  // æ¸…空选择状态
  selectedRows.value = [];
  selectAll.value = false;
  isIndeterminate.value = false;
  // é‡ç½®åˆ†é¡µ
  pagination.currentPage = 1;
  pagination.total = 0;
  // åŠ è½½æ–‡æ¡£åˆ—è¡¨
  if (isShowButton.value) {
    loadDocumentList();
  } else {
    // å¦‚果不是叶子节点,清空文档列表
    documentList.value = [];
  }
};
// æäº¤æ–‡æ¡£è¡¨å•
const submitDocumentForm = () => {
  proxy.$refs.documentFormRef.validate(async (valid) => {
    if (valid) {
      try {
        // æž„建提交数据
        const submitData = {
          ...documentForm,
          // è®¾ç½®å½“前选中的分类ID
          documentClassificationId: currentId.value || documentForm.documentClassificationId,
          // æ·»åŠ é™„ä»¶ä¿¡æ¯
          // attachments: documentForm.attachments
        };
        if (documentOperationType.value === "edit") {
          // ç¼–辑模式,更新现有数据
          const res = await updateDocument(submitData);
          if (res.code === 200) {
            ElMessage.success("编辑成功");
            // é‡æ–°åŠ è½½æ–‡æ¡£åˆ—è¡¨
            await loadDocumentList();
            // åˆ·æ–°é™„件列表
            if (attachmentManagerRef.value && documentForm.id) {
              attachmentManagerRef.value.loadAttachmentList(documentForm.id);
            }
          } else {
            ElMessage.error(res.msg || "编辑失败");
          }
        } else {
          // æ–°å¢žæ¨¡å¼ï¼Œæ·»åŠ æ–°æ•°æ®
          const res = await addDocument(submitData);
          if (res.code === 200) {
            ElMessage.success("新增成功");
            // é‡æ–°åŠ è½½æ–‡æ¡£åˆ—è¡¨
            await loadDocumentList();
            // åˆ·æ–°é™„件列表
            if (attachmentManagerRef.value && res.data && res.data.id) {
              attachmentManagerRef.value.loadAttachmentList(res.data.id);
            }
          } else {
            ElMessage.error(res.msg || "新增失败");
          }
        }
        closeDocumentDia();
      } catch (error) {
        ElMessage.error("操作失败,请重试");
      }
    }
  });
};
// å…³é—­æ–‡æ¡£å¼¹æ¡†
const closeDocumentDia = () => {
  proxy.$refs.documentFormRef.resetFields();
  documentDia.value = false;
  // æ¸…空表单数据
  Object.keys(documentForm).forEach(key => {
    documentForm[key] = "";
  });
  documentForm.attachments = []; // å…³é—­å¼¹æ¡†æ—¶ä¹Ÿæ¸…空附件
};
// å¤„理位置选择变化
const handleLocationChange = (value) => {
  if (value) {
    // æ£€æŸ¥é€‰æ‹©çš„æ˜¯å¦ä¸ºå¶å­èŠ‚ç‚¹
    const isLeafNode = checkIfLeafNode(locationTree.value, value);
    if (!isLeafNode) {
      ElMessage.warning("请选择最底层的位置(如:柜层)");
      documentForm.warehouseGoodsShelvesRowcolId = "";
      return;
    }
  }
};
// æ£€æŸ¥æ˜¯å¦ä¸ºå¶å­èŠ‚ç‚¹
const checkIfLeafNode = (tree, value) => {
  for (let item of tree) {
    if (item.value === value || item.id === value) {
      // å¦‚果没有子节点,则为叶子节点
      return !item.children || item.children.length === 0;
    }
    if (item.children && item.children.length > 0) {
      const result = checkIfLeafNode(item.children, value);
      if (result !== null) {
        return result;
      }
    }
  }
  return null;
};
// åˆ é™¤æ–‡æ¡£
const handleDelete = () => {
  if (selectedRows.value.length > 0) {
    ElMessageBox.confirm(`确定要删除选中的 ${selectedRows.value.length} æ¡è®°å½•吗?`, "删除提示", {
      confirmButtonText: "确认",
      cancelButtonText: "取消",
      type: "warning",
    })
      .then(async () => {
        try {
          const selectedIds = selectedRows.value.map(row => row.id);
          const res = await deleteDocument(selectedIds);
          if (res.code === 200) {
            ElMessage.success("删除成功");
            // é‡æ–°åŠ è½½æ–‡æ¡£åˆ—è¡¨
            await loadDocumentList();
          } else {
            ElMessage.error(res.msg || "删除失败");
          }
        } catch (error) {
          ElMessage.error("删除失败,请重试");
        }
      })
      .catch(() => {
        ElMessage("已取消");
      });
  } else {
    ElMessage.warning("请选择要删除的数据");
  }
};
// PIMTable é€‰æ‹©å˜åŒ–事件
const handleSelectionChange = (selection) => {
  selectedRows.value = selection;
  // æ›´æ–°å…¨é€‰çŠ¶æ€
  const selectedCount = selection.length;
  const totalCount = documentList.value.length;
  if (selectedCount === 0) {
    selectAll.value = false;
    isIndeterminate.value = false;
  } else if (selectedCount === totalCount) {
    selectAll.value = true;
    isIndeterminate.value = false;
  } else {
    selectAll.value = false;
    isIndeterminate.value = true;
  }
};
// åŠ è½½æ–‡æ¡£åˆ—è¡¨
const loadDocumentList = async () => {
  try {
    tableLoading.value = true;
    // æž„建查询参数
    const query = {
      page: pagination.currentPage,
      size: pagination.pageSize,
      documentClassificationId:currentId.value
    };
    const res = await getDocumentList(query);
    if (res.code === 200) {
      documentList.value = res.data.records || [];
      pagination.total = res.data.total || 0;
    } else {
      ElMessage.error(res.msg || "获取文档列表失败");
      documentList.value = [];
      pagination.total = 0;
    }
    // é‡ç½®é€‰æ‹©çŠ¶æ€
    selectedRows.value = [];
    selectAll.value = false;
    isIndeterminate.value = false;
  } catch (error) {
    ElMessage.error("获取文档列表失败,请重试");
    documentList.value = [];
    pagination.total = 0;
  } finally {
    tableLoading.value = false;
  }
};
// å¤„理分页变化
const handlePagination = (current, size) => {
  pagination.currentPage = current;
  pagination.pageSize = size;
  loadDocumentList();
};
// è°ƒç”¨tree过滤方法
const filterNode = (value, data, node) => {
  if (!value) {
    return true;
  }
  let val = value.toLowerCase();
  return chooseNode(val, data, node);
};
// è¿‡æ»¤çˆ¶èŠ‚ç‚¹ / å­èŠ‚ç‚¹
const chooseNode = (value, data, node) => {
  if (data.category && data.category.toLowerCase().indexOf(value) !== -1) {
    return true;
  }
  const level = node.level;
  if (level === 1) {
    return false;
  }
  let parentData = node.parent;
  let index = 0;
  while (index < level - 1) {
    if (parentData.data.category && parentData.data.category.toLowerCase().indexOf(value) !== -1) {
      return true;
    }
    parentData = parentData.parent;
    index++;
  }
  return false;
};
// æ‰“开附件
const openAttachment = (row) => {
  attachmentManagerRef.value.open([], row.id);
};
onMounted(() => {
  initCategoryTree();
  initLocationTree();
  // ä¸åœ¨åˆå§‹åŒ–时加载文档列表,等待用户选择分类后再加载
});
</script>
<style scoped>
.document-view {
  display: flex;
  height: 100%;
}
.left {
  width: 380px;
  padding: 16px;
  background: #ffffff;
  border-right: 1px solid #e4e7ed;
}
.right {
  width: calc(100% - 380px);
  padding: 16px;
  background: #ffffff;
}
.custom-tree-node {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 14px;
  padding-right: 8px;
}
.tree-node-content {
  display: flex;
  align-items: center;
  height: 100%;
}
.orange-icon {
  color: orange;
  font-size: 18px;
  margin-right: 8px;
}
.table-container {
  background: #ffffff;
  border-radius: 8px;
  overflow: hidden;
  position: relative;
}
.add-row {
  display: flex;
  align-items: center;
  gap: 8px;
  background-color: #f5f7fa;
  cursor: pointer;
  transition: background-color 0.2s ease;
  padding: 12px 16px;
  margin-bottom: 16px;
  border-radius: 6px;
  border: 1px dashed #d9d9d9;
}
.add-row:hover {
  background-color: #e4e7ed;
  border-color: #c0c4cc;
}
.add-icon {
  color: #909399;
  font-size: 16px;
}
.add-row span {
  color: #606266;
  font-size: 14px;
}
.empty-data {
  text-align: center;
  color: #909399;
  padding: 40px;
  font-size: 14px;
}
.dialog-footer {
  text-align: right;
}
.operation-column {
  position: absolute;
  right: 0;
  top: 0;
  width: 120px;
  background: #ffffff;
  border-left: 1px solid #e4e7ed;
  z-index: 1;
  box-shadow: -2px 0 4px rgba(0, 0, 0, 0.1);
}
.operation-header {
  height: 40px;
  line-height: 40px;
  text-align: center;
  background: #fafafa;
  border-bottom: 1px solid #e4e7ed;
  font-weight: 500;
  color: #606266;
}
.operation-cell {
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-bottom: 1px solid #e4e7ed;
}
.operation-cell:last-child {
  border-bottom: none;
}
.attachment-section {
  width: 100%;
}
.attachment-list {
  margin-bottom: 10px;
}
.attachment-item {
  display: flex;
  align-items: center;
  padding: 8px 12px;
  background-color: #f5f7fa;
  border-radius: 4px;
  margin-bottom: 8px;
}
.file-icon {
  margin-right: 8px;
  color: #409eff;
}
.file-name {
  flex: 1;
  color: #606266;
  font-size: 14px;
}
</style>