spring
15 小时以前 470ce94b21d780fc6c0b1bb5d9e1672af1f4d66e
fix: BOM功能调整
已添加1个文件
已修改4个文件
441 ■■■■ 文件已修改
src/api/productionManagement/productBom.js 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/product/ProductSelectDialog.vue 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/processRoute/processRouteItem/index.vue 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productStructure/index.vue 289 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionProcess/Edit.vue 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/productionManagement/productBom.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,38 @@
// äº§å“BOM页面接口
import request from "@/utils/request";
// åˆ†é¡µæŸ¥è¯¢
export function listPage(query) {
  return request({
    url: "/productBom/listPage",
    method: "get",
    params: query,
  });
}
// æ–°å¢ž
export function add(data) {
  return request({
    url: "/productBom/add",
    method: "post",
    data: data,
  });
}
// ä¿®æ”¹
export function update(data) {
  return request({
    url: "/productBom/update",
    method: "put",
    data: data,
  });
}
// æ‰¹é‡åˆ é™¤
export function batchDelete(ids) {
  return request({
    url: "/productBom/batchDelete",
    method: "delete",
    data: ids,
  });
}
src/views/basicData/product/ProductSelectDialog.vue
@@ -33,12 +33,14 @@
    <!-- åˆ—表 -->
    <el-table
        ref="tableRef"
        v-loading="loading"
        :data="tableData"
        height="420"
        highlight-current-row
        row-key="id"
        @selection-change="handleSelectionChange"
        @select="handleSelect"
    >
      <el-table-column type="selection" width="55" />
      <el-table-column type="index" label="#" width="60"/>
@@ -70,7 +72,7 @@
</template>
<script setup lang="ts">
import {computed, onMounted, reactive, ref, watch} from "vue";
import {computed, onMounted, reactive, ref, watch, nextTick} from "vue";
import {ElMessage} from "element-plus";
import {productModelList} from '@/api/basicData/productModel'
@@ -83,6 +85,7 @@
const props = defineProps<{
  modelValue: boolean;
  single?: boolean; // æ˜¯å¦åªèƒ½é€‰æ‹©ä¸€ä¸ªï¼Œé»˜è®¤false(可选择多个)
}>();
const emit = defineEmits(['update:modelValue', 'confirm']);
@@ -105,14 +108,48 @@
const loading = ref(false);
const tableData = ref<ProductRow[]>([]);
const total = ref(0);
const multipleSelection = ref<ProductRow[]>([])
const multipleSelection = ref<ProductRow[]>([]);
const tableRef = ref();
function close() {
  visible.value = false;
}
const handleSelectionChange = (val: ProductRow[]) => {
  multipleSelection.value = val
  if (props.single && val.length > 1) {
    // å¦‚果限制为单个选择,只保留最后一个选中的
    const lastSelected = val[val.length - 1];
    multipleSelection.value = [lastSelected];
    // æ¸…空表格选中状态,然后重新选中最后一个
    nextTick(() => {
      if (tableRef.value) {
        tableRef.value.clearSelection();
        tableRef.value.toggleRowSelection(lastSelected, true);
      }
    });
  } else {
    multipleSelection.value = val;
  }
}
// å¤„理单个选择
const handleSelect = (selection: ProductRow[], row: ProductRow) => {
  if (props.single) {
    // å¦‚果限制为单个,清空其他选择,只保留当前行
    if (selection.includes(row)) {
      // é€‰ä¸­å½“前行时,清空其他选中
      multipleSelection.value = [row];
      nextTick(() => {
        if (tableRef.value) {
          tableData.value.forEach((item) => {
            if (item.id !== row.id) {
              tableRef.value.toggleRowSelection(item, false);
            }
          });
        }
      });
    }
  }
}
function onSearch() {
@@ -136,7 +173,11 @@
    ElMessage.warning("请选择一条产品");
    return;
  }
  emit("confirm", multipleSelection.value);
  if (props.single && multipleSelection.value.length > 1) {
    ElMessage.warning("只能选择一个产品");
    return;
  }
  emit("confirm", props.single ? [multipleSelection.value[0]] : multipleSelection.value);
  close();
}
@@ -157,6 +198,13 @@
  }
}
// ç›‘听弹窗打开,重置选择
watch(() => props.modelValue, (visible) => {
  if (visible) {
    multipleSelection.value = [];
  }
});
onMounted(() => {
  loadData()
})
src/views/productionManagement/processRoute/processRouteItem/index.vue
@@ -1,7 +1,7 @@
<template>
  <div class="app-container">
    <div class="operate-button">
      <div style="margin-bottom: 15px;">
    <PageHeader content="工艺路线项目">
      <template #right-button>
        <el-button
            type="primary"
            @click="isShowProductSelectDialog = true"
@@ -9,16 +9,16 @@
          é€‰æ‹©äº§å“
        </el-button>
        <el-button type="primary" @click="handleSubmit">确认</el-button>
      </div>
      <el-switch
          v-model="isTable"
          inline-prompt
          active-text="表格"
          inactive-text="列表"
          @change="handleViewChange"
      />
    </div>
        <el-switch
            v-model="isTable"
            inline-prompt
            active-text="表格"
            inactive-text="列表"
            @change="handleViewChange"
            style="margin-left: 10px;"
        />
      </template>
    </PageHeader>
    <el-table
        v-if="isTable"
        ref="multipleTable"
@@ -404,11 +404,6 @@
  padding: 0 !important;
}
.operate-button {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
/* ä¿®æ”¹ï¼šè‡ªå®šä¹‰æ­¥éª¤æ¡å®¹å™¨æ ·å¼ */
.custom-steps {
src/views/productionManagement/productStructure/index.vue
@@ -1,5 +1,9 @@
<template>
  <div class="app-container">
    <div style="text-align: right; margin-bottom: 10px;">
      <el-button type="primary" @click="handleAdd">新增</el-button>
      <el-button type="danger" plain @click="handleBatchDelete" :disabled="selectedRows.length === 0">删除</el-button>
    </div>
    <PIMTable
        rowKey="id"
        :column="tableColumn"
@@ -19,95 +23,314 @@
      </template>
    </PIMTable>
    <StructureEdit v-if="showEdit" v-model:show-model="showEdit" :record="currentRow"/>
    <!-- æ–°å¢ž/编辑弹窗 -->
    <el-dialog
        v-model="dialogVisible"
        :title="operationType === 'add' ? '新增BOM' : '编辑BOM'"
        width="600px"
        @close="closeDialog"
    >
      <el-form
          ref="formRef"
          :model="form"
          :rules="rules"
          label-width="120px"
      >
        <el-form-item label="产品名称" prop="productModelId">
          <el-button type="primary" @click="showProductSelectDialog = true">
            {{ form.productName || '选择产品' }}
          </el-button>
        </el-form-item>
        <el-form-item label="版本号" prop="version">
          <el-input v-model="form.version" placeholder="请输入版本号" clearable />
        </el-form-item>
        <el-form-item label="备注" prop="remark">
          <el-input
              v-model="form.remark"
              type="textarea"
              :rows="3"
              placeholder="请输入备注"
              clearable
          />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="closeDialog">取消</el-button>
        <el-button type="primary" @click="handleSubmit">确定</el-button>
      </template>
    </el-dialog>
    <!-- äº§å“é€‰æ‹©å¼¹çª— -->
    <ProductSelectDialog
        v-model="showProductSelectDialog"
        @confirm="handleProductSelect"
        single
    />
  </div>
</template>
<script setup>
import {ref} from "vue";
import {productModelList} from "@/api/basicData/productModel.js";
import { ref, reactive, toRefs, onMounted, getCurrentInstance, defineAsyncComponent } from "vue";
import { listPage, add, update, batchDelete } from "@/api/productionManagement/productBom.js";
import { useRouter } from 'vue-router'
import { ElMessageBox } from 'element-plus'
import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
const router = useRouter()
const { proxy } = getCurrentInstance()
const StructureEdit = defineAsyncComponent(() => import('@/views/productionManagement/productStructure/StructureEdit.vue'))
const tableColumn = ref([
  {
    label: "产品编码",
    prop: "productCode",
    slot: "detail"
    label: "BOM编号",
    prop: "bomNo",
    minWidth: 140
  },
  {
    label: "产品名称",
    prop: "productName",
    dataType: 'slot',
    slot: "detail"
    slot: "detail",
    minWidth: 160
  },
  {
    label: "规格型号",
    prop: "model",
    prop: "productModelName",
    minWidth: 140
  },
  {
    label: "单位",
    prop: "unit",
    label: "版本号",
    prop: "version",
    width: 100
  },
  {
    label: "备注",
    prop: "remark",
    minWidth: 160
  },
  {
    dataType: "action",
    label: "操作",
    align: "center",
    fixed: "right",
    width: 150,
    operation: [
      {
        name: "编辑",
        type: "text",
        clickFun: (row) => {
          handleEdit(row)
        }
      },
      {
        name: "删除",
        type: "danger",
        link: true,
        clickFun: (row) => {
          handleDelete(row)
        }
      }
    ]
  }
]);
const tableData = ref([]);
const tableLoading = ref(false);
const showEdit = ref(false);
const selectedRows = ref([]);
const currentRow = ref({});
const dialogVisible = ref(false);
const operationType = ref('add'); // add | edit
const formRef = ref(null);
const showProductSelectDialog = ref(false);
const page = reactive({
  current: 1,
  size: 10,
  total: 0,
});
const data = reactive({
  form: {
    id: undefined,
    productName: "",
    productModelName: "",
    productModelId: "",
    remark: "",
    version: ""
  },
  rules: {
    productName: [{required: true, message: "请输入", trigger: "blur"}],
  },
  modelForm: {
    otherModel: '',
    model: "",
    unit: "",
    speculativeTradingName: [],
  },
    productModelId: [{ required: true, message: "请选择产品", trigger: "change" }],
    version: [{ required: true, message: "请输入版本号", trigger: "blur" }]
  }
});
const {form, rules} = toRefs(data);
const { form, rules } = toRefs(data);
// è¡¨æ ¼é€‰æ‹©æ•°æ®
const handleSelectionChange = (selection) => {
  selectedRows.value = selection;
};
// æŸ¥è¯¢è§„格型号
// åˆ†é¡µ
const pagination = (obj) => {
  page.current = obj.page;
  page.size = obj.limit;
  getModelList();
  getList();
};
// æŸ¥è¯¢åˆ—表
const getList = () => {
  tableLoading.value = true;
  listPage({
    current: page.current,
    size: page.size,
  })
    .then((res) => {
      const records = res?.data?.records || [];
      tableData.value = records;
      page.total = res?.data?.total || 0;
    })
    .catch((err) => {
      console.error("获取列表失败:", err);
    })
    .finally(() => {
      tableLoading.value = false;
    });
};
// æ–°å¢ž
const handleAdd = () => {
  operationType.value = 'add';
  Object.assign(form.value, {
    id: undefined,
    productName: "",
    productModelName: "",
    productModelId: "",
    remark: "",
    version: ""
  });
  dialogVisible.value = true;
};
// ç¼–辑
const handleEdit = (row) => {
  operationType.value = 'edit';
  Object.assign(form.value, {
    id: row.id,
    productName: row.productName || "",
    productModelName: row.productModelName || "",
    productModelId: row.productModelId || "",
    remark: row.remark || "",
    version: row.version || ""
  });
  dialogVisible.value = true;
};
// åˆ é™¤ï¼ˆå•条)
const handleDelete = (row) => {
  ElMessageBox.confirm('确认删除该BOM?', '提示', {
    confirmButtonText: '确认',
    cancelButtonText: '取消',
    type: 'warning'
  })
    .then(() => {
      batchDelete([row.id])
        .then(() => {
          proxy.$modal.msgSuccess('删除成功');
          getList();
        })
        .catch(() => {
          proxy.$modal.msgError('删除失败');
        });
    })
    .catch(() => {});
};
// æ‰¹é‡åˆ é™¤
const handleBatchDelete = () => {
  if (!selectedRows.value.length) {
    proxy.$modal.msgWarning('请选择数据');
    return;
  }
  const ids = selectedRows.value.map(item => item.id);
  ElMessageBox.confirm('选中的内容将被删除,是否确认删除?', '删除提示', {
    confirmButtonText: '确认',
    cancelButtonText: '取消',
    type: 'warning'
  })
    .then(() => {
      batchDelete(ids)
        .then(() => {
          proxy.$modal.msgSuccess('删除成功');
          getList();
        })
        .catch(() => {
          proxy.$modal.msgError('删除失败');
        });
    })
    .catch(() => {});
};
// äº§å“é€‰æ‹©
const handleProductSelect = (products) => {
  if (products && products.length > 0) {
    const product = products[0];
    form.value.productModelId = product.id;
    form.value.productName = product.productName;
    form.value.productModelName = product.model;
  }
  showProductSelectDialog.value = false;
};
// æäº¤è¡¨å•
const handleSubmit = () => {
  formRef.value.validate((valid) => {
    if (valid) {
      const payload = { ...form.value };
      if (operationType.value === 'add') {
        add(payload)
          .then(() => {
            proxy.$modal.msgSuccess('新增成功');
            closeDialog();
            getList();
          })
          .catch(() => {
            proxy.$modal.msgError('新增失败');
          });
      } else {
        update(payload)
          .then(() => {
            proxy.$modal.msgSuccess('修改成功');
            closeDialog();
            getList();
          })
          .catch(() => {
            proxy.$modal.msgError('修改失败');
          });
      }
    }
  });
};
// å…³é—­å¼¹çª—
const closeDialog = () => {
  dialogVisible.value = false;
  formRef.value?.resetFields();
};
// æŸ¥çœ‹è¯¦æƒ…
const showDetail = (id) => {
  router.push({
    path: '/productionManagement/productStructureDetail',
    query: {
      id: id
    }
  })
}
const getModelList = () => {
  tableLoading.value = true;
  productModelList({
    current: page.current,
    size: page.size,
  }).then((res) => {
    tableData.value = res.records;
    page.total = res.total;
    tableLoading.value = false;
  });
};
onMounted(() => {
  getModelList();
})
  getList();
});
</script>
src/views/productionManagement/productionProcess/Edit.vue
@@ -43,7 +43,7 @@
</template>
<script setup>
import { ref, computed, getCurrentInstance } from "vue";
import { ref, computed, getCurrentInstance, watch } from "vue";
import {update} from "@/api/productionManagement/productionProcess.js";
const props = defineProps({
@@ -64,6 +64,7 @@
const formState = ref({
  id: props.record.id,
  name: props.record.name,
  no: props.record.no,
  remark: props.record.remark,
  salaryQuota: props.record.salaryQuota,
});
@@ -77,6 +78,32 @@
  },
});
// ç›‘听 record å˜åŒ–,更新表单数据
watch(() => props.record, (newRecord) => {
  if (newRecord && isShow.value) {
    formState.value = {
      id: newRecord.id,
      name: newRecord.name || '',
      no: newRecord.no || '',
      remark: newRecord.remark || '',
      salaryQuota: newRecord.salaryQuota || '',
    };
  }
}, { immediate: true, deep: true });
// ç›‘听弹窗打开,重新初始化表单数据
watch(() => props.visible, (visible) => {
  if (visible && props.record) {
    formState.value = {
      id: props.record.id,
      name: props.record.name || '',
      no: props.record.no || '',
      remark: props.record.remark || '',
      salaryQuota: props.record.salaryQuota || '',
    };
  }
});
let { proxy } = getCurrentInstance()
const closeModal = () => {