src/views/equipmentManagement/spareParts/index.vue
@@ -1,48 +1,55 @@
<template>
  <div class="spare-part-category">
      <div class="search_form">
         <el-form :inline="true" :model="queryParams" class="search-form">
            <el-form-item label="备件名称">
               <el-input
                  v-model="queryParams.name"
                  placeholder="请输入备件名称"
                  clearable
                  style="width: 240px"
               />
            </el-form-item>
            <el-form-item>
               <el-button type="primary" @click="handleQuery">查询</el-button>
               <el-button @click="resetQuery">重置</el-button>
            </el-form-item>
         </el-form>
         <div>
            <el-button type="primary" @click="addCategory" >新增</el-button>
         </div>
      </div>
    <div class="table_list">
      <div class="actions">
        <el-text class="mx-1" size="large">设备分类</el-text>
        <div>
          <el-button @click="fetchTreeData" :loading="loading">刷新</el-button>
          <el-button type="primary" @click="addCategory" >新增</el-button>
        </div>
      </div>
      <el-table
        v-loading="loading"
        :data="renderTableData"
        style="width: 100%; margin-top: 10px;"
        border
        row-key="id"
        :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
      >
        <el-table-column prop="name" label="分类名称" width="450">
          <template #default="{ row }">
            <span :style="{ paddingLeft: getIndentation(row) + 'px' }">
              {{ row.name }}
            </span>
          </template>
        </el-table-column>
        <el-table-column prop="sparePartsNo" label="分类编号" width="200"></el-table-column>
      <el-table-column prop="deviceNameStr" label="设备名称"  width="300"></el-table-column>
        <el-table-column prop="name" label="备件名称" width="200"></el-table-column>
        <el-table-column prop="sparePartsNo" label="备件编号" width="200"></el-table-column>
        <el-table-column prop="status" label="状态" width="100">
          <template #default="{ row }">
            <el-tag type="success" size="small">{{ row.status }}</el-tag>
          </template>
        </el-table-column>
        <el-table-column prop="description" label="描述" win-width="330"></el-table-column>
        <el-table-column label="操作" width="180" fixed="right">
        <el-table-column prop="price" label="价格" width="140"></el-table-column>
        <el-table-column prop="description" label="描述" width="150"></el-table-column>
        <el-table-column label="操作" width="150" fixed="right" align="center">
          <template #default="{ row }">
            <el-button
              type="text"
              size="small"
              link
                     type="primary"
              @click="() => editCategory(row)"
              :disabled="loading"
            >
              编辑
            </el-button>
            <el-button
              type="text"
              size="small"
                     link
              @click="() => deleteCategory(row.id)"
              style="color: #f56c6c;"
              :disabled="loading"
@@ -55,10 +62,28 @@
    </div>
    <el-dialog title="分类管理" v-model="dialogVisible" width="60%">
      <el-form :model="form" :rules="rules" ref="formRef" label-width="100px">
        <el-form-item label="分类名称" prop="name">
        <el-form-item label="设备" prop="deviceLedgerIds">
          <el-select
            v-model="form.deviceLedgerIds"
            placeholder="请选择设备"
            filterable
            default-first-option
            :reserve-keyword="false"
            multiple
            style="width: 100%"
          >
            <el-option
              v-for="(item, index) in deviceOptions"
              :key="index"
              :label="item.deviceName"
              :value="item.id"
            ></el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="备件名称" prop="name">
          <el-input v-model="form.name"></el-input>
        </el-form-item>
        <el-form-item label="分类编号" prop="sparePartsNo">
        <el-form-item label="备件编号" prop="sparePartsNo">
          <el-input v-model="form.sparePartsNo"></el-input>
        </el-form-item>
        <el-form-item label="状态" prop="status">
@@ -70,17 +95,15 @@
        <el-form-item label="描述" prop="description">
          <el-input v-model="form.description"></el-input>
        </el-form-item>
        <el-form-item label="上级分类" prop="parentId">
          <el-select v-model="form.parentId" placeholder="请选择上级分类">
            <el-option label="无上级分类" :value="null"></el-option>
            <el-option
              v-for="(item, index) in categories"
              :key="index"
              :label="item.name"
              :value="item.id"
            ></el-option>
          </el-select>
        <el-form-item label="价格" prop="price">
          <el-input-number
            v-model="form.price"
            placeholder="请输入价格"
            :min="0"
            :step="0.01"
            :precision="2"
            style="width: 100%"
          ></el-input-number>
        </el-form-item>
      </el-form>
      <template #footer>
@@ -96,7 +119,8 @@
<script setup>
import { ref, computed, onMounted, reactive, watch } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { getSparePartsList, addSparePart, editSparePart, delSparePart,getSparePartsTree } from "@/api/equipmentManagement/spareParts";
import { getSparePartsList, addSparePart, editSparePart, delSparePart } from "@/api/equipmentManagement/spareParts";
import { getDeviceLedger } from "@/api/equipmentManagement/ledger";
// 加载状态
const loading = ref(false);
@@ -111,8 +135,14 @@
// const renderTableData = computed(() => buildTree(categories.value));
const renderTableData = ref([]);
const operationType = ref('add')
// 设备选项
const deviceOptions = ref([]);
// 表单引用
const formRef = ref(null);
// 查询参数
const queryParams = reactive({
  name: ''
});
// 表单数据
const form = reactive({
  id:'',
@@ -120,19 +150,34 @@
  sparePartsNo: '',
  status: '',
  description: '',
  parentId: null
  deviceLedgerIds: [],
  price: null
});
// 表单验证规则
const rules = reactive({
  name: [
    { required: true, message: '请输入分类名称', trigger: 'blur' }
    { required: true, message: '请输入备件名称', trigger: 'blur' }
  ],
  sparePartsNo: [
    { required: true, message: '请输入分类编号', trigger: 'blur' }
    { required: true, message: '请输入备件编号', trigger: 'blur' }
  ],
  status: [
    { required: true, message: '请选择状态', trigger: 'change' }
  ],
  deviceLedgerIds: [
    {
      required: true,
      message: '请选择设备',
      trigger: 'change',
      validator: (rule, value, callback) => {
        if (operationType.value === 'add' && (!value || value.length === 0)) {
          callback(new Error('请选择设备'));
        } else {
          callback();
        }
      }
    }
  ]
});
// 获取缩进量
@@ -159,54 +204,80 @@
  });
  return result;
};
//获取树形结构
const fetchTreeData = async () => {
  fetchCategories();
// 获取列表数据
const fetchListData = async () => {
  loading.value = true;
  try {
    const res = await getSparePartsTree();
    if (res.code === 200) {
      renderTableData.value = res.data;
    } else {
      ElMessage.error(res.message || '获取分类列表失败');
    const params = {};
    if (queryParams.name) {
      params.name = queryParams.name;
    }
  }catch (error) {
    ElMessage.error('获取分类列表失败');
    const res = await getSparePartsList(params);
    if (res.code === 200) {
      renderTableData.value = res.data.records || [];
      categories.value = res.data.records || [];
    }
  } catch (error) {
      loading.value = false;
  } finally {
    loading.value = false;
  }
}
// 获取分类列表
const fetchCategories = async () => {
  loading.value = true;
// 查询
const handleQuery = () => {
  fetchListData();
}
// 重置查询
const resetQuery = () => {
  queryParams.name = '';
  fetchListData();
}
// 加载设备列表(在打开弹框时调用)
const loadDeviceName = async () => {
  try {
    const res = await getSparePartsList();
    if (res.code === 200) {
      categories.value = res.data.records;
    } else {
      ElMessage.error(res.message || '获取分类列表失败');
    }
    const { data } = await getDeviceLedger();
    deviceOptions.value = data || [];
  } catch (error) {
    ElMessage.error('获取分类列表失败');
  } finally {
    loading.value = false;
    ElMessage.error('获取设备列表失败');
  }
};
// 新增分类
const addCategory = () => {
const addCategory = async () => {
  await loadDeviceName();
  form.id = '';
  form.name = '';
  form.sparePartsNo = '';
  form.status = '';
  form.description = '';
  form.parentId = null;
  form.deviceLedgerIds = [];
  form.price = null;
  operationType.value = 'add'
  dialogVisible.value = true;
  console.log('dialogVisible 更新为', dialogVisible.value);
};
// 编辑分类
const editCategory = (row) => {
const editCategory = async (row) => {
  await loadDeviceName();
  Object.assign(form, row);
  // 如果后端返回的是 deviceIds 字符串,需要转换为数组
  if (row.deviceIds && typeof row.deviceIds === 'string') {
    // 确保ID类型与设备选项中的ID类型一致
    const deviceIdsArray = row.deviceIds.split(',').map(id => id.trim()).filter(id => id);
    // 如果设备选项中的ID是数字类型,则转换为数字
    if (deviceOptions.value.length > 0 && typeof deviceOptions.value[0].id === 'number') {
      form.deviceLedgerIds = deviceIdsArray.map(id => Number(id)).filter(id => !isNaN(id));
    } else {
      form.deviceLedgerIds = deviceIdsArray;
    }
  } else if (row.deviceIds && Array.isArray(row.deviceIds)) {
    form.deviceLedgerIds = row.deviceIds;
  } else {
    form.deviceLedgerIds = [];
  }
  operationType.value = 'edit'
  dialogVisible.value = true;
};
@@ -223,7 +294,7 @@
    const res = await delSparePart(id);
    if (res.code === 200) {
      ElMessage.success('删除成功');
      fetchTreeData();
      fetchListData();
    } else {
      ElMessage.error(res.message || '删除失败');
    }
@@ -242,19 +313,31 @@
  try {
    await formRef.value.validate();
    formLoading.value = true;
    // 构建提交数据
    const submitData = {
      ...form,
      deviceIds: form.deviceLedgerIds && form.deviceLedgerIds.length > 0
        ? form.deviceLedgerIds.join(',')
        : ''
    };
    // 删除不需要的字段
    delete submitData.deviceLedgerIds;
    if (operationType.value === 'edit') {
      let res = await editSparePart(form);
      let res = await editSparePart(submitData);
      if (res.code === 200) {
      ElMessage.success('编辑成功');
      dialogVisible.value = false;
      fetchTreeData();
    }
    } else {
      let res = await addSparePart(form);
        if (res.code === 200) {
        ElMessage.success('编辑成功');
        dialogVisible.value = false;
        fetchTreeData();
        fetchListData();
      }
    } else {
      let res = await addSparePart(submitData);
      if (res.code === 200) {
        ElMessage.success('新增成功');
        dialogVisible.value = false;
        fetchListData();
      }
    }
  } catch (error) {
@@ -264,10 +347,9 @@
  }
};
// 组件挂载时获取分类列表
// 组件挂载时获取列表数据
onMounted(() => {
  fetchCategories();
  fetchTreeData();
  fetchListData();
});
</script>
@@ -275,43 +357,13 @@
.spare-part-category {
  padding: 20px;
}
.search_form {
   display: flex;
   align-items: flex-start;
   justify-content: space-between;
}
.table_list {
  margin-top: unset;
}
.actions {
  display: flex;
  justify-content: space-between;
  margin-bottom: 10px;
  align-items: center;
}
/* 嵌套树形结构样式 */
.nested-tree-node {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding: 0 4px;
  height: 30px;
  line-height: 30px;
}
.category-code {
  color: #606266;
  font-size: 12px;
  margin-left: 8px;
}
.tree-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-left: auto;
}
/* 表格样式调整 */
.el-table {
  font-size: 14px;
}
.el-table__header-wrapper th {
@@ -321,22 +373,6 @@
.el-table__row:hover > td {
  background-color: #fafafa;
}
/* 嵌套树形结构特定样式 */
.nested-tree {
  background-color: transparent;
}
.nested-tree .el-tree-node__content {
  height: auto;
  background-color: transparent;
}
/* 搜索框样式调整 */
.actions .el-input {
  margin-right: 10px;
  width: 200px;
}
/* 按钮组样式 */
@@ -354,64 +390,5 @@
.nested-tree .el-tree-node__expand-icon {
  font-size: 12px;
  margin-right: 4px;
}
/* 空状态样式 */
.el-table .cell:empty::before {
  content: '-';
  color: #c0c4cc;
}
/* 展开/折叠功能样式 */
.expand-icon {
  display: inline-block;
  width: 20px;
  height: 20px;
  text-align: center;
  line-height: 20px;
  cursor: pointer;
  font-size: 12px;
  color: #909399;
}
.expand-icon.expanded {
  color: #409eff;
}
/* 展开内容样式 */
.expand-content {
  padding: 10px 20px;
}
/* 子级内容样式 */
.child-content {
  padding-left: 40px;
}
/* 无子分类提示样式 */
.no-children {
  padding: 10px 20px;
  color: #909399;
  font-size: 14px;
}
/* 确保展开的表格样式正确 */
.el-table .el-table__expanded-cell {
  background-color: #fafafa;
}
/* 展开的子表格样式 */
.el-table .el-table {
  border-top: none;
  border-bottom: none;
}
/* 展开的子表格单元格样式 */
.expand-content .el-table__body-wrapper .el-table__row {
  background-color: #ffffff;
}
.expand-content .el-table__body-wrapper .el-table__row:hover > td {
  background-color: #fafafa;
}
</style>