From 9c7505a22f6c249184228de7c6f256a4d880dfed Mon Sep 17 00:00:00 2001
From: zhaowei <zhaowei>
Date: 星期五, 14 三月 2025 09:36:06 +0800
Subject: [PATCH] 1、设备类管理页面完成增删改查功能 2、产品结构树工序及工步层级引入设备类概念由系统参数配置决定是否展示,若设备类存在时则NC文档挂载在设备类下不存在时则按照对应工序或工步层级下 3、产品结构树工序及工步层级的刀具列表实现增删改查功能 4、新增设备类管理页面并完成布局 5、产品结构树接口调整,产品结构树节点实体信息通过点击后调取接口获取 6、产品结构树搜素输入框增加防抖机制,避免每次输入都重新计算消耗性能使页面卡顿 7、新增工序及工步层级的刀具列表并添加系统权限 8、用户管理页面移除新增用户时的密码校验 9、产品结构树各层级属性信息展示由每行3列调整为每行4列 10、产品结构树页面在工序和工步层级实现对设备类的增删改查功能

---
 src/views/dnc/base/modules/ProductStructure/ProcessStep/ProcessStepInfo.vue                |   14 
 src/views/dnc/base/modules/DeviceStructure/DeviceStructureMainBottom.vue                   |    4 
 src/views/dnc/base/modules/ProductStructure/DeviceCustomType/DeviceCustomTypeTableList.vue |  167 +++
 src/views/dnc/base/modules/ProductStructure/Process/ProcessModalForm.vue                   |    1 
 src/views/dnc/base/modules/DeviceCustomTypeManagement/DeviceCustomTypeManagementModal.vue  |  223 ++++
 src/views/system/modules/UserModal.vue                                                     |   11 
 src/views/dnc/base/modules/ProductStructure/DeviceCustomType/DeviceCustomTypeModal.vue     |   80 +
 src/views/dnc/base/modules/DeviceCustomTypeManagement/WorkshopDeviceListModal.vue          |  125 ++
 src/views/dnc/base/modules/ProductStructure/ProductStructureMain.vue                       |    4 
 src/views/dnc/base/modules/ProductStructure/ProductStructureMainBottom.vue                 |   58 
 src/views/dnc/base/DeviceCustomTypeManagement.vue                                          |  189 +++
 src/views/dnc/base/modules/ProductStructure/DeviceCustomType/DeviceCustomTypeModalForm.vue |  150 ++
 src/views/dnc/common/TableContextMenu.vue                                                  |    6 
 src/views/dnc/base/modules/ProductStructure/ProductStructureTree.vue                       |  106 +
 src/views/dnc/base/modules/ProductStructure/ProductStructureTreeContextMenu.vue            |  275 ++--
 src/views/dnc/base/ProductStructure.vue                                                    |   41 
 src/views/dnc/base/modules/ProductStructure/Document/NcDocumentTableList.vue               |    2 
 src/views/dnc/base/modules/ProductStructure/Cutter/CutterModal.vue                         |   77 +
 src/views/dnc/base/modules/ProductStructure/Process/ProcessInfo.vue                        |   20 
 src/views/dnc/base/modules/ProductStructure/Product/ProductInfo.vue                        |   12 
 src/views/dnc/base/modules/ProductStructure/Cutter/CutterTableList.vue                     |  153 ++
 src/views/dnc/base/modules/ProductStructure/Component/ComponentInfo.vue                    |   16 
 .env.development                                                                           |    5 
 src/api/dnc.js                                                                             |   27 
 /dev/null                                                                                  |   34 
 src/views/dnc/base/modules/ProductStructure/ProductStructureMainTop.vue                    |  746 +++++++------
 yarn.lock                                                                                  |  350 ++++++
 src/views/dnc/base/modules/ProductStructure/Part/PartInfo.vue                              |   16 
 src/views/dnc/base/modules/DeviceStructure/DeviceStructureTree.vue                         |    1 
 src/views/dnc/base/modules/ProductStructure/Cutter/CutterModalForm.vue                     |  152 ++
 src/views/dnc/base/modules/ProductStructure/Permission/AssignPermissionModal.vue           |  207 +-
 31 files changed, 2,516 insertions(+), 756 deletions(-)

diff --git a/.env.development b/.env.development
index baa73e5..ed82b16 100644
--- a/.env.development
+++ b/.env.development
@@ -1,7 +1,8 @@
 NODE_ENV=development
-# VUE_APP_API_BASE_URL=http://192.168.124.118:3000
+# VUE_APP_API_BASE_URL=http://192.168.1.118:3000
 # VUE_APP_API_BASE_URL=http://195.0.1.10:6099
-VUE_APP_API_BASE_URL=http://192.168.124.26:9999
+VUE_APP_API_BASE_URL=http://192.168.1.12:9999
+# VUE_APP_API_BASE_URL=http://127.0.0.1:9999
 # VUE_APP_API_BASE_URL=http://195.0.1.10:8099
 VUE_APP_CAS_BASE_URL=http://cas.example.org:8443/cas
 VUE_APP_ONLINE_BASE_URL=http://fileview.jeecg.com/onlinePreview
\ No newline at end of file
diff --git a/src/api/dnc.js b/src/api/dnc.js
index ef1c3db..4875d2a 100644
--- a/src/api/dnc.js
+++ b/src/api/dnc.js
@@ -15,7 +15,7 @@
   // 鑾峰彇浜у搧缁撴瀯鏍�
   getProductStructureTreeApi: () => getAction('/nc/product/getTree'),
   // 鑾峰彇浜у搧缁撴瀯鏍戣妭鐐瑰疄浣撲俊鎭�
-  getProductStructureTreeNodeEntityApi:params=> getAction('/nc/product/getTreeById',params),
+  getProductStructureTreeNodeEntityApi: params => getAction('/nc/product/getTreeById', params),
   // 鑾峰彇鎵�鏈夎澶囩殑鍒楄〃
   getAllDeviceListApi: (type) => getAction('/nc/device/list/all'),
   // 瀵煎叆NC绋嬪簭/鏂囨。
@@ -51,30 +51,32 @@
   appointCurrentDocumentVersionApi: fileId => putAction(`/nc/file/assign/version/${fileId}`),
   // 鏂囨。姣斿
   fileCompareApi: fileIdArray => getAction(`/nc/file/comparison/${fileIdArray[0]}/${fileIdArray[1]}`),
-  // 鑾峰彇鎵�鏈夐儴闂ㄥ垪琛�
-  getAllDepartmentsListApi: () => getAction('/ucenter/depart/list/all'),
-  // 鑾峰彇鎵�鏈夌敤鎴峰垪琛�
+  // 鏉冮檺閰嶇疆寮圭獥涓幏鍙栨墍鏈夎溅闂村垪琛�
+  getAllDepartmentsListApi: () => getAction('/mdc/mdcProduction/queryTreeListByConfig'),
+  // 鏉冮檺閰嶇疆寮圭獥涓幏鍙栨墍鏈夌敤鎴峰垪琛�
   getAllUsersListApi: () => getAction('/sys/user/list'),
-  // 鑾峰彇鏈夋潈闄愮殑閮ㄩ棬鍒楄〃
+  // 鏉冮檺閰嶇疆寮圭獥涓幏鍙栨湁鏉冮檺鐨勯儴闂ㄥ垪琛�
   getHasPermissionDepartApi: ({ type, id }) => getAction(`/nc/product/get/perm/depart/${type}/${id}`),
-  // 鑾峰彇鏈夋潈闄愮殑鐢ㄦ埛鍒楄〃
+  // 鏉冮檺閰嶇疆寮圭獥涓幏鍙栨湁鏉冮檺鐨勭敤鎴峰垪琛�
   getHasPermissionUserApi: ({ type, id }) => getAction(`/nc/product/get/perm/user/${type}/${id}`),
-  // 鍒嗛厤閮ㄩ棬鏉冮檺
+  // 鏉冮檺閰嶇疆寮圭獥涓垎閰嶉儴闂ㄦ潈闄�
   assignPermissionToDepart: ({ treeNodeType, treeNodeId, isAssignSonNode, departIdArray }) => {
     return postAction(`/nc/product/assign/add/department/${treeNodeType}/${treeNodeId}/${isAssignSonNode}`, departIdArray)
   },
-  // 绉婚櫎閮ㄩ棬鏉冮檺
+  // 鏉冮檺閰嶇疆寮圭獥涓Щ闄ら儴闂ㄦ潈闄�
   removePermissionFromDepart: ({ treeNodeType, treeNodeId, isAssignSonNode, departIdArray }) => {
     return postAction(`/nc/product/assign/remove/department/${treeNodeType}/${treeNodeId}/${isAssignSonNode}`, departIdArray)
   },
-  // 鍒嗛厤鐢ㄦ埗鏉冮檺
+  // 鏉冮檺閰嶇疆寮圭獥涓垎閰嶇敤鎴舵潈闄�
   assignPermissionToUser: ({ treeNodeType, treeNodeId, isAssignSonNode, userIdArray }) => {
     return postAction(`/nc/product/assign/add/user/${treeNodeType}/${treeNodeId}/${isAssignSonNode}`, userIdArray)
   },
-  // 绉婚櫎鐢ㄦ埛鏉冮檺
+  // 鏉冮檺閰嶇疆寮圭獥涓Щ闄ょ敤鎴锋潈闄�
   removePermissionFromUser: ({ treeNodeType, treeNodeId, isAssignSonNode, userIdArray }) => {
     return postAction(`/nc/product/assign/remove/user/${treeNodeType}/${treeNodeId}/${isAssignSonNode}`, userIdArray)
   },
+  // 閫氳繃杞﹂棿ID鑾峰彇璁惧绫诲垪琛�
+  getDeviceCustomTypeListByProductionIdApi: productionId => getAction('/nc/deviceManagement/getDeviceManagementListByProductionId', { productionId }),
 
   //-------------------------璁惧缁撴瀯鏍�------------------------------------------------
   // 鑾峰彇璁惧鏍�
@@ -90,5 +92,8 @@
   // 绉婚櫎鐢ㄦ埛鏉冮檺
   removeDeviceTreePermissionFromUser: ({ treeNodeType, treeNodeId, isAssignSonNode, userIdArray }) => {
     return postAction(`/nc/device/assign/remove/user/${treeNodeType}/${treeNodeId}/${isAssignSonNode}`, userIdArray)
-  }
+  },
+  //-------------------------璁惧绫荤鐞�------------------------------------------------
+  // 鑾峰彇鏈夋潈闄愮殑杞﹂棿鏍戝垪琛�
+  getHasPermissionWorkshopTreeApi: () => getAction('/nc/deviceManagement/getProductionIdsByUserId')
 }
\ No newline at end of file
diff --git a/src/views/dnc/base/DeviceCustomTypeManagement.vue b/src/views/dnc/base/DeviceCustomTypeManagement.vue
new file mode 100644
index 0000000..10187b2
--- /dev/null
+++ b/src/views/dnc/base/DeviceCustomTypeManagement.vue
@@ -0,0 +1,189 @@
+<template>
+  <a-card :bordered="false">
+
+    <!-- 鏌ヨ鍖哄煙 -->
+    <div class="table-page-search-wrapper">
+      <a-form layout="inline" @keyup.enter.native="searchQuery">
+        <a-row :gutter="24">
+          <a-col :md="4" :sm="4">
+            <a-form-item label="杞﹂棿">
+              <a-select v-model="queryParam.productionId" placeholder="璇烽�夋嫨杞﹂棿">
+                <a-select-option v-for="item in workshopTreeData" :key="item.id">
+                  {{ item.productionName }}
+                </a-select-option>
+              </a-select>
+            </a-form-item>
+          </a-col>
+
+          <a-col :md="4" :sm="4">
+            <a-form-item label="璁惧绫诲悕绉�">
+              <a-input placeholder="璇疯緭鍏ヨ澶囩被鍚嶇О" v-model="queryParam.deviceManagementName"></a-input>
+            </a-form-item>
+          </a-col>
+
+          <a-col :md="4" :sm="4">
+            <a-space>
+              <a-button type="primary" @click="searchQuery" icon="search">鏌ヨ</a-button>
+              <a-button type="primary" @click="searchReset" icon="reload">閲嶇疆</a-button>
+            </a-space>
+          </a-col>
+        </a-row>
+      </a-form>
+    </div>
+
+    <!-- 鎿嶄綔鎸夐挳鍖哄煙 -->
+    <div class="table-operator" style="border-top: 5px">
+      <a-button @click="handleAdd" type="primary" icon="plus">娣诲姞璁惧绫�</a-button>
+      <!--      <a-button type="primary" icon="download" @click="handleExportXls('璁惧绫讳俊鎭�')">瀵煎嚭</a-button>-->
+      <a-dropdown v-if="selectedRowKeys.length > 0">
+        <a-menu slot="overlay" @click="handleMenuClick">
+          <a-menu-item key="1">
+            <a-icon type="delete" @click="batchDel"/>
+            鍒犻櫎
+          </a-menu-item>
+        </a-menu>
+        <a-button style="margin-left: 8px">
+          鎵归噺鎿嶄綔
+          <a-icon type="down"/>
+        </a-button>
+      </a-dropdown>
+    </div>
+
+    <!-- table鍖哄煙-begin -->
+    <div>
+      <div class="ant-alert ant-alert-info" style="margin-bottom: 16px;">
+        <i class="anticon anticon-info-circle ant-alert-icon"></i>宸查�夋嫨&nbsp;<a style="font-weight: 600">{{
+          selectedRowKeys.length
+        }}</a>椤�&nbsp;&nbsp;
+        <a style="margin-left: 24px" @click="onClearSelected">娓呯┖</a>
+      </div>
+
+      <a-table
+        ref="table"
+        bordered
+        size="middle"
+        rowKey="id"
+        :scroll="{y:465}"
+        :columns="columns"
+        :dataSource="dataSource"
+        :pagination="ipagination"
+        :loading="loading"
+        :rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
+        @change="handleTableChange">
+        <template slot="action" slot-scope="text, record">
+          <a @click="handleEdit(record)">缂栬緫</a>
+
+          <a-divider type="vertical"/>
+
+          <a-popconfirm title="纭畾鍒犻櫎鍚�?" @confirm="() => handleDelete(record.id)">
+            <a>鍒犻櫎</a>
+          </a-popconfirm>
+        </template>
+      </a-table>
+    </div>
+    <!-- table鍖哄煙-end -->
+
+    <DeviceCustomTypeManagementModal ref="modalForm" :workshopTreeData="workshopTreeData" @ok="loadData"/>
+  </a-card>
+</template>
+
+<script>
+import { JeecgListMixin } from '@/mixins/JeecgListMixin'
+import dncApi from '@/api/dnc'
+import DeviceCustomTypeManagementModal
+  from '@views/dnc/base/modules/DeviceCustomTypeManagement/DeviceCustomTypeManagementModal.vue'
+
+export default {
+  name: 'DeviceCustomTypeManagement',
+  mixins: [JeecgListMixin],
+  components: {
+    DeviceCustomTypeManagementModal
+  },
+  data() {
+    return {
+      columns: [
+        {
+          title: '#',
+          dataIndex: '',
+          key: 'rowIndex',
+          width: 60,
+          align: 'center',
+          customRender: function(t, r, index) {
+            return parseInt(index) + 1
+          }
+        },
+        {
+          title: '杞﹂棿',
+          align: 'center',
+          width: 200,
+          dataIndex: 'productionId_dictText'
+        },
+        {
+          title: '璁惧绫�',
+          align: 'center',
+          width: 200,
+          dataIndex: 'deviceManagementCode'
+        },
+        {
+          title: '璁惧绫诲悕绉�',
+          align: 'center',
+          width: 200,
+          dataIndex: 'deviceManagementName'
+        },
+        {
+          title: '璁惧缁勭紪鍙�',
+          align: 'center',
+          dataIndex: 'equipmentIds',
+          width: 400,
+          ellipsis: true
+        },
+        {
+          title: '鎿嶄綔',
+          dataIndex: 'action',
+          scopedSlots: { customRender: 'action' },
+          align: 'center',
+          width: 80
+        }
+      ],
+      url: {
+        list: '/nc/deviceManagement/query',
+        delete: '/nc/deviceManagement/delete',
+        deleteBatch: '/nc/deviceManagement/deleteBatch'
+      },
+      workshopTreeData: []
+    }
+  },
+  created() {
+    this.getWorkshopListByApi()
+  },
+  methods: {
+    // 璋冪敤鎺ュ彛鑾峰彇鏌ヨ鍖哄煙杞﹂棿鏍戝垪琛�
+    getWorkshopListByApi() {
+      dncApi.getHasPermissionWorkshopTreeApi()
+        .then(res => {
+          if (res.success) {
+            this.workshopTreeData = res.result
+          }
+        })
+    },
+
+    handleAdd: function() {
+      this.$refs.modalForm.add()
+      this.$refs.modalForm.title = '鏂板'
+      this.$refs.modalForm.disabledEdit = false
+    },
+
+    handleEdit: function(record) {
+      this.$refs.modalForm.edit(record)
+      this.$refs.modalForm.title = '缂栬緫'
+      this.$refs.modalForm.disabledEdit = true
+    },
+
+    handleMenuClick(e) {
+      if (e.key == 1) {
+        this.batchDel()
+      }
+    }
+  }
+}
+</script>
\ No newline at end of file
diff --git a/src/views/dnc/base/ProductStructure.vue b/src/views/dnc/base/ProductStructure.vue
index 2095795..e679568 100644
--- a/src/views/dnc/base/ProductStructure.vue
+++ b/src/views/dnc/base/ProductStructure.vue
@@ -11,30 +11,35 @@
 
     <!--瀵煎叆鏂囦欢鍏叡寮圭獥-->
     <ImportFileModal/>
+
+    <!--鏂板鎴栫紪杈戣澶囩被鍏叡寮圭獥-->
+    <DeviceCustomTypeModal/>
   </a-card>
 </template>
 
 <script>
-  import ProductStructureTree from './modules/ProductStructure/ProductStructureTree'
-  import ProductStructureMain from './modules/ProductStructure/ProductStructureMain'
-  import ImportFileModal from '../common/ImportFileModal'
+import ProductStructureTree from './modules/ProductStructure/ProductStructureTree'
+import ProductStructureMain from './modules/ProductStructure/ProductStructureMain'
+import ImportFileModal from '../common/ImportFileModal'
+import DeviceCustomTypeModal from '@views/dnc/base/modules/ProductStructure/DeviceCustomType/DeviceCustomTypeModal.vue'
 
-  export default {
-    name: 'ProductStructure',
-    components: {
-      ProductStructureTree,
-      ProductStructureMain,
-      ImportFileModal
-    },
-    data() {
-      return {}
-    },
-    methods: {}
-  }
+export default {
+  name: 'ProductStructure',
+  components: {
+    DeviceCustomTypeModal,
+    ProductStructureTree,
+    ProductStructureMain,
+    ImportFileModal
+  },
+  data() {
+    return {}
+  },
+  methods: {}
+}
 </script>
 
 <style scoped lang="less">
-  /deep/ .ant-card-body {
-    padding: 8px;
-  }
+/deep/ .ant-card-body {
+  padding: 8px;
+}
 </style>
\ No newline at end of file
diff --git a/src/views/dnc/base/modules/DeviceCustomTypeManagement/DeviceCustomTypeManagementModal.vue b/src/views/dnc/base/modules/DeviceCustomTypeManagement/DeviceCustomTypeManagementModal.vue
new file mode 100644
index 0000000..04289e6
--- /dev/null
+++ b/src/views/dnc/base/modules/DeviceCustomTypeManagement/DeviceCustomTypeManagementModal.vue
@@ -0,0 +1,223 @@
+<template>
+  <a-modal
+    :title="title"
+    :width="700"
+    @cancel="close"
+    :visible="visible"
+    :footer="null"
+    style="height: 100%;overflow: auto;padding-bottom: 53px;">
+
+    <a-spin :spinning="confirmLoading">
+      <a-form-model ref="form" :form="form" :model="model" :rules="validatorRules" :labelCol="labelCol"
+                    :wrapperCol="wrapperCol">
+        <a-row :gutter="24">
+          <a-col :span="12">
+            <a-form-model-item label="杞﹂棿鍒嗛厤" prop="productionId">
+              <a-select v-model="model.productionId" placeholder="璇烽�夋嫨杞﹂棿" @change="handleSelectChange"
+                        :disabled="disabledEdit">
+                <a-select-option v-for="item in workshopTreeData" :key="item.id">
+                  {{ item.productionName }}
+                </a-select-option>
+              </a-select>
+            </a-form-model-item>
+          </a-col>
+
+          <a-col :span="12">
+            <a-form-model-item label="璁惧绫�" prop="deviceManagementCode">
+              <a-input allow-clear placeholder="璇疯緭鍏ヨ澶囩被" :readOnly="disabledEdit"
+                       v-model="model.deviceManagementCode"/>
+            </a-form-model-item>
+          </a-col>
+        </a-row>
+
+        <a-row :gutter="24">
+          <a-col :span="12">
+            <a-form-model-item label="璁惧绫诲悕绉�" prop="deviceManagementName">
+              <a-input allow-clear placeholder="璇疯緭鍏ヨ澶囩被鍚嶇О" :readOnly="disabledEdit"
+                       v-model="model.deviceManagementName"/>
+            </a-form-model-item>
+          </a-col>
+        </a-row>
+
+        <a-row :gutter="24">
+          <a-col :span="24">
+            <a-form-model-item label="璁惧缁勭紪鍙�" :labelCol="labelColLong" :wrapperCol="wrapperColLong"
+                               prop="equipmentIds">
+              <a-input-search v-model="model.equipmentIds" :disabled="!model.productionId" @search="deviceSearch"
+                              enter-button :placeholder='!model.productionId?"璇烽�夋嫨杞﹂棿":"璇烽�夋嫨璁惧缁勭紪鍙�"'/>
+            </a-form-model-item>
+          </a-col>
+        </a-row>
+      </a-form-model>
+    </a-spin>
+
+
+    <div class="drawer-bottom-button">
+      <a-space>
+        <a-button @click="close">鍙栨秷</a-button>
+        <a-button @click="handleSubmit" type="primary" :loading="confirmLoading">鎻愪氦</a-button>
+      </a-space>
+    </div>
+
+    <workshop-device-list-modal :productionId="model.productionId" ref="deviceListModel"
+                                @sendSelectedRowKeys="getDeviceRowKeys"/>
+  </a-modal>
+</template>
+
+<script>
+import { httpAction } from '@/api/manage'
+import WorkshopDeviceListModal from '@views/dnc/base/modules/DeviceCustomTypeManagement/WorkshopDeviceListModal.vue'
+
+export default {
+  name: 'DeviceCustomTypeManagementModal',
+  components: { WorkshopDeviceListModal },
+  props: {
+    workshopTreeData: {
+      type: Array
+    }
+  },
+  data() {
+    return {
+      title: '',
+      form: this.$form.createForm(this),
+      validatorRules: {
+        productionId: [
+          {
+            required: true, message: '璇烽�夋嫨杞﹂棿!'
+          }
+        ],
+        deviceManagementCode: [
+          {
+            required: true, message: '璇疯緭鍏ヨ澶囩被!'
+          }
+        ],
+        deviceManagementName: [
+          {
+            required: true, message: '璇疯緭鍏ヨ澶囩被鍚嶇О!'
+          }
+        ],
+        equipmentIds: [
+          {
+            required: true, message: '璇烽�夋嫨璁惧缂栧彿!'
+          }
+        ]
+      },
+      visible: false,
+      disabledEdit: false,
+      model: {},
+      labelCol: {
+        xs: { span: 24 },
+        sm: { span: 8 }
+      },
+      wrapperCol: {
+        xs: { span: 24 },
+        sm: { span: 14 }
+      },
+      labelColLong: {
+        xs: { span: 24 },
+        sm: { span: 4 }
+      },
+      wrapperColLong: {
+        xs: { span: 24 },
+        sm: { span: 19 }
+      },
+      confirmLoading: false,
+      url: {
+        add: '/nc/deviceManagement/add',
+        edit: '/nc/deviceManagement/edit'
+      }
+    }
+  },
+  methods: {
+    // 杞﹂棿閫夋嫨鍣ㄥ�煎彂鐢熸敼鍙樻椂瑙﹀彂娓呯┖宸查�夋嫨鐨勮澶囩紪鍙风粍
+    handleSelectChange() {
+      if (this.model.equipmentIds) delete this.model.equipmentIds
+    },
+
+    /**
+     * 褰撹澶囬�夋嫨瀹屾垚鍚庢彁浜ゅ皢宸查�夋嫨鐨勮澶囩紪鍙峰垪琛ㄤ互閫楀彿闅斿紑鎷嗗垎鍒板弬鏁颁腑
+     * @param selectedRowKeys
+     */
+    getDeviceRowKeys(selectedRowKeys) {
+      if (selectedRowKeys.length === 0) return
+      this.$set(this.model, 'equipmentIds', selectedRowKeys.join(','))
+      this.$refs.form.clearValidate()
+    },
+
+    deviceSearch() {
+      this.$refs.deviceListModel.openPage()
+      this.$refs.deviceListModel.title = '閫夋嫨璁惧'
+      this.$refs.deviceListModel.disableSubmit = false
+    },
+
+    add() {
+      this.edit({})
+    },
+
+    edit(record) {
+      this.visible = true
+      this.model = Object.assign({}, record)
+    },
+
+    handleSubmit() {
+      const that = this
+      // 瑙﹀彂琛ㄥ崟楠岃瘉
+      this.$refs.form.validate(valid => {
+        if (valid) {
+          that.confirmLoading = true
+          let apiUrl, method
+          if (!this.model.id) {
+            apiUrl = this.url.add
+            method = 'post'
+          } else {
+            apiUrl = this.url.edit
+            method = 'put'
+          }
+          httpAction(apiUrl, this.model, method)
+            .then((res) => {
+              if (res.success) {
+                that.$notification.success({
+                  message: '娑堟伅',
+                  description: res.message
+                })
+                that.$emit('ok')
+                that.close()
+              } else {
+                that.$notification.warning({
+                  message: '娑堟伅',
+                  description: res.message
+                })
+              }
+            })
+            .finally(() => {
+              that.confirmLoading = false
+            })
+        } else {
+          return false
+        }
+      })
+    },
+
+    close() {
+      this.$emit('close')
+      this.visible = false
+      this.$refs.form.clearValidate()
+    }
+  }
+
+}
+</script>
+
+<style scoped>
+.drawer-bottom-button {
+  position: absolute;
+  bottom: -40px;
+  width: 100%;
+  border-top: 1px solid #e8e8e8;
+  padding: 10px 16px;
+  text-align: right;
+  left: 0;
+  background: #fff;
+  border-radius: 0 0 2px 2px;
+}
+</style>
\ No newline at end of file
diff --git a/src/views/dnc/base/modules/DeviceCustomTypeManagement/WorkshopDeviceListModal.vue b/src/views/dnc/base/modules/DeviceCustomTypeManagement/WorkshopDeviceListModal.vue
new file mode 100644
index 0000000..eae46ff
--- /dev/null
+++ b/src/views/dnc/base/modules/DeviceCustomTypeManagement/WorkshopDeviceListModal.vue
@@ -0,0 +1,125 @@
+<template>
+  <a-modal :title="title" :width="1000" :visible="visible" :maskClosable="false" @ok="handleOk" @cancel="close"
+           cancelText="鍏抽棴">
+    <div class="table-page-search-wrapper">
+      <a-form layout="inline" @keyup.enter.native="searchQuery">
+        <a-row :gutter="24">
+          <a-col :md="8" :sm="8">
+            <a-form-item label="璁惧缂栧彿">
+              <a-input placeholder="璇疯緭鍏ヨ澶囩紪鍙�" v-model="queryParam.equipmentId"/>
+            </a-form-item>
+          </a-col>
+
+          <a-col :md="8" :sm="8">
+            <a-form-item label="璁惧鍚嶇О">
+              <a-input placeholder="璇疯緭鍏ヨ澶囧悕绉�" v-model="queryParam.equipmentName"/>
+            </a-form-item>
+          </a-col>
+
+          <a-col :md="3" :sm="3">
+            <a-space>
+              <a-button type="primary" @click="searchQuery" icon="search">鏌ヨ</a-button>
+              <a-button type="primary" @click="searchReset" icon="reload">閲嶇疆</a-button>
+            </a-space>
+          </a-col>
+        </a-row>
+      </a-form>
+    </div>
+
+    <!-- table鍖哄煙-begin -->
+    <a-table ref="table" size="middle" bordered rowKey="equipmentId" :scroll="{y: 300}" :columns="columns"
+             :dataSource="dataSource" :pagination="ipagination" :loading="loading"
+             :rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange, type:'checkbox'}"
+             @change="handleTableChange">
+    </a-table>
+    <!-- table鍖哄煙-end -->
+  </a-modal>
+</template>
+
+<script>
+import { JeecgListMixin } from '@/mixins/JeecgListMixin'
+
+export default {
+  name: 'WorkshopDeviceListModal',
+  mixins: [JeecgListMixin],
+  components: {},
+  props: {
+    productionId: {
+      type: String
+    }
+  },
+  data() {
+    return {
+      title: '',
+      visible: false,
+      disableMixinCreated: true,
+      columns: [
+        {
+          title: '璁惧缂栧彿',
+          align: 'center',
+          dataIndex: 'equipmentId'
+        },
+        {
+          title: '璁惧鍚嶇О',
+          align: 'center',
+          dataIndex: 'equipmentName'
+        },
+        {
+          title: '璁惧鍨嬪彿',
+          align: 'center',
+          dataIndex: 'equipmentModel'
+        },
+        {
+          title: '鏈哄簥IP',
+          align: 'center',
+          dataIndex: 'equipmentIp'
+        },
+        {
+          title: '椹卞姩绫诲瀷',
+          align: 'center',
+          dataIndex: 'driveType'
+        },
+        {
+          title: '绔彛',
+          align: 'center',
+          dataIndex: 'dataPort'
+        },
+        {
+          title: '鏁版帶绯荤粺',
+          align: 'center',
+          dataIndex: 'controlSystem'
+        }
+      ],
+      url: {
+        list: '/nc/deviceManagement/getEquipmentListByProductionId'
+      }
+    }
+  },
+  methods: {
+    openPage() {
+      this.visible = true
+      this.onClearSelected()
+      this.dataSource = []
+      this.queryParam = Object.assign({}, { productionId: this.productionId })
+      this.loadData()
+    },
+    searchQuery() {
+      this.loadData()
+      this.onClearSelected()
+    },
+    searchReset() {
+      this.queryParam = Object.assign({}, { productionId: this.productionId })
+      this.loadData()
+      this.onClearSelected()
+    },
+    close() {
+      this.$emit('close')
+      this.visible = false
+    },
+    handleOk() {
+      this.$emit('sendSelectedRowKeys', this.selectedRowKeys)
+      this.close()
+    }
+  }
+}
+</script>
\ No newline at end of file
diff --git a/src/views/dnc/base/modules/DeviceStructure/DeviceStructureMainBottom.vue b/src/views/dnc/base/modules/DeviceStructure/DeviceStructureMainBottom.vue
index 9bfa203..fa67d6c 100644
--- a/src/views/dnc/base/modules/DeviceStructure/DeviceStructureMainBottom.vue
+++ b/src/views/dnc/base/modules/DeviceStructure/DeviceStructureMainBottom.vue
@@ -1,6 +1,6 @@
 <template>
-  <div>
-    <a-tabs v-model="activeTabKey" @change="handleTabChange">
+  <div style="height: 100%">
+    <a-tabs style="height: 100%" v-model="activeTabKey" @change="handleTabChange">
       <a-tab-pane :key="1" tab="璁惧灞炴��" v-if="currentTreeNodeInfo.type===2&&!currentDocumentInfo.docId">
         <DeviceInfo :currentLevelDetails="currentTreeNodeInfo.entity" :size="containerSize"/>
       </a-tab-pane>
diff --git a/src/views/dnc/base/modules/DeviceStructure/DeviceStructureTree.vue b/src/views/dnc/base/modules/DeviceStructure/DeviceStructureTree.vue
index a930ddd..fef8b3d 100644
--- a/src/views/dnc/base/modules/DeviceStructure/DeviceStructureTree.vue
+++ b/src/views/dnc/base/modules/DeviceStructure/DeviceStructureTree.vue
@@ -82,6 +82,7 @@
       getTreeDataByApi() {
         this.loading = true
         this.cardLoading = true
+        this.treeDataSource = []
         dncApi.getDeviceTreeDataApi()
           .then(res => {
             if (res.success) {
diff --git a/src/views/dnc/base/modules/ProductStructure/Component/ComponentInfo.vue b/src/views/dnc/base/modules/ProductStructure/Component/ComponentInfo.vue
index b9c1cf1..a886650 100644
--- a/src/views/dnc/base/modules/ProductStructure/Component/ComponentInfo.vue
+++ b/src/views/dnc/base/modules/ProductStructure/Component/ComponentInfo.vue
@@ -1,6 +1,6 @@
 <template>
-  <a-descriptions bordered :size="size">
-    <a-descriptions-item label="鍚嶇О">{{currentLevelDetails.componentName}}</a-descriptions-item>
+  <a-descriptions bordered :size="size" :column="4">
+    <a-descriptions-item label="鍚嶇О" :span="2">{{currentLevelDetails.componentName}}</a-descriptions-item>
     <a-descriptions-item label="浠e彿">{{currentLevelDetails.componentCode}}</a-descriptions-item>
     <a-descriptions-item label="鍨嬪彿	">{{currentLevelDetails.componentModel}}</a-descriptions-item>
     <a-descriptions-item label="鐗╂枡缂栫爜">{{currentLevelDetails.materielCode}}</a-descriptions-item>
@@ -10,12 +10,12 @@
     <a-descriptions-item label="鐢熶骇绫诲瀷">{{currentLevelDetails.produceType}}</a-descriptions-item>
     <a-descriptions-item label="澶勭悊绫诲瀷	">{{currentLevelDetails.processType}}</a-descriptions-item>
     <a-descriptions-item label="缁撴瀯绫诲瀷	">{{currentLevelDetails.structureType}}</a-descriptions-item>
-    <a-descriptions-item label="閲嶉噺" :span="2">{{currentLevelDetails.componentWeight}}</a-descriptions-item>
-    <a-descriptions-item label="鍒涘缓浜�">{{currentLevelDetails.createName}}</a-descriptions-item>
-    <a-descriptions-item label="鍒涘缓鏃堕棿" :span="2">{{currentLevelDetails.createTime}}</a-descriptions-item>
-    <a-descriptions-item label="淇敼浜�">{{currentLevelDetails.updateName}}</a-descriptions-item>
-    <a-descriptions-item label="淇敼鏃堕棿" :span="2">{{currentLevelDetails.updateTime}}</a-descriptions-item>
-    <a-descriptions-item label="鎻忚堪" :span="3">{{currentLevelDetails.description}}</a-descriptions-item>
+    <a-descriptions-item label="閲嶉噺" >{{currentLevelDetails.componentWeight}}</a-descriptions-item>
+    <a-descriptions-item label="鍒涘缓浜�">{{currentLevelDetails.createBy_dictText}}</a-descriptions-item>
+    <a-descriptions-item label="鍒涘缓鏃堕棿">{{currentLevelDetails.createTime}}</a-descriptions-item>
+    <a-descriptions-item label="淇敼浜�">{{currentLevelDetails.updateBy_dictText}}</a-descriptions-item>
+    <a-descriptions-item label="淇敼鏃堕棿">{{currentLevelDetails.updateTime}}</a-descriptions-item>
+    <a-descriptions-item label="鎻忚堪" :span="4">{{currentLevelDetails.description}}</a-descriptions-item>
   </a-descriptions>
 </template>
 
diff --git a/src/views/dnc/base/modules/ProductStructure/Cutter/CutterInfo.vue b/src/views/dnc/base/modules/ProductStructure/Cutter/CutterInfo.vue
deleted file mode 100644
index 62a3182..0000000
--- a/src/views/dnc/base/modules/ProductStructure/Cutter/CutterInfo.vue
+++ /dev/null
@@ -1,34 +0,0 @@
-<template>
-  <a-descriptions bordered :size="size">
-    <a-descriptions-item label="鍒�鍏峰悕绉�">{{currentLevelDetails.cutterName}}</a-descriptions-item>
-    <a-descriptions-item label="鍒�鍏风紪鍙�">{{currentLevelDetails.cutterCode}}</a-descriptions-item>
-    <a-descriptions-item label="鍒�鍏风被鍨�">{{currentLevelDetails.cutterType}}</a-descriptions-item>
-    <a-descriptions-item label="棰濆畾瀵垮懡">{{currentLevelDetails.lifetime}}</a-descriptions-item>
-    <a-descriptions-item label="鍒�浣�">{{currentLevelDetails.cutterSpacing}}</a-descriptions-item>
-    <a-descriptions-item label="鏁伴噺">{{currentLevelDetails.quantity}}</a-descriptions-item>
-    <a-descriptions-item label="鎻忚堪" :span="3">{{currentLevelDetails.description}}</a-descriptions-item>
-  </a-descriptions>
-</template>
-
-<script>
-export default {
-  name: 'CutterInfo',
-  components: {},
-  props: {
-    currentLevelDetails: {
-      type: Object
-    },
-    size: {
-      type: String
-    }
-  },
-  data() {
-    return {}
-  },
-  methods: {}
-}
-</script>
-
-<style scoped>
-
-</style>
\ No newline at end of file
diff --git a/src/views/dnc/base/modules/ProductStructure/Cutter/CutterModal.vue b/src/views/dnc/base/modules/ProductStructure/Cutter/CutterModal.vue
new file mode 100644
index 0000000..2503b1c
--- /dev/null
+++ b/src/views/dnc/base/modules/ProductStructure/Cutter/CutterModal.vue
@@ -0,0 +1,77 @@
+<template>
+  <j-modal
+    :title="title"
+    :width="width"
+    :visible="visible"
+    switchFullscreen
+    :maskClosable="false"
+    @ok="handleOk"
+    @cancel="handleCancel"
+    :okButtonProps="{ class:{'jee-hidden': disableSubmit} }"
+    cancelText="鍏抽棴">
+    <CutterModalForm ref="realForm" @ok="submitCallback" :disableSubmit="disableSubmit"/>
+  </j-modal>
+</template>
+
+<script>
+import CutterModalForm from './CutterModalForm.vue'
+
+export default {
+  name: 'CutterModal',
+  components: {
+    CutterModalForm
+  },
+  props: {
+    currentTreeNodeInfo: {
+      type: Object
+    }
+  },
+  data() {
+    return {
+      title: '',
+      width: 700,
+      visible: false,
+      disableSubmit: false
+    }
+  },
+  methods: {
+    // 娣诲姞鎵�鐢ㄥ垁鍏�
+    handleCutterAdd() {
+      const { id, type } = this.currentTreeNodeInfo
+      this.visible = true
+      this.$nextTick(() => {
+        if (this.$refs.realForm) {
+          this.$refs.realForm.add({
+            attributionId: id,
+            attributionType: type
+          })
+        }
+      })
+    },
+
+    // 缂栬緫鍒�鍏蜂俊鎭�
+    handleCutterEdit(record) {
+      this.visible = true
+      this.$nextTick(() => {
+        if (this.$refs.realForm) {
+          this.$refs.realForm.edit({ ...record })
+        }
+      })
+    },
+
+    handleOk() {
+      this.$refs.realForm.submitForm()
+    },
+
+    submitCallback() {
+      this.$emit('submitSuccess')
+      this.visible = false
+    },
+
+    handleCancel() {
+      this.$emit('close')
+      this.visible = false
+    }
+  }
+}
+</script>
\ No newline at end of file
diff --git a/src/views/dnc/base/modules/ProductStructure/Cutter/CutterModalForm.vue b/src/views/dnc/base/modules/ProductStructure/Cutter/CutterModalForm.vue
new file mode 100644
index 0000000..3c5c2c9
--- /dev/null
+++ b/src/views/dnc/base/modules/ProductStructure/Cutter/CutterModalForm.vue
@@ -0,0 +1,152 @@
+<template>
+  <a-spin :spinning="confirmLoading">
+    <a-form-model ref="form" :model="model" :rules="validatorRules" :labelCol="labelCol" :wrapperCol="wrapperCol">
+      <a-row>
+        <a-col :span="12">
+          <a-form-model-item label="鍒�鍏风紪鍙�" prop="cutterCode">
+            <a-input v-model="model.cutterCode" placeholder="璇疯緭鍏ュ垁鍏风紪鍙�" :readOnly="disableSubmit"/>
+          </a-form-model-item>
+        </a-col>
+        <a-col :span="12">
+          <a-form-model-item label="鍒�鍏峰悕绉�" prop="cutterName">
+            <a-input v-model="model.cutterName" placeholder="璇疯緭鍏ュ垁鍏峰悕绉�" :readOnly="disableSubmit"/>
+          </a-form-model-item>
+        </a-col>
+      </a-row>
+
+      <a-row>
+        <a-col :span="12">
+          <a-form-model-item label="鍒�鍏风被鍨�">
+            <j-dict-select-tag v-model="model.cutterType" placeholder="璇烽�夋嫨鍒�鍏风被鍨�" dictCode="cutter_type"
+                               :disabled="disableSubmit"/>
+          </a-form-model-item>
+        </a-col>
+        <a-col :span="12">
+          <a-form-model-item label="棰濆畾瀵垮懡">
+            <a-input-number v-model="model.lifetime" :min="0" placeholder="璇疯緭鍏ラ瀹氬鍛�" style="width: 100%"
+                            :disabled="disableSubmit"/>
+          </a-form-model-item>
+        </a-col>
+      </a-row>
+
+      <a-row>
+        <a-col :span="12">
+          <a-form-model-item label="鍒�浣�">
+            <a-input v-model="model.cutterSpacing" placeholder="璇疯緭鍏ュ垁浣�" :readOnly="disableSubmit"/>
+          </a-form-model-item>
+        </a-col>
+        <a-col :span="12">
+          <a-form-model-item label="鍒�鍏锋暟閲�">
+            <a-input-number v-model="model.quantity" placeholder="璇疯緭鍏ュ垁鍏锋暟閲�" :min="1" style="width: 100%"
+                            :disabled="disableSubmit"/>
+          </a-form-model-item>
+        </a-col>
+      </a-row>
+
+      <a-row>
+        <a-col :span="24">
+          <a-form-model-item label="鎻忚堪" :labelCol="labelColLong" :wrapperCol="wrapperColLong">
+            <a-textarea v-model="model.description" placeholder="璇疯緭鍏ユ弿杩�" :readOnly="disableSubmit"/>
+          </a-form-model-item>
+        </a-col>
+      </a-row>
+    </a-form-model>
+  </a-spin>
+</template>
+
+<script>
+import { httpAction } from '@/api/manage'
+
+export default {
+  name: 'CutterModalForm',
+  components: {},
+  props: {
+    disableSubmit: {
+      type: Boolean
+    }
+  },
+  data() {
+    return {
+      model: {
+        quantity: 1
+      },
+      labelCol: {
+        xs: { span: 24 },
+        sm: { span: 8 }
+      },
+      wrapperCol: {
+        xs: { span: 24 },
+        sm: { span: 14 }
+      },
+      labelColLong: {
+        xs: { span: 24 },
+        sm: { span: 4 }
+      },
+      wrapperColLong: {
+        xs: { span: 24 },
+        sm: { span: 19 }
+      },
+      confirmLoading: false,
+      validatorRules: {
+        cutterCode: [
+          { required: true, message: '璇疯緭鍏ュ垁鍏风紪鍙�!' }
+        ],
+        cutterName: [
+          { required: true, message: '璇疯緭鍏ュ垁鍏峰悕绉�!' }
+        ]
+      },
+      url: {
+        add: '/nc/cutter/add',
+        edit: '/nc/cutter/edit'
+      }
+    }
+  },
+  created() {
+    //澶囦唤model鍘熷鍊�
+    this.modelDefault = JSON.parse(JSON.stringify(this.model))
+  },
+  methods: {
+    add(params) {
+      this.edit({ ...this.modelDefault, ...params })
+    },
+    edit(record) {
+      this.model = Object.assign({}, record)
+      console.log('model', this.model)
+    },
+    submitForm() {
+      const that = this
+      // 瑙﹀彂琛ㄥ崟楠岃瘉
+      this.$refs.form.validate(valid => {
+        if (valid) {
+          that.confirmLoading = true
+          let httpUrl, method
+          if (!this.model.id) {
+            httpUrl = this.url.add
+            method = 'post'
+          } else {
+            httpUrl = this.url.edit
+            method = 'put'
+          }
+          httpAction(httpUrl, this.model, method).then((res) => {
+            if (res.success) {
+              that.$notification.success({
+                message: '娑堟伅',
+                description: res.message
+              })
+              that.$emit('ok')
+            } else {
+              that.$notification.warning({
+                message: '娑堟伅',
+                description: res.message
+              })
+            }
+          }).finally(() => {
+            that.confirmLoading = false
+          })
+        }
+
+      })
+    }
+  }
+}
+</script>
\ No newline at end of file
diff --git a/src/views/dnc/base/modules/ProductStructure/Cutter/CutterTableList.vue b/src/views/dnc/base/modules/ProductStructure/Cutter/CutterTableList.vue
new file mode 100644
index 0000000..09270d2
--- /dev/null
+++ b/src/views/dnc/base/modules/ProductStructure/Cutter/CutterTableList.vue
@@ -0,0 +1,153 @@
+<template>
+  <div>
+    <!-- 鏌ヨ鍖哄煙 -->
+    <div class="table-page-search-wrapper">
+      <a-form layout="inline" @keyup.enter.native="searchQuery">
+        <a-row :gutter="24">
+          <a-col :md="5" :sm="5">
+            <a-form-item label="鍒�鍏风紪鍙�">
+              <a-input placeholder="璇疯緭鍏ュ垁鍏风紪鍙�" v-model="queryParam.cutterCode" allow-clear/>
+            </a-form-item>
+          </a-col>
+
+          <a-col :md="5" :sm="5">
+            <a-form-item label="鍒�鍏峰悕绉�">
+              <a-input placeholder="璇疯緭鍏ュ垁鍏峰悕绉�" v-model="queryParam.cutterName" allow-clear/>
+            </a-form-item>
+          </a-col>
+
+          <a-col :md="4" :sm="4">
+            <a-space>
+              <a-button type="primary" @click="searchQuery" icon="search">鏌ヨ</a-button>
+              <a-button type="primary" @click="handleAdd" icon="plus" v-has="'cutter_add'">鏂板</a-button>
+              <a-button type="primary" @click="handleExportXls('鍒�鍏峰垪琛�')" icon="export">瀵煎嚭</a-button>
+              <a-upload name="file" :showUploadList="false" :multiple="false" :headers="tokenHeader"
+                        :action="importExcelUrl" @change="handleImportExcel">
+                <a-button type="primary" icon="import">瀵煎叆</a-button>
+              </a-upload>
+            </a-space>
+          </a-col>
+        </a-row>
+      </a-form>
+    </div>
+
+    <a-table :columns="columns" :data-source="dataSource" bordered :pagination="ipagination" :size="size" rowKey="id"
+             @change="handleTableChange" :scroll="{y:189}">
+      <template slot="action" slot-scope="text, record">
+        <a @click="handleEdit(record)">缂栬緫</a>
+
+        <a-divider type="vertical"/>
+
+        <a-dropdown>
+          <a class="ant-dropdown-link">
+            鏇村
+            <a-icon type="down"/>
+          </a>
+          <a-menu slot="overlay">
+            <a-menu-item>
+              <a href="javascript:;" @click="handleDetail(record)">璇︽儏</a>
+            </a-menu-item>
+
+            <a-menu-item>
+              <a-popconfirm title="纭畾鍒犻櫎鍚�?" @confirm="() => handleDelete(record.id)">
+                <a>鍒犻櫎</a>
+              </a-popconfirm>
+            </a-menu-item>
+          </a-menu>
+        </a-dropdown>
+      </template>
+    </a-table>
+
+    <CutterModal ref="cutterModalRef" :currentTreeNodeInfo="currentLevelInfo" @submitSuccess="loadData"/>
+  </div>
+</template>
+
+<script>
+import { JeecgListMixin } from '@/mixins/JeecgListMixin'
+import CutterModal from '@views/dnc/base/modules/ProductStructure/Cutter/CutterModal.vue'
+
+export default {
+  name: 'CutterTableList',
+  components: { CutterModal },
+  mixins: [JeecgListMixin],
+  props: {
+    currentLevelInfo: {
+      type: Object
+    },
+    size: {
+      type: String
+    }
+  },
+  data() {
+    return {
+      disableMixinCreated: true,
+      columns: [
+        {
+          title: '搴忓彿',
+          dataIndex: 'rowIndex',
+          key: 'rowIndex',
+          width: 65,
+          align: 'center',
+          customRender: function(t, r, index) {
+            return parseInt(index) + 1
+          }
+        },
+        { title: '鍒�鍏风紪鍙�', dataIndex: 'cutterCode', align: 'center' },
+        { title: '鍒�鍏峰悕绉�', dataIndex: 'cutterName', align: 'center' },
+        { title: '鍒�鍏风被鍨�', dataIndex: 'cutterType_dictText', align: 'center' },
+        { title: '棰濆畾瀵垮懡', dataIndex: 'lifetime', width: 150, align: 'center' },
+        { title: '鍒�浣�', dataIndex: 'cutterSpacing', width: 150, align: 'center' },
+        { title: '鍒�鍏锋暟閲�', dataIndex: 'quantity', width: 150, align: 'center' },
+        { title: '鎿嶄綔', dataIndex: 'action', scopedSlots: { customRender: 'action' }, align: 'center', width: 150 }
+      ],
+      url: {
+        list: '/nc/cutter/getByBusinessId',
+        delete: '/nc/cutter/delete',
+        exportXlsUrl: '/nc/cutter/exportXls',
+        importExcelUrl: '/nc/cutter/importExcel'
+      }
+    }
+  },
+  computed: {
+    importExcelUrl: function() {
+      return `${window._CONFIG['domianURL']}${this.url.importExcelUrl}`
+    }
+  },
+  methods: {
+    setQueryParamAndLoadData(pageNo = 1) {
+      const { id, type } = this.currentLevelInfo
+      this.queryParam = Object.assign({}, { attributionId: id, attributionType: type })
+      this.loadData(pageNo)
+    },
+
+    handleAdd() {
+      if (!this.$refs.cutterModalRef) return
+      this.$refs.cutterModalRef.title = '娣诲姞鍒�鍏�'
+      this.$refs.cutterModalRef.disableSubmit = false
+      this.$refs.cutterModalRef.handleCutterAdd()
+    },
+
+    /**
+     * 缂栬緫琛ㄦ牸琛屼俊鎭�
+     * @param record 琛ㄦ牸琛屼俊鎭�
+     */
+    handleEdit(record) {
+      if (!this.$refs.cutterModalRef) return
+      this.$refs.cutterModalRef.title = '缂栬緫鍒�鍏蜂俊鎭�'
+      this.$refs.cutterModalRef.disableSubmit = false
+      this.$refs.cutterModalRef.handleCutterEdit(record)
+    },
+
+    /**
+     * 鏌ョ湅琛ㄦ牸瀹屾暣琛屼俊鎭�
+     * @param record 琛ㄦ牸琛屼俊鎭�
+     */
+    handleDetail: function(record) {
+      if (!this.$refs.cutterModalRef) return
+      this.$refs.cutterModalRef.title = '鍒�鍏疯鎯�'
+      this.$refs.cutterModalRef.disableSubmit = true
+      this.$refs.cutterModalRef.handleCutterEdit(record)
+    }
+  }
+}
+</script>
\ No newline at end of file
diff --git a/src/views/dnc/base/modules/ProductStructure/DeviceCustomType/DeviceCustomTypeModal.vue b/src/views/dnc/base/modules/ProductStructure/DeviceCustomType/DeviceCustomTypeModal.vue
new file mode 100644
index 0000000..82753ab
--- /dev/null
+++ b/src/views/dnc/base/modules/ProductStructure/DeviceCustomType/DeviceCustomTypeModal.vue
@@ -0,0 +1,80 @@
+<template>
+  <j-modal
+    :title="title"
+    :width="width"
+    :visible="visible"
+    switchFullscreen
+    :maskClosable="false"
+    @ok="handleOk"
+    @cancel="handleCancel"
+    cancelText="鍏抽棴">
+    <DeviceCustomTypeModalForm ref="realForm" @ok="handleCancel"/>
+  </j-modal>
+</template>
+
+<script>
+import DeviceCustomTypeModalForm from './DeviceCustomTypeModalForm'
+
+export default {
+  name: 'DeviceCustomTypeModal',
+  components: {
+    DeviceCustomTypeModalForm
+  },
+  data() {
+    return {
+      title: '',
+      width: 700,
+      visible: false
+    }
+  },
+  created() {
+    this.$bus.$on('treeMenuItemMethodTrigger', this.triggerCorrespondingMethod)
+    this.$bus.$on('tableMenuItemMethodTrigger', this.triggerCorrespondingMethod)
+  },
+  methods: {
+    // 鐐瑰嚮娣诲姞璁惧绫绘柊澧炲伐搴忔垨宸ユ涓嬭澶囩被
+    handleAddType(treeNodeInfo) {
+      this.visible = true
+      const { treeKey, type } = treeNodeInfo
+      console.log('treeNodeInfo', treeNodeInfo)
+      this.$nextTick(() => {
+        if (this.$refs.realForm) {
+          this.$refs.realForm.add({
+            attributionId: treeKey,
+            attributionType: type
+          })
+          this.$refs.realForm.getWorkshopListByApi()
+        }
+      })
+    },
+
+    // 缂栬緫璁惧绫讳俊鎭�
+    handleTypeEdit(_, tableRowInfo) {
+      this.visible = true
+      this.$nextTick(() => {
+        if (this.$refs.realForm) {
+          this.$refs.realForm.getWorkshopListByApi()
+          this.$refs.realForm.handleProductionChange()
+          this.$refs.realForm.edit({ ...tableRowInfo })
+        }
+      })
+    },
+
+    handleCancel() {
+      this.visible = false
+    },
+
+    handleOk() {
+      this.$refs.realForm.submitForm()
+    },
+
+    triggerCorrespondingMethod({ methodName, modalTitle, treeNodeInfo, tableRowInfo }) {
+      console.log('瑙﹀彂鍙抽敭鑿滃崟')
+      if (this[methodName]) {
+        this.title = modalTitle
+        this[methodName](treeNodeInfo, tableRowInfo)
+      }
+    }
+  }
+}
+</script>
\ No newline at end of file
diff --git a/src/views/dnc/base/modules/ProductStructure/DeviceCustomType/DeviceCustomTypeModalForm.vue b/src/views/dnc/base/modules/ProductStructure/DeviceCustomType/DeviceCustomTypeModalForm.vue
new file mode 100644
index 0000000..b587cd7
--- /dev/null
+++ b/src/views/dnc/base/modules/ProductStructure/DeviceCustomType/DeviceCustomTypeModalForm.vue
@@ -0,0 +1,150 @@
+<template>
+  <a-spin :spinning="confirmLoading">
+    <a-form-model ref="form" :model="model" :rules="validatorRules" :labelCol="labelCol" :wrapperCol="wrapperCol">
+      <a-row>
+        <a-col :span="12">
+          <a-form-model-item label="杞﹂棿" prop="productionId">
+            <a-select v-model="model.productionId" placeholder="璇烽�夋嫨杞﹂棿" @change="handleProductionChange">
+              <a-select-option v-for="item in workshopTreeData" :key="item.id">
+                {{ item.productionName }}
+              </a-select-option>
+            </a-select>
+          </a-form-model-item>
+        </a-col>
+
+        <a-col :span="12">
+          <a-form-model-item label="璁惧绫�" prop="deviceManagementId">
+            <a-select v-model="model.deviceManagementId" placeholder="璇烽�夋嫨璁惧绫�" :disabled="!model.productionId"
+                      @change="handleCodeSelectChange">
+              <a-select-option v-for="item in deviceCustomTypeList" :key="item.id">
+                {{ item.deviceManagementCode }}
+              </a-select-option>
+            </a-select>
+          </a-form-model-item>
+        </a-col>
+      </a-row>
+
+      <a-row>
+        <a-col :span="12">
+          <a-form-model-item label="璁惧绫诲悕绉�">
+            <a-input v-model="model.deviceManagementId_dictText" placeholder="璇烽�夋嫨璁惧绫�" readOnly/>
+          </a-form-model-item>
+        </a-col>
+      </a-row>
+    </a-form-model>
+  </a-spin>
+</template>
+
+<script>
+import { httpAction } from '@/api/manage'
+import dncApi from '@/api/dnc'
+
+export default {
+  name: 'DeviceCustomTypeModalForm',
+  components: {},
+  data() {
+    return {
+      model: {},
+      workshopTreeData: [],
+      deviceCustomTypeList: [],
+      labelCol: {
+        xs: { span: 24 },
+        sm: { span: 6 }
+      },
+      wrapperCol: {
+        xs: { span: 24 },
+        sm: { span: 17 }
+      },
+      confirmLoading: false,
+      validatorRules: {
+        productionId: [
+          { required: true, message: '璇烽�夋嫨杞﹂棿!' }
+        ],
+        deviceManagementId: [
+          { required: true, message: '璇烽�夋嫨璁惧绫�!' }
+        ]
+      },
+      url: {
+        add: '/nc/deviceType/add',
+        edit: '/nc/deviceType/edit'
+      }
+    }
+  },
+  created() {
+    //澶囦唤model鍘熷鍊�
+    this.modelDefault = JSON.parse(JSON.stringify(this.model))
+  },
+  methods: {
+    // 璋冪敤鎺ュ彛鑾峰彇鏌ヨ鍖哄煙杞﹂棿鏍戝垪琛�
+    getWorkshopListByApi() {
+      dncApi.getHasPermissionWorkshopTreeApi()
+        .then(res => {
+          if (res.success) this.workshopTreeData = res.result
+        })
+    },
+
+    // 杞﹂棿閫夋嫨鏀瑰彉鍚庢竻绌鸿澶囩被閫夋嫨骞堕噸鏂拌幏鍙栬澶囩被鍒楄〃
+    handleProductionChange() {
+      if (this.model.deviceManagementId) delete this.model.deviceManagementId
+      if (this.model.deviceManagementId_dictText) delete this.model.deviceManagementId_dictText
+      dncApi.getDeviceCustomTypeListByProductionIdApi(this.model.productionId)
+        .then(res => {
+          if (res.success) this.deviceCustomTypeList = res.result
+        })
+    },
+
+    /**
+     * 璁惧绫婚�夋嫨鏀瑰彉鍚庤Е鍙戝~鍏呰澶囩被瀵瑰簲璁惧绫诲悕绉�
+     * @param value
+     */
+    handleCodeSelectChange(value) {
+      this.$set(this.model, 'deviceManagementId_dictText', this.deviceCustomTypeList.find(item => item.id === value).deviceManagementName)
+    },
+
+    add(params) {
+      this.edit({ ...this.modelDefault, ...params })
+    },
+
+    edit(record) {
+      this.model = Object.assign({}, record)
+    },
+
+    submitForm() {
+      const that = this
+      // 瑙﹀彂琛ㄥ崟楠岃瘉
+      this.$refs.form.validate(valid => {
+        if (valid) {
+          that.confirmLoading = true
+          let httpUrl, method
+          if (!this.model.id) {
+            httpUrl = that.url.add
+            method = 'post'
+          } else {
+            httpUrl = that.url.edit
+            method = 'put'
+          }
+          httpAction(httpUrl, that.model, method)
+            .then((res) => {
+              if (res.success) {
+                that.$notification.success({
+                  message: '娑堟伅',
+                  description: res.message
+                })
+                that.$bus.$emit('deviceCustomTypeAddSubmitSuccess')
+                that.$emit('ok')
+              } else {
+                that.$notification.warning({
+                  message: '娑堟伅',
+                  description: res.message
+                })
+              }
+            })
+            .finally(() => {
+              that.confirmLoading = false
+            })
+        }
+      })
+    }
+  }
+}
+</script>
\ No newline at end of file
diff --git a/src/views/dnc/base/modules/ProductStructure/DeviceCustomType/DeviceCustomTypeTableList.vue b/src/views/dnc/base/modules/ProductStructure/DeviceCustomType/DeviceCustomTypeTableList.vue
new file mode 100644
index 0000000..ca58908
--- /dev/null
+++ b/src/views/dnc/base/modules/ProductStructure/DeviceCustomType/DeviceCustomTypeTableList.vue
@@ -0,0 +1,167 @@
+<template>
+  <div style="flex: 0.25;margin-right: 10px">
+    <a-table :columns="columns" :data-source="dataSource" bordered :pagination="false"
+             :scroll="{y:189}" :customRow="customRow" :size="size" rowKey="id" @change="handleTableChange">
+
+    </a-table>
+  </div>
+</template>
+
+<script>
+import { JeecgListMixin } from '@/mixins/JeecgListMixin'
+import DeviceCustomTypeModal from '@views/dnc/base/modules/ProductStructure/DeviceCustomType/DeviceCustomTypeModal.vue'
+import { deleteAction } from '@api/manage'
+
+export default {
+  name: 'DeviceCustomTypeTableList',
+  components: { DeviceCustomTypeModal },
+  mixins: [JeecgListMixin],
+  props: {
+    currentTreeNodeInfo: {
+      type: Object
+    },
+    size: {
+      type: String
+    }
+  },
+  data() {
+    return {
+      disableMixinCreated: true,
+      columns: [
+        // {
+        //   title: '杞﹂棿',
+        //   dataIndex: 'productionId_dictText',
+        //   align: 'center'
+        // },
+        {
+          title: '璁惧绫�',
+          dataIndex: 'deviceManagementCode_dictText',
+          align: 'center'
+        },
+        {
+          title: '璁惧绫诲悕绉�',
+          dataIndex: 'deviceManagementId_dictText',
+          align: 'center'
+        }
+      ],
+      dataSource: [],
+      currentRightClickedTypeInfo: {},
+      currentClickedTypeInfo: {},
+      url: {
+        list: '/nc/deviceType/getByBusinessId',
+        delete: '/nc/deviceType/delete'
+      }
+    }
+  },
+  watch: {
+    currentTreeNodeInfo: {
+      handler(value) {
+        this.currentClickedTypeInfo = {}
+      },
+      immediate: true
+    }
+  },
+  created() {
+    this.$bus.$on('tableMenuItemMethodTrigger', this.triggerCorrespondingMethod)
+    this.$bus.$on('deviceCustomTypeAddSubmitSuccess', this.loadData)
+  },
+  methods: {
+    setQueryParamAndLoadData() {
+      console.log('currentLevelInfo', this.currentTreeNodeInfo)
+      const { id, type } = this.currentTreeNodeInfo
+      this.queryParam = Object.assign({}, { attributionId: id, attributionType: type })
+      this.loadData()
+    },
+
+    customRow(record) {
+      return {
+        on: {
+          contextmenu: event => {
+            event.preventDefault()
+            this.currentRightClickedTypeInfo = Object.assign({ param: 'deviceCustomType' }, record)
+            this.$emit('handleTableContextMenuOpen', this.currentRightClickedTypeInfo)
+          },
+          click: () => {
+            if (this.currentClickedTypeInfo.docId === record.docId) return
+            this.currentClickedTypeInfo = Object.assign({}, record)
+            // this.$bus.$emit('sendCurrentClickedDocumentInfo', record)
+          }
+        }
+      }
+    },
+
+    /**
+     * 琛ㄦ牸鍒嗛〉銆佹帓搴忔敼鍙樸�佺瓫閫夋椂瑙﹀彂
+     * @param pagination 鍒嗛〉鍣ㄩ�夐」
+     * @param filters 绛涢�夐�夐」
+     * @param sorter 鎺掑簭閫夐」
+     */
+    handleTableChange(pagination, filters, sorter) {
+      if (sorter.order) {
+        this.isorter.column = sorter.field
+        this.isorter.order = sorter.order === 'ascend' ? 'asc' : 'desc'
+      } else {
+        this.isorter.column = 'createTime'
+        this.isorter.order = 'desc'
+      }
+      for (let key in filters) {
+        this.filters[key] = filters[key].join(',')
+      }
+      this.ipagination = pagination
+      this.loadData()
+    },
+
+    /**
+     * 鍒犻櫎宸ュ簭鎴栧伐姝ュ眰绾ф坊鍔犵殑璁惧绫�
+     * @param tableRowInfo
+     */
+    handleTypeDelete(tableRowInfo) {
+      const { id } = tableRowInfo
+      const { $confirm, url, loadData, $notification, $destroyAll } = this
+      $confirm({
+        title: '鎻愮ず',
+        content: '纭鍒犻櫎姝ゆ潯璁板綍鍚楋紵',
+        okText: '纭',
+        okType: 'danger',
+        cancelText: '鍙栨秷',
+        onOk: () => {
+          if (!url.delete) {
+            this.$message.error('璇疯缃畊rl.delete灞炴��!')
+            return
+          }
+
+          deleteAction(url.delete, { id: id })
+            .then((res) => {
+              if (res.success) {
+                $notification.success({
+                  message: '娑堟伅',
+                  description: res.message
+                })
+                loadData()
+              } else {
+                $notification.warning({
+                  message: '娑堟伅',
+                  description: res.message
+                })
+              }
+            })
+            .finally(() => {
+              $destroyAll()
+            })
+        },
+        onCancel: () => {
+          $destroyAll()
+        }
+      })
+    },
+
+    triggerCorrespondingMethod({ methodName, modalTitle, tableRowInfo }) {
+      if (this[methodName] && tableRowInfo.param === 'deviceCustomType') this[methodName](tableRowInfo, modalTitle)
+    }
+  }
+}
+</script>
+
+<style scoped>
+
+</style>
\ No newline at end of file
diff --git a/src/views/dnc/base/modules/ProductStructure/Document/NcDocumentTableList.vue b/src/views/dnc/base/modules/ProductStructure/Document/NcDocumentTableList.vue
index 302e9a6..079bd14 100644
--- a/src/views/dnc/base/modules/ProductStructure/Document/NcDocumentTableList.vue
+++ b/src/views/dnc/base/modules/ProductStructure/Document/NcDocumentTableList.vue
@@ -1,5 +1,5 @@
 <template>
-  <div>
+  <div style="flex: 1">
     <a-table :columns="columns" :data-source="dataSource" bordered :pagination="ipagination"
              :scroll="{y:189}" :customRow="customRow" :size="size" rowKey="docId" @change="handleTableChange">
 
diff --git a/src/views/dnc/base/modules/ProductStructure/Part/PartInfo.vue b/src/views/dnc/base/modules/ProductStructure/Part/PartInfo.vue
index b91d89a..048e7cf 100644
--- a/src/views/dnc/base/modules/ProductStructure/Part/PartInfo.vue
+++ b/src/views/dnc/base/modules/ProductStructure/Part/PartInfo.vue
@@ -1,6 +1,6 @@
 <template>
-  <a-descriptions bordered :size="size">
-    <a-descriptions-item label="闆朵欢鍚嶇О">{{currentLevelDetails.partsName}}</a-descriptions-item>
+  <a-descriptions bordered :size="size" :column="4">
+    <a-descriptions-item label="闆朵欢鍚嶇О" :span="2">{{currentLevelDetails.partsName}}</a-descriptions-item>
     <a-descriptions-item label="闆朵欢浠e彿">{{currentLevelDetails.partsCode}}</a-descriptions-item>
     <a-descriptions-item label="闆朵欢鍨嬪彿	">{{currentLevelDetails.partsModel}}</a-descriptions-item>
     <a-descriptions-item label="鐗╂枡缂栫爜">{{currentLevelDetails.materielCode}}</a-descriptions-item>
@@ -10,12 +10,12 @@
     <a-descriptions-item label="鐢熶骇绫诲瀷">{{currentLevelDetails.produceType}}</a-descriptions-item>
     <a-descriptions-item label="澶勭悊绫诲瀷	">{{currentLevelDetails.processType}}</a-descriptions-item>
     <a-descriptions-item label="缁撴瀯绫诲瀷	">{{currentLevelDetails.structureType}}</a-descriptions-item>
-    <a-descriptions-item label="閲嶉噺" :span="2">{{currentLevelDetails.partsWeight}}</a-descriptions-item>
-    <a-descriptions-item label="鍒涘缓浜�">{{currentLevelDetails.createName}}</a-descriptions-item>
-    <a-descriptions-item label="鍒涘缓鏃堕棿" :span="2">{{currentLevelDetails.createTime}}</a-descriptions-item>
-    <a-descriptions-item label="淇敼浜�">{{currentLevelDetails.updateName}}</a-descriptions-item>
-    <a-descriptions-item label="淇敼鏃堕棿" :span="2">{{currentLevelDetails.updateTime}}</a-descriptions-item>
-    <a-descriptions-item label="鎻忚堪" :span="3">{{currentLevelDetails.description}}</a-descriptions-item>
+    <a-descriptions-item label="閲嶉噺">{{currentLevelDetails.partsWeight}}</a-descriptions-item>
+    <a-descriptions-item label="鍒涘缓浜�">{{currentLevelDetails.createBy_dictText}}</a-descriptions-item>
+    <a-descriptions-item label="鍒涘缓鏃堕棿">{{currentLevelDetails.createTime}}</a-descriptions-item>
+    <a-descriptions-item label="淇敼浜�">{{currentLevelDetails.updateBy_dictText}}</a-descriptions-item>
+    <a-descriptions-item label="淇敼鏃堕棿">{{currentLevelDetails.updateTime}}</a-descriptions-item>
+    <a-descriptions-item label="鎻忚堪" :span="4">{{currentLevelDetails.description}}</a-descriptions-item>
   </a-descriptions>
 </template>
 
diff --git a/src/views/dnc/base/modules/ProductStructure/Permission/AssignPermissionModal.vue b/src/views/dnc/base/modules/ProductStructure/Permission/AssignPermissionModal.vue
index 07b8f9e..6491b13 100644
--- a/src/views/dnc/base/modules/ProductStructure/Permission/AssignPermissionModal.vue
+++ b/src/views/dnc/base/modules/ProductStructure/Permission/AssignPermissionModal.vue
@@ -27,124 +27,123 @@
 </template>
 
 <script>
-  import dncApi from '@/api/dnc'
-  import DepartPermissionTransfer from './DepartPermissionTransfer'
-  import UserPermissionTransfer from './UserPermissionTransfer'
-  import { queryProductionTreeList } from '@/api/api'
+import dncApi from '@/api/dnc'
+import DepartPermissionTransfer from './DepartPermissionTransfer'
+import UserPermissionTransfer from './UserPermissionTransfer'
 
-  export default {
-    name: 'AssignPermissionModal',
-    components: { UserPermissionTransfer, DepartPermissionTransfer },
-    props: {
-      currentTreeNodeInfo: {
-        type: Object
-      }
-    },
-    data() {
-      return {
-        visible: false,
-        title: '',
-        isAssignSonNode: true,
-        activeTabKey: 1,
-        allDepartmentsList: [],
-        allUsersList: [],
-        allTreeKeys: [],
-        hasLoadedDataTabKeyArray: []
-      }
-    },
-    watch: {
-      visible: {
-        handler(value) {
-          if (value) {
-            this.activeTabKey = 1
-            this.isAssignSonNode = true
-            this.getAllDepartmentsListByApi()
-          }
-        }
-      },
-      activeTabKey: {
-        handler(value) {
-          if (this.hasLoadedDataTabKeyArray.includes(value)) return
-          if (value === 2) this.getAllUsersListByApi()
-          this.hasLoadedDataTabKeyArray.push(value)
+export default {
+  name: 'AssignPermissionModal',
+  components: { UserPermissionTransfer, DepartPermissionTransfer },
+  props: {
+    currentTreeNodeInfo: {
+      type: Object
+    }
+  },
+  data() {
+    return {
+      visible: false,
+      title: '',
+      isAssignSonNode: true,
+      activeTabKey: 1,
+      allDepartmentsList: [],
+      allUsersList: [],
+      allTreeKeys: [],
+      hasLoadedDataTabKeyArray: []
+    }
+  },
+  watch: {
+    visible: {
+      handler(value) {
+        if (value) {
+          this.activeTabKey = 1
+          this.isAssignSonNode = true
+          this.getAllDepartmentsListByApi()
         }
       }
     },
-    created() {
-      this.$bus.$on('treeMenuItemMethodTrigger', this.triggerCorrespondingMethod)
+    activeTabKey: {
+      handler(value) {
+        if (this.hasLoadedDataTabKeyArray.includes(value)) return
+        if (value === 2) this.getAllUsersListByApi()
+        this.hasLoadedDataTabKeyArray.push(value)
+      }
+    }
+  },
+  created() {
+    this.$bus.$on('treeMenuItemMethodTrigger', this.triggerCorrespondingMethod)
+  },
+  methods: {
+    // 鐐瑰嚮鏍戣妭鐐瑰彸閿彍鍗曟潈闄愰厤缃寜閽悗瑙﹀彂
+    handleAssignPermission() {
+      this.visible = true
     },
-    methods: {
-      // 鐐瑰嚮鏍戣妭鐐瑰彸閿彍鍗曟潈闄愰厤缃寜閽悗瑙﹀彂
-      handleAssignPermission() {
-        this.visible = true
-      },
 
-      // 璋冪敤鎺ュ彛鑾峰彇鎵�鏈夎溅闂村垪琛�
-      getAllDepartmentsListByApi() {
-        this.allTreeKeys = []
-        queryProductionTreeList()
-          .then(res => {
-            if (res.success) {
-              this.allDepartmentsList = res.result
-              this.generateList(this.allDepartmentsList)
-              this.$nextTick(() => {
-                this.$refs.departPermissionTransferRef.getHasPermissionDepartByApi()
-                this.$refs.departPermissionTransferRef.expandedKeys = this.allTreeKeys
-                this.$refs.departPermissionTransferRef.flatten(JSON.parse(JSON.stringify(this.allDepartmentsList)))
-              })
-              // 鍙湁涓婃閫�鍑烘椂鍦ㄨ溅闂村垎閰峵ab鐣岄潰鎵嶄細杩涘叆姝ゅ垽鏂�
-              // 鑻ヤ笂娆¢��鍑烘椂鍦ㄧ敤鎴峰垎閰峵ab鐣岄潰鍒欏啀娆¤繘鍏ユ椂key鐢�2鍙樹负1鏃朵細瑙﹀彂watch鐩戞祴activeTabKey鍙樺寲鍒欎細灏唊ey:1鍔犲叆hasLoadedDataTabKeyArray锛屽洜姝ゆ棤闇�鍐嶆鍔犲叆key:1
-              if (!this.hasLoadedDataTabKeyArray.includes(this.activeTabKey)) this.hasLoadedDataTabKeyArray.push(this.activeTabKey)
-            }
-          })
-      },
-
-      /**
-       * 閫掑綊鑾峰緱鎵�鏈夋爲鑺傜偣key
-       * @param data
-       */
-      generateList(data) {
-        for (let i = 0; i < data.length; i++) {
-          const node = data[i]
-          const key = node.key
-          this.allTreeKeys.push(key)
-          if (node.children) this.generateList(node.children)
-        }
-      },
-
-      // 璋冪敤鎺ュ彛鑾峰彇鎵�鏈夌敤鎴峰垪琛�
-      getAllUsersListByApi() {
-        dncApi.getAllUsersListApi()
-          .then(res => {
-            if (res.success) {
-              this.allUsersList = res.result.records
-              this.$nextTick(() => this.$refs.userPermissionTransferRef.getHasPermissionUserByApi())
-            }
-          })
-      },
-
-      setAdminDisabled() {
-        this.allUsersList = this.allUsersList.map(item => {
-          return {
-            ...item,
-            disabled: item.username === 'admin'
+    // 璋冪敤鎺ュ彛鑾峰彇鎵�鏈夎溅闂村垪琛�
+    getAllDepartmentsListByApi() {
+      this.allTreeKeys = []
+      dncApi.getAllDepartmentsListApi()
+        .then(res => {
+          if (res.success) {
+            this.allDepartmentsList = res.result
+            this.generateList(this.allDepartmentsList)
+            this.$nextTick(() => {
+              this.$refs.departPermissionTransferRef.getHasPermissionDepartByApi()
+              this.$refs.departPermissionTransferRef.expandedKeys = this.allTreeKeys
+              this.$refs.departPermissionTransferRef.flatten(JSON.parse(JSON.stringify(this.allDepartmentsList)))
+            })
+            // 鍙湁涓婃閫�鍑烘椂鍦ㄨ溅闂村垎閰峵ab鐣岄潰鎵嶄細杩涘叆姝ゅ垽鏂�
+            // 鑻ヤ笂娆¢��鍑烘椂鍦ㄧ敤鎴峰垎閰峵ab鐣岄潰鍒欏啀娆¤繘鍏ユ椂key鐢�2鍙樹负1鏃朵細瑙﹀彂watch鐩戞祴activeTabKey鍙樺寲鍒欎細灏唊ey:1鍔犲叆hasLoadedDataTabKeyArray锛屽洜姝ゆ棤闇�鍐嶆鍔犲叆key:1
+            if (!this.hasLoadedDataTabKeyArray.includes(this.activeTabKey)) this.hasLoadedDataTabKeyArray.push(this.activeTabKey)
           }
         })
-      },
+    },
 
-      handleModalClose() {
-        this.visible = false
-        this.hasLoadedDataTabKeyArray = []
-      },
+    /**
+     * 閫掑綊鑾峰緱鎵�鏈夋爲鑺傜偣key
+     * @param data
+     */
+    generateList(data) {
+      for (let i = 0; i < data.length; i++) {
+        const node = data[i]
+        const key = node.key
+        this.allTreeKeys.push(key)
+        if (node.children) this.generateList(node.children)
+      }
+    },
 
-      triggerCorrespondingMethod({ methodName, modalTitle }) {
-        if (this[methodName]) {
-          this[methodName]()
-          this.title = modalTitle
+    // 璋冪敤鎺ュ彛鑾峰彇鎵�鏈夌敤鎴峰垪琛�
+    getAllUsersListByApi() {
+      dncApi.getAllUsersListApi()
+        .then(res => {
+          if (res.success) {
+            this.allUsersList = res.result.records
+            this.$nextTick(() => this.$refs.userPermissionTransferRef.getHasPermissionUserByApi())
+          }
+        })
+    },
+
+    setAdminDisabled() {
+      this.allUsersList = this.allUsersList.map(item => {
+        return {
+          ...item,
+          disabled: item.username === 'admin'
         }
+      })
+    },
+
+    handleModalClose() {
+      this.visible = false
+      this.hasLoadedDataTabKeyArray = []
+    },
+
+    triggerCorrespondingMethod({ methodName, modalTitle }) {
+      if (this[methodName]) {
+        this[methodName]()
+        this.title = modalTitle
       }
     }
   }
+}
 </script>
 
 <style scoped>
diff --git a/src/views/dnc/base/modules/ProductStructure/Process/ProcessInfo.vue b/src/views/dnc/base/modules/ProductStructure/Process/ProcessInfo.vue
index 2595b24..264c31b 100644
--- a/src/views/dnc/base/modules/ProductStructure/Process/ProcessInfo.vue
+++ b/src/views/dnc/base/modules/ProductStructure/Process/ProcessInfo.vue
@@ -1,19 +1,19 @@
 <template>
-  <a-descriptions bordered :size="size">
-    <a-descriptions-item label="宸ュ簭鍚嶇О">{{currentLevelDetails.processName}}</a-descriptions-item>
-    <a-descriptions-item label="宸ュ簭鍙�">{{currentLevelDetails.processCode}}</a-descriptions-item>
-    <a-descriptions-item label="宸ヨ壓缂栧彿	">{{currentLevelDetails.craftNo}}</a-descriptions-item>
+  <a-descriptions bordered :size="size" :column="4">
+    <a-descriptions-item label="宸ュ簭鍚嶇О" :span="2">{{currentLevelDetails.processName}}</a-descriptions-item>
+    <a-descriptions-item label="宸ュ簭鍙�" :span="2">{{currentLevelDetails.processCode}}</a-descriptions-item>
+    <a-descriptions-item label="宸ヨ壓缂栧彿" :span="2">{{currentLevelDetails.craftNo}}</a-descriptions-item>
     <a-descriptions-item label="宸ュ簭绫诲瀷">{{currentLevelDetails.processType}}</a-descriptions-item>
     <a-descriptions-item label="鍔犲伐璁惧鍨嬪彿">{{currentLevelDetails.processingEquipmentModel}}</a-descriptions-item>
     <a-descriptions-item label="鍔犲伐璁惧绫诲瀷">{{currentLevelDetails.processingEquipmentOs}}</a-descriptions-item>
     <a-descriptions-item label="鍔犲伐璁惧缂栧彿">{{currentLevelDetails.processingEquipmentCode}}</a-descriptions-item>
     <a-descriptions-item label="宸ヨ缂栧彿">{{currentLevelDetails.assembleStep}}</a-descriptions-item>
-    <a-descriptions-item label="宸ヨ鍚嶇О	">{{currentLevelDetails.assembleName}}</a-descriptions-item>
-    <a-descriptions-item label="鍒涘缓浜�">{{currentLevelDetails.createName}}</a-descriptions-item>
-    <a-descriptions-item label="鍒涘缓鏃堕棿" :span="2">{{currentLevelDetails.createTime}}</a-descriptions-item>
-    <a-descriptions-item label="淇敼浜�">{{currentLevelDetails.updateName}}</a-descriptions-item>
-    <a-descriptions-item label="淇敼鏃堕棿" :span="2">{{currentLevelDetails.updateTime}}</a-descriptions-item>
-    <a-descriptions-item label="鎻忚堪" :span="3">{{currentLevelDetails.description}}</a-descriptions-item>
+    <a-descriptions-item label="宸ヨ鍚嶇О">{{currentLevelDetails.assembleName}}</a-descriptions-item>
+    <a-descriptions-item label="鍒涘缓浜�">{{currentLevelDetails.createBy_dictText}}</a-descriptions-item>
+    <a-descriptions-item label="鍒涘缓鏃堕棿">{{currentLevelDetails.createTime}}</a-descriptions-item>
+    <a-descriptions-item label="淇敼浜�">{{currentLevelDetails.updateBy_dictText}}</a-descriptions-item>
+    <a-descriptions-item label="淇敼鏃堕棿">{{currentLevelDetails.updateTime}}</a-descriptions-item>
+    <a-descriptions-item label="鎻忚堪" :span="4">{{currentLevelDetails.description}}</a-descriptions-item>
   </a-descriptions>
 </template>
 
diff --git a/src/views/dnc/base/modules/ProductStructure/Process/ProcessModalForm.vue b/src/views/dnc/base/modules/ProductStructure/Process/ProcessModalForm.vue
index 0823c70..25d939d 100644
--- a/src/views/dnc/base/modules/ProductStructure/Process/ProcessModalForm.vue
+++ b/src/views/dnc/base/modules/ProductStructure/Process/ProcessModalForm.vue
@@ -122,7 +122,6 @@
     created() {
       //澶囦唤model鍘熷鍊�
       this.modelDefault = JSON.parse(JSON.stringify(this.model))
-      this.$bus.$on('sendAllDeviceList', deviceList => this.deviceList = deviceList)
     },
     methods: {
       add(params) {
diff --git a/src/views/dnc/base/modules/ProductStructure/ProcessStep/ProcessStepInfo.vue b/src/views/dnc/base/modules/ProductStructure/ProcessStep/ProcessStepInfo.vue
index d4649d5..550a750 100644
--- a/src/views/dnc/base/modules/ProductStructure/ProcessStep/ProcessStepInfo.vue
+++ b/src/views/dnc/base/modules/ProductStructure/ProcessStep/ProcessStepInfo.vue
@@ -1,5 +1,5 @@
 <template>
-  <a-descriptions bordered :size="size">
+  <a-descriptions bordered :size="size" :column="4">
     <a-descriptions-item label="宸ユ鍚嶇О">{{currentLevelDetails.stepName}}</a-descriptions-item>
     <a-descriptions-item label="宸ユ鍙�">{{currentLevelDetails.stepCode}}</a-descriptions-item>
     <a-descriptions-item label="宸ユ绫诲瀷">{{currentLevelDetails.stepType}}</a-descriptions-item>
@@ -7,12 +7,12 @@
     <a-descriptions-item label="鍔犲伐璁惧绫诲瀷">{{currentLevelDetails.processingEquipmentOs}}</a-descriptions-item>
     <a-descriptions-item label="鍔犲伐璁惧缂栧彿">{{currentLevelDetails.deviceNo}}</a-descriptions-item>
     <a-descriptions-item label="宸ヨ缂栧彿">{{currentLevelDetails.assembleStep}}</a-descriptions-item>
-    <a-descriptions-item label="宸ヨ鍚嶇О	" :span="2">{{currentLevelDetails.assembleName}}</a-descriptions-item>
-    <a-descriptions-item label="鍒涘缓浜�">{{currentLevelDetails.createName}}</a-descriptions-item>
-    <a-descriptions-item label="鍒涘缓鏃堕棿" :span="2">{{currentLevelDetails.createTime}}</a-descriptions-item>
-    <a-descriptions-item label="淇敼浜�">{{currentLevelDetails.updateName}}</a-descriptions-item>
-    <a-descriptions-item label="淇敼鏃堕棿" :span="2">{{currentLevelDetails.updateTime}}</a-descriptions-item>
-    <a-descriptions-item label="鎻忚堪" :span="3">{{currentLevelDetails.description}}</a-descriptions-item>
+    <a-descriptions-item label="宸ヨ鍚嶇О">{{currentLevelDetails.assembleName}}</a-descriptions-item>
+    <a-descriptions-item label="鍒涘缓浜�">{{currentLevelDetails.createBy_dictText}}</a-descriptions-item>
+    <a-descriptions-item label="鍒涘缓鏃堕棿">{{currentLevelDetails.createTime}}</a-descriptions-item>
+    <a-descriptions-item label="淇敼浜�">{{currentLevelDetails.updateBy_dictText}}</a-descriptions-item>
+    <a-descriptions-item label="淇敼鏃堕棿">{{currentLevelDetails.updateTime}}</a-descriptions-item>
+    <a-descriptions-item label="鎻忚堪" :span="4">{{currentLevelDetails.description}}</a-descriptions-item>
   </a-descriptions>
 </template>
 
diff --git a/src/views/dnc/base/modules/ProductStructure/Product/ProductInfo.vue b/src/views/dnc/base/modules/ProductStructure/Product/ProductInfo.vue
index d28dc8a..e20f3c8 100644
--- a/src/views/dnc/base/modules/ProductStructure/Product/ProductInfo.vue
+++ b/src/views/dnc/base/modules/ProductStructure/Product/ProductInfo.vue
@@ -1,12 +1,12 @@
 <template>
-  <a-descriptions bordered :size="size">
-    <a-descriptions-item label="鍚嶇О">{{currentLevelDetails.productName}}</a-descriptions-item>
+  <a-descriptions bordered :size="size" :column="4">
+    <a-descriptions-item label="鍚嶇О" :span="2">{{currentLevelDetails.productName}}</a-descriptions-item>
     <a-descriptions-item label="鍨嬪彿">{{currentLevelDetails.productModel}}</a-descriptions-item>
     <a-descriptions-item label="浠g爜	">{{currentLevelDetails.productNo}}</a-descriptions-item>
-    <a-descriptions-item label="鍒涘缓浜�">{{currentLevelDetails.createName}}</a-descriptions-item>
-    <a-descriptions-item label="鍒涘缓鏃堕棿" :span="2">{{currentLevelDetails.createTime}}</a-descriptions-item>
-    <a-descriptions-item label="淇敼浜�">{{currentLevelDetails.updateName}}</a-descriptions-item>
-    <a-descriptions-item label="淇敼鏃堕棿" :span="2">{{currentLevelDetails.updateTime}}</a-descriptions-item>
+    <a-descriptions-item label="鍒涘缓浜�">{{currentLevelDetails.createBy_dictText}}</a-descriptions-item>
+    <a-descriptions-item label="鍒涘缓鏃堕棿">{{currentLevelDetails.createTime}}</a-descriptions-item>
+    <a-descriptions-item label="淇敼浜�">{{currentLevelDetails.updateBy_dictText}}</a-descriptions-item>
+    <a-descriptions-item label="淇敼鏃堕棿">{{currentLevelDetails.updateTime}}</a-descriptions-item>
   </a-descriptions>
 </template>
 
diff --git a/src/views/dnc/base/modules/ProductStructure/ProductStructureMain.vue b/src/views/dnc/base/modules/ProductStructure/ProductStructureMain.vue
index 30e0928..4f9ae99 100644
--- a/src/views/dnc/base/modules/ProductStructure/ProductStructureMain.vue
+++ b/src/views/dnc/base/modules/ProductStructure/ProductStructureMain.vue
@@ -1,12 +1,12 @@
 <template>
   <div style="height: 100%;max-height: 748px">
     <!--浜у搧缁撴瀯鏍戝彸渚ч《閮ㄥ尯鍩�-->
-    <div style="height: 45%;overflow: hidden">
+    <div style="height: 55%;overflow: hidden">
       <ProductStructureMainTop :size="tabContainerSize"/>
     </div>
 
     <!--浜у搧缁撴瀯鏍戝彸渚у簳閮ㄥ尯鍩�-->
-    <div style="height: 55%;overflow: hidden">
+    <div style="height: 45%;overflow: hidden">
       <ProductStructureMainBottom :size="tabContainerSize"/>
     </div>
   </div>
diff --git a/src/views/dnc/base/modules/ProductStructure/ProductStructureMainBottom.vue b/src/views/dnc/base/modules/ProductStructure/ProductStructureMainBottom.vue
index c00a390..ddaa77d 100644
--- a/src/views/dnc/base/modules/ProductStructure/ProductStructureMainBottom.vue
+++ b/src/views/dnc/base/modules/ProductStructure/ProductStructureMainBottom.vue
@@ -1,6 +1,7 @@
 <template>
-  <div v-if="currentLevelInfo.type!==4">
-    <a-tabs v-model="activeTabKey" v-if="Object.keys(currentLevelInfo).length>0" @change="handleTabChange">
+  <div v-if="+currentLevelInfo.type!==4" style="height: 100%">
+    <a-tabs style="height: 100%" v-model="activeTabKey" v-if="Object.keys(currentLevelInfo).length>0"
+            @change="handleTabChange">
       <a-tab-pane :key="1" tab="灞炴�т俊鎭�" v-if="+currentLevelInfo.type===1">
         <ProductInfo :currentLevelDetails="currentLevelInfo.entity" :size="containerSize"/>
       </a-tab-pane>
@@ -21,8 +22,10 @@
         <ProcessStepInfo :currentLevelDetails="currentLevelInfo.entity" :size="containerSize"/>
       </a-tab-pane>
 
-      <a-tab-pane :key="2" tab="鍒�鍏蜂俊鎭�" v-if="+currentLevelInfo.type===5||+currentLevelInfo.type===6">
-        <CutterInfo :currentLevelDetails="currentLevelInfo.entity" :size="containerSize"/>
+      <!--绯荤粺鍙傛暟閰嶇疆涓嫢鍙傛暟涓�1鏃舵墠灞曠ず瀵瑰簲灞傜骇鐨勫垁鍏峰垪琛�-->
+      <a-tab-pane :key="2" tab="鍒�鍏峰垪琛�"
+                  v-if="+currentLevelInfo.type===5&&isProcessHasCutterList||+currentLevelInfo.type===6&&isProcessStepHasCutterList">
+        <CutterTableList ref="cutterTableListRef" :currentLevelInfo="currentLevelInfo" :size="containerSize"/>
       </a-tab-pane>
 
       <template v-if="currentLevelInfo.hasOwnProperty('attributionType')">
@@ -57,19 +60,19 @@
 import ComponentInfo from './Component/ComponentInfo'
 import PartInfo from './Part/PartInfo'
 import ProcessInfo from './Process/ProcessInfo'
+import ProcessStepInfo from './ProcessStep/ProcessStepInfo'
+import CutterTableList from './Cutter/CutterTableList'
 import DocumentInfo from '../../../common/DocumentInfo'
 import DocumentVersionTableList from '../../../common/DocumentVersionTableList'
 import UseDocumentEquipmentTableList from './Document/UseNcDocumentEquipmentTableList'
-import ProcessStepInfo from './ProcessStep/ProcessStepInfo'
 import FilePreview from '../../../common/FilePreview'
 import TableContextMenu from '../../../common/TableContextMenu'
-import CutterInfo from '@views/dnc/base/modules/ProductStructure/Cutter/CutterInfo.vue'
-import dncApi from '@/api/dnc'
+import { getAction } from '@/api/manage'
 
 export default {
   name: 'ProductStructureMainBottom',
   components: {
-    CutterInfo,
+    CutterTableList,
     TableContextMenu,
     FilePreview,
     ProcessStepInfo,
@@ -85,6 +88,8 @@
     return {
       activeTabKey: 1,
       containerSize: 'small',
+      isProcessHasCutterList: false,
+      isProcessStepHasCutterList: false,
       currentLevelInfo: {},
       currentRightClickedTableRowInfo: {},
       hasLoadedDataTabKeyArray: []
@@ -94,30 +99,47 @@
     this.$bus.$on('sendCurrentClickedDocumentInfo', this.receiveCurrentLevelInfo)
     this.$bus.$on('sendCurrentTreeNodeInfo', this.receiveCurrentLevelInfo)
     this.$bus.$on('reloadMainBottomTableData', this.reloadMainBottomTableData)
+    this.getCutterListDisplayPermission('dnc_cutter_process', 'isProcessHasCutterList')
+    this.getCutterListDisplayPermission('dnc_cutter_step', 'isProcessStepHasCutterList')
   },
   methods: {
+    /**
+     * 鑾峰彇鍒�鍏峰垪琛ㄥ湪宸ュ簭鍜屽伐姝ュ眰绾х殑灞曠ず鏉冮檺
+     * @param settingKey 鍚勫眰绾у睍绀烘潈闄恔ey
+     * @param dataProperty 缁勪欢data涓殑灞炴�у�肩敤鏉ユ帶鍒舵槸鍚﹀睍绀�
+     */
+    getCutterListDisplayPermission(settingKey, dataProperty) {
+      getAction(`/system/sysParams/query/by/settingKey?settingKey=${settingKey}`).then(res => {
+        if (res.success) {
+          this[dataProperty] = res.result.settingValue === '1'
+        }
+      })
+    },
+
     /**
      * 鎺ユ敹鏍戠粍浠朵互鍙婅〃鏍间紶鏉ョ殑褰撳墠閫変腑鎴栫偣鍑荤殑椤逛俊鎭�
      * @param levelInfo 褰撳墠灞傜骇淇℃伅
      */
     receiveCurrentLevelInfo(levelInfo) {
-      const { id, type } = levelInfo
-      dncApi.getProductStructureTreeNodeEntityApi({ id, type })
-        .then(res => {
-          if (res.success) {
-            console.log('res-------------', res)
-            this.currentLevelInfo = Object.assign(levelInfo, { entity: res.result[0] })
-          }
-        })
+      this.currentLevelInfo = levelInfo
       this.activeTabKey = 1
       this.hasLoadedDataTabKeyArray = []
     },
 
+    /**
+     * tab鏍忓垏鎹㈡椂瑙﹀彂
+     * @param activeTabKey 褰撳墠婵�娲荤殑tabKey
+     */
     handleTabChange(activeTabKey) {
       if (!this.hasLoadedDataTabKeyArray.includes(activeTabKey)) {
         switch (activeTabKey) {
           case 2:
-            this.$nextTick(() => this.$refs.filePreviewRef.getFilePreviewByApi())
+            if (this.currentLevelInfo.hasOwnProperty('attributionType')) {
+              console.log('ref', this.$refs.filePreviewRef)
+              this.$nextTick(() => this.$refs.filePreviewRef.getFilePreviewByApi())
+            } else {
+              this.$nextTick(() => this.$refs.cutterTableListRef.setQueryParamAndLoadData(1))
+            }
             break
           case 3:
             this.$nextTick(() => this.$refs.documentVersionTableRef.loadData())
@@ -172,6 +194,6 @@
 }
 
 /deep/ .ant-tabs-tabpane {
-  overflow: auto;
+  overflow: hidden auto;
 }
 </style>
\ No newline at end of file
diff --git a/src/views/dnc/base/modules/ProductStructure/ProductStructureMainTop.vue b/src/views/dnc/base/modules/ProductStructure/ProductStructureMainTop.vue
index 4dcd066..e67db7c 100644
--- a/src/views/dnc/base/modules/ProductStructure/ProductStructureMainTop.vue
+++ b/src/views/dnc/base/modules/ProductStructure/ProductStructureMainTop.vue
@@ -1,10 +1,18 @@
 <template>
-  <div v-if="currentTreeNodeInfo.type!==4">
+  <div v-if="+currentTreeNodeInfo.type!==4">
     <a-tabs v-model="activeTabKey" @contextmenu.native="e=>e.preventDefault()" @change="handleTabChange"
             v-if="Object.keys(currentTreeNodeInfo).length!==0">
-      <a-tab-pane :key="1" tab="NC鏂囨。" v-if="currentTreeNodeInfo.type===5||currentTreeNodeInfo.type===6">
-        <NcDocumentTableList ref="ncDocumentTableListRef" :currentTreeNodeInfo="currentTreeNodeInfo"
-                             @handleTableContextMenuOpen="handleTableContextMenuOpen" :size="tableContainerSize"/>
+      <a-tab-pane :key="1" tab="NC绠$悊" v-if="+currentTreeNodeInfo.type===5||+currentTreeNodeInfo.type===6">
+        <div style="display: flex;">
+          <!--绯荤粺鍙傛暟閰嶇疆涓嫢鍙傛暟涓�1鏃舵墠灞曠ず瀵瑰簲灞傜骇鐨勮澶囩被鍒楄〃-->
+          <DeviceCustomTypeTableList
+            v-if="+currentTreeNodeInfo.type===5&&isProcessHasDeviceTypeList||+currentTreeNodeInfo.type===6&&isProcessStepHasDeviceTypeList"
+            ref="deviceCustomTypeTableList" :currentTreeNodeInfo="currentTreeNodeInfo"
+            @handleTableContextMenuOpen="handleTableContextMenuOpen" :size="tableContainerSize"/>
+
+          <NcDocumentTableList ref="ncDocumentTableListRef" :currentTreeNodeInfo="currentTreeNodeInfo"
+                               @handleTableContextMenuOpen="handleTableContextMenuOpen" :size="tableContainerSize"/>
+        </div>
       </a-tab-pane>
 
       <a-tab-pane :key="2" tab="鍏朵粬鏂囨。">
@@ -21,398 +29,426 @@
 </template>
 
 <script>
-  import dncApi from '@/api/dnc'
-  import NcDocumentTableList from './Document/NcDocumentTableList'
-  import OtherDocumentTableList from './Document/OtherDocumentTableList'
-  import TableContextMenu from '../../../common/TableContextMenu'
-  import DocumentBatchDeleteModal from '../../../common/DocumentBatchDeleteModal'
+import dncApi from '@/api/dnc'
+import NcDocumentTableList from './Document/NcDocumentTableList'
+import OtherDocumentTableList from './Document/OtherDocumentTableList'
+import TableContextMenu from '../../../common/TableContextMenu'
+import DocumentBatchDeleteModal from '../../../common/DocumentBatchDeleteModal'
+import DeviceCustomTypeTableList
+  from '@views/dnc/base/modules/ProductStructure/DeviceCustomType/DeviceCustomTypeTableList.vue'
+import { getAction } from '@api/manage'
 
-  export default {
-    name: 'ProductStructureMainTop',
-    components: { DocumentBatchDeleteModal, TableContextMenu, OtherDocumentTableList, NcDocumentTableList },
-    data() {
-      return {
-        activeTabKey: 2,
-        tableContainerSize: 'small',
-        currentRightClickedTableRowInfo: {},
-        currentTreeNodeInfo: {},
-        hasLoadedDataTabKeyArray: []
+export default {
+  name: 'ProductStructureMainTop',
+  components: {
+    DeviceCustomTypeTableList,
+    DocumentBatchDeleteModal,
+    TableContextMenu,
+    OtherDocumentTableList,
+    NcDocumentTableList
+  },
+  data() {
+    return {
+      activeTabKey: 2,
+      tableContainerSize: 'small',
+      isProcessHasDeviceTypeList: false,
+      isProcessStepHasDeviceTypeList: false,
+      currentRightClickedTableRowInfo: {},
+      currentTreeNodeInfo: {},
+      hasLoadedDataTabKeyArray: []
+    }
+  },
+  created() {
+    this.$bus.$on('sendCurrentTreeNodeInfo', this.receiveCurrentTreeNodeInfo)
+    this.$bus.$on('reloadDocumentListData', this.reloadDocumentListData)
+    this.$bus.$on('tableMenuItemMethodTrigger', this.triggerCorrespondingMethod)
+    this.getDeviceTypeListDisplayPermission('dnc_device_type_process', 'isProcessHasDeviceTypeList')
+    this.getDeviceTypeListDisplayPermission('dnc_device_type_step', 'isProcessStepHasDeviceTypeList')
+  },
+  methods: {
+    /**
+     * 鑾峰彇璁惧绫诲瀷鍒楄〃鍦ㄥ伐搴忓拰宸ユ灞傜骇鐨勫睍绀烘潈闄�
+     * @param settingKey 鍚勫眰绾у睍绀烘潈闄恔ey
+     * @param dataProperty 缁勪欢data涓殑灞炴�у�肩敤鏉ユ帶鍒舵槸鍚﹀睍绀�
+     */
+    getDeviceTypeListDisplayPermission(settingKey, dataProperty) {
+      getAction(`/system/sysParams/query/by/settingKey?settingKey=${settingKey}`).then(res => {
+        if (res.success) {
+          this[dataProperty] = res.result.settingValue === '1'
+        }
+      })
+    },
+
+    /**
+     * 鎺ユ敹鏍戠粍浠朵紶鏉ョ殑褰撳墠閫変腑鐨勬爲鑺傜偣淇℃伅
+     * @param treeNodeInfo 鏍戣妭鐐逛俊鎭�
+     */
+    receiveCurrentTreeNodeInfo(treeNodeInfo) {
+      console.log('treeNodeInfo', treeNodeInfo)
+      // 浠庢爲缁勪欢鎺ュ彈鏍戣妭鐐逛俊鎭悗浠庣埗缁勪欢娴佸叆瀛愮粍浠�
+      this.currentTreeNodeInfo = treeNodeInfo
+      // 娓呯┖涓婁竴鑺傜偣宸茬粡鍔犺浇杩囧緱鏂囨。鍒楄〃tabKey
+      this.hasLoadedDataTabKeyArray = []
+      if (+treeNodeInfo.type === 5 || +treeNodeInfo.type === 6) {
+        this.activeTabKey = 1
+        this.$nextTick(() => {
+          if (this.$refs.deviceCustomTypeTableList) this.$refs.deviceCustomTypeTableList.setQueryParamAndLoadData()
+          else if (this.$refs.ncDocumentTableListRef) this.$refs.ncDocumentTableListRef.loadData(1)
+        })
+      } else {
+        this.activeTabKey = 2
+        this.$nextTick(() => {
+          if (this.$refs.otherDocumentTableListRef) this.$refs.otherDocumentTableListRef.loadData(1)
+        })
+      }
+      this.hasLoadedDataTabKeyArray.push(this.activeTabKey)
+    },
+
+    /**
+     * 鎺у埗鍙抽敭鑿滃崟寮�鍚�
+     * @param record 褰撳墠琛ㄦ牸琛屼俊鎭�
+     */
+    handleTableContextMenuOpen(record) {
+      this.currentRightClickedTableRowInfo = Object.assign({}, record)
+      console.log('currentRightClickedTableRowInfo', this.currentRightClickedTableRowInfo)
+      this.$refs.tableContextMenuRef.currentMenuLevel = record.param
+      this.$refs.tableContextMenuRef.menuStyle.top = event.clientY + 'px'
+      this.$refs.tableContextMenuRef.menuStyle.left = event.clientX + 'px'
+      this.$refs.tableContextMenuRef.menuVisible = true
+      document.body.addEventListener('click', this.handleMenuClose)
+    },
+
+    /**
+     * tab鏍忓垏鎹㈡椂瑙﹀彂
+     * @param activeTabKey 鍒囨崲鍚庣殑tabKey
+     */
+    handleTabChange(activeTabKey) {
+      if (!this.hasLoadedDataTabKeyArray.includes(activeTabKey)) {
+        this.$nextTick(() => {
+          if (this.$refs.otherDocumentTableListRef) this.$refs.otherDocumentTableListRef.loadData(1)
+        })
+        // 闃绘鎺ュ彛鍦ㄥ悓涓�鏂囨。涓�娆$偣鍑诲唴澶氭瑙﹀彂
+        this.hasLoadedDataTabKeyArray.push(activeTabKey)
       }
     },
-    created() {
-      this.$bus.$on('sendCurrentTreeNodeInfo', this.receiveCurrentTreeNodeInfo)
-      this.$bus.$on('reloadDocumentListData', this.reloadDocumentListData)
-      this.$bus.$on('tableMenuItemMethodTrigger', this.triggerCorrespondingMethod)
+    /**
+     * 鏂囨。浠ュ強NC绋嬪簭瀵煎叆/鍑哄簱/鍏ュ簱鎴愬姛鍚庤Е鍙戦噸鏂板姞杞芥枃妗e垪琛�
+     * @param docClassCode 鏂囨。绫诲埆
+     * @param attributionId 鑺傜偣Id
+     */
+    reloadDocumentListData({ docClassCode, attributionId }) {
+      // 濡傛灉涓婁紶鐨勬枃妗d笉鏄墍灞炰簬褰撳墠鎵�灞曠ず鑺傜偣鐨勬枃妗e垯涓嶉噸鏂拌幏鍙栨枃妗e垪琛�
+      if (this.currentTreeNodeInfo.id !== attributionId) return
+      if (docClassCode === 'NC') {
+        if (this.$refs.ncDocumentTableListRef) this.$refs.ncDocumentTableListRef.loadData(1)
+      } else {
+        if (this.$refs.otherDocumentTableListRef) this.$refs.otherDocumentTableListRef.loadData(1)
+      }
     },
-    methods: {
-      /**
-       * 鎺у埗鍙抽敭鑿滃崟寮�鍚�
-       * @param record 褰撳墠琛ㄦ牸琛屼俊鎭�
-       */
-      handleTableContextMenuOpen(record) {
-        this.currentRightClickedTableRowInfo = Object.assign({}, record)
-        console.log('currentRightClickedTableRowInfo', this.currentRightClickedTableRowInfo)
-        this.$refs.tableContextMenuRef.currentMenuLevel = record.param
-        this.$refs.tableContextMenuRef.menuStyle.top = event.clientY + 'px'
-        this.$refs.tableContextMenuRef.menuStyle.left = event.clientX + 'px'
-        this.$refs.tableContextMenuRef.menuVisible = true
-        document.body.addEventListener('click', this.handleMenuClose)
-      },
 
-      /**
-       * 鎺ユ敹鏍戠粍浠朵紶鏉ョ殑褰撳墠閫変腑鐨勬爲鑺傜偣淇℃伅
-       * @param treeNodeInfo 鏍戣妭鐐逛俊鎭�
-       */
-      receiveCurrentTreeNodeInfo(treeNodeInfo) {
-        // 浠庢爲缁勪欢鎺ュ彈鏍戣妭鐐逛俊鎭悗浠庣埗缁勪欢娴佸叆瀛愮粍浠�
-        this.currentTreeNodeInfo = treeNodeInfo
-        // 娓呯┖涓婁竴鑺傜偣宸茬粡鍔犺浇杩囧緱鏂囨。鍒楄〃tabKey
-        this.hasLoadedDataTabKeyArray = []
-        if (treeNodeInfo.type === 5 || treeNodeInfo.type === 6) {
-          this.activeTabKey = 1
-          this.$nextTick(() => {
-            if (this.$refs.ncDocumentTableListRef) this.$refs.ncDocumentTableListRef.loadData(1)
-          })
-        } else {
-          this.activeTabKey = 2
-          this.$nextTick(() => {
-            if (this.$refs.otherDocumentTableListRef) this.$refs.otherDocumentTableListRef.loadData(1)
-          })
-        }
-        this.hasLoadedDataTabKeyArray.push(this.activeTabKey)
-      },
-
-      /**
-       * tab鏍忓垏鎹㈡椂瑙﹀彂
-       * @param activeTabKey 鍒囨崲鍚庣殑tabKey
-       */
-      handleTabChange(activeTabKey) {
-        if (!this.hasLoadedDataTabKeyArray.includes(activeTabKey)) {
-          this.$nextTick(() => {
-            if (this.$refs.otherDocumentTableListRef) this.$refs.otherDocumentTableListRef.loadData(1)
-          })
-          // 闃绘鎺ュ彛鍦ㄥ悓涓�鏂囨。涓�娆$偣鍑诲唴澶氭瑙﹀彂
-          this.hasLoadedDataTabKeyArray.push(activeTabKey)
-        }
-      },
-      /**
-       * 鏂囨。浠ュ強NC绋嬪簭瀵煎叆/鍑哄簱/鍏ュ簱鎴愬姛鍚庤Е鍙戦噸鏂板姞杞芥枃妗e垪琛�
-       * @param docClassCode 鏂囨。绫诲埆
-       * @param attributionId 鑺傜偣Id
-       */
-      reloadDocumentListData({ docClassCode, attributionId }) {
-        // 濡傛灉涓婁紶鐨勬枃妗d笉鏄墍灞炰簬褰撳墠鎵�灞曠ず鑺傜偣鐨勬枃妗e垯涓嶉噸鏂拌幏鍙栨枃妗e垪琛�
-        if (this.currentTreeNodeInfo.id !== attributionId) return
-        if (docClassCode === 'NC') {
-          if (this.$refs.ncDocumentTableListRef) this.$refs.ncDocumentTableListRef.loadData(1)
-        } else {
-          if (this.$refs.otherDocumentTableListRef) this.$refs.otherDocumentTableListRef.loadData(1)
-        }
-      },
-
-      // 涓嬭浇褰撳墠鍙抽敭閫変腑鏂囨。
-      handleDownload() {
-        const that = this
-        const { docId, docName } = this.currentRightClickedTableRowInfo
-        dncApi.downloadDocumentApi({ docId, docName })
-          .then(res => {
-            if (res && !res.success) {
-              that.$notification.error({
-                message: '娑堟伅',
-                description: res.message
-              })
-            }
-          })
-          .catch(err => {
+    // 涓嬭浇褰撳墠鍙抽敭閫変腑鏂囨。
+    handleDownload() {
+      const that = this
+      const { docId, docName } = this.currentRightClickedTableRowInfo
+      dncApi.downloadDocumentApi({ docId, docName })
+        .then(res => {
+          if (res && !res.success) {
             that.$notification.error({
               message: '娑堟伅',
-              description: err.message
+              description: res.message
             })
+          }
+        })
+        .catch(err => {
+          that.$notification.error({
+            message: '娑堟伅',
+            description: err.message
           })
-      },
-
-      // 鍒犻櫎褰撳墠鍙抽敭閫変腑鏂囨。
-      handleDelete() {
-        const { docId, param, attributionId } = this.currentRightClickedTableRowInfo
-        const that = this
-        that.$confirm({
-          title: '鎻愮ず',
-          content: `鍒犻櫎鍚庝笉鍙彇娑堬紝纭鍒犻櫎鍚楋紵`,
-          okText: '纭',
-          cancelText: '鍙栨秷',
-          onOk: () => {
-            dncApi.deleteDocumentApi(docId)
-              .then((res) => {
-                if (res.success) {
-                  that.$notification.success({
-                    message: '娑堟伅',
-                    description: res.message
-                  })
-                  that.reloadDocumentListData({ docClassCode: param, attributionId })
-                } else {
-                  that.$notification.warning({
-                    message: '娑堟伅',
-                    description: res.message
-                  })
-                }
-              })
-              .finally(() => {
-                that.$destroyAll()
-              })
-          },
-          onCancel: () => {
-            that.$destroyAll()
-          }
         })
-      },
+    },
 
-      /**
-       * 鐐瑰嚮鎵归噺鍒犻櫎鍚庡嚭鐜板脊绐�
-       * @param modalTitle 寮圭獥鏍囬
-       */
-      handleBatchRemove(modalTitle) {
-        if (!this.$refs.documentBatchDeleteModalRef) return
-        this.$refs.documentBatchDeleteModalRef.title = modalTitle
-        this.$refs.documentBatchDeleteModalRef.visible = true
-      },
+    // 鍒犻櫎褰撳墠鍙抽敭閫変腑鏂囨。
+    handleDelete() {
+      const { docId, param, attributionId } = this.currentRightClickedTableRowInfo
+      const that = this
+      that.$confirm({
+        title: '鎻愮ず',
+        content: `鍒犻櫎鍚庝笉鍙彇娑堬紝纭鍒犻櫎鍚楋紵`,
+        okText: '纭',
+        cancelText: '鍙栨秷',
+        onOk: () => {
+          dncApi.deleteDocumentApi(docId)
+            .then((res) => {
+              if (res.success) {
+                that.$notification.success({
+                  message: '娑堟伅',
+                  description: res.message
+                })
+                that.reloadDocumentListData({ docClassCode: param, attributionId })
+              } else {
+                that.$notification.warning({
+                  message: '娑堟伅',
+                  description: res.message
+                })
+              }
+            })
+            .finally(() => {
+              that.$destroyAll()
+            })
+        },
+        onCancel: () => {
+          that.$destroyAll()
+        }
+      })
+    },
 
-      /**
-       * 鍑哄簱褰撳墠鍙抽敭閫変腑鏂囨。
-       * @param menuLabel
-       */
-      handlePull(menuLabel) {
-        const that = this
-        const { docId, docName, param, attributionId } = this.currentRightClickedTableRowInfo
-        that.$confirm({
-          title: '鎻愮ず',
-          content: `纭${menuLabel}鍚楋紵`,
-          okText: '纭',
-          cancelText: '鍙栨秷',
-          onOk: () => {
-            dncApi.documentOutboundApi({ docId, docName })
-              .then(res => {
-                console.log('res------------------', res)
-                if (res.success) {
-                  that.reloadDocumentListData({ docClassCode: param, attributionId })
-                  that.$notification.success({
-                    message: '娑堟伅',
-                    description: `${menuLabel}鎴愬姛`
-                  })
-                } else {
-                  that.$notification.error({
-                    message: '娑堟伅',
-                    description: res.message
-                  })
-                }
-              })
-              .catch(err => {
+    /**
+     * 鐐瑰嚮鎵归噺鍒犻櫎鍚庡嚭鐜板脊绐�
+     * @param modalTitle 寮圭獥鏍囬
+     */
+    handleBatchRemove(modalTitle) {
+      if (!this.$refs.documentBatchDeleteModalRef) return
+      this.$refs.documentBatchDeleteModalRef.title = modalTitle
+      this.$refs.documentBatchDeleteModalRef.visible = true
+    },
+
+    /**
+     * 鍑哄簱褰撳墠鍙抽敭閫変腑鏂囨。
+     * @param menuLabel
+     */
+    handlePull(menuLabel) {
+      const that = this
+      const { docId, docName, param, attributionId } = this.currentRightClickedTableRowInfo
+      that.$confirm({
+        title: '鎻愮ず',
+        content: `纭${menuLabel}鍚楋紵`,
+        okText: '纭',
+        cancelText: '鍙栨秷',
+        onOk: () => {
+          dncApi.documentOutboundApi({ docId, docName })
+            .then(res => {
+              console.log('res------------------', res)
+              if (res.success) {
+                that.reloadDocumentListData({ docClassCode: param, attributionId })
+                that.$notification.success({
+                  message: '娑堟伅',
+                  description: `${menuLabel}鎴愬姛`
+                })
+              } else {
                 that.$notification.error({
                   message: '娑堟伅',
-                  description: err.message
+                  description: res.message
                 })
+              }
+            })
+            .catch(err => {
+              that.$notification.error({
+                message: '娑堟伅',
+                description: err.message
               })
-              .finally(() => {
-                that.$destroyAll()
-              })
-          },
-          onCancel: () => {
-            that.$destroyAll()
-          }
-        })
-      },
+            })
+            .finally(() => {
+              that.$destroyAll()
+            })
+        },
+        onCancel: () => {
+          that.$destroyAll()
+        }
+      })
+    },
 
-      /**
-       * 鍙栨秷鍑哄簱褰撳墠鍙抽敭閫変腑鏂囨。
-       * @param menuLabel
-       */
-      handleCancelPull(menuLabel) {
-        const that = this
-        const { docId, param, attributionId } = this.currentRightClickedTableRowInfo
-        that.$confirm({
-          title: '鎻愮ず',
-          content: `纭${menuLabel}鍚楋紵`,
-          okText: '纭',
-          cancelText: '鍙栨秷',
-          onOk: () => {
-            dncApi.documentCancelOutboundApi(docId)
-              .then(res => {
-                if (res.success) {
-                  this.reloadDocumentListData({ docClassCode: param, attributionId })
-                  that.$notification.success({
-                    message: '娑堟伅',
-                    description: res.message
-                  })
-                } else {
-                  that.$notification.error({
-                    message: '娑堟伅',
-                    description: res.message
-                  })
-                }
-              })
-              .catch(err => {
+    /**
+     * 鍙栨秷鍑哄簱褰撳墠鍙抽敭閫変腑鏂囨。
+     * @param menuLabel
+     */
+    handleCancelPull(menuLabel) {
+      const that = this
+      const { docId, param, attributionId } = this.currentRightClickedTableRowInfo
+      that.$confirm({
+        title: '鎻愮ず',
+        content: `纭${menuLabel}鍚楋紵`,
+        okText: '纭',
+        cancelText: '鍙栨秷',
+        onOk: () => {
+          dncApi.documentCancelOutboundApi(docId)
+            .then(res => {
+              if (res.success) {
+                this.reloadDocumentListData({ docClassCode: param, attributionId })
+                that.$notification.success({
+                  message: '娑堟伅',
+                  description: res.message
+                })
+              } else {
                 that.$notification.error({
                   message: '娑堟伅',
-                  description: err.message
+                  description: res.message
                 })
+              }
+            })
+            .catch(err => {
+              that.$notification.error({
+                message: '娑堟伅',
+                description: err.message
               })
-              .finally(() => {
-                that.$destroyAll()
-              })
-          },
-          onCancel() {
-            that.$destroyAll()
-          }
-        })
-      },
+            })
+            .finally(() => {
+              that.$destroyAll()
+            })
+        },
+        onCancel() {
+          that.$destroyAll()
+        }
+      })
+    },
 
-      /**
-       * 鍙戝竷褰撳墠鍙抽敭閫変腑鏂囨。
-       * @param menuLabel
-       */
-      handlePublish(menuLabel) {
-        const that = this
-        const { docId, param, attributionId } = this.currentRightClickedTableRowInfo
-        that.$confirm({
-          title: '鎻愮ず',
-          content: `纭${menuLabel}鍚楋紵`,
-          okText: '纭',
-          cancelText: '鍙栨秷',
-          onOk: () => {
-            dncApi.documentPublishApi(docId)
-              .then(res => {
-                if (res.success) {
-                  this.reloadDocumentListData({ docClassCode: param, attributionId })
-                  this.$bus.$emit('reloadMainBottomTableData', 'documentVersion')
-                  that.$notification.success({
-                    message: '娑堟伅',
-                    description: res.message
-                  })
-                } else {
-                  that.$notification.error({
-                    message: '娑堟伅',
-                    description: res.message
-                  })
-                }
-              })
-              .catch(err => {
+    /**
+     * 鍙戝竷褰撳墠鍙抽敭閫変腑鏂囨。
+     * @param menuLabel
+     */
+    handlePublish(menuLabel) {
+      const that = this
+      const { docId, param, attributionId } = this.currentRightClickedTableRowInfo
+      that.$confirm({
+        title: '鎻愮ず',
+        content: `纭${menuLabel}鍚楋紵`,
+        okText: '纭',
+        cancelText: '鍙栨秷',
+        onOk: () => {
+          dncApi.documentPublishApi(docId)
+            .then(res => {
+              if (res.success) {
+                this.reloadDocumentListData({ docClassCode: param, attributionId })
+                this.$bus.$emit('reloadMainBottomTableData', 'documentVersion')
+                that.$notification.success({
+                  message: '娑堟伅',
+                  description: res.message
+                })
+              } else {
                 that.$notification.error({
                   message: '娑堟伅',
-                  description: err.message
+                  description: res.message
                 })
+              }
+            })
+            .catch(err => {
+              that.$notification.error({
+                message: '娑堟伅',
+                description: err.message
               })
-              .finally(() => {
-                that.$destroyAll()
-              })
-          },
-          onCancel() {
-            that.$destroyAll()
-          }
-        })
-      },
+            })
+            .finally(() => {
+              that.$destroyAll()
+            })
+        },
+        onCancel() {
+          that.$destroyAll()
+        }
+      })
+    },
 
-      /**
-       * 閲嶆柊鍙戝竷褰撳墠鍙抽敭閫変腑鏂囨。骞堕噸鏂板彂甯冮��鍥炰笂涓�鏂囨。鐗堟湰
-       * @param menuLabel
-       */
-      handleRepublish(menuLabel) {
-        const that = this
-        const { docId, param, attributionId } = this.currentRightClickedTableRowInfo
-        that.$confirm({
-          title: '鎻愮ず',
-          content: `纭${menuLabel}鍚楋紵`,
-          okText: '纭',
-          cancelText: '鍙栨秷',
-          onOk: () => {
-            dncApi.documentRepublishApi(docId)
-              .then(res => {
-                if (res.success) {
-                  this.reloadDocumentListData({ docClassCode: param, attributionId })
-                  that.$notification.success({
-                    message: '娑堟伅',
-                    description: res.message
-                  })
-                } else {
-                  that.$notification.error({
-                    message: '娑堟伅',
-                    description: res.message
-                  })
-                }
-              })
-              .catch(err => {
+    /**
+     * 閲嶆柊鍙戝竷褰撳墠鍙抽敭閫変腑鏂囨。骞堕噸鏂板彂甯冮��鍥炰笂涓�鏂囨。鐗堟湰
+     * @param menuLabel
+     */
+    handleRepublish(menuLabel) {
+      const that = this
+      const { docId, param, attributionId } = this.currentRightClickedTableRowInfo
+      that.$confirm({
+        title: '鎻愮ず',
+        content: `纭${menuLabel}鍚楋紵`,
+        okText: '纭',
+        cancelText: '鍙栨秷',
+        onOk: () => {
+          dncApi.documentRepublishApi(docId)
+            .then(res => {
+              if (res.success) {
+                this.reloadDocumentListData({ docClassCode: param, attributionId })
+                that.$notification.success({
+                  message: '娑堟伅',
+                  description: res.message
+                })
+              } else {
                 that.$notification.error({
                   message: '娑堟伅',
-                  description: err.message
+                  description: res.message
                 })
+              }
+            })
+            .catch(err => {
+              that.$notification.error({
+                message: '娑堟伅',
+                description: err.message
               })
-              .finally(() => {
-                that.$destroyAll()
-              })
-          },
-          onCancel() {
-            that.$destroyAll()
-          }
-        })
-      },
+            })
+            .finally(() => {
+              that.$destroyAll()
+            })
+        },
+        onCancel() {
+          that.$destroyAll()
+        }
+      })
+    },
 
-      /**
-       * 閲嶅綊妗e綋鍓嶅彸閿�変腑鏂囨。涓斿悗缁棤娉曠户缁彂甯冩垨褰掓。
-       * @param menuLabel
-       */
-      handlePigeonhole(menuLabel) {
-        const that = this
-        const { docId, param, attributionId } = this.currentRightClickedTableRowInfo
-        that.$confirm({
-          title: '鎻愮ず',
-          content: `${menuLabel}鍚庝笉鍙彇娑堬紝纭${menuLabel}鍚楋紵`,
-          okText: '纭',
-          cancelText: '鍙栨秷',
-          onOk: () => {
-            dncApi.documentPigeonholeApi(docId)
-              .then(res => {
-                if (res.success) {
-                  this.reloadDocumentListData({ docClassCode: param, attributionId })
-                  that.$notification.success({
-                    message: '娑堟伅',
-                    description: res.message
-                  })
-                } else {
-                  that.$notification.error({
-                    message: '娑堟伅',
-                    description: res.message
-                  })
-                }
-              })
-              .catch(err => {
+    /**
+     * 閲嶅綊妗e綋鍓嶅彸閿�変腑鏂囨。涓斿悗缁棤娉曠户缁彂甯冩垨褰掓。
+     * @param menuLabel
+     */
+    handlePigeonhole(menuLabel) {
+      const that = this
+      const { docId, param, attributionId } = this.currentRightClickedTableRowInfo
+      that.$confirm({
+        title: '鎻愮ず',
+        content: `${menuLabel}鍚庝笉鍙彇娑堬紝纭${menuLabel}鍚楋紵`,
+        okText: '纭',
+        cancelText: '鍙栨秷',
+        onOk: () => {
+          dncApi.documentPigeonholeApi(docId)
+            .then(res => {
+              if (res.success) {
+                this.reloadDocumentListData({ docClassCode: param, attributionId })
+                that.$notification.success({
+                  message: '娑堟伅',
+                  description: res.message
+                })
+              } else {
                 that.$notification.error({
                   message: '娑堟伅',
-                  description: err.message
+                  description: res.message
                 })
+              }
+            })
+            .catch(err => {
+              that.$notification.error({
+                message: '娑堟伅',
+                description: err.message
               })
-              .finally(() => {
-                that.$destroyAll()
-              })
-          },
-          onCancel() {
-            that.$destroyAll()
-          }
-        })
-      },
+            })
+            .finally(() => {
+              that.$destroyAll()
+            })
+        },
+        onCancel() {
+          that.$destroyAll()
+        }
+      })
+    },
 
-      // 鎺у埗鍙抽敭鑿滃崟鍏抽棴
-      handleMenuClose() {
-        this.$refs.tableContextMenuRef.menuVisible = false
-        document.body.removeEventListener('click', this.handleMenuClose)
-      },
+    // 鎺у埗鍙抽敭鑿滃崟鍏抽棴
+    handleMenuClose() {
+      this.$refs.tableContextMenuRef.menuVisible = false
+      document.body.removeEventListener('click', this.handleMenuClose)
+    },
 
-      triggerCorrespondingMethod({ methodName, modalTitle }) {
-        if (this[methodName]) this[methodName](modalTitle)
-      }
+    triggerCorrespondingMethod({ methodName, modalTitle }) {
+      if (this[methodName]) this[methodName](modalTitle)
     }
   }
+}
 </script>
 
 <style scoped>
-  /deep/ .ant-table-tbody .ant-table-row {
-    cursor: pointer;
-  }
+/deep/ .ant-table-tbody .ant-table-row {
+  cursor: pointer;
+}
 </style>
\ No newline at end of file
diff --git a/src/views/dnc/base/modules/ProductStructure/ProductStructureTree.vue b/src/views/dnc/base/modules/ProductStructure/ProductStructureTree.vue
index 97b73a0..37a06d1 100644
--- a/src/views/dnc/base/modules/ProductStructure/ProductStructureTree.vue
+++ b/src/views/dnc/base/modules/ProductStructure/ProductStructureTree.vue
@@ -2,24 +2,26 @@
   <a-card class="tree_con" :loading="cardLoading" :bordered="false" @contextmenu.native="e=>e.preventDefault()">
     <a-spin :spinning="loading">
       <div style="display: flex;flex-direction: column;height: 100%">
-        <div style="display: flex">
+        <div style="display: flex;justify-content: space-between">
           <a-input placeholder="杈撳叆鍏抽敭瀛楄繘琛屾悳绱�" allowClear v-model="searchInput"
                    @change="handleSearchInputChange"/>
-          <a-dropdown :trigger="['click']" placement="bottomCenter" style="margin: 0 8px">
-            <a-menu slot="overlay">
-              <a-menu-item key="1" @click="expandedKeys = allTreeKeys">灞曞紑鎵�鏈�</a-menu-item>
-              <a-menu-item key="2" @click="expandedKeys = ['-1']">鍚堝苟鎵�鏈�</a-menu-item>
-              <a-menu-item key="3" @click="getTreeDataByApi">鍒锋柊</a-menu-item>
-            </a-menu>
-            <a-button>
-              <a-icon type="bars"/>
-            </a-button>
-          </a-dropdown>
-          <a-button type="primary" v-has="'product_add'"
+          <a-tooltip title="鍒锋柊">
+            <a-button icon="reload" @click="getTreeDataByApi" style="width: 18%;margin-left: 8px"></a-button>
+          </a-tooltip>
+          <a-button type="primary" v-has="'product_add'" icon="plus" style="margin-left: 8px"
                     @click="$refs.productModalFormRef.triggerCorrespondingMethod({modalTitle:'娣诲姞浜у搧',methodName:'handleProductAdd'})">
-            <a-icon type="plus"></a-icon>
-            浜у搧
+            娣诲姞浜у搧
           </a-button>
+          <!--          <a-dropdown :trigger="['click']" placement="bottomCenter" style="margin: 0 8px">-->
+          <!--            <a-menu slot="overlay">-->
+          <!--              <a-menu-item key="1" @click="expandedKeys = allTreeKeys">灞曞紑鎵�鏈�</a-menu-item>-->
+          <!--              <a-menu-item key="2" @click="expandedKeys = ['-1']">鍚堝苟鎵�鏈�</a-menu-item>-->
+          <!--              <a-menu-item key="3" @click="getTreeDataByApi">鍒锋柊</a-menu-item>-->
+          <!--            </a-menu>-->
+          <!--            <a-button>-->
+          <!--              <a-icon type="bars"/>-->
+          <!--            </a-button>-->
+          <!--          </a-dropdown>-->
         </div>
 
         <!--浜у搧缁撴瀯鏍�-->
@@ -27,9 +29,9 @@
           <a-tree blockNode show-icon :expandedKeys.sync="expandedKeys"
                   :selectedKeys="selectedKeys" :treeData="treeDataSource" :autoExpandParent="autoExpandParent"
                   @select="handleTreeSelect" @expand="handleTreeExpand" @rightClick="handleTreeRightClick">
-            <template slot="title" slot-scope="{ label, parentId, entity, key:treeKey,type}">
+            <template slot="title" slot-scope="{ label, parentId, key:treeKey,type}">
               <ProductStructureTreeContextMenu ref="contextMenuRef"
-                                               :treeParams="{label,treeKey,searchValue,type,entity}"/>
+                                               :treeParams="{label,treeKey,searchValue,type}"/>
             </template>
 
             <a-icon slot="switcherIcon" type="down"/>
@@ -73,10 +75,12 @@
 import ProcessModal from './Process/ProcessModal'
 import ProcessStepModal from './ProcessStep/ProcessStepModal'
 import AssignPermissionModal from './Permission/AssignPermissionModal'
+import DeviceCustomTypeModal from '@views/dnc/base/modules/ProductStructure/DeviceCustomType/DeviceCustomTypeModal.vue'
 
 export default {
   name: 'ProductStructureTree',
   components: {
+    DeviceCustomTypeModal,
     AssignPermissionModal,
     ProcessStepModal,
     ProcessModal,
@@ -100,7 +104,6 @@
       autoExpandParent: true,
       checkStrictly: true,
       allTreeKeys: [],
-      currentSelected: {},
       rightClickSelected: {},
       url: {
         delete: '/nc/product/delete'
@@ -115,10 +118,10 @@
     getTreeDataByApi() {
       this.loading = true
       this.cardLoading = true
+      this.treeDataSource = []
       dncApi.getProductStructureTreeApi()
         .then(res => {
           if (res.success) {
-            console.log('res=================', res)
             this.dataList = []
             this.allTreeKeys = []
             this.treeDataSource = res.result
@@ -140,22 +143,62 @@
      * @param {node} node 鑺傜偣瀵硅薄
      */
     handleTreeSelect(selectedKeys, { node }) {
+      const that = this
       let record = node.dataRef
-      this.currentSelected = Object.assign({}, record)
-      // 鍚戝彸渚х埗绾х粍浠跺彂閫佸綋鍓嶉�変腑鏍戣妭鐐逛俊鎭�
-      this.$bus.$emit('sendCurrentTreeNodeInfo', this.currentSelected)
+      const { id, type } = record
+      dncApi.getProductStructureTreeNodeEntityApi({ id, type })
+        .then(res => {
+          if (res.success) {
+            let currentSelectedNodeInfo
+            if (res.result.length > 0) {
+              currentSelectedNodeInfo = Object.assign({}, record, { entity: res.result[0] })
+            } else {
+              currentSelectedNodeInfo = {}
+              that.$notification.warning({
+                message: '娑堟伅',
+                description: '鏆傛棤璇ヨ妭鐐硅缁嗕俊鎭�'
+              })
+            }
+            // 鍚戝彸渚х埗绾х粍浠跺彂閫佸綋鍓嶉�変腑鏍戣妭鐐逛俊鎭�
+            this.$bus.$emit('sendCurrentTreeNodeInfo', currentSelectedNodeInfo)
+          } else {
+            that.$notification.error({
+              message: '娑堟伅',
+              description: res.message
+            })
+          }
+        })
       if (selectedKeys.length === 0) return
       this.selectedKeys = selectedKeys
     },
 
     /**
      * 鏍戣妭鐐瑰彸閿崟鍑昏妭鐐规椂瑙﹀彂
-     * @param event 浜嬩欢瀵硅薄
      * @param node 鑺傜偣瀵硅薄
      */
-    handleTreeRightClick({ event, node }) {
+    handleTreeRightClick({ node }) {
+      const that = this
       const record = node.dataRef
-      this.rightClickSelected = Object.assign({}, record)
+      const { id, type } = record
+      dncApi.getProductStructureTreeNodeEntityApi({ id, type })
+        .then(res => {
+          if (res.success) {
+            if (res.result.length > 0) {
+              that.rightClickSelected = Object.assign({}, record, { entity: res.result[0] })
+            } else {
+              that.rightClickSelected = {}
+              that.$notification.warning({
+                message: '娑堟伅',
+                description: '鏆傛棤璇ヨ妭鐐硅缁嗕俊鎭�'
+              })
+            }
+          } else {
+            that.$notification.error({
+              message: '娑堟伅',
+              description: res.message
+            })
+          }
+        })
     },
 
     // 鏍戣妭鐐瑰彸閿崟鍑昏彍鍗曚腑鍒犻櫎鎸夐挳鏃惰Е鍙�
@@ -216,8 +259,18 @@
       this.autoExpandParent = false
     },
 
-    /* 杈撳叆鏌ヨ鍐呭鍙樺寲鏃惰Е鍙� */
+    // 杈撳叆鏌ヨ鍐呭鍙樺寲鏃惰Е鍙戯紙澧炲姞闃叉姈鏈哄埗锛�
     handleSearchInputChange() {
+      const that = this
+      let timer
+      if (timer) clearTimeout(timer)
+      timer = setTimeout(function() {
+        that.searchAndExpandTreeNode() // 鍔犲皬鎷彿璋冪敤鍑芥暟
+      }, 1000)
+    },
+
+    // 闃叉姈鍑芥暟涓Е鍙戞悳绱㈠苟灞曞紑鏍戣妭鐐�
+    searchAndExpandTreeNode() {
       let search = this.searchInput
       let expandedKeys
       let autoExpandParent
@@ -237,7 +290,6 @@
         expandedKeys = this.beforeSearchExpandedKeys
         autoExpandParent = false
       }
-
       Object.assign(this, {
         expandedKeys,
         searchValue: search,
@@ -292,7 +344,7 @@
      * @param treeNode
      */
     setTreeNodeIcon(treeNode) {
-      switch (treeNode.type) {
+      switch (+treeNode.type) {
         case 1:
           treeNode.slots = { icon: 'product' }
           break
diff --git a/src/views/dnc/base/modules/ProductStructure/ProductStructureTreeContextMenu.vue b/src/views/dnc/base/modules/ProductStructure/ProductStructureTreeContextMenu.vue
index fc878a6..338a3a0 100644
--- a/src/views/dnc/base/modules/ProductStructure/ProductStructureTreeContextMenu.vue
+++ b/src/views/dnc/base/modules/ProductStructure/ProductStructureTreeContextMenu.vue
@@ -1,14 +1,20 @@
 <template>
   <a-dropdown :trigger="['contextmenu']">
-    <span v-if="treeParams.label.indexOf(treeParams.searchValue) > -1">{{ treeParams.label.substr(0, treeParams.label.indexOf(treeParams.searchValue)) }}<span
-      class="replaceSearch">{{ treeParams.searchValue }}</span>{{ treeParams.label.substr(treeParams.label.indexOf(treeParams.searchValue) + treeParams.searchValue.length) }}</span>
+    <span v-if="treeParams.label.indexOf(treeParams.searchValue) > -1">{{
+        treeParams.label.substr(0, treeParams.label.indexOf(treeParams.searchValue))
+      }}<span
+        class="replaceSearch">{{
+          treeParams.searchValue
+        }}</span>{{
+        treeParams.label.substr(treeParams.label.indexOf(treeParams.searchValue) + treeParams.searchValue.length)
+      }}</span>
     <span v-else>{{ treeParams.label }}</span>
     <template #overlay>
       <a-menu @click="({ key: menuKey }) => onContextMenuClick(treeParams.treeKey, menuKey)"
               @contextmenu="event=>event.preventDefault()">
         <a-menu-item v-for="item in defaultContextMenuList[getCurrentMenuLevel]" :key="item.code" v-has="item.code">
           <a-icon :type="item.icon"/>
-          {{item.label}}
+          {{ item.label }}
         </a-menu-item>
       </a-menu>
     </template>
@@ -16,141 +22,144 @@
 </template>
 
 <script>
-  export default {
-    name: 'ProductStructureTreeContextMenu',
-    components: {},
-    props: {
-      treeParams: {
-        type: Object
-      }
-    },
-    data() {
-      return {
-        defaultContextMenuList: {
-          //浜у搧
-          product: [
-            { label: '娣诲姞浜у搧', code: 'product_add', icon: 'plus', isCommonMethod: false },
-            { label: '娣诲姞閮ㄤ欢', code: 'product_add_child', icon: 'plus', isCommonMethod: false },
-            { label: '缂栬緫浜у搧淇℃伅', code: 'product_edit', icon: 'edit', isCommonMethod: false },
-            // {  label: '瀵煎嚭鏂囨。', code: 'product_export', icon: 'export', isCommonMethod: true },
-            { label: '瀵煎叆鍏朵粬鏂囨。', code: 'product_import', icon: 'import', isCommonMethod: true },
-            { label: '鍒犻櫎', code: 'product_delete', icon: 'delete', isCommonMethod: true },
-            { label: '鏉冮檺閰嶇疆', code: 'public_assign_permission', icon: 'idcard', isCommonMethod: true }
-          ],
-          //閮ㄤ欢
-          component: [
-            { label: '娣诲姞瀛愰儴浠�', code: 'component_add', icon: 'plus', isCommonMethod: false },
-            { label: '娣诲姞闆朵欢', code: 'component_add_child', icon: 'plus', isCommonMethod: false },
-            { label: '鍒涘缓宸ュ簭', code: 'component_add_relative', icon: 'plus', isCommonMethod: false },
-            { label: '缂栬緫閮ㄤ欢淇℃伅', code: 'component_edit', icon: 'edit', isCommonMethod: false },
-            // {  label: '瀵煎嚭鏂囨。', code: 'component_export', icon: 'export', isCommonMethod: true },
-            { label: '瀵煎叆鍏朵粬鏂囨。', code: 'component_import', icon: 'import', isCommonMethod: true },
-            { label: '鍒犻櫎', code: 'component_delete', icon: 'delete', isCommonMethod: true },
-            { label: '鏉冮檺閰嶇疆', code: 'public_assign_permission', icon: 'idcard', isCommonMethod: true }
-          ],
-          //闆朵欢
-          part: [
-            { label: '娣诲姞闆朵欢', code: 'parts_add', icon: 'plus', isCommonMethod: false },
-            { label: '鍒涘缓宸ヨ壓瑙勭▼鐗堟湰', code: 'parts_add_relative', icon: 'plus', isCommonMethod: false },
-            // { label: '鍒涘缓宸ュ簭', code: 'parts_add_relative', icon: 'plus', isCommonMethod: false },
-            { label: '缂栬緫闆朵欢淇℃伅', code: 'parts_edit', icon: 'edit', isCommonMethod: false },
-            // {  label: '瀵煎嚭鏂囨。', code: 'parts_export', icon: 'export', isCommonMethod: true },
-            { label: '瀵煎叆鍏朵粬鏂囨。', code: 'parts_import', icon: 'import', isCommonMethod: true },
-            { label: '鍒犻櫎', code: 'parts_delete', icon: 'delete', isCommonMethod: true },
-            { label: '鏉冮檺閰嶇疆', code: 'public_assign_permission', icon: 'idcard', isCommonMethod: true }
-          ],
-          //宸ヨ壓瑙勭▼鐗堟湰
-          processSpecVersion: [
-            { label: '鍒涘缓宸ヨ壓瑙勭▼鐗堟湰', code: 'version_add', icon: 'plus', isCommonMethod: false },
-            { label: '鍒涘缓宸ュ簭', code: 'version_add_child', icon: 'plus', isCommonMethod: false },
-            { label: '缂栬緫宸ヨ壓瑙勭▼鐗堟湰淇℃伅', code: 'version_edit', icon: 'edit', isCommonMethod: false },
-            { label: '鍒犻櫎', code: 'version_delete', icon: 'delete', isCommonMethod: true },
-            { label: '鏉冮檺閰嶇疆', code: 'public_assign_permission', icon: 'idcard', isCommonMethod: true }
-          ],
-          //宸ュ簭
-          process: [
-            { label: '鍒涘缓宸ュ簭', code: 'process_add', icon: 'plus', isCommonMethod: false },
-            { label: '鍒涘缓宸ユ', code: 'process_add_child', icon: 'plus', isCommonMethod: false },
-            { label: '缂栬緫宸ュ簭淇℃伅', code: 'process_edit', icon: 'edit', isCommonMethod: false },
-            { label: '鍒犻櫎', code: 'process_delete', icon: 'delete', isCommonMethod: true },
-            // {  label: '瀵煎嚭NC绋嬪簭', code: 'process_export', icon: 'export', isCommonMethod: true },
-            { label: '瀵煎叆NC绋嬪簭', code: 'process_import', icon: 'import', isCommonMethod: true },
-            { label: '鏉冮檺閰嶇疆', code: 'public_assign_permission', icon: 'idcard', isCommonMethod: true }
-          ],
-          //宸ユ
-          processStep: [
-            { label: '鍒涘缓宸ユ', code: 'processStep_add', icon: 'plus', isCommonMethod: false },
-            { label: '缂栬緫宸ユ淇℃伅', code: 'processStep_edit', icon: 'edit', isCommonMethod: false },
-            { label: '鍒犻櫎', code: 'processStep_delete', icon: 'delete', isCommonMethod: true },
-            // {  label: '瀵煎嚭NC绋嬪簭', code: 'processStep_export', icon: 'import', isCommonMethod: true },
-            { label: '瀵煎叆NC绋嬪簭', code: 'processStep_import', icon: 'import', isCommonMethod: true },
-            { label: '鏉冮檺閰嶇疆', code: 'public_assign_permission', icon: 'idcard', isCommonMethod: true }
-          ]
-        }
-      }
-    },
-    computed: {
-      getCurrentMenuLevel() {
-        switch (this.treeParams.type) {
-          case 1:
-            return 'product'
-          case 2:
-            return 'component'
-          case 3:
-            return 'part'
-          case 4:
-            return 'processSpecVersion'
-          case 5:
-            return 'process'
-          case 6:
-            return 'processStep'
-        }
-      },
-      getCurrentDocClassCode() {
-        switch (this.treeParams.type) {
-          case 1:
-            return 'OTHER'
-          case 2:
-            return 'OTHER'
-          case 3:
-            return 'OTHER'
-          case 5:
-            return 'NC'
-          case 6:
-            return 'NC'
-        }
-      }
-    },
-    methods: {
-      onContextMenuClick(treeKey, menuKey) {
-        const level = this.getCurrentMenuLevel
-        const param = this.getCurrentDocClassCode
-        console.log('level---------------------', level)
-        console.log('param---------------------', param)
-        const treeNodeInfo = Object.assign({}, this.treeParams, { param })
-        console.log('treeNodeInfo******************', treeNodeInfo)
-        const menuKeyArray = menuKey.split('_')
-        const isCommonMethod = this.defaultContextMenuList[level].find(item => item.code === menuKey).isCommonMethod
-        // product_add => handleAdd 瑙﹀彂瀵瑰簲缁勪欢浜嬩欢
-        let methodName
-        // 鍒ゆ柇鏄惁涓哄叕鍏辨柟娉曪紝濡傛灉涓哄叕鍏辨柟娉曞垯鎴彇涓撴湁灞炴�roduct/component/part/process绛夊瓧娈�
-        if (isCommonMethod) {
-          methodName = 'handle' + menuKeyArray.map(item => item[0].toUpperCase() + item.slice(1)).slice(1).join('')
-        } else {
-          methodName = 'handle' + menuKeyArray.map(item => item[0].toUpperCase() + item.slice(1)).join('')
-        }
-        const modalTitle = this.defaultContextMenuList[level].find(item => item.code === menuKey).label
-        this.$bus.$emit('treeMenuItemMethodTrigger', { methodName, modalTitle, treeNodeInfo })
+export default {
+  name: 'ProductStructureTreeContextMenu',
+  components: {},
+  props: {
+    treeParams: {
+      type: Object
+    }
+  },
+  data() {
+    return {
+      defaultContextMenuList: {
+        //浜у搧
+        product: [
+          { label: '娣诲姞浜у搧', code: 'product_add', icon: 'plus', isCommonMethod: false },
+          { label: '娣诲姞閮ㄤ欢', code: 'product_add_child', icon: 'plus', isCommonMethod: false },
+          { label: '缂栬緫浜у搧淇℃伅', code: 'product_edit', icon: 'edit', isCommonMethod: false },
+          // {  label: '瀵煎嚭鏂囨。', code: 'product_export', icon: 'export', isCommonMethod: true },
+          { label: '瀵煎叆鍏朵粬鏂囨。', code: 'product_import', icon: 'import', isCommonMethod: true },
+          { label: '鍒犻櫎', code: 'product_delete', icon: 'delete', isCommonMethod: true },
+          { label: '鏉冮檺閰嶇疆', code: 'public_assign_permission', icon: 'idcard', isCommonMethod: true }
+        ],
+        //閮ㄤ欢
+        component: [
+          { label: '娣诲姞瀛愰儴浠�', code: 'component_add', icon: 'plus', isCommonMethod: false },
+          { label: '娣诲姞闆朵欢', code: 'component_add_child', icon: 'plus', isCommonMethod: false },
+          { label: '鍒涘缓宸ュ簭', code: 'component_add_relative', icon: 'plus', isCommonMethod: false },
+          { label: '缂栬緫閮ㄤ欢淇℃伅', code: 'component_edit', icon: 'edit', isCommonMethod: false },
+          // {  label: '瀵煎嚭鏂囨。', code: 'component_export', icon: 'export', isCommonMethod: true },
+          { label: '瀵煎叆鍏朵粬鏂囨。', code: 'component_import', icon: 'import', isCommonMethod: true },
+          { label: '鍒犻櫎', code: 'component_delete', icon: 'delete', isCommonMethod: true },
+          { label: '鏉冮檺閰嶇疆', code: 'public_assign_permission', icon: 'idcard', isCommonMethod: true }
+        ],
+        //闆朵欢
+        part: [
+          { label: '娣诲姞闆朵欢', code: 'parts_add', icon: 'plus', isCommonMethod: false },
+          { label: '鍒涘缓宸ヨ壓瑙勭▼鐗堟湰', code: 'parts_add_relative', icon: 'plus', isCommonMethod: false },
+          // { label: '鍒涘缓宸ュ簭', code: 'parts_add_relative', icon: 'plus', isCommonMethod: false },
+          { label: '缂栬緫闆朵欢淇℃伅', code: 'parts_edit', icon: 'edit', isCommonMethod: false },
+          // {  label: '瀵煎嚭鏂囨。', code: 'parts_export', icon: 'export', isCommonMethod: true },
+          { label: '瀵煎叆鍏朵粬鏂囨。', code: 'parts_import', icon: 'import', isCommonMethod: true },
+          { label: '鍒犻櫎', code: 'parts_delete', icon: 'delete', isCommonMethod: true },
+          { label: '鏉冮檺閰嶇疆', code: 'public_assign_permission', icon: 'idcard', isCommonMethod: true }
+        ],
+        //宸ヨ壓瑙勭▼鐗堟湰
+        processSpecVersion: [
+          { label: '鍒涘缓宸ヨ壓瑙勭▼鐗堟湰', code: 'version_add', icon: 'plus', isCommonMethod: false },
+          { label: '鍒涘缓宸ュ簭', code: 'version_add_child', icon: 'plus', isCommonMethod: false },
+          { label: '缂栬緫宸ヨ壓瑙勭▼鐗堟湰淇℃伅', code: 'version_edit', icon: 'edit', isCommonMethod: false },
+          { label: '鍒犻櫎', code: 'version_delete', icon: 'delete', isCommonMethod: true },
+          { label: '鏉冮檺閰嶇疆', code: 'public_assign_permission', icon: 'idcard', isCommonMethod: true }
+        ],
+        //宸ュ簭
+        process: [
+          { label: '鍒涘缓宸ュ簭', code: 'process_add', icon: 'plus', isCommonMethod: false },
+          { label: '鍒涘缓宸ユ', code: 'process_add_child', icon: 'plus', isCommonMethod: false },
+          { label: '娣诲姞璁惧绫�', code: 'process_add_type', icon: 'plus', isCommonMethod: true },
+          { label: '缂栬緫宸ュ簭淇℃伅', code: 'process_edit', icon: 'edit', isCommonMethod: false },
+          { label: '鍒犻櫎', code: 'process_delete', icon: 'delete', isCommonMethod: true },
+          // {  label: '瀵煎嚭NC绋嬪簭', code: 'process_export', icon: 'export', isCommonMethod: true },
+          { label: '瀵煎叆NC绋嬪簭', code: 'process_import', icon: 'import', isCommonMethod: true },
+          { label: '鏉冮檺閰嶇疆', code: 'public_assign_permission', icon: 'idcard', isCommonMethod: true }
+        ],
+        //宸ユ
+        processStep: [
+          { label: '鍒涘缓宸ユ', code: 'processStep_add', icon: 'plus', isCommonMethod: false },
+          { label: '缂栬緫宸ユ淇℃伅', code: 'processStep_edit', icon: 'edit', isCommonMethod: false },
+          { label: '娣诲姞璁惧绫�', code: 'processStep_add_type', icon: 'plus', isCommonMethod: true },
+          { label: '鍒犻櫎', code: 'processStep_delete', icon: 'delete', isCommonMethod: true },
+          // {  label: '瀵煎嚭NC绋嬪簭', code: 'processStep_export', icon: 'import', isCommonMethod: true },
+          { label: '瀵煎叆NC绋嬪簭', code: 'processStep_import', icon: 'import', isCommonMethod: true },
+          { label: '鏉冮檺閰嶇疆', code: 'public_assign_permission', icon: 'idcard', isCommonMethod: true }
+        ]
       }
     }
+  },
+  computed: {
+    getCurrentMenuLevel() {
+      switch (+this.treeParams.type) {
+        case 1:
+          return 'product'
+        case 2:
+          return 'component'
+        case 3:
+          return 'part'
+        case 4:
+          return 'processSpecVersion'
+        case 5:
+          return 'process'
+        case 6:
+          return 'processStep'
+      }
+    },
+    getCurrentDocClassCode() {
+      switch (+this.treeParams.type) {
+        case 1:
+          return 'OTHER'
+        case 2:
+          return 'OTHER'
+        case 3:
+          return 'OTHER'
+        case 5:
+          return 'NC'
+        case 6:
+          return 'NC'
+      }
+    }
+  },
+  methods: {
+    onContextMenuClick(treeKey, menuKey) {
+      const level = this.getCurrentMenuLevel
+      const param = this.getCurrentDocClassCode
+      console.log('level---------------------', level)
+      console.log('param---------------------', param)
+      const treeNodeInfo = Object.assign({}, this.treeParams, { param })
+      console.log('treeNodeInfo******************', treeNodeInfo)
+      const menuKeyArray = menuKey.split('_')
+      const isCommonMethod = this.defaultContextMenuList[level].find(item => item.code === menuKey).isCommonMethod
+      // product_add => handleAdd 瑙﹀彂瀵瑰簲缁勪欢浜嬩欢
+      let methodName
+      // 鍒ゆ柇鏄惁涓哄叕鍏辨柟娉曪紝濡傛灉涓哄叕鍏辨柟娉曞垯鎴彇涓撴湁灞炴�roduct/component/part/process绛夊瓧娈�
+      if (isCommonMethod) {
+        methodName = 'handle' + menuKeyArray.map(item => item[0].toUpperCase() + item.slice(1)).slice(1).join('')
+      } else {
+        methodName = 'handle' + menuKeyArray.map(item => item[0].toUpperCase() + item.slice(1)).join('')
+      }
+      console.log('methodName', methodName)
+      const modalTitle = this.defaultContextMenuList[level].find(item => item.code === menuKey).label
+      this.$bus.$emit('treeMenuItemMethodTrigger', { methodName, modalTitle, treeNodeInfo })
+    }
   }
+}
 </script>
 
 <style scoped>
-  .replaceSearch {
-    color: #40a9ff;
-    font-weight: bold;
-    background-color: rgb(204, 204, 204);
-  }
+.replaceSearch {
+  color: #40a9ff;
+  font-weight: bold;
+  background-color: rgb(204, 204, 204);
+}
 
 </style>
\ No newline at end of file
diff --git a/src/views/dnc/common/TableContextMenu.vue b/src/views/dnc/common/TableContextMenu.vue
index 7635a07..1d62177 100644
--- a/src/views/dnc/common/TableContextMenu.vue
+++ b/src/views/dnc/common/TableContextMenu.vue
@@ -41,6 +41,12 @@
         },
         currentMenuLevel: '',
         defaultContextMenuList: {
+          //璁惧绫�
+          deviceCustomType:[
+            { label: '缂栬緫璁惧绫讳俊鎭�', code: 'type_edit', subMenu: [], icon: 'edit', isCommonMethod: false },
+            { label: '鍒犻櫎', code: 'type_delete', subMenu: [], icon: 'delete', isCommonMethod: false },
+            { label: '瀵煎叆NC绋嬪簭', code: 'type_document_import', subMenu: [], icon: 'import', isCommonMethod: true },
+          ],
           //NC鏂囨。
           NC: [
             { label: '缂栬緫鏂囨。淇℃伅', code: 'document_edit', subMenu: [], icon: 'edit', isCommonMethod: false },
diff --git a/src/views/system/modules/UserModal.vue b/src/views/system/modules/UserModal.vue
index a497293..58dc87c 100644
--- a/src/views/system/modules/UserModal.vue
+++ b/src/views/system/modules/UserModal.vue
@@ -376,11 +376,12 @@
         validatorRules: {
           username: [{ required: true, message: '璇疯緭鍏ョ敤鎴疯处鍙�!' },
             { validator: this.validateUsername }],
-          password: [{
-            required: true,
-            pattern: /^(?=.*[a-zA-Z])(?=.*\d)(?=.*[~!@#$%^&*()_+`\-={}:";'<>?,./]).{8,}$/,
-            message: '瀵嗙爜鐢�8浣嶆暟瀛椼�佸ぇ灏忓啓瀛楁瘝鍜岀壒娈婄鍙风粍鎴�!'
-          },
+          password: [
+          //   {
+          //   required: true,
+          //   pattern: /^(?=.*[a-zA-Z])(?=.*\d)(?=.*[~!@#$%^&*()_+`\-={}:";'<>?,./]).{8,}$/,
+          //   message: '瀵嗙爜鐢�8浣嶆暟瀛椼�佸ぇ灏忓啓瀛楁瘝鍜岀壒娈婄鍙风粍鎴�!'
+          // },
             { validator: this.validateToNextPassword, trigger: 'change' }],
           confirmpassword: [{ required: true, message: '璇烽噸鏂拌緭鍏ョ櫥褰曞瘑鐮�!' },
             { validator: this.compareToFirstPassword }],
diff --git a/yarn.lock b/yarn.lock
index eafa3de..4d56384 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -355,10 +355,20 @@
   dependencies:
     "@babel/types" "^7.10.4"
 
+"@babel/helper-string-parser@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c"
+  integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==
+
 "@babel/helper-validator-identifier@^7.10.4":
   version "7.10.4"
   resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2"
   integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==
+
+"@babel/helper-validator-identifier@^7.25.9":
+  version "7.25.9"
+  resolved "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7"
+  integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==
 
 "@babel/helper-wrap-function@^7.10.4":
   version "7.10.4"
@@ -392,6 +402,13 @@
   version "7.10.4"
   resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.10.4.tgz#9eedf27e1998d87739fb5028a5120557c06a1a64"
   integrity sha512-8jHII4hf+YVDsskTF6WuMB3X4Eh+PsUkC2ljq22so5rHvH+T8BzyL94VOdyFLNR8tBSVXOTbNHOKpR4TfRxVtA==
+
+"@babel/parser@^7.23.5":
+  version "7.26.9"
+  resolved "https://registry.npmmirror.com/@babel/parser/-/parser-7.26.9.tgz#d9e78bee6dc80f9efd8f2349dcfbbcdace280fd5"
+  integrity sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==
+  dependencies:
+    "@babel/types" "^7.26.9"
 
 "@babel/plugin-proposal-async-generator-functions@^7.2.0":
   version "7.10.4"
@@ -861,6 +878,14 @@
     lodash "^4.17.13"
     to-fast-properties "^2.0.0"
 
+"@babel/types@^7.26.9":
+  version "7.26.9"
+  resolved "https://registry.npmmirror.com/@babel/types/-/types-7.26.9.tgz#08b43dec79ee8e682c2ac631c010bdcac54a21ce"
+  integrity sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==
+  dependencies:
+    "@babel/helper-string-parser" "^7.25.9"
+    "@babel/helper-validator-identifier" "^7.25.9"
+
 "@hapi/address@2.x.x":
   version "2.1.4"
   resolved "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5"
@@ -1316,6 +1341,17 @@
     semver "^6.0.0"
     string.prototype.padstart "^3.0.0"
 
+"@vue/compiler-sfc@2.7.16":
+  version "2.7.16"
+  resolved "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-2.7.16.tgz#ff81711a0fac9c68683d8bb00b63f857de77dc83"
+  integrity sha512-KWhJ9k5nXuNtygPU7+t1rX6baZeqOYLEforUPjgNDBnLicfHCoi48H87Q8XyLZOrNNsmhuwKqtpDQWjEFe6Ekg==
+  dependencies:
+    "@babel/parser" "^7.23.5"
+    postcss "^8.4.14"
+    source-map "^0.6.1"
+  optionalDependencies:
+    prettier "^1.18.2 || ^2.0.0"
+
 "@vue/component-compiler-utils@^3.0.0", "@vue/component-compiler-utils@^3.1.0":
   version "3.1.2"
   resolved "https://registry.npmjs.org/@vue/component-compiler-utils/-/component-compiler-utils-3.1.2.tgz#8213a5ff3202f9f2137fe55370f9e8b9656081c3"
@@ -1507,6 +1543,11 @@
   version "4.2.2"
   resolved "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d"
   integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==
+
+abbrev@1:
+  version "1.1.1"
+  resolved "https://registry.npmmirror.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
+  integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
 
 abs-svg-path@~0.1.1:
   version "0.1.1"
@@ -1919,6 +1960,13 @@
   resolved "https://registry.npmjs.org/async-validator/-/async-validator-3.3.0.tgz#1d92193bbe60d6d6c8b246692c7005e9ed14a8ee"
   integrity sha512-cAHGD9EL8aCqWXjnb44q94MWiDFzUo1tMhvLb2WzcpWqGiKugsjWG9cvl+jPgkPca7asNbsBU3fa0cwkI/P+Xg==
 
+async-validator@~1.8.1:
+  version "1.8.5"
+  resolved "https://registry.npmmirror.com/async-validator/-/async-validator-1.8.5.tgz#dc3e08ec1fd0dddb67e60842f02c0cd1cec6d7f0"
+  integrity sha512-tXBM+1m056MAX0E8TL2iCjg8WvSyXu0Zc8LNtYqrVeyoL3+esHRZ4SieE9fKQyyU09uONjnMEjrNBMqT0mbvmA==
+  dependencies:
+    babel-runtime "6.x"
+
 async@^2.1.2, async@^2.4.1, async@^2.6.2:
   version "2.6.3"
   resolved "https://registry.npmjs.org/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff"
@@ -2164,7 +2212,7 @@
     babel-traverse "^6.24.1"
     babel-types "^6.24.1"
 
-babel-helper-vue-jsx-merge-props@^2.0.3:
+babel-helper-vue-jsx-merge-props@^2.0.0, babel-helper-vue-jsx-merge-props@^2.0.3:
   version "2.0.3"
   resolved "https://registry.npmjs.org/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-2.0.3.tgz#22aebd3b33902328e513293a8e4992b384f9f1b6"
   integrity sha512-gsLiKK7Qrb7zYJNgiXKpXblxbV5ffSwR0f5whkPAaBAR4fhi6bwRZxX9wBlIc5M/v8CCkXUbXZL4N/nSE97cqg==
@@ -2696,6 +2744,31 @@
   version "1.0.0"
   resolved "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
   integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
+
+bpmn-js@^7.2.1:
+  version "7.5.0"
+  resolved "https://registry.npmmirror.com/bpmn-js/-/bpmn-js-7.5.0.tgz#0e4675acdbdff676db859f8cb7ec395d20e7bbd8"
+  integrity sha512-0ANaE6Bikg1GmkcvO7RK0MQPX+EKYKBc+q7OWk39/16NcCdNZ/4UiRcCr9n0u1VUCIDsSU/jJ79TIZFnV5CNjw==
+  dependencies:
+    bpmn-moddle "^7.0.4"
+    css.escape "^1.5.1"
+    diagram-js "^6.8.2"
+    diagram-js-direct-editing "^1.6.1"
+    ids "^1.0.0"
+    inherits "^2.0.4"
+    min-dash "^3.5.2"
+    min-dom "^3.1.3"
+    object-refs "^0.3.0"
+    tiny-svg "^2.2.2"
+
+bpmn-moddle@^7.0.4:
+  version "7.1.3"
+  resolved "https://registry.npmmirror.com/bpmn-moddle/-/bpmn-moddle-7.1.3.tgz#5b4592001e8f23dbdb18c557531e06489462bf42"
+  integrity sha512-ZcBfw0NSOdYTSXFKEn7MOXHItz7VfLZTrFYKO8cK6V8ZzGjCcdiLIOiw7Lctw1PJsihhLiZQS8Htj2xKf+NwCg==
+  dependencies:
+    min-dash "^3.5.2"
+    moddle "^5.0.2"
+    moddle-xml "^9.0.6"
 
 brace-expansion@^1.1.7:
   version "1.1.11"
@@ -3497,6 +3570,11 @@
   resolved "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"
   integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==
 
+component-event@^0.1.4:
+  version "0.1.4"
+  resolved "https://registry.npmmirror.com/component-event/-/component-event-0.1.4.tgz#3de78fc28782381787e24bf2a7c536bf0142c9b4"
+  integrity sha512-GMwOG8MnUHP1l8DZx1ztFO0SJTFnIzZnBDkXAj8RM2ntV2A6ALlDxgbMY1Fvxlg6WPQ+5IM/a6vg4PEYbjg/Rw==
+
 component-indexof@0.0.3:
   version "0.0.3"
   resolved "https://registry.npmjs.org/component-indexof/-/component-indexof-0.0.3.tgz#11d091312239eb8f32c8f25ae9cb002ffe8d3c24"
@@ -3904,6 +3982,11 @@
   resolved "https://registry.npmjs.org/css-what/-/css-what-3.3.0.tgz#10fec696a9ece2e591ac772d759aacabac38cd39"
   integrity sha512-pv9JPyatiPaQ6pf4OvD/dbfm0o5LviWmwxNWzblYf/1u9QZd0ihV+PMwy5jdQWQ3349kZmKEx9WXuSka2dM4cg==
 
+css.escape@^1.5.1:
+  version "1.5.1"
+  resolved "https://registry.npmmirror.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb"
+  integrity sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==
+
 css@^2.0.0:
   version "2.2.4"
   resolved "https://registry.npmjs.org/css/-/css-2.2.4.tgz#c646755c73971f2bba6a601e2cf2fd71b1298929"
@@ -4044,6 +4127,11 @@
   dependencies:
     clap "^1.0.9"
     source-map "^0.5.3"
+
+csstype@^3.1.0:
+  version "3.1.3"
+  resolved "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81"
+  integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
 
 current-script-polyfill@^1.0.0:
   version "1.0.0"
@@ -4301,9 +4389,9 @@
   resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
   integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=
 
-deepmerge@^1.5.2:
+deepmerge@^1.2.0, deepmerge@^1.5.2:
   version "1.5.2"
-  resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-1.5.2.tgz#10499d868844cdad4fee0842df8c7f6f0c95a753"
+  resolved "https://registry.npmmirror.com/deepmerge/-/deepmerge-1.5.2.tgz#10499d868844cdad4fee0842df8c7f6f0c95a753"
   integrity sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ==
 
 default-gateway@^4.2.0:
@@ -4432,6 +4520,54 @@
   resolved "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c"
   integrity sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==
 
+diagram-js-direct-editing@^1.6.1:
+  version "1.8.0"
+  resolved "https://registry.npmmirror.com/diagram-js-direct-editing/-/diagram-js-direct-editing-1.8.0.tgz#7a178cd9203f262842a3e0023f9644e04a157858"
+  integrity sha512-B4Xj+PJfgBjbPEzT3uZQEkZI5xHFB0Izc+7BhDFuHidzrEMzQKZrFGdA3PqfWhReHf3dp+iB6Tt11G9eGNjKMw==
+  dependencies:
+    min-dash "^3.5.2"
+    min-dom "^3.1.3"
+
+diagram-js@^6.8.2:
+  version "6.8.2"
+  resolved "https://registry.npmmirror.com/diagram-js/-/diagram-js-6.8.2.tgz#d3c37915c62c0cc5d95bc533098c8a35b38e67be"
+  integrity sha512-5EKYHjW2mmGsn9/jSenSkm8cScK5sO9eETBRQNIIzgZjxBDJn6eX964L2d7/vrAW9SeuijGUsztL9+NUinSsNg==
+  dependencies:
+    css.escape "^1.5.1"
+    didi "^4.0.0"
+    hammerjs "^2.0.1"
+    inherits "^2.0.1"
+    min-dash "^3.5.0"
+    min-dom "^3.1.2"
+    object-refs "^0.3.0"
+    path-intersection "^2.2.0"
+    tiny-svg "^2.2.1"
+
+didi@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.npmmirror.com/didi/-/didi-4.0.0.tgz#2b89d892a67fd3777f7642d3bf06697b69e9b622"
+  integrity sha512-AzMElh8mCHOPWPCWfGjoJRla31fMXUT6+287W5ef3IPmtuBcyG9+MkFS7uPP6v3t2Cl086KwWfRB9mESa0OsHQ==
+
+diff2html@^3.3.1:
+  version "3.4.51"
+  resolved "https://registry.npmmirror.com/diff2html/-/diff2html-3.4.51.tgz#6355170d09d8625525f0846be2c73bc2838ab797"
+  integrity sha512-/rVCSDyokkzSCEGaGjkkElXtIRwyNDRzIa3S8VUhR6pjk25p6+AMnb1s2zGmhjl66D5m/HnV3IeZoxnWsvTy+w==
+  dependencies:
+    diff "^7.0.0"
+    hogan.js "3.0.2"
+  optionalDependencies:
+    highlight.js "11.9.0"
+
+diff@^3.5.0:
+  version "3.5.0"
+  resolved "https://registry.npmmirror.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
+  integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==
+
+diff@^7.0.0:
+  version "7.0.0"
+  resolved "https://registry.npmmirror.com/diff/-/diff-7.0.0.tgz#3fb34d387cd76d803f6eebea67b921dab0182a9a"
+  integrity sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==
+
 diffie-hellman@^5.0.0:
   version "5.0.3"
   resolved "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875"
@@ -4549,6 +4685,11 @@
   dependencies:
     domelementtype "1"
 
+domify@^1.3.1:
+  version "1.4.2"
+  resolved "https://registry.npmmirror.com/domify/-/domify-1.4.2.tgz#2d3e8ac49cae41206cc84be31ca401050ae780a6"
+  integrity sha512-m4yreHcUWHBncGVV7U+yQzc12vIlq0jMrtHZ5mW6dQMiL/7skSYNVX9wqKwOtyO9SGCgevrAFEgOCAHmamHTUA==
+
 domutils@1.5.1:
   version "1.5.1"
   resolved "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf"
@@ -4647,6 +4788,18 @@
   version "1.3.496"
   resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.496.tgz#3f43d32930481d82ad3663d79658e7c59a58af0b"
   integrity sha512-TXY4mwoyowwi4Lsrq9vcTUYBThyc1b2hXaTZI13p8/FRhY2CTaq5lK+DVjhYkKiTLsKt569Xes+0J5JsVXFurQ==
+
+element-ui@^2.12.0, element-ui@^2.13.2, element-ui@^2.15.14:
+  version "2.15.14"
+  resolved "https://registry.npmmirror.com/element-ui/-/element-ui-2.15.14.tgz#3c34df79467636592812d720d2e6784e7a6ec2ea"
+  integrity sha512-2v9fHL0ZGINotOlRIAJD5YuVB8V7WKxrE9Qy7dXhRipa035+kF7WuU/z+tEmLVPBcJ0zt8mOu1DKpWcVzBK8IA==
+  dependencies:
+    async-validator "~1.8.1"
+    babel-helper-vue-jsx-merge-props "^2.0.0"
+    deepmerge "^1.2.0"
+    normalize-wheel "^1.0.1"
+    resize-observer-polyfill "^1.5.0"
+    throttle-debounce "^1.0.1"
 
 elliptic@^6.0.0, elliptic@^6.5.2:
   version "6.5.3"
@@ -5917,6 +6070,11 @@
     duplexer "^0.1.1"
     pify "^4.0.1"
 
+hammerjs@^2.0.1:
+  version "2.0.8"
+  resolved "https://registry.npmmirror.com/hammerjs/-/hammerjs-2.0.8.tgz#04ef77862cff2bb79d30f7692095930222bf60f1"
+  integrity sha512-tSQXBXS/MWQOn/RKckawJ61vvsDpCom87JgxiYdGwHdOa0ht0vzUWDlfioofFCRU0L+6NGDt6XzbgoJvZkMeRQ==
+
 handle-thing@^2.0.0:
   version "2.0.1"
   resolved "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e"
@@ -6042,6 +6200,16 @@
   resolved "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e"
   integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==
 
+highlight.js@11.9.0:
+  version "11.9.0"
+  resolved "https://registry.npmmirror.com/highlight.js/-/highlight.js-11.9.0.tgz#04ab9ee43b52a41a047432c8103e2158a1b8b5b0"
+  integrity sha512-fJ7cW7fQGCYAkgv4CPfwFHrfd/cLS4Hau96JuJ+ZTOWhjnhoeN1ub1tFmALm/+lW5z4WCAuAV9bm05AP0mS6Gw==
+
+highlight.js@^9.18.5:
+  version "9.18.5"
+  resolved "https://registry.npmmirror.com/highlight.js/-/highlight.js-9.18.5.tgz#d18a359867f378c138d6819edfc2a8acd5f29825"
+  integrity sha512-a5bFyofd/BHCX52/8i8uJkjr9DYwXIPnM/plwI6W7ezItLGqzt7X2G2nXuYSfsIJdkwwj/g9DG1LkcGJI/dDoA==
+
 highlight.js@^9.6.0:
   version "9.18.1"
   resolved "https://registry.npmjs.org/highlight.js/-/highlight.js-9.18.1.tgz#ed21aa001fe6252bb10a3d76d47573c6539fe13c"
@@ -6055,6 +6223,14 @@
     hash.js "^1.0.3"
     minimalistic-assert "^1.0.0"
     minimalistic-crypto-utils "^1.0.1"
+
+hogan.js@3.0.2:
+  version "3.0.2"
+  resolved "https://registry.npmmirror.com/hogan.js/-/hogan.js-3.0.2.tgz#4cd9e1abd4294146e7679e41d7898732b02c7bfd"
+  integrity sha512-RqGs4wavGYJWE07t35JQccByczmNUXQT0E12ZYV1VKYu5UiAU9lsos/yBAcf840+zrUQQxgVduCR5/B8nNtibg==
+  dependencies:
+    mkdirp "0.3.0"
+    nopt "1.0.10"
 
 home-or-tmp@^2.0.0:
   version "2.0.0"
@@ -6284,6 +6460,11 @@
   dependencies:
     postcss "^6.0.1"
 
+ids@^1.0.0:
+  version "1.0.5"
+  resolved "https://registry.npmmirror.com/ids/-/ids-1.0.5.tgz#0aeb4777e77e0017a983c9f7b0ca0cccf352afe4"
+  integrity sha512-XQ0yom/4KWTL29sLG+tyuycy7UmeaM/79GRtSJq6IG9cJGIPeBz5kwDCguie3TwxaMNIc3WtPi0cTa1XYHicpw==
+
 ieee754@^1.1.4:
   version "1.1.13"
   resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84"
@@ -6388,6 +6569,11 @@
   version "1.0.1"
   resolved "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607"
   integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc=
+
+indexof@0.0.1:
+  version "0.0.1"
+  resolved "https://registry.npmmirror.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d"
+  integrity sha512-i0G7hLJ1z0DE8dsqJa2rycj9dBmNKgXBvotXtZYXakU9oivfB9Uj2ZBC27qqef2U58/ZLwalxa1X/RDCdkHtVg==
 
 infer-owner@^1.0.3, infer-owner@^1.0.4:
   version "1.0.4"
@@ -7299,6 +7485,11 @@
   resolved "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
   integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY=
 
+lodash.clonedeep@^4.5.0:
+  version "4.5.0"
+  resolved "https://registry.npmmirror.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
+  integrity sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==
+
 lodash.defaults@^3.1.2:
   version "3.1.2"
   resolved "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-3.1.2.tgz#c7308b18dbf8bc9372d701a73493c61192bd2e2c"
@@ -7360,6 +7551,11 @@
   version "4.1.2"
   resolved "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
   integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=
+
+lodash.merge@^4.6.2:
+  version "4.6.2"
+  resolved "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
+  integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
 
 lodash.pick@^4.4.0:
   version "4.4.0"
@@ -7488,6 +7684,11 @@
   integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=
   dependencies:
     object-visit "^1.0.0"
+
+matches-selector@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.npmmirror.com/matches-selector/-/matches-selector-1.2.0.tgz#d1814e7e8f43e69d22ac33c9af727dc884ecf12a"
+  integrity sha512-c4vLwYWyl+Ji+U43eU/G5FwxWd4ZH0ePUsFs5y0uwD9HUEFBXUQ1zUUan+78IpRD+y4pUfG0nAzNM292K7ItvA==
 
 math-expression-evaluator@^1.2.14:
   version "1.2.22"
@@ -7652,6 +7853,22 @@
   resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
   integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
 
+min-dash@^3.0.0, min-dash@^3.5.0, min-dash@^3.5.2, min-dash@^3.8.1:
+  version "3.8.1"
+  resolved "https://registry.npmmirror.com/min-dash/-/min-dash-3.8.1.tgz#09a8bd8a041d65eec4732042cde9cb24a6e84b0d"
+  integrity sha512-evumdlmIlg9mbRVPbC4F5FuRhNmcMS5pvuBUbqb1G9v09Ro0ImPEgz5n3khir83lFok1inKqVDjnKEg3GpDxQg==
+
+min-dom@^3.1.2, min-dom@^3.1.3:
+  version "3.2.1"
+  resolved "https://registry.npmmirror.com/min-dom/-/min-dom-3.2.1.tgz#c272a814397d8bfe97edd12670e7ac34123c043f"
+  integrity sha512-v6YCmnDzxk4rRJntWTUiwggLupPw/8ZSRqUq0PDaBwVZEO/wYzCH4SKVBV+KkEvf3u0XaWHly5JEosPtqRATZA==
+  dependencies:
+    component-event "^0.1.4"
+    domify "^1.3.1"
+    indexof "0.0.1"
+    matches-selector "^1.2.0"
+    min-dash "^3.8.1"
+
 mini-css-extract-plugin@^0.8.0:
   version "0.8.2"
   resolved "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.8.2.tgz#a875e169beb27c88af77dd962771c9eedc3da161"
@@ -7752,12 +7969,33 @@
     for-in "^1.0.2"
     is-extendable "^1.0.1"
 
+mkdirp@0.3.0:
+  version "0.3.0"
+  resolved "https://registry.npmmirror.com/mkdirp/-/mkdirp-0.3.0.tgz#1bbf5ab1ba827af23575143490426455f481fe1e"
+  integrity sha512-OHsdUcVAQ6pOtg5JYWpCBo9W/GySVuwvP9hueRMW7UqshC0tbfzLv8wjySTPm3tfUZ/21CE9E1pJagOA91Pxew==
+
 mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@~0.5.0, mkdirp@~0.5.1:
   version "0.5.5"
   resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
   integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
   dependencies:
     minimist "^1.2.5"
+
+moddle-xml@^9.0.6:
+  version "9.0.6"
+  resolved "https://registry.npmmirror.com/moddle-xml/-/moddle-xml-9.0.6.tgz#282b2a2232065a82556ba3fbbe3010b374f95cbf"
+  integrity sha512-tl0reHpsY/aKlLGhXeFlQWlYAQHFxTkFqC8tq8jXRYpQSnLVw13T6swMaourLd7EXqHdWsc+5ggsB+fEep6xZQ==
+  dependencies:
+    min-dash "^3.5.2"
+    moddle "^5.0.2"
+    saxen "^8.1.2"
+
+moddle@^5.0.2:
+  version "5.0.4"
+  resolved "https://registry.npmmirror.com/moddle/-/moddle-5.0.4.tgz#1108c9ff210df552ef4589513e620d171e4468e6"
+  integrity sha512-Kjb+hjuzO+YlojNGxEUXvdhLYTHTtAABDlDcJTtTcn5MbJF9Zkv4I1Fyvp3Ypmfgg1EfHDZ3PsCQTuML9JD6wg==
+  dependencies:
+    min-dash "^3.0.0"
 
 moment-timezone@^0.5.31:
   version "0.5.34"
@@ -7839,6 +8077,11 @@
   version "2.14.1"
   resolved "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01"
   integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==
+
+nanoid@^3.3.8:
+  version "3.3.8"
+  resolved "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.8.tgz#b1be3030bee36aaff18bacb375e5cce521684baf"
+  integrity sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==
 
 nanomatch@^1.2.9:
   version "1.2.13"
@@ -7957,6 +8200,13 @@
   resolved "https://registry.npmjs.org/node-releases/-/node-releases-1.1.59.tgz#4d648330641cec704bff10f8e4fe28e453ab8e8e"
   integrity sha512-H3JrdUczbdiwxN5FuJPyCHnGHIFqQ0wWxo+9j1kAXAzqNMAHlo+4I/sYYxpyK0irQ73HgdiyzD32oqQDcU2Osw==
 
+nopt@1.0.10:
+  version "1.0.10"
+  resolved "https://registry.npmmirror.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee"
+  integrity sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==
+  dependencies:
+    abbrev "1"
+
 normalize-package-data@^2.3.2, normalize-package-data@^2.3.4, normalize-package-data@^2.5.0:
   version "2.5.0"
   resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
@@ -8003,6 +8253,11 @@
   version "3.3.0"
   resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559"
   integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==
+
+normalize-wheel@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.npmmirror.com/normalize-wheel/-/normalize-wheel-1.0.1.tgz#aec886affdb045070d856447df62ecf86146ec45"
+  integrity sha512-1OnlAPZ3zgrk8B91HyRj+eVv+kS5u+Z0SCsak6Xil/kmgEia50ga7zfkumayonZrImffAxPU/5WcyGhzetHNPA==
 
 npm-run-path@^2.0.0:
   version "2.0.2"
@@ -8091,6 +8346,11 @@
   version "0.9.2"
   resolved "https://registry.npmjs.org/object-path/-/object-path-0.9.2.tgz#0fd9a74fc5fad1ae3968b586bda5c632bd6c05a5"
   integrity sha1-D9mnT8X60a45aLWGvaXGMr1sBaU=
+
+object-refs@^0.3.0:
+  version "0.3.0"
+  resolved "https://registry.npmmirror.com/object-refs/-/object-refs-0.3.0.tgz#934f4f0fb6b409e78be15fa60f616108aed63786"
+  integrity sha512-eP0ywuoWOaDoiake/6kTJlPJhs+k0qNm4nYRzXLNHj6vh+5M3i9R1epJTdxIPGlhWc4fNRQ7a6XJNCX+/L4FOQ==
 
 object-visit@^1.0.0:
   version "1.0.1"
@@ -8474,6 +8734,11 @@
   resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
   integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
 
+path-intersection@^2.2.0:
+  version "2.2.1"
+  resolved "https://registry.npmmirror.com/path-intersection/-/path-intersection-2.2.1.tgz#8476b75fefb7ac402f810d304e0eb0c080c11fe7"
+  integrity sha512-9u8xvMcSfuOiStv9bPdnRJQhGQXLKurew94n4GPQCdH1nj9QKC9ObbNoIpiRq8skiOBxKkt277PgOoFgAt3/rA==
+
 path-is-absolute@^1.0.0, path-is-absolute@^1.0.1:
   version "1.0.1"
   resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
@@ -8547,6 +8812,11 @@
   version "4.1.3"
   resolved "https://registry.npmjs.org/photoswipe/-/photoswipe-4.1.3.tgz#59f49494eeb9ddab5888d03392926a19bc197550"
   integrity sha512-89Z43IRUyw7ycTolo+AaiDn3W1EEIfox54hERmm9bI12IB9cvRfHSHez3XhAyU8XW2EAFrC+2sKMhh7SJwn0bA==
+
+picocolors@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
+  integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
 
 picomatch@^2.0.4, picomatch@^2.2.1:
   version "2.2.2"
@@ -9252,6 +9522,15 @@
     source-map "^0.6.1"
     supports-color "^6.1.0"
 
+postcss@^8.4.14:
+  version "8.5.3"
+  resolved "https://registry.npmmirror.com/postcss/-/postcss-8.5.3.tgz#1463b6f1c7fb16fe258736cba29a2de35237eafb"
+  integrity sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==
+  dependencies:
+    nanoid "^3.3.8"
+    picocolors "^1.1.1"
+    source-map-js "^1.2.1"
+
 prelude-ls@~1.1.2:
   version "1.1.2"
   resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
@@ -9266,6 +9545,11 @@
   version "1.19.1"
   resolved "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb"
   integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==
+
+"prettier@^1.18.2 || ^2.0.0":
+  version "2.8.8"
+  resolved "https://registry.npmmirror.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da"
+  integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==
 
 pretty-error@^2.0.2, pretty-error@^2.1.1:
   version "2.1.1"
@@ -9816,7 +10100,7 @@
   resolved "https://registry.npmjs.org/reselect/-/reselect-3.0.1.tgz#efdaa98ea7451324d092b2b2163a6a1d7a9a2147"
   integrity sha1-79qpjqdFEyTQkrKyFjpqHXqaIUc=
 
-resize-observer-polyfill@^1.5.1:
+resize-observer-polyfill@^1.5.0, resize-observer-polyfill@^1.5.1:
   version "1.5.1"
   resolved "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464"
   integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==
@@ -10018,6 +10302,11 @@
   version "1.2.4"
   resolved "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
   integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
+
+saxen@^8.1.2:
+  version "8.1.2"
+  resolved "https://registry.npmmirror.com/saxen/-/saxen-8.1.2.tgz#e677b32afe93667c9d939d3f3de02e09df108e54"
+  integrity sha512-xUOiiFbc3Ow7p8KMxwsGICPx46ZQvy3+qfNVhrkwfz3Vvq45eGt98Ft5IQaA1R/7Tb5B5MKh9fUR9x3c3nDTxw==
 
 schema-utils@^0.3.0:
   version "0.3.0"
@@ -10350,6 +10639,11 @@
   version "2.0.1"
   resolved "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"
   integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==
+
+source-map-js@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
+  integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
 
 source-map-resolve@^0.5.0, source-map-resolve@^0.5.2:
   version "0.5.3"
@@ -10935,6 +11229,11 @@
     loader-utils "^1.1.0"
     neo-async "^2.6.0"
 
+throttle-debounce@^1.0.1:
+  version "1.1.0"
+  resolved "https://registry.npmmirror.com/throttle-debounce/-/throttle-debounce-1.1.0.tgz#51853da37be68a155cb6e827b3514a3c422e89cd"
+  integrity sha512-XH8UiPCQcWNuk2LYePibW/4qL97+ZQ1AN3FNXwZRBNPPowo/NRU5fAlDCSNBJIYCKbioZfuYtMhG4quqoJhVzg==
+
 through2@^2.0.0:
   version "2.0.5"
   resolved "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd"
@@ -10974,6 +11273,11 @@
   version "2.1.0"
   resolved "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423"
   integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==
+
+tiny-svg@^2.2.1, tiny-svg@^2.2.2:
+  version "2.2.4"
+  resolved "https://registry.npmmirror.com/tiny-svg/-/tiny-svg-2.2.4.tgz#8d4a16bd2c4644c8444fd3c2ece91db7631cfb35"
+  integrity sha512-NOi39lBknf4UdDEahNkbEAJnzhu1ZcN2j75IS2vLRmIhsfxdZpTChfLKBcN1ShplVmPIXJAIafk6YY5/Aa80lQ==
 
 tinycolor2@^1.4.1:
   version "1.4.1"
@@ -11443,6 +11747,16 @@
   resolved "https://registry.npmmirror.com/vue-calendar-component/-/vue-calendar-component-2.8.2.tgz#8f60c05a72c8aacbdd224016f4e1a96664abd8aa"
   integrity sha512-BJh7xOBzM7QVcapcN4EbPQ1eZ8Pii1/oy+dzqjZTilRSIDD7SRPdFpnUJwZvs8lCrhtBAyJbYFsdm2SogXWHVQ==
 
+vue-code-diff@^1.2.0:
+  version "1.2.0"
+  resolved "https://registry.npmmirror.com/vue-code-diff/-/vue-code-diff-1.2.0.tgz#e38013307bab9296cc3512b2e7c9afed7890ae4a"
+  integrity sha512-kgSCl1Cr3I0u0z5DyJFbqQ1Hk7s9uBl7bfGwIn1X8f1xGmWdu4eRqfeOLTS0ut06VfGslaprXdaSvgn7YGDh0Q==
+  dependencies:
+    diff "^3.5.0"
+    diff2html "^3.3.1"
+    highlight.js "^9.18.5"
+    vue "^2.6.12"
+
 vue-cropper@^0.5.4:
   version "0.5.4"
   resolved "https://registry.npmjs.org/vue-cropper/-/vue-cropper-0.5.4.tgz#0db3776894eea8673a3203a1513882a0eb4558ad"
@@ -11601,6 +11915,14 @@
   version "2.6.11"
   resolved "https://registry.npmjs.org/vue/-/vue-2.6.11.tgz#76594d877d4b12234406e84e35275c6d514125c5"
   integrity sha512-VfPwgcGABbGAue9+sfrD4PuwFar7gPb1yl1UK1MwXoQPAw0BKSqWfoYCT/ThFrdEVWoI51dBuyCoiNU9bZDZxQ==
+
+vue@^2.6.11, vue@^2.6.12:
+  version "2.7.16"
+  resolved "https://registry.npmmirror.com/vue/-/vue-2.7.16.tgz#98c60de9def99c0e3da8dae59b304ead43b967c9"
+  integrity sha512-4gCtFXaAA3zYZdTp5s4Hl2sozuySsgz4jy1EnpBHNfpMa9dK1ZCG7viqBPCwXtmgc8nHqUsAu3G4gtmXkkY3Sw==
+  dependencies:
+    "@vue/compiler-sfc" "2.7.16"
+    csstype "^3.1.0"
 
 vuedraggable@^2.20.0:
   version "2.24.0"
@@ -11946,6 +12268,16 @@
   dependencies:
     errno "~0.1.7"
 
+workflow-bpmn-modeler@^0.2.8:
+  version "0.2.8"
+  resolved "https://registry.npmmirror.com/workflow-bpmn-modeler/-/workflow-bpmn-modeler-0.2.8.tgz#632facb1f63bcce6b85378fa7bf3a7e0da07a675"
+  integrity sha512-7Y2+YMx5mFDrpDAMoEK2EMVp5Z4585tYBLuqOxcbB5cdY6Vt1VjE1OBhzcwlFU6MWfpeisMGKbYtlPSHAOzsGg==
+  dependencies:
+    bpmn-js "^7.2.1"
+    element-ui "^2.12.0"
+    vue "^2.6.10"
+    xcrud "0.4.1"
+
 wrap-ansi@^2.0.0:
   version "2.1.0"
   resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85"
@@ -11998,6 +12330,16 @@
   dependencies:
     async-limiter "~1.0.0"
 
+xcrud@0.4.1:
+  version "0.4.1"
+  resolved "https://registry.npmmirror.com/xcrud/-/xcrud-0.4.1.tgz#0b644bb20a307c769b8878b0d81b2791dfe1fce1"
+  integrity sha512-aUbZZDARyNhbdBqT5nrHsXkYMM3Bo0i8EFvVgEyq444cxvYkqz4I71XWN2bPVhK/axfX4RawmIHjOQTz2v/apw==
+  dependencies:
+    element-ui "^2.13.2"
+    lodash.clonedeep "^4.5.0"
+    lodash.merge "^4.6.2"
+    vue "^2.6.11"
+
 xe-utils@2.4.8:
   version "2.4.8"
   resolved "https://registry.yarnpkg.com/xe-utils/-/xe-utils-2.4.8.tgz#0efda3ca81f6b55f68a8a31e276fb17da59c98b3"

--
Gitblit v1.9.3