Browse Source

pms 设置BOM树节点属性 物料详情 抽屉效果

zhangcl 4 months ago
parent
commit
51dd5e4d02

+ 34 - 0
src/api/pms/commonbommaterial/index.ts

@@ -0,0 +1,34 @@
+import request from '@/config/axios'
+import {IotDeviceVO} from "@/api/pms/device";
+
+export interface CommonBomMaterialVO {
+  id: number
+  name: string
+  code: string
+  deviceCategoryId: number
+  bomNodeId: number
+  materialId: number
+  quantity: number
+  status: number
+  remark: string
+  createTime: Date
+}
+
+
+export const CommonBomMaterialApi = {
+  getCommonBomMaterialPage: async (params: PageParam) => {
+    return await request.get({ url: '/pms/iot-common-bom-material/page', params })
+  },
+  getCommonBomMaterial: async (id: number) => {
+    return await request.get({ url: '/pms/iot-common-bom-material/get?id=' + id })
+  },
+  createCommonBomMaterial: async (data: CommonBomMaterialVO) => {
+    return await request.post({ url: '/pms/iot-common-bom-material/create', data })
+  },
+  updateCommonBomMaterial: async (data: CommonBomMaterialVO) => {
+    return await request.put({ url: '/pms/iot-common-bom-material/update', data })
+  },
+  deleteCommonBomMaterial: async (id: number) => {
+    return await request.delete({ url: '/pms/iot-common-bom-material/delete?id=' + id })
+  }
+}

+ 1 - 0
src/api/pms/material/index.ts

@@ -5,6 +5,7 @@ export interface MaterialVO {
   name: string
   code: string
   materialGroupId: number
+  materialGroupName: string
   model: string
   unit: string
   sort: number

+ 44 - 41
src/views/pms/bom/MaterialList.vue

@@ -1,5 +1,5 @@
 <template>
-  <Dialog v-model="dialogVisible" title="选择品牌" style="width: 1100px; max-height: 800px">
+  <Dialog v-model="dialogVisible" title="选择物料" style="width: 1300px; max-height: 800px">
     <ContentWrap>
       <el-form
         class="-mb-15px"
@@ -8,24 +8,34 @@
         :inline="true"
         label-width="68px"
       >
-        <el-form-item label="字典标签" prop="label">
+        <el-form-item label="物料名称" prop="name">
           <el-input
-            v-model="queryParams.label"
-            placeholder="请输入字典标签"
+            v-model="queryParams.name"
+            placeholder="请输入物料名称"
             clearable
             @keyup.enter="handleQuery"
-            class="!w-240px"
+            class="!w-200px"
           />
         </el-form-item>
-        <el-form-item label="状态" prop="status">
-          <el-select v-model="queryParams.status" placeholder="数据状态" clearable class="!w-240px">
-            <el-option
-              v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
-              :key="dict.value"
-              :label="dict.label"
-              :value="dict.value"
-            />
-          </el-select>
+        <el-form-item label="物料编码" prop="code">
+          <el-input
+            v-model="queryParams.code"
+            placeholder="请输入物料名称"
+            clearable
+            @keyup.enter="handleQuery"
+            class="!w-200px"
+          />
+        </el-form-item>
+        <el-form-item label="物料组" prop="materialGroupId">
+          <el-tree-select
+            v-model="queryParams.materialGroupId"
+            :data="materialGroupList"
+            :props="defaultProps"
+            check-strictly
+            node-key="id"
+            placeholder="请选择所属物料组"
+            class="!w-220px"
+          />
         </el-form-item>
         <el-form-item>
           <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
@@ -38,24 +48,17 @@
         <el-table-column width="60" label="选择">
           <template #default="{ row }">
             <el-radio
-              :model-value="selectedRow?.id"
+
               :label="row.id"
               @click.stop="selectRow(row)"
               class="no-label-radio"
             />
           </template>
         </el-table-column>
-        <el-table-column label="字典编码" align="center" prop="id" />
-        <el-table-column label="字典标签" align="center" prop="label" />
-        <el-table-column label="字典键值" align="center" prop="value" />
-        <!--      <el-table-column label="字典排序" align="center" prop="sort" />-->
-        <el-table-column label="状态" align="center" prop="status">
-          <template #default="scope">
-            <dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
-          </template>
-        </el-table-column>
-        <!--      <el-table-column label="颜色类型" align="center" prop="colorType" />-->
-        <!--      <el-table-column label="CSS Class" align="center" prop="cssClass" />-->
+        <el-table-column label="物料名称" align="center" prop="name" />
+        <el-table-column label="物料编码" align="center" prop="code" />
+        <el-table-column label="规格型号" align="center" prop="model" />
+        <el-table-column label="单位" align="center" prop="unit" />
         <el-table-column label="备注" align="center" prop="remark" show-overflow-tooltip />
         <el-table-column
           label="创建时间"
@@ -64,11 +67,6 @@
           width="180"
           :formatter="dateFormatter"
         />
-<!--        <el-table-column label="操作" align="center">-->
-<!--          <template #default="scope">-->
-<!--            <el-button link type="primary" @click="choose(scope.row)"> 选择 </el-button>-->
-<!--          </template>-->
-<!--        </el-table-column>-->
       </el-table>
       <!-- 分页 -->
       <Pagination
@@ -82,25 +80,31 @@
 </template>
 
 <script setup lang="ts">
-import * as DictDataApi from '@/api/system/dict/dict.data'
 import { DictDataVO } from '@/api/system/dict/dict.data'
-import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
 import { dateFormatter } from '@/utils/formatTime'
-import * as DictTypeApi from '@/api/system/dict/dict.type'
+import * as MaterialApi from '@/api/pms/material'
+import {defaultProps, handleTree} from "@/utils/tree";
+import * as MaterialGroupApi from '@/api/pms/materialgroup'
+
+defineOptions({
+  name: 'MaterialList'
+})
 
+const materialGroupList = ref<Tree[]>([]) // 物料组树形结构
 const emit = defineEmits(['choose']) // 定义 success 事件,用于操作成功后的回调
 const dialogVisible = ref(false) // 弹窗的是否展示
 const loading = ref(true) // 列表的加载中
 const queryFormRef = ref() // 搜索的表单
 const list = ref<DictDataVO[]>([]) // 列表的数据
-const dictTypeList = ref<DictTypeApi.DictTypeVO[]>() // 字典类型的列表
 const total = ref(0) // 列表的总页数
 const queryParams = reactive({
   pageNo: 1,
   pageSize: 10,
-  label: '',
-  status: undefined,
-  dictType: 'pms_device_brand'
+  materialGroupId: '',
+  code: undefined,
+  name: '',
+  model: '',
+  unit: ''
 })
 
 const selectedRow = ref(null);
@@ -123,7 +127,7 @@ defineExpose({ open }) // 提供 open 方法,用于打开弹窗
 const getList = async () => {
   loading.value = true
   try {
-    const data = await DictDataApi.getDictDataPage(queryParams)
+    const data = await MaterialApi.getMaterialPage(queryParams)
     list.value = data.list
     total.value = data.total
   } finally {
@@ -147,9 +151,8 @@ const resetQuery = () => {
 }
 /** 初始化 **/
 onMounted(async () => {
+  materialGroupList.value = handleTree(await MaterialGroupApi.getSimpleMaterialGroupList())
   await getList()
-  // 查询字典(精简)列表
-  dictTypeList.value = await DictTypeApi.getSimpleDictTypeList()
 })
 </script>
 <style lang="scss">

+ 128 - 0
src/views/pms/bom/MaterialListDrawer.vue

@@ -0,0 +1,128 @@
+<template>
+  <el-drawer
+    title="物料详情"
+    :append-to-body="true"
+    :model-value="modelValue"
+    @update:model-value="$emit('update:modelValue', $event)"
+    :show-close="false"
+    direction="rtl"
+    :size="computedSize"
+    :before-close="handleClose"
+  >
+    <template v-if="nodeId">
+      <div v-loading="loading" style="height: 100%">
+        <el-table :data="materials" style="width: 100%">
+          <el-table-column prop="name" label="物料名称" width="180" />
+          <el-table-column prop="code" label="物料编码" width="180" />
+          <el-table-column prop="model" label="规格型号" width="180" />
+          <el-table-column prop="unit" label="单位" width="180" />
+          <el-table-column
+            label="创建时间"
+            align="center"
+            prop="createTime"
+            width="180"
+            :formatter="dateFormatter"
+          />
+          <el-table-column label="操作" align="right">
+            <template #default="scope">
+              <el-button
+                size="small"
+                type="danger"
+                @click="emit('delete', scope.row)"
+              >删除</el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+        <!-- 分页 -->
+        <Pagination
+          :total="total"
+          v-model:page="queryParams.pageNo"
+          v-model:limit="queryParams.pageSize"
+          @pagination="loadMaterials(props.nodeId)"
+        />
+      </div>
+    </template>
+
+  </el-drawer>
+</template>
+<script setup lang="ts">
+
+import { ref, watch, defineOptions, defineEmits } from 'vue'
+import { ElMessage } from 'element-plus'
+import * as PmsMaterialApi from '@/api/pms/material'
+import {dateFormatter} from "@/utils/formatTime";
+const drawerVisible = ref<boolean>(false)
+const emit = defineEmits(['update:modelValue', 'add', 'delete'])
+
+defineOptions({
+  name: 'MaterialListDrawer'
+})
+
+const queryParams = reactive({
+  pageNo: 1,
+  pageSize: 10,
+  createTime: [],
+  bomId: '',
+  name: '',
+  code: ''
+})
+
+const windowWidth = ref(window.innerWidth)
+// 动态计算百分比
+const computedSize = computed(() => {
+  return windowWidth.value > 1200 ? '80%' : '80%'
+})
+
+const loading = ref(false)
+const total = ref(0) // 列表的总页数
+const materials = ref([])
+
+const props = defineProps({
+  modelValue: Boolean,
+  nodeId: Number
+})
+
+// 监听bom树节点ID变化
+watch(() => props.nodeId, async (newVal) => {
+  if (newVal) {
+    await loadMaterials(newVal)
+  }
+})
+
+// 加载指定bom节点下的物料数据
+const loadMaterials = async (nodeId) => {
+  queryParams.bomId = nodeId
+  try {
+    loading.value = true
+    // API调用
+    const data = await PmsMaterialApi.listByBomId(queryParams)
+    materials.value = data.list
+    total.value = data.total
+  } catch (error) {
+    ElMessage.error('数据加载失败')
+  } finally {
+    loading.value = false
+  }
+}
+
+// 打开抽屉
+const openDrawer = () => {
+  drawerVisible.value = true
+}
+
+// 关闭抽屉
+const closeDrawer = () => {
+  drawerVisible.value = false
+}
+
+// 关闭抽屉
+const handleClose = () => {
+  emit('update:modelValue', false)
+  materials.value = []
+}
+
+defineExpose({ openDrawer, closeDrawer }) // 暴露方法给父组件
+
+</script>
+
+<style lang="scss" scoped></style>

+ 60 - 4
src/views/pms/bom/index.vue

@@ -94,6 +94,22 @@
               >
                 修改
               </el-button>
+              <el-button
+                link
+                type="primary"
+                @click="openSelectMaterialForm(scope.row.id)"
+                v-hasPermi="['rq:iot-bom:update']"
+              >
+                添加物料
+              </el-button>
+              <el-button
+                link
+                type="primary"
+                @click="handleView(scope.row.id)"
+                v-hasPermi="['rq:iot-bom:update']"
+              >
+                物料详情
+              </el-button>
               <el-button
                 link
                 type="danger"
@@ -113,25 +129,36 @@
   <BomForm ref="formRef" :category_id="selectedId" @success="getList" />
   <!-- 添加物料列表 -->
   <MaterialList ref="materialListRef" @choose="chooseMaterial" />
+  <!-- 抽屉组件 -->
+  <MaterialListDrawer
+    :model-value="drawerVisible"
+    @update:model-value="val => drawerVisible = val"
+    :node-id="currentBomNodeId"
+  />
 </template>
 <script lang="ts" setup>
 import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
 import { dateFormatter } from '@/utils/formatTime'
 import * as BomApi from '@/api/pms/bom'
+import {CommonBomMaterialApi, CommonBomMaterialVO} from '@/api/pms/commonbommaterial'
 import BomForm from './BomForm.vue'
 import DeviceCategoryTree from './DeviceCategoryTree.vue'
 import { useTreeStore } from '@/store/modules/treeStore';
 import { ref, computed } from 'vue';
 import { handleTree } from '@/utils/tree'
 import MaterialList from "@/views/pms/bom/MaterialList.vue";
+import MaterialListDrawer from "@/views/pms/bom/MaterialListDrawer.vue";
 
 defineOptions({ name: 'Bom' })
 
+const showDrawer = ref()
+const drawerVisible = ref<boolean>(false)
 const treeStore = useTreeStore();
 const message = useMessage() // 消息弹窗
 const { t } = useI18n() // 国际化
 const isExpandAll = ref(true) // 是否展开,默认全部展开
 const loading = ref(true) // 列表的加载中
+const currentBomNodeId = ref() // 当前选中的bom节点
 const refreshTable = ref(true) // 重新渲染表格状态
 const list = ref() // 列表的数据
 const queryParams = reactive({
@@ -143,6 +170,16 @@ const queryParams = reactive({
 })
 const queryFormRef = ref() // 搜索的表单
 
+const CommonBomMaterialData = ref({
+  id: undefined,
+  deviceCategoryId: undefined,
+  bomNodeId: undefined,
+  name: undefined,
+  code: undefined,
+  materialId: undefined,
+  quantity: undefined,
+})
+
 // 从 Store 中获取左侧设备分类树选中的 节点ID
 const selectedId = computed(() => treeStore.selectedId);
 
@@ -159,12 +196,31 @@ const getList = async () => {
 
 /** 选择物料操作 */
 const materialListRef = ref()
-const openSelectMaterialForm = () => {
-  materialListRef.value.open()
+const openSelectMaterialForm = (id?: number) => {
+  materialListRef.value.open(id)
+  currentBomNodeId.value = id
 }
 
-const chooseMaterial = (row) => {
-  // formData.value.brand = row.id
+/** 查看物料详情 */
+const handleView = (nodeId) => {
+  currentBomNodeId.value = nodeId
+  drawerVisible.value = true
+  showDrawer.value.openDrawer()
+}
+
+const chooseMaterial = async(row) => {
+  // 将物料关联到bom节点
+  try {
+    CommonBomMaterialData.value.deviceCategoryId = selectedId.value
+    CommonBomMaterialData.value.bomNodeId = currentBomNodeId.value
+    CommonBomMaterialData.value.materialId = row.id
+    const data = CommonBomMaterialData.value as unknown as CommonBomMaterialVO
+    await CommonBomMaterialApi.createCommonBomMaterial(data);
+    message.success(t('common.createSuccess'))
+  } finally {
+    // formLoading.value = false
+  }
+
 }
 
 /** 搜索按钮操作 */