Explorar el Código

Merge remote-tracking branch 'origin/master'

chris hace 3 años
padre
commit
a60a68756c

+ 31 - 3
package-lock.json

@@ -1394,6 +1394,13 @@
         "babel-runtime": "^6.26.0",
         "core-js": "^2.5.0",
         "regenerator-runtime": "^0.10.5"
+      },
+      "dependencies": {
+        "core-js": {
+          "version": "2.6.12",
+          "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
+          "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ=="
+        }
       }
     },
     "babel-preset-env": {
@@ -1571,6 +1578,14 @@
         "lodash": "^4.2.0",
         "mkdirp": "^0.5.1",
         "source-map-support": "^0.4.2"
+      },
+      "dependencies": {
+        "core-js": {
+          "version": "2.6.12",
+          "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
+          "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==",
+          "dev": true
+        }
       }
     },
     "babel-runtime": {
@@ -1582,6 +1597,11 @@
         "regenerator-runtime": "^0.11.0"
       },
       "dependencies": {
+        "core-js": {
+          "version": "2.6.12",
+          "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
+          "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ=="
+        },
         "regenerator-runtime": {
           "version": "0.11.1",
           "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
@@ -3146,9 +3166,9 @@
       }
     },
     "core-js": {
-      "version": "2.6.12",
-      "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
-      "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ=="
+      "version": "3.19.2",
+      "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.19.2.tgz",
+      "integrity": "sha512-ciYCResnLIATSsXuXnIOH4CbdfgV+H1Ltg16hJFN7/v6OxqnFr/IFGeLacaZ+fHLAm0TBbXwNK9/DNBzBUrO/g=="
     },
     "core-util-is": {
       "version": "1.0.3",
@@ -18948,6 +18968,14 @@
             "lodash": "^4.17.4",
             "mkdirp": "^0.5.1",
             "source-map-support": "^0.4.15"
+          },
+          "dependencies": {
+            "core-js": {
+              "version": "2.6.12",
+              "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
+              "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==",
+              "dev": true
+            }
           }
         },
         "json5": {

+ 2 - 1
package.json

@@ -37,7 +37,8 @@
     "vue-print-nb": "^1.7.5",
     "vue-router": "3.0.1",
     "vuedraggable": "^2.24.3",
-    "vuex": "3.0.1"
+    "vuex": "3.0.1",
+    "core-js": "^3.12.1"
   },
   "devDependencies": {
     "autoprefixer": "7.1.2",

+ 78 - 0
src/api/production.js

@@ -9,6 +9,14 @@ export function getSchedulingList (params) {
   })
 }
 
+// 排产详情
+export function getInfo (id) {
+  return request({
+    url: request.adornUrl(`/biz-service/ProdProduction/info/${id}`),
+    method: 'get'
+  })
+}
+
 // 获取生产监控列表信息
 export function getMonitoringList (params) {
   return request({
@@ -18,6 +26,16 @@ export function getMonitoringList (params) {
   })
 }
 
+
+// 获取生产监控详情
+export function getMonitoringDetail (productionId) {
+  return request({
+    url: request.adornUrl(`/biz-service/ProductionMonitoring/info/${productionId}`),
+    method: 'get'
+  })
+}
+
+
 // 获取生产记录列表信息
 export function getRecordingList (params) {
   return request({
@@ -27,6 +45,15 @@ export function getRecordingList (params) {
   })
 }
 
+// 生产安排
+export function plan (data) {
+  return request({
+    url: request.adornUrl(`/biz-service/ProdProduction/plan`),
+    method: 'post',
+    data
+  })
+}
+
 // 获取设备列表信息
 export function getEquipmentList (params) {
   return request({
@@ -43,3 +70,54 @@ export function getEquipmentDetail (id) {
     method: 'get'
   })
 }
+
+// 获取排产模板列表
+export function getMouldList (params) {
+  return request({
+    url: request.adornUrl(`/biz-service/ProdProductionMould/list`),
+    method: 'get',
+    params: params
+  })
+}
+
+// 根据排产模板查询步骤详情
+export function getMouldDetail (mouldId) {
+  return request({
+    url: request.adornUrl(`/biz-service/ProdProductionMould/info/${mouldId}`),
+    method: 'get'
+  })
+}
+
+// 根据产品ID查询步骤详情
+export function getMouldDetailByProductId (productId) {
+  return request({
+    url: request.adornUrl(`/biz-service/ProdProductionMould/info/product/${productId}`),
+    method: 'get'
+  })
+}
+
+// 获取未设置模板产品名称列表
+export function getProductList () {
+  return request({
+    url: request.adornUrl(`/biz-service/ProdProductionMould/product/name/list`),
+    method: 'get'
+  })
+}
+
+// 保存模板
+export function saveProdProductionMould (data) {
+  return request({
+    url: request.adornUrl(`/biz-service/ProdProductionMould/save`),
+    method: 'post',
+    data
+  })
+}
+
+// 更新模板
+export function updateProdProductionMould (data) {
+  return request({
+    url: request.adornUrl(`/biz-service/ProdProductionMould/update`),
+    method: 'post',
+    data
+  })
+}

+ 9 - 0
src/api/worktype.js

@@ -0,0 +1,9 @@
+import request from '@/utils/httpRequest'
+
+// 根据工种ID获取掌握人列表
+export function workTypeMasterList (workTypeId) {
+  return request({
+    url: request.adornUrl(`/biz-service/workType/master/${workTypeId}`),
+    method: 'GET'
+  })
+}

+ 461 - 3
src/views/modules/production/monitoring-details.vue

@@ -1,13 +1,471 @@
 <template>
+  <div class="production">
+    <el-dialog title="生产监控详情" 
+      width="70%" 
+      :close-on-click-modal="false" 
+      :visible.sync="visible">
+      <el-form :model="dataForm" :rules="dataRule" ref="dataForm" label-width="auto">
+        <el-row class="my-row">
+          <el-col :span="8">
+            <el-form-item label="产品编号:">
+              <el-input v-model="dataForm.prodCode" disabled></el-input>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row class="my-row" style="height: 350px; background-color: #efefef;">
+          <super-flow
+            v-if="visible"
+            ref="superFlow"
+            :node-list="nodeList"
+            :link-list="linkList"
+            :graph-menu="graphMenuList"
+            :node-menu="nodeMenuList"
+            :link-menu="linkMenuList"
+            :link-desc="linkDesc">
+          </super-flow>
+        </el-row>
+      </el-form>
+      <span slot="footer" class="dialog-footer">
+        <el-button @click="visible = false">取消</el-button>
+        <el-button type="primary" @click="dataFormSubmit()">确认提交</el-button>
+      </span>
+    </el-dialog>
 
+    <el-dialog
+        :title="drawerConf.title"
+        :visible.sync="drawerConf.visible"
+        :close-on-click-modal="false"
+        width="500px">
+      <el-form
+          @keyup.native.enter="settingSubmit"
+          @submit.native.prevent
+          v-show="drawerConf.type === drawerType.node"
+          ref="nodeSetting"
+          :rules="dataRule1"
+          :model="nodeSetting">
+        <el-row class="my-row">
+          <el-col :span="24">
+            <el-form-item
+                label="节点名称"
+                prop="name">
+              <el-input
+                  v-model="nodeSetting.name"
+                  placeholder="请输入节点名称"
+                  maxlength="30"
+                  disabled>
+              </el-input>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row class="my-row" >
+          <el-col :span="24" v-if="drawerConf.prop !== 'end'">
+            <el-form-item
+              label="操作人员"
+              prop="operatorIds">
+              <el-select
+                v-model="nodeSetting.operatorIds"
+                multiple
+                style="width:100%"
+                placeholder="请选择">
+                <el-option
+                  v-for="item in operatorList"
+                  :key="item.userId"
+                  :label="item.name"
+                  :value="item.userId">
+                </el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+      <span
+          slot="footer"
+          class="dialog-footer">
+        <el-button
+            @click="drawerConf.cancel">
+          取 消
+        </el-button>
+        <el-button
+            type="primary"
+            @click="settingSubmit">
+          确 定
+        </el-button>
+      </span>
+    </el-dialog>
+  </div>
 </template>
 
 <script>
+  import { getStepId } from '@/api/crafts'
+  import { getMonitoringDetail } from '@/api/production'
+  import { uuid } from '../common/vue-super-flow/utils'
+  const drawerType = {
+    node: 0,
+    link: 1
+  }
   export default {
-    name: 'monitoring-details'
+    name: 'monitoring-details',
+    data () {
+      return {
+        mouldId: '',
+        visible: false,
+        dataForm: {},
+        drawerType,
+        operatorList: [],
+        operatorIds: [],
+        productList: [],
+        display: false,
+        drawerConf: {
+          title: '',
+          visible: false,
+          prop: '',
+          type: null,
+          info: null,
+          open: (type, info) => {
+            if (info.meta.workTypeId && info.meta.prop !== 'end') {
+              this.getOperatorList(info.meta.workTypeId);
+            }
+            
+            const conf = this.drawerConf
+            conf.visible = true
+            conf.type = type
+            conf.info = info
+            if (conf.type === drawerType.node) {
+              conf.title = '节点'
+              conf.prop = info.meta.prop
+              if (this.$refs.nodeSetting) this.$refs.nodeSetting.resetFields()
+              this.$set(this.nodeSetting, 'name', info.meta.name)
+              this.$set(this.nodeSetting, 'desc', info.meta.desc)
+              // this.$set(this.nodeSetting, 'prop', info.meta.prop)
+              this.$set(this.nodeSetting, 'type', info.meta.type)
+              this.$set(this.nodeSetting, 'workTypeId', info.meta.workTypeId)
+              this.$set(this.nodeSetting, 'operatorIds', info.meta.operatorIds)
+              
+            } else {
+              conf.title = '连线'
+              if (this.$refs.linkSetting) this.$refs.linkSetting.resetFields()
+              // this.$set(this.linkSetting, 'desc', info.meta ? info.meta.desc : '')
+            }
+          },
+          cancel: () => {
+            this.drawerConf.visible = false
+            if (this.drawerConf.type === drawerType.node) {
+              this.$refs.nodeSetting.clearValidate()
+            } else {
+              this.$refs.linkSetting.clearValidate()
+            }
+          }
+        },
+        linkSetting: {
+          desc: ''
+        },
+        nodeSetting: {
+          name: '',
+          desc: '',
+          type: 1,
+          workTypeId: ''
+        },
+        origin: [100, 100],
+        nodeList: [],
+        linkList: [],
+        graphMenuList: [
+          [
+            {
+              label: '开始节点',
+              disable: true,
+              selected: async (graph, coordinate) => {
+                const start = graph.nodeList.find(node => node.meta.prop === 'start')
+                let id = ''
+                await getStepId().then(({ data }) => {
+                  id = data.data.stepId
+                })
+                if (!start) {
+                  graph.addNode({
+                    width: 90,
+                    height: 50,
+                    coordinate: coordinate,
+                    id: id,
+                    meta: {
+                      prop: 'start',
+                      name: '开始节点'
+                    }
+                  })
+                }
+              }
+            },
+            {
+              label: '节点',
+              disable: true,
+              selected: async (graph, coordinate) => {
+                let id = ''
+                await getStepId().then(({ data }) => {
+                  id = data.data.stepId
+                })
+                graph.addNode({
+                  width: 120,
+                  height: 70,
+                  coordinate: coordinate,
+                  id: id,
+                  meta: {
+                    prop: 'condition',
+                    name: '节点名称'
+                  }
+                })
+              }
+            },
+            {
+              label: '结束节点',
+              disable: true,
+              selected: async (graph, coordinate) => {
+                let id = ''
+                await getStepId().then(({ data }) => {
+                  id = data.data.stepId
+                })
+                graph.addNode({
+                  width: 90,
+                  height: 50,
+                  coordinate: coordinate,
+                  id: id,
+                  meta: {
+                    prop: 'end',
+                    name: '结束节点'
+                  }
+                })
+              }
+            }
+          ],
+          [
+            {
+              label: '完成',
+              selected: (graph, coordinate) => {
+                graph.selectAll()
+                this.datas = graph
+                console.log(graph)
+              }
+            }
+          ]
+        ],
+        nodeMenuList: [
+          // [
+          //   {
+          //     label: '删除',
+          //     disable: true,
+          //     hidden (node) {
+          //       return node.meta.prop === 'start'
+          //     },
+          //     selected (node, coordinate) {
+          //       node.remove()
+          //     }
+          //   }
+          // ],
+          // [
+          //   {
+          //     label: '编辑',
+          //     selected: (node, coordinate) => {
+          //       this.drawerConf.open(drawerType.node, node)
+          //     }
+          //   }
+          // ]
+        ],
+        linkMenuList: [
+          [
+            {
+              label: '删除',
+              selected: (link, coordinate) => {
+                link.remove()
+              }
+            }
+          ]
+        ],
+        datas: {},
+        dataRule: {
+          mouldName: [{required: true, message: '请输入模板名称', trigger: 'blur'}]
+        },
+        dataRule1: {
+           operatorIds: [{ required: true, message: '操作人员不能为空', trigger: 'change' }]
+        }
+      }
+    },
+    methods: {
+      // 初始化表单
+      async init (id, prodCode, disable) {
+        this.visible = true
+        this.display = disable
+        this.nodeList = []
+        this.linkList = []
+        this.mouldId = id
+        this.dataForm.prodCode = prodCode
+
+        await getMonitoringDetail(id).then(async ({data}) => {
+          if (data && data.code === '200') {
+            this.dataForm = {...this.dataForm, proTechnologyStepList: data.data}
+            console.log(this.dataForm.proTechnologyStepList)
+            // 图纸
+            if (this.dataForm.proTechnologyStepList) {
+              const dataline = []
+              const datanode = []
+              await this.dataForm.proTechnologyStepList.forEach((v, i) => {
+                // eslint-disable-next-line no-unused-vars
+                const sortNo = []
+                
+                const datas = v.sort((a, b) => Number(a['sortNo']) - Number(b['sortNo']))
+                let length = datas.length
+                datas.forEach((item, index) => {
+                  const find = datanode.find(map => map.id === item.stepId)
+                  if (!find) {
+                    datanode.push({
+                      id: item.stepId,
+                      // width: (index === 0 || item.workTypeId === '0') ? 140 : 180,
+                      // height: (index === 0 || item.workTypeId === '0') ? 80 : 100,
+                      width: 140,
+                      height: 80,
+                      coordinate: item.coordinate.split(','),
+                      meta: {
+                        name: item.stepName + (item.state === '0' ? '(已完成)' : '(未完成)'),
+                        desc: item.operatorName,
+                        prop: index === 0 ? 'start' : (index === length - 1) ? 'end' : 'condition',
+                        notes: item.notes || '',
+                        workTypeId: item.workTypeId || '',
+                        type: item.type || '',
+                        operatorIds: item.operatorId == null ? [] : item.operatorId.split(',')
+                      }
+                    })
+                  }
+                  const id = item.stepId
+                  if ((index + 1) < datas.length) {
+                    if (datas[index + 1]) {
+                      dataline.push({
+                        id: uuid('link'),
+                        startId: id,
+                        endId: datas[index + 1].stepId,
+                        meta: '',
+                        // startAt: [(index === 0 || item.workTypeId === '0') ? 90 : 120, (index === 0 || item.workTypeId === '0') ? 25 : 35],
+                        // endAt: [0, (index === 0 || item.workTypeId === '0') ? 25 : 35]
+                        startAt: [120, 35],
+                        endAt: [0, 35]
+                      })
+                    }
+                  }
+                })
+              })
+              
+              this.$nextTick(() => {
+                setTimeout(() => {
+                  this.nodeList = datanode
+                  this.linkList = dataline
+                }, 200)
+              })
+            }
+          }
+        })
+      },
+      linkDesc (link) {
+        return link.meta ? link.meta.desc : ''
+      },
+      settingSubmit () {
+        this.$refs['nodeSetting'].validate((valid) => {
+          if (valid) {
+            const conf = this.drawerConf
+            if (this.drawerConf.type === drawerType.node) {
+              if (!conf.info.meta) conf.info.meta = {}
+              Object.keys(this.nodeSetting).forEach(key => {
+                if (key == 'operatorIds') {
+                  let idList = this.nodeSetting[key];
+                  let nameList = [];
+                  idList.forEach(id => {
+                    let name = this.operatorList.find(item => {
+                      return item.userId === id
+                    }).name
+                    nameList.push(name)
+                  })
+                 
+                  this.$set(conf.info.meta, 'desc', nameList.join(','))
+                }
+                this.$set(conf.info.meta, key, this.nodeSetting[key])
+              })
+              this.$refs.nodeSetting.resetFields()
+            } else {
+              if (!conf.info.meta) conf.info.meta = {}
+              Object.keys(this.linkSetting).forEach(key => {
+                this.$set(conf.info.meta, key, this.linkSetting[key])
+              })
+              this.$refs.linkSetting.resetFields()
+            }
+            conf.visible = false
+          }
+        })
+      },
+      dataFormSubmit () {},
+      getLineData (dataAll, dList, lList, id, sortNo) {
+        const _l = []
+        lList.forEach(l => {
+          if (l.start.id === id) {
+            _l.push(l.end.id)
+          }
+        })
+        if (!sortNo) {
+          const data = dList.filter(v => v.meta.prop === 'start')[0]
+          _l.forEach(item => {
+            dataAll.push([{
+              'notes': data.meta.desc || '',
+              'sortNo': 0,
+              'stepId': data.id,
+              'stepName': data.meta.name,
+              'workTypeId': data.meta.workTypeId || null,
+              'type': data.meta.type || null,
+              'coordinate': data.coordinate.join(','),
+              'operatorIds': data.meta.operatorIds
+            }])
+          })
+        }
+        dList.forEach(v => {
+          const _i = _l.indexOf(v.id)
+          if (_i > -1) {
+            if (!v.meta.workTypeId && v.meta.prop !== 'end') {
+              this.$message.error('完善节点工种')
+              return
+            }
+            if (!sortNo) {
+              dataAll[_i].push({
+                'notes': v.meta.desc || '',
+                'sortNo': (sortNo + 1),
+                'stepId': v.id,
+                'stepName': v.meta.name,
+                'workTypeId': v.meta.workTypeId || null,
+                'type': v.meta.type || null,
+                'coordinate': v.coordinate.join(',')
+              })
+              this.getLineData(dataAll[_i], dList, lList, v.id, (sortNo + 1))
+            } else {
+              let _has = false
+              dataAll.forEach(items => {
+                if (items.stepId === v.id) {
+                  _has = true
+                }
+              })
+              if (!_has) {
+                dataAll.push({
+                  'notes': v.meta.desc || '',
+                  'sortNo': (sortNo + 1),
+                  'stepId': v.id,
+                  'stepName': v.meta.name,
+                  'workTypeId': v.meta.prop === 'end' ? 0 : (v.meta.workTypeId || ''),
+                  'type': v.meta.type || '',
+                  'coordinate': v.coordinate.join(',')
+                })
+              }
+              this.getLineData(dataAll, dList, lList, v.id, (sortNo + 1))
+            }
+          }
+        })
+      }
+    }
   }
 </script>
 
-<style scoped>
-
+<style scoped lang='scss'>
+/deep/ .my-row .super-flow__node {
+  width: 180px;
+  height: 80px;
+}
 </style>

+ 3 - 3
src/views/modules/production/monitoring.vue

@@ -72,7 +72,7 @@
         width="150"
         label="操作">
         <template slot-scope="scope">
-          <el-button v-if="isAuth('prod:monitoring:info')" type="text" size="small" @click="detail(scope.row.id, true)">查看</el-button>
+          <el-button v-if="isAuth('prod:monitoring:info')" type="text" size="small" @click="detail(scope.row.id, scope.row.prodCode, true)">查看</el-button>
         </template>
       </el-table-column>
     </el-table>
@@ -169,10 +169,10 @@
         this.dataListSelections = val
       },
       // 新增 / 修改
-      detail (id, disable) {
+      detail (id, prodCode, disable) {
         this.detailsVisible = true
         this.$nextTick(() => {
-          this.$refs.details.init(id, disable)
+          this.$refs.details.init(id, prodCode, disable)
         })
       },
       // 创建新产品

+ 450 - 3
src/views/modules/production/recording-details.vue

@@ -1,13 +1,460 @@
+
 <template>
+  <div class="production">
+    <el-dialog title="生产记录详情" 
+      width="70%" 
+      :close-on-click-modal="false" 
+      :visible.sync="visible">
+      <el-form :model="dataForm" :rules="dataRule" ref="dataForm" label-width="auto">
+        <el-row class="my-row" style="height: 350px; background-color: #efefef;">
+          <super-flow
+            v-if="visible"
+            ref="superFlow"
+            :node-list="nodeList"
+            :link-list="linkList"
+            :graph-menu="graphMenuList"
+            :node-menu="nodeMenuList"
+            :link-menu="linkMenuList"
+            :link-desc="linkDesc">
+          </super-flow>
+        </el-row>
+      </el-form>
+      <span slot="footer" class="dialog-footer">
+        <el-button @click="visible = false">取消</el-button>
+        <el-button type="primary" @click="dataFormSubmit()">确认提交</el-button>
+      </span>
+    </el-dialog>
 
+    <el-dialog
+        :title="drawerConf.title"
+        :visible.sync="drawerConf.visible"
+        :close-on-click-modal="false"
+        width="500px">
+      <el-form
+          @keyup.native.enter="settingSubmit"
+          @submit.native.prevent
+          v-show="drawerConf.type === drawerType.node"
+          ref="nodeSetting"
+          :rules="dataRule1"
+          :model="nodeSetting">
+        <el-row class="my-row">
+          <el-col :span="24">
+            <el-form-item
+                label="节点名称"
+                prop="name">
+              <el-input
+                  v-model="nodeSetting.name"
+                  placeholder="请输入节点名称"
+                  maxlength="30"
+                  disabled>
+              </el-input>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row class="my-row" >
+          <el-col :span="24" v-if="drawerConf.prop !== 'end'">
+            <el-form-item
+              label="操作人员"
+              prop="operatorIds">
+              <el-select
+                v-model="nodeSetting.operatorIds"
+                multiple
+                style="width:100%"
+                placeholder="请选择">
+                <el-option
+                  v-for="item in operatorList"
+                  :key="item.userId"
+                  :label="item.name"
+                  :value="item.userId">
+                </el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+      <span
+          slot="footer"
+          class="dialog-footer">
+        <el-button
+            @click="drawerConf.cancel">
+          取 消
+        </el-button>
+        <el-button
+            type="primary"
+            @click="settingSubmit">
+          确 定
+        </el-button>
+      </span>
+    </el-dialog>
+  </div>
 </template>
 
 <script>
+  import { getStepId } from '@/api/crafts'
+  import { getMonitoringDetail } from '@/api/production'
+  import { uuid } from '../common/vue-super-flow/utils'
+  const drawerType = {
+    node: 0,
+    link: 1
+  }
   export default {
-    name: 'recording-details'
+    name: 'recording-details',
+    data () {
+      return {
+        mouldId: '',
+        visible: false,
+        dataForm: {},
+        drawerType,
+        operatorList: [],
+        operatorIds: [],
+        productList: [],
+        display: false,
+        drawerConf: {
+          title: '',
+          visible: false,
+          prop: '',
+          type: null,
+          info: null,
+          open: (type, info) => {
+            if (info.meta.workTypeId && info.meta.prop !== 'end') {
+              this.getOperatorList(info.meta.workTypeId);
+            }
+            
+            const conf = this.drawerConf
+            conf.visible = true
+            conf.type = type
+            conf.info = info
+            if (conf.type === drawerType.node) {
+              conf.title = '节点'
+              conf.prop = info.meta.prop
+              if (this.$refs.nodeSetting) this.$refs.nodeSetting.resetFields()
+              this.$set(this.nodeSetting, 'name', info.meta.name)
+              this.$set(this.nodeSetting, 'desc', info.meta.desc)
+              // this.$set(this.nodeSetting, 'prop', info.meta.prop)
+              this.$set(this.nodeSetting, 'type', info.meta.type)
+              this.$set(this.nodeSetting, 'workTypeId', info.meta.workTypeId)
+              this.$set(this.nodeSetting, 'operatorIds', info.meta.operatorIds)
+              
+            } else {
+              conf.title = '连线'
+              if (this.$refs.linkSetting) this.$refs.linkSetting.resetFields()
+              // this.$set(this.linkSetting, 'desc', info.meta ? info.meta.desc : '')
+            }
+          },
+          cancel: () => {
+            this.drawerConf.visible = false
+            if (this.drawerConf.type === drawerType.node) {
+              this.$refs.nodeSetting.clearValidate()
+            } else {
+              this.$refs.linkSetting.clearValidate()
+            }
+          }
+        },
+        linkSetting: {
+          desc: ''
+        },
+        nodeSetting: {
+          name: '',
+          desc: '',
+          type: 1,
+          workTypeId: ''
+        },
+        origin: [100, 100],
+        nodeList: [],
+        linkList: [],
+        graphMenuList: [
+          [
+            {
+              label: '开始节点',
+              disable: true,
+              selected: async (graph, coordinate) => {
+                const start = graph.nodeList.find(node => node.meta.prop === 'start')
+                let id = ''
+                await getStepId().then(({ data }) => {
+                  id = data.data.stepId
+                })
+                if (!start) {
+                  graph.addNode({
+                    width: 90,
+                    height: 50,
+                    coordinate: coordinate,
+                    id: id,
+                    meta: {
+                      prop: 'start',
+                      name: '开始节点'
+                    }
+                  })
+                }
+              }
+            },
+            {
+              label: '节点',
+              disable: true,
+              selected: async (graph, coordinate) => {
+                let id = ''
+                await getStepId().then(({ data }) => {
+                  id = data.data.stepId
+                })
+                graph.addNode({
+                  width: 120,
+                  height: 70,
+                  coordinate: coordinate,
+                  id: id,
+                  meta: {
+                    prop: 'condition',
+                    name: '节点名称'
+                  }
+                })
+              }
+            },
+            {
+              label: '结束节点',
+              disable: true,
+              selected: async (graph, coordinate) => {
+                let id = ''
+                await getStepId().then(({ data }) => {
+                  id = data.data.stepId
+                })
+                graph.addNode({
+                  width: 90,
+                  height: 50,
+                  coordinate: coordinate,
+                  id: id,
+                  meta: {
+                    prop: 'end',
+                    name: '结束节点'
+                  }
+                })
+              }
+            }
+          ],
+          [
+            {
+              label: '完成',
+              selected: (graph, coordinate) => {
+                graph.selectAll()
+                this.datas = graph
+                console.log(graph)
+              }
+            }
+          ]
+        ],
+        nodeMenuList: [
+          // [
+          //   {
+          //     label: '删除',
+          //     disable: true,
+          //     hidden (node) {
+          //       return node.meta.prop === 'start'
+          //     },
+          //     selected (node, coordinate) {
+          //       node.remove()
+          //     }
+          //   }
+          // ],
+          // [
+          //   {
+          //     label: '编辑',
+          //     selected: (node, coordinate) => {
+          //       this.drawerConf.open(drawerType.node, node)
+          //     }
+          //   }
+          // ]
+        ],
+        linkMenuList: [
+          [
+            {
+              label: '删除',
+              selected: (link, coordinate) => {
+                link.remove()
+              }
+            }
+          ]
+        ],
+        datas: {},
+        dataRule: {
+          mouldName: [{required: true, message: '请输入模板名称', trigger: 'blur'}]
+        },
+        dataRule1: {
+           operatorIds: [{ required: true, message: '操作人员不能为空', trigger: 'change' }]
+        }
+      }
+    },
+    methods: {
+      // 初始化表单
+      async init (id, disable) {
+        this.visible = true
+        this.display = disable
+        this.nodeList = []
+        this.linkList = []
+        this.mouldId = id;
+
+        await getMonitoringDetail(id).then(async ({data}) => {
+          if (data && data.code === '200') {
+            this.dataForm = {...this.dataForm, proTechnologyStepList: data.data}
+            console.log(this.dataForm.proTechnologyStepList)
+            // 图纸
+            if (this.dataForm.proTechnologyStepList) {
+              const dataline = []
+              const datanode = []
+              await this.dataForm.proTechnologyStepList.forEach((v, i) => {
+                // eslint-disable-next-line no-unused-vars
+                const sortNo = []
+                
+                const datas = v.sort((a, b) => Number(a['sortNo']) - Number(b['sortNo']))
+                let length = datas.length
+                datas.forEach((item, index) => {
+                  const find = datanode.find(map => map.id === item.stepId)
+                  if (!find) {
+                    datanode.push({
+                      id: item.stepId,
+                      // width: (index === 0 || item.workTypeId === '0') ? 140 : 180,
+                      // height: (index === 0 || item.workTypeId === '0') ? 80 : 100,
+                      width: 140,
+                      height: 80,
+                      coordinate: item.coordinate.split(','),
+                      meta: {
+                        name: item.stepName + (item.state === '0' ? '(已完成)' : '(未完成)'),
+                        desc: item.operatorName,
+                        prop: index === 0 ? 'start' : (index === length - 1) ? 'end' : 'condition',
+                        notes: item.notes || '',
+                        workTypeId: item.workTypeId || '',
+                        type: item.type || '',
+                        operatorIds: item.operatorId == null ? [] : item.operatorId.split(',')
+                      }
+                    })
+                  }
+                  const id = item.stepId
+                  if ((index + 1) < datas.length) {
+                    if (datas[index + 1]) {
+                      dataline.push({
+                        id: uuid('link'),
+                        startId: id,
+                        endId: datas[index + 1].stepId,
+                        meta: '',
+                        // startAt: [(index === 0 || item.workTypeId === '0') ? 90 : 120, (index === 0 || item.workTypeId === '0') ? 25 : 35],
+                        // endAt: [0, (index === 0 || item.workTypeId === '0') ? 25 : 35]
+                        startAt: [120, 35],
+                        endAt: [0, 35]
+                      })
+                    }
+                  }
+                })
+              })
+              
+              this.$nextTick(() => {
+                setTimeout(() => {
+                  this.nodeList = datanode
+                  this.linkList = dataline
+                }, 200)
+              })
+            }
+          }
+        })
+      },
+      linkDesc (link) {
+        return link.meta ? link.meta.desc : ''
+      },
+      settingSubmit () {
+        this.$refs['nodeSetting'].validate((valid) => {
+          if (valid) {
+            const conf = this.drawerConf
+            if (this.drawerConf.type === drawerType.node) {
+              if (!conf.info.meta) conf.info.meta = {}
+              Object.keys(this.nodeSetting).forEach(key => {
+                if (key == 'operatorIds') {
+                  let idList = this.nodeSetting[key];
+                  let nameList = [];
+                  idList.forEach(id => {
+                    let name = this.operatorList.find(item => {
+                      return item.userId === id
+                    }).name
+                    nameList.push(name)
+                  })
+                 
+                  this.$set(conf.info.meta, 'desc', nameList.join(','))
+                }
+                this.$set(conf.info.meta, key, this.nodeSetting[key])
+              })
+              this.$refs.nodeSetting.resetFields()
+            } else {
+              if (!conf.info.meta) conf.info.meta = {}
+              Object.keys(this.linkSetting).forEach(key => {
+                this.$set(conf.info.meta, key, this.linkSetting[key])
+              })
+              this.$refs.linkSetting.resetFields()
+            }
+            conf.visible = false
+          }
+        })
+      },
+      dataFormSubmit () {},
+      getLineData (dataAll, dList, lList, id, sortNo) {
+        const _l = []
+        lList.forEach(l => {
+          if (l.start.id === id) {
+            _l.push(l.end.id)
+          }
+        })
+        if (!sortNo) {
+          const data = dList.filter(v => v.meta.prop === 'start')[0]
+          _l.forEach(item => {
+            dataAll.push([{
+              'notes': data.meta.desc || '',
+              'sortNo': 0,
+              'stepId': data.id,
+              'stepName': data.meta.name,
+              'workTypeId': data.meta.workTypeId || null,
+              'type': data.meta.type || null,
+              'coordinate': data.coordinate.join(','),
+              'operatorIds': data.meta.operatorIds
+            }])
+          })
+        }
+        dList.forEach(v => {
+          const _i = _l.indexOf(v.id)
+          if (_i > -1) {
+            if (!v.meta.workTypeId && v.meta.prop !== 'end') {
+              this.$message.error('完善节点工种')
+              return
+            }
+            if (!sortNo) {
+              dataAll[_i].push({
+                'notes': v.meta.desc || '',
+                'sortNo': (sortNo + 1),
+                'stepId': v.id,
+                'stepName': v.meta.name,
+                'workTypeId': v.meta.workTypeId || null,
+                'type': v.meta.type || null,
+                'coordinate': v.coordinate.join(',')
+              })
+              this.getLineData(dataAll[_i], dList, lList, v.id, (sortNo + 1))
+            } else {
+              let _has = false
+              dataAll.forEach(items => {
+                if (items.stepId === v.id) {
+                  _has = true
+                }
+              })
+              if (!_has) {
+                dataAll.push({
+                  'notes': v.meta.desc || '',
+                  'sortNo': (sortNo + 1),
+                  'stepId': v.id,
+                  'stepName': v.meta.name,
+                  'workTypeId': v.meta.prop === 'end' ? 0 : (v.meta.workTypeId || ''),
+                  'type': v.meta.type || '',
+                  'coordinate': v.coordinate.join(',')
+                })
+              }
+              this.getLineData(dataAll, dList, lList, v.id, (sortNo + 1))
+            }
+          }
+        })
+      }
+    }
   }
 </script>
 
-<style scoped>
-
+<style scoped lang='scss'>
 </style>

+ 496 - 1
src/views/modules/production/scheduling-details.vue

@@ -1,10 +1,505 @@
 <template>
+  <div class="production">
+    <el-dialog title="排产" 
+      width="70%" 
+      :close-on-click-modal="false" 
+      :visible.sync="visible">
+      <el-form :model="dataForm" :rules="dataRule" ref="dataForm" label-width="auto">
+        <el-row class="my-row" style="height: 350px; background-color: #efefef;">
+          <super-flow
+            v-if="visible"
+            ref="superFlow"
+            :node-list="nodeList"
+            :link-list="linkList"
+            :graph-menu="graphMenuList"
+            :node-menu="nodeMenuList"
+            :link-menu="linkMenuList"
+            :link-desc="linkDesc">
+          </super-flow>
+        </el-row>
+      </el-form>
+      <span slot="footer" class="dialog-footer">
+        <el-button @click="visible = false">取消</el-button>
+        <el-button type="primary" @click="dataFormSubmit()">确认提交</el-button>
+      </span>
+    </el-dialog>
 
+    <el-dialog
+        :title="drawerConf.title"
+        :visible.sync="drawerConf.visible"
+        :close-on-click-modal="false"
+        width="500px">
+      <el-form
+          @keyup.native.enter="settingSubmit"
+          @submit.native.prevent
+          v-show="drawerConf.type === drawerType.node"
+          ref="nodeSetting"
+          :rules="dataRule1"
+          :model="nodeSetting">
+        <el-row class="my-row">
+          <el-col :span="24">
+            <el-form-item
+                label="节点名称"
+                prop="name">
+              <el-input
+                  v-model="nodeSetting.name"
+                  placeholder="请输入节点名称"
+                  maxlength="30"
+                  disabled>
+              </el-input>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row class="my-row" >
+          <el-col :span="24" v-if="drawerConf.prop !== 'end'">
+            <el-form-item
+              label="操作人员"
+              prop="operatorIds">
+              <el-select
+                v-model="nodeSetting.operatorIds"
+                multiple
+                style="width:100%"
+                placeholder="请选择">
+                <el-option
+                  v-for="item in operatorList"
+                  :key="item.userId"
+                  :label="item.name"
+                  :value="item.userId">
+                </el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+      <span
+          slot="footer"
+          class="dialog-footer">
+        <el-button
+            @click="drawerConf.cancel">
+          取 消
+        </el-button>
+        <el-button
+            type="primary"
+            @click="settingSubmit">
+          确 定
+        </el-button>
+      </span>
+    </el-dialog>
+  </div>
 </template>
 
 <script>
+  import { getProduct, getWorkType, getStepId } from '@/api/crafts'
+  import { getInfo, plan } from '@/api/production'
+  import { workTypeMasterList } from '@/api/worktype'
+  import { uuid } from '../common/vue-super-flow/utils'
+  const drawerType = {
+    node: 0,
+    link: 1
+  }
   export default {
-    name: 'scheduling-details'
+    name: 'scheduling-details',
+    data () {
+      return {
+        productionId: '',
+        visible: false,
+        dataForm: {},
+        drawerType,
+        operatorList: [],
+        operatorIds: [],
+        drawerConf: {
+          title: '',
+          visible: false,
+          prop: '',
+          type: null,
+          info: null,
+          open: (type, info) => {
+            if (info.meta.workTypeId && info.meta.prop !== 'end') {
+              this.getOperatorList(info.meta.workTypeId);
+            }
+            
+            const conf = this.drawerConf
+            conf.visible = true
+            conf.type = type
+            conf.info = info
+            if (conf.type === drawerType.node) {
+              conf.title = '节点'
+              conf.prop = info.meta.prop
+              if (this.$refs.nodeSetting) this.$refs.nodeSetting.resetFields()
+              this.$set(this.nodeSetting, 'name', info.meta.name)
+              this.$set(this.nodeSetting, 'desc', info.meta.desc)
+              // this.$set(this.nodeSetting, 'prop', info.meta.prop)
+              this.$set(this.nodeSetting, 'type', info.meta.type)
+              this.$set(this.nodeSetting, 'workTypeId', info.meta.workTypeId)
+              this.$set(this.nodeSetting, 'operatorIds', info.meta.operatorIds)
+              
+            } else {
+              conf.title = '连线'
+              if (this.$refs.linkSetting) this.$refs.linkSetting.resetFields()
+              // this.$set(this.linkSetting, 'desc', info.meta ? info.meta.desc : '')
+            }
+          },
+          cancel: () => {
+            this.drawerConf.visible = false
+            if (this.drawerConf.type === drawerType.node) {
+              this.$refs.nodeSetting.clearValidate()
+            } else {
+              this.$refs.linkSetting.clearValidate()
+            }
+          }
+        },
+        linkSetting: {
+          desc: ''
+        },
+        nodeSetting: {
+          name: '',
+          desc: '',
+          type: 1,
+          workTypeId: ''
+        },
+        nodeList: [],
+        linkList: [],
+        graphMenuList: [
+          [
+            {
+              label: '开始节点',
+              disable (graph) {
+                return !!graph.nodeList.find(node => node.meta.prop === 'start')
+              },
+              selected: async (graph, coordinate) => {
+                const start = graph.nodeList.find(node => node.meta.prop === 'start')
+                let id = ''
+                await getStepId().then(({ data }) => {
+                  id = data.data.stepId
+                })
+                if (!start) {
+                  graph.addNode({
+                    width: 90,
+                    height: 50,
+                    coordinate: coordinate,
+                    id: id,
+                    meta: {
+                      prop: 'start',
+                      name: '开始节点'
+                    }
+                  })
+                }
+              }
+            },
+            {
+              label: '节点',
+              disable: false,
+              selected: async (graph, coordinate) => {
+                let id = ''
+                await getStepId().then(({ data }) => {
+                  id = data.data.stepId
+                })
+                graph.addNode({
+                  width: 120,
+                  height: 70,
+                  coordinate: coordinate,
+                  id: id,
+                  meta: {
+                    prop: 'condition',
+                    name: '节点名称'
+                  }
+                })
+              }
+            },
+            {
+              label: '结束节点',
+              disable (graph) {
+                return !!graph.nodeList.find(point => point.meta.prop === 'end')
+              },
+              selected: async (graph, coordinate) => {
+                let id = ''
+                await getStepId().then(({ data }) => {
+                  id = data.data.stepId
+                })
+                graph.addNode({
+                  width: 90,
+                  height: 50,
+                  coordinate: coordinate,
+                  id: id,
+                  meta: {
+                    prop: 'end',
+                    name: '结束节点'
+                  }
+                })
+              }
+            }
+          ],
+          [
+            {
+              label: '完成',
+              selected: (graph, coordinate) => {
+                graph.selectAll()
+                this.datas = graph
+                console.log(graph)
+              }
+            }
+          ]
+        ],
+        nodeMenuList: [
+          [
+            {
+              label: '删除',
+              disable: false,
+              hidden (node) {
+                return node.meta.prop === 'start'
+              },
+              selected (node, coordinate) {
+                node.remove()
+              }
+            }
+          ],
+          [
+            {
+              label: '编辑',
+              selected: (node, coordinate) => {
+                this.drawerConf.open(drawerType.node, node)
+              }
+            }
+          ]
+        ],
+        linkMenuList: [
+          [
+            {
+              label: '删除',
+              selected: (link, coordinate) => {
+                link.remove()
+              }
+            }
+          ]
+        ],
+        datas: {},
+        dataRule: {},
+        dataRule1: {
+           operatorIds: [{ required: true, message: '操作人员不能为空', trigger: 'change' }]
+        }
+      }
+    },
+    methods: {
+      // 初始化表彰
+      async init (id, disable) {
+        this.visible = true
+        this.nodeList = []
+        this.linkList = []
+        this.productionId = id;
+
+        await getInfo(id).then(async ({data}) => {
+          if (data && data.code === '200') {
+            this.dataForm = {...this.dataForm, proTechnologyStepLists :data.data}
+            // 图纸
+            if (this.dataForm.proTechnologyStepLists) {
+              const dataline = []
+              const datanode = []
+              await this.dataForm.proTechnologyStepLists.forEach((v, i) => {
+                // eslint-disable-next-line no-unused-vars
+                const sortNo = []
+                const datas = v.sort((a, b) => Number(a['sortNo']) - Number(b['sortNo']))
+                let length = datas.length
+                datas.forEach((item, index) => {
+                  const find = datanode.find(map => map.id === item.stepId)
+                  if (!find) {
+                    datanode.push({
+                      id: item.stepId,
+                      width: (index === 0 || item.workTypeId === '0') ? 90 : 120,
+                      height: (index === 0 || item.workTypeId === '0') ? 50 : 70,
+                      coordinate: item.coordinate.split(','),
+                      meta: {
+                        name: item.stepName,
+                        prop: index === 0 ? 'start' : (index === length - 1) ? 'end' : 'condition',
+                        notes: item.notes || '',
+                        workTypeId: item.workTypeId || '',
+                        type: item.type || ''
+                      }
+                    })
+                  }
+                  const id = item.stepId
+                  if ((index + 1) < datas.length) {
+                    if (datas[index + 1]) {
+                      dataline.push({
+                        id: uuid('link'),
+                        startId: id,
+                        endId: datas[index + 1].stepId,
+                        meta: '',
+                        startAt: [(index === 0 || item.workTypeId === '0') ? 90 : 120, (index === 0 || item.workTypeId === '0') ? 25 : 35],
+                        endAt: [0, (index === 0 || item.workTypeId === '0') ? 25 : 35]
+                      })
+                    }
+                  }
+                })
+              })
+              
+              this.$nextTick(() => {
+                setTimeout(() => {
+                  this.nodeList = datanode
+                  this.linkList = dataline
+                }, 200)
+              })
+            }
+          }
+        })
+      },
+      // 按工种ID查询操作人列表
+      getOperatorList(workTypeId) {
+        workTypeMasterList(workTypeId).then(({data}) => {
+          if (data && data.code === '200') {
+            this.operatorList = []
+            data.data.forEach(item => {
+              this.operatorList.push(item);
+            })
+          }
+        })
+      },
+      linkDesc (link) {
+        return link.meta ? link.meta.desc : ''
+      },
+      settingSubmit () {
+        this.$refs['nodeSetting'].validate((valid) => {
+          if (valid) {
+            const conf = this.drawerConf
+            if (this.drawerConf.type === drawerType.node) {
+              if (!conf.info.meta) conf.info.meta = {}
+              Object.keys(this.nodeSetting).forEach(key => {
+                if (key == 'operatorIds') {
+                  let idList = this.nodeSetting[key];
+                  let nameList = [];
+                  idList.forEach(id => {
+                    let name = this.operatorList.find(item => {
+                      return item.userId === id
+                    }).name
+                    nameList.push(name)
+                  })
+                 
+                  this.$set(conf.info.meta, 'desc', nameList.join(','))
+                }
+                this.$set(conf.info.meta, key, this.nodeSetting[key])
+              })
+              this.$refs.nodeSetting.resetFields()
+            } else {
+              if (!conf.info.meta) conf.info.meta = {}
+              Object.keys(this.linkSetting).forEach(key => {
+                this.$set(conf.info.meta, key, this.linkSetting[key])
+              })
+              this.$refs.linkSetting.resetFields()
+            }
+            conf.visible = false
+          }
+        })
+      },
+      dataFormSubmit () {
+        if (!this.datas.nodeList) {
+          this.$message.error('请先完成流程图!')
+          return
+        }
+        if (this.datas.nodeList.length <= 2) {
+          this.$message.error('请先完成流程图!')
+          return
+        }
+
+        this.$refs['dataForm'].validate((valid) => {
+          if(valid) {
+            const proTechnologyStepLists = []
+         
+            let productionPlanSteps = []
+            for (let index = 0; index < this.datas.nodeList.length; index++) {
+              const v = this.datas.nodeList[index];
+               let tmp = v.meta.operatorIds || []
+              if(v.meta.prop !== 'end' && tmp.length == 0){
+                this.$message.error(`请选择 ${v.meta.name} 的操作人员!`)
+                return
+              }
+              productionPlanSteps.push({stepId: v.id, operatorId: tmp.join(',')})
+            }
+
+            let submitData = {
+              productionId: this.productionId,
+              productionPlanSteps: productionPlanSteps
+            }
+            plan(submitData).then(({data}) => {
+              if (data && data.code === '200') {
+                  this.$message({
+                  message: '操作成功',
+                  type: 'success',
+                  duration: 1500,
+                  onClose: () => {
+                    this.$emit('refreshDataList')
+                    this.visible = false
+                  }
+                })
+              } else {
+                 this.$message.error(data.msg)
+              }
+            })
+          }
+        })
+      },
+      getLineData (dataAll, dList, lList, id, sortNo) {
+        const _l = []
+        lList.forEach(l => {
+          if (l.start.id === id) {
+            _l.push(l.end.id)
+          }
+        })
+        if (!sortNo) {
+          const data = dList.filter(v => v.meta.prop === 'start')[0]
+          _l.forEach(item => {
+            dataAll.push([{
+              'notes': data.meta.desc || '',
+              'sortNo': 0,
+              'stepId': data.id,
+              'stepName': data.meta.name,
+              'workTypeId': data.meta.workTypeId || null,
+              'type': data.meta.type || null,
+              'coordinate': data.coordinate.join(','),
+              'operatorIds': data.meta.operatorIds
+            }])
+          })
+        }
+        dList.forEach(v => {
+          const _i = _l.indexOf(v.id)
+          if (_i > -1) {
+            if (!v.meta.workTypeId && v.meta.prop !== 'end') {
+              this.$message.error('完善节点工种')
+              return
+            }
+            if (!sortNo) {
+              dataAll[_i].push({
+                'notes': v.meta.desc || '',
+                'sortNo': (sortNo + 1),
+                'stepId': v.id,
+                'stepName': v.meta.name,
+                'workTypeId': v.meta.workTypeId || null,
+                'type': v.meta.type || null,
+                'coordinate': v.coordinate.join(',')
+              })
+              this.getLineData(dataAll[_i], dList, lList, v.id, (sortNo + 1))
+            } else {
+              let _has = false
+              dataAll.forEach(items => {
+                if (items.stepId === v.id) {
+                  _has = true
+                }
+              })
+              if (!_has) {
+                dataAll.push({
+                  'notes': v.meta.desc || '',
+                  'sortNo': (sortNo + 1),
+                  'stepId': v.id,
+                  'stepName': v.meta.name,
+                  'workTypeId': v.meta.prop === 'end' ? 0 : (v.meta.workTypeId || ''),
+                  'type': v.meta.type || '',
+                  'coordinate': v.coordinate.join(',')
+                })
+              }
+              this.getLineData(dataAll, dList, lList, v.id, (sortNo + 1))
+            }
+          }
+        })
+      },
+    }
   }
 </script>
 

+ 631 - 0
src/views/modules/production/scheduling-mould-details.vue

@@ -0,0 +1,631 @@
+<template>
+  <div class="production">
+    <el-dialog title="排产模板详情" 
+      width="70%" 
+      :close-on-click-modal="false" 
+      :visible.sync="visible">
+      <el-form :model="dataForm" :rules="dataRule" ref="dataForm" label-width="auto">
+        <el-row class="my-row">
+          <el-col :span="8">
+            <el-form-item label="产品" prop="productId">
+              <el-select
+                v-model="dataForm.productId"
+                :disabled="display"
+                @change="productChange"
+                remote
+                placeholder="请选择">
+                <el-option
+                  v-for="item in productList"
+                  :key="item.productId"
+                  :label="item.productName"
+                  :value="item.productId">
+                </el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+           <el-col :span="12">
+            <el-form-item label="模板名称" prop="mouldName">
+              <el-input placeholder="请输入模板名称" v-model="dataForm.mouldName" maxlength="30" show-word-limit></el-input>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row class="my-row" style="height: 350px; background-color: #efefef;">
+          <super-flow
+            v-if="visible"
+            ref="superFlow"
+            :node-list="nodeList"
+            :link-list="linkList"
+            :graph-menu="graphMenuList"
+            :node-menu="nodeMenuList"
+            :link-menu="linkMenuList"
+            :link-desc="linkDesc">
+          </super-flow>
+        </el-row>
+      </el-form>
+      <span slot="footer" class="dialog-footer">
+        <el-button @click="visible = false">取消</el-button>
+        <el-button type="primary" @click="dataFormSubmit()">确认提交</el-button>
+      </span>
+    </el-dialog>
+
+    <el-dialog
+        :title="drawerConf.title"
+        :visible.sync="drawerConf.visible"
+        :close-on-click-modal="false"
+        width="500px">
+      <el-form
+          @keyup.native.enter="settingSubmit"
+          @submit.native.prevent
+          v-show="drawerConf.type === drawerType.node"
+          ref="nodeSetting"
+          :rules="dataRule1"
+          :model="nodeSetting">
+        <el-row class="my-row">
+          <el-col :span="24">
+            <el-form-item
+                label="节点名称"
+                prop="name">
+              <el-input
+                  v-model="nodeSetting.name"
+                  placeholder="请输入节点名称"
+                  maxlength="30"
+                  disabled>
+              </el-input>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row class="my-row" >
+          <el-col :span="24" v-if="drawerConf.prop !== 'end'">
+            <el-form-item
+              label="操作人员"
+              prop="operatorIds">
+              <el-select
+                v-model="nodeSetting.operatorIds"
+                multiple
+                style="width:100%"
+                placeholder="请选择">
+                <el-option
+                  v-for="item in operatorList"
+                  :key="item.userId"
+                  :label="item.name"
+                  :value="item.userId">
+                </el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+      <span
+          slot="footer"
+          class="dialog-footer">
+        <el-button
+            @click="drawerConf.cancel">
+          取 消
+        </el-button>
+        <el-button
+            type="primary"
+            @click="settingSubmit">
+          确 定
+        </el-button>
+      </span>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+  import { getStepId } from '@/api/crafts'
+  import { getMouldDetail, getProductList, getMouldDetailByProductId, saveProdProductionMould, updateProdProductionMould } from '@/api/production'
+  import { workTypeMasterList } from '@/api/worktype'
+  import { uuid } from '../common/vue-super-flow/utils'
+  const drawerType = {
+    node: 0,
+    link: 1
+  }
+  export default {
+    name: 'scheduling-details',
+    data () {
+      return {
+        mouldId: '',
+        visible: false,
+        dataForm: {
+          mouldName: ''
+        },
+        drawerType,
+        operatorList: [],
+        operatorIds: [],
+        productList: [],
+        display: false,
+        drawerConf: {
+          title: '',
+          visible: false,
+          prop: '',
+          type: null,
+          info: null,
+          open: (type, info) => {
+            if (info.meta.workTypeId && info.meta.prop !== 'end') {
+              this.getOperatorList(info.meta.workTypeId);
+            }
+            
+            const conf = this.drawerConf
+            conf.visible = true
+            conf.type = type
+            conf.info = info
+            if (conf.type === drawerType.node) {
+              conf.title = '节点'
+              conf.prop = info.meta.prop
+              if (this.$refs.nodeSetting) this.$refs.nodeSetting.resetFields()
+              this.$set(this.nodeSetting, 'name', info.meta.name)
+              this.$set(this.nodeSetting, 'desc', info.meta.desc)
+              // this.$set(this.nodeSetting, 'prop', info.meta.prop)
+              this.$set(this.nodeSetting, 'type', info.meta.type)
+              this.$set(this.nodeSetting, 'workTypeId', info.meta.workTypeId)
+              this.$set(this.nodeSetting, 'operatorIds', info.meta.operatorIds)
+              
+            } else {
+              conf.title = '连线'
+              if (this.$refs.linkSetting) this.$refs.linkSetting.resetFields()
+              // this.$set(this.linkSetting, 'desc', info.meta ? info.meta.desc : '')
+            }
+          },
+          cancel: () => {
+            this.drawerConf.visible = false
+            if (this.drawerConf.type === drawerType.node) {
+              this.$refs.nodeSetting.clearValidate()
+            } else {
+              this.$refs.linkSetting.clearValidate()
+            }
+          }
+        },
+        linkSetting: {
+          desc: ''
+        },
+        nodeSetting: {
+          name: '',
+          desc: '',
+          type: 1,
+          workTypeId: ''
+        },
+        nodeList: [],
+        linkList: [],
+        graphMenuList: [
+          [
+            {
+              label: '开始节点',
+              disable: true,
+              selected: async (graph, coordinate) => {
+                const start = graph.nodeList.find(node => node.meta.prop === 'start')
+                let id = ''
+                await getStepId().then(({ data }) => {
+                  id = data.data.stepId
+                })
+                if (!start) {
+                  graph.addNode({
+                    width: 90,
+                    height: 50,
+                    coordinate: coordinate,
+                    id: id,
+                    meta: {
+                      prop: 'start',
+                      name: '开始节点'
+                    }
+                  })
+                }
+              }
+            },
+            {
+              label: '节点',
+              disable: true,
+              selected: async (graph, coordinate) => {
+                let id = ''
+                await getStepId().then(({ data }) => {
+                  id = data.data.stepId
+                })
+                graph.addNode({
+                  width: 120,
+                  height: 70,
+                  coordinate: coordinate,
+                  id: id,
+                  meta: {
+                    prop: 'condition',
+                    name: '节点名称'
+                  }
+                })
+              }
+            },
+            {
+              label: '结束节点',
+              disable: true,
+              selected: async (graph, coordinate) => {
+                let id = ''
+                await getStepId().then(({ data }) => {
+                  id = data.data.stepId
+                })
+                graph.addNode({
+                  width: 90,
+                  height: 50,
+                  coordinate: coordinate,
+                  id: id,
+                  meta: {
+                    prop: 'end',
+                    name: '结束节点'
+                  }
+                })
+              }
+            }
+          ],
+          [
+            {
+              label: '完成',
+              selected: (graph, coordinate) => {
+                graph.selectAll()
+                this.datas = graph
+                console.log(graph)
+              }
+            }
+          ]
+        ],
+        nodeMenuList: [
+          [
+            {
+              label: '删除',
+              disable: true,
+              hidden (node) {
+                return node.meta.prop === 'start'
+              },
+              selected (node, coordinate) {
+                node.remove()
+              }
+            }
+          ],
+          [
+            {
+              label: '编辑',
+              selected: (node, coordinate) => {
+                this.drawerConf.open(drawerType.node, node)
+              }
+            }
+          ]
+        ],
+        linkMenuList: [
+          [
+            {
+              label: '删除',
+              selected: (link, coordinate) => {
+                link.remove()
+              }
+            }
+          ]
+        ],
+        datas: {},
+        dataRule: {
+          mouldName: [{required: true, message: '请输入模板名称', trigger: 'blur'}]
+        },
+        dataRule1: {
+           operatorIds: [{ required: true, message: '操作人员不能为空', trigger: 'change' }]
+        }
+      }
+    },
+    methods: {
+      // 初始化产品名称列表
+      async initProductList() {
+        getProductList().then(({data}) => {
+          if(data && data.code === '200') {
+            data.data.forEach(item => {
+              this.productList.push(item)
+            })
+          }
+        })
+      },
+      // 初始化表单
+      async init (id, disable) {
+        this.visible = true
+        this.display = disable
+        this.nodeList = []
+        this.linkList = []
+        this.mouldId = id;
+        if(!disable){
+          await this.initProductList();
+        }
+
+        await getMouldDetail(id).then(async ({data}) => {
+          if (data && data.code === '200') {
+            this.dataForm = data.data
+            
+            // 图纸
+            if (this.dataForm.proTechnologyStepList) {
+              const dataline = []
+              const datanode = []
+              await this.dataForm.proTechnologyStepList.forEach((v, i) => {
+                // eslint-disable-next-line no-unused-vars
+                const sortNo = []
+                const datas = v.sort((a, b) => Number(a['sortNo']) - Number(b['sortNo']))
+                let length = datas.length
+                datas.forEach((item, index) => {
+                  const find = datanode.find(map => map.id === item.stepId)
+                  if (!find) {
+                    datanode.push({
+                      id: item.stepId,
+                      width: (index === 0 || item.workTypeId === '0') ? 90 : 120,
+                      height: (index === 0 || item.workTypeId === '0') ? 50 : 70,
+                      coordinate: item.coordinate.split(','),
+                      meta: {
+                        name: item.stepName,
+                        desc: item.operatorName,
+                        prop: index === 0 ? 'start' : (index === length - 1) ? 'end' : 'condition',
+                        notes: item.notes || '',
+                        workTypeId: item.workTypeId || '',
+                        type: item.type || '',
+                        operatorIds: item.operatorId.split(',')
+                      }
+                    })
+                  }
+                  const id = item.stepId
+                  if ((index + 1) < datas.length) {
+                    if (datas[index + 1]) {
+                      dataline.push({
+                        id: uuid('link'),
+                        startId: id,
+                        endId: datas[index + 1].stepId,
+                        meta: '',
+                        startAt: [(index === 0 || item.workTypeId === '0') ? 90 : 120, (index === 0 || item.workTypeId === '0') ? 25 : 35],
+                        endAt: [0, (index === 0 || item.workTypeId === '0') ? 25 : 35]
+                      })
+                    }
+                  }
+                })
+              })
+              
+              this.$nextTick(() => {
+                setTimeout(() => {
+                  this.nodeList = datanode
+                  this.linkList = dataline
+                }, 200)
+              })
+            }
+          }
+        })
+      },
+      //根据产品ID查询步骤详情
+      async productChange(productId) {
+        getMouldDetailByProductId(productId).then(async ({data}) => {
+           if (data && data.code === '200') {
+            this.dataForm = {...this.dataForm, proTechnologyStepLists :data.data}
+            // 图纸
+            if (this.dataForm.proTechnologyStepLists) {
+              const dataline = []
+              const datanode = []
+              await this.dataForm.proTechnologyStepLists.forEach((v, i) => {
+                // eslint-disable-next-line no-unused-vars
+                const sortNo = []
+                const datas = v.sort((a, b) => Number(a['sortNo']) - Number(b['sortNo']))
+                let length = datas.length
+                datas.forEach((item, index) => {
+                  const find = datanode.find(map => map.id === item.stepId)
+                  if (!find) {
+                    datanode.push({
+                      id: item.stepId,
+                      width: (index === 0 || item.workTypeId === '0') ? 90 : 120,
+                      height: (index === 0 || item.workTypeId === '0') ? 50 : 70,
+                      coordinate: item.coordinate.split(','),
+                      meta: {
+                        name: item.stepName,
+                        prop: index === 0 ? 'start' : (index === length - 1) ? 'end' : 'condition',
+                        notes: item.notes || '',
+                        workTypeId: item.workTypeId || '',
+                        type: item.type || ''
+                      }
+                    })
+                  }
+                  const id = item.stepId
+                  if ((index + 1) < datas.length) {
+                    if (datas[index + 1]) {
+                      dataline.push({
+                        id: uuid('link'),
+                        startId: id,
+                        endId: datas[index + 1].stepId,
+                        meta: '',
+                        startAt: [(index === 0 || item.workTypeId === '0') ? 90 : 120, (index === 0 || item.workTypeId === '0') ? 25 : 35],
+                        endAt: [0, (index === 0 || item.workTypeId === '0') ? 25 : 35]
+                      })
+                    }
+                  }
+                })
+              })
+              
+              this.$nextTick(() => {
+                setTimeout(() => {
+                  this.nodeList = datanode
+                  this.linkList = dataline
+                }, 200)
+              })
+            }
+          }
+        })
+      },
+      // 按工种ID查询操作人列表
+      getOperatorList(workTypeId) {
+        workTypeMasterList(workTypeId).then(({data}) => {
+          if (data && data.code === '200') {
+            this.operatorList = []
+            data.data.forEach(item => {
+              this.operatorList.push(item);
+            })
+          }
+        })
+      },
+      linkDesc (link) {
+        return link.meta ? link.meta.desc : ''
+      },
+      settingSubmit () {
+        this.$refs['nodeSetting'].validate((valid) => {
+          if (valid) {
+            const conf = this.drawerConf
+            if (this.drawerConf.type === drawerType.node) {
+              if (!conf.info.meta) conf.info.meta = {}
+              Object.keys(this.nodeSetting).forEach(key => {
+                if (key == 'operatorIds') {
+                  let idList = this.nodeSetting[key];
+                  let nameList = [];
+                  idList.forEach(id => {
+                    let name = this.operatorList.find(item => {
+                      return item.userId === id
+                    }).name
+                    nameList.push(name)
+                  })
+                 
+                  this.$set(conf.info.meta, 'desc', nameList.join(','))
+                }
+                this.$set(conf.info.meta, key, this.nodeSetting[key])
+              })
+              this.$refs.nodeSetting.resetFields()
+            } else {
+              if (!conf.info.meta) conf.info.meta = {}
+              Object.keys(this.linkSetting).forEach(key => {
+                this.$set(conf.info.meta, key, this.linkSetting[key])
+              })
+              this.$refs.linkSetting.resetFields()
+            }
+            conf.visible = false
+          }
+        })
+      },
+      dataFormSubmit () {
+        if (!this.datas.nodeList) {
+          this.$message.error('请先完成流程图!')
+          return
+        }
+        if (this.datas.nodeList.length <= 2) {
+          this.$message.error('请先完成流程图!')
+          return
+        }
+
+        this.$refs['dataForm'].validate((valid) => {
+          if(valid) {
+            const proTechnologyStepLists = []
+         
+            let productionPlanSteps = []
+            for (let index = 0; index < this.datas.nodeList.length; index++) {
+              const v = this.datas.nodeList[index];
+               let tmp = v.meta.operatorIds || []
+              if(v.meta.prop !== 'end' && tmp.length == 0){
+                this.$message.error(`请选择 ${v.meta.name} 的操作人员!`)
+                return
+              }
+              productionPlanSteps.push({stepId: v.id, operatorId: tmp.join(',')})
+            }
+
+            let submitData = {
+              mouldName: this.dataForm.mouldName,
+              productId: this.dataForm.productId,
+              stepList: productionPlanSteps
+            }
+
+            if(this.mouldId === 0) {
+              // 新增
+              saveProdProductionMould(submitData).then(({data}) => {
+                if (data && data.code === '200') {
+                    this.$message({
+                    message: '操作成功',
+                    type: 'success',
+                    duration: 1500,
+                    onClose: () => {
+                      this.$emit('refreshDataList')
+                      this.visible = false
+                    }
+                  })
+                } else {
+                  this.$message.error(data.msg)
+                }
+              })
+            } else {
+              // 更新
+              submitData.mouldId = this.mouldId;
+              updateProdProductionMould(submitData).then(({data}) => {
+                if (data && data.code === '200') {
+                    this.$message({
+                    message: '操作成功',
+                    type: 'success',
+                    duration: 1500,
+                    onClose: () => {
+                      this.$emit('refreshDataList')
+                      this.visible = false
+                    }
+                  })
+                } else {
+                  this.$message.error(data.msg)
+                }
+              })
+            }
+          }
+        })
+      },
+      getLineData (dataAll, dList, lList, id, sortNo) {
+        const _l = []
+        lList.forEach(l => {
+          if (l.start.id === id) {
+            _l.push(l.end.id)
+          }
+        })
+        if (!sortNo) {
+          const data = dList.filter(v => v.meta.prop === 'start')[0]
+          _l.forEach(item => {
+            dataAll.push([{
+              'notes': data.meta.desc || '',
+              'sortNo': 0,
+              'stepId': data.id,
+              'stepName': data.meta.name,
+              'workTypeId': data.meta.workTypeId || null,
+              'type': data.meta.type || null,
+              'coordinate': data.coordinate.join(','),
+              'operatorIds': data.meta.operatorIds
+            }])
+          })
+        }
+        dList.forEach(v => {
+          const _i = _l.indexOf(v.id)
+          if (_i > -1) {
+            if (!v.meta.workTypeId && v.meta.prop !== 'end') {
+              this.$message.error('完善节点工种')
+              return
+            }
+            if (!sortNo) {
+              dataAll[_i].push({
+                'notes': v.meta.desc || '',
+                'sortNo': (sortNo + 1),
+                'stepId': v.id,
+                'stepName': v.meta.name,
+                'workTypeId': v.meta.workTypeId || null,
+                'type': v.meta.type || null,
+                'coordinate': v.coordinate.join(',')
+              })
+              this.getLineData(dataAll[_i], dList, lList, v.id, (sortNo + 1))
+            } else {
+              let _has = false
+              dataAll.forEach(items => {
+                if (items.stepId === v.id) {
+                  _has = true
+                }
+              })
+              if (!_has) {
+                dataAll.push({
+                  'notes': v.meta.desc || '',
+                  'sortNo': (sortNo + 1),
+                  'stepId': v.id,
+                  'stepName': v.meta.name,
+                  'workTypeId': v.meta.prop === 'end' ? 0 : (v.meta.workTypeId || ''),
+                  'type': v.meta.type || '',
+                  'coordinate': v.coordinate.join(',')
+                })
+              }
+              this.getLineData(dataAll, dList, lList, v.id, (sortNo + 1))
+            }
+          }
+        })
+      },
+    }
+  }
+</script>
+
+<style scoped>
+
+</style>

+ 152 - 0
src/views/modules/production/scheduling-mould.vue

@@ -0,0 +1,152 @@
+<!-- 排产模板 -->
+<template>
+  <div class="production">
+    <el-form :inline="true" :model="dataForm" @keyup.enter.native="queryPage()">
+      <el-form-item label="产品名称">
+        <el-input v-model="dataForm.productName" placeholder="产品名称" clearable/>
+      </el-form-item>
+      <el-form-item label="模板名称">
+        <el-input v-model="dataForm.mouldName" placeholder="模板名称" clearable/>
+      </el-form-item>
+      <el-form-item>
+        <el-button @click="queryPage()">查询</el-button>
+        <el-button @click="detail(0, false)">新增模板</el-button>
+      </el-form-item>
+    </el-form>
+    <el-table
+      :data="dataList"
+      border
+      v-loading="dataListLoading"
+      style="width: 100%;">
+      <el-table-column
+        label="序号"
+        type="index"
+        width="50"
+        align="center">
+      </el-table-column>
+      <el-table-column
+        prop="mouldName"
+        header-align="center"
+        align="center"
+        label="模板名称">
+      </el-table-column>
+      <el-table-column
+        prop="productName"
+        header-align="center"
+        align="center"
+        label="产品名称">
+      </el-table-column>
+      <el-table-column
+        prop="creatorName"
+        header-align="center"
+        align="center"
+        label="创建人">
+      </el-table-column>
+      <el-table-column
+        prop="createTime"
+        header-align="center"
+        align="center"
+        min-width="160"
+        label="创建时间">
+      </el-table-column>
+      <el-table-column
+        fixed="right"
+        header-align="center"
+        align="center"
+        width="150"
+        label="操作">
+        <template slot-scope="scope">
+          <el-button type="text" size="small" @click="detail(scope.row.mouldId, true)">编辑</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <el-pagination
+      @size-change="sizeChangeHandle"
+      @current-change="currentChangeHandle"
+      :current-page="pageIndex"
+      :page-sizes="[10, 20, 50, 100]"
+      :page-size="pageSize"
+      :total="totalPage"
+      layout="total, sizes, prev, pager, next, jumper">
+    </el-pagination>
+    <!-- 弹窗, 新增 / 修改 -->
+    <detail v-if="detailsVisible" ref="details" @refreshDataList="getDataList"/>
+  </div>
+</template>
+
+<script>
+  import Detail from './scheduling-mould-details'
+  import { getMouldList } from '@/api/production'
+  export default {
+    name: 'scheduling',
+    components: {Detail},
+    data () {
+      return {
+        detailsVisible: false,
+        dataForm: {},
+        dataList: [],
+        pageIndex: 1,
+        pageSize: 10,
+        totalPage: 0,
+        dataListLoading: false,
+        dataListSelections: []
+      }
+    },
+    created () {
+      this.getDataList()
+    },
+    methods: {
+      // 查询
+      queryPage () {
+        this.pageIndex = 1
+        this.getDataList()
+      },
+      // 获取数据列表
+      getDataList () {
+        this.dataListLoading = true
+        let submitData = {
+          'current': this.pageIndex,
+          'size': this.pageSize,
+          'mouldName': this.dataForm.mouldName ? this.dataForm.mouldName : null,
+          'productName': this.dataForm.productName ? this.dataForm.productName : null
+        }
+        getMouldList(submitData).then(({data}) => {
+          if (data && data.code === '200') {
+            this.dataList = data.data.records
+            this.totalPage = Number(data.data.total)
+          } else {
+            this.dataList = []
+            this.totalPage = 0
+          }
+          this.dataListLoading = false
+        })
+      },
+      // 每页数
+      sizeChangeHandle (val) {
+        this.pageSize = val
+        this.pageIndex = 1
+        this.getDataList()
+      },
+      // 当前页
+      currentChangeHandle (val) {
+        this.pageIndex = val
+        this.getDataList()
+      },
+      // 多选
+      selectionChangeHandle (val) {
+        this.dataListSelections = val
+      },
+      // 新增 / 修改
+      detail (id, disable) {
+        this.detailsVisible = true
+        this.$nextTick(() => {
+          this.$refs.details.init(id, disable)
+        })
+      }
+    }
+  }
+</script>
+
+<style scoped>
+
+</style>

+ 205 - 5
src/views/modules/tech/ctafts-add-or-detail.vue

@@ -38,8 +38,41 @@
         <el-input v-model="dataForm.notes" :disabled="display" placeholder="备注说明"></el-input>
       </el-form-item>
       <el-form-item label="" label-width="0px">
-        <upload-component :display="display" :title="'文件上传'" :accept="'*'" :file-obj-list="fileList" @uploadSuccess="uploadSuccess"/>
+        <upload-component :display="display" :title="'附件'" :accept="'*'" :file-obj-list="fileList" @uploadSuccess="uploadSuccess"/>
       </el-form-item>
+
+      <!-- <div class="super-flow-demo1">
+        <div class="node-container">
+          <span
+              class="node-item"
+              v-for="item in nodeItemList"
+              @mousedown="evt => nodeItemMouseDown(evt, item.value)">
+            {{ item.label }}
+          </span>
+        </div>
+        <div
+            class="flow-container"
+            ref="flowContainer">
+          <super-flow
+              ref="superFlow"
+              :graph-menu="graphMenuList"
+              :node-menu="nodeMenuList"
+              :link-menu="linkMenuList"
+              :link-base-style="linkBaseStyle"
+              :link-style="linkStyle"
+              :link-desc="linkDesc">
+            <template v-slot:node="{meta}">
+              <div
+                  @mouseup="nodeMouseUp"
+                  @click="nodeClick"
+                  class="flow-node ellipsis">
+                {{ meta.name }}
+              </div>
+            </template>
+          </super-flow>
+        </div>
+      </div> -->
+
       <el-form-item label="工艺步骤" prop="nodeList">
       </el-form-item>
       <el-row class="my-row" style="height: 350px; background-color: #efefef;">
@@ -106,7 +139,7 @@
               </el-select>
             </el-form-item>
           </el-col>
-          <el-col v-if="drawerConf.prop !== 'start' && drawerConf.prop !== 'end'" :span="12">
+          <el-col v-if="drawerConf.prop !== 'end'" :span="12">
             <el-form-item
               label="工种"
               prop="workTypeId">
@@ -371,6 +404,61 @@
         previewVisible: false,
         optionsProducts: [],
         optionLevel: [],
+        nodeItemList: [
+          {
+            label: '开始节点',
+            value: () => ({
+              width: 120,
+              height: 40,
+              meta: {
+                prop: 'start',
+                name: '开始节点'
+              }
+            })
+          },
+          {
+            label: '节点',
+            value: () => ({
+              width: 120,
+              height: 40,
+              meta: {
+                prop: 'condition',
+                name: '节点'
+              }
+            })
+          },
+          {
+            label: '结束节点',
+            value: () => ({
+              width: 120,
+              height: 40,
+              meta: {
+                prop: 'end',
+                name: '结束节点'
+              }
+            })
+          }
+        ],
+        dragConf: {
+          isDown: false,
+          isMove: false,
+          offsetTop: 0,
+          offsetLeft: 0,
+          clientX: 0,
+          clientY: 0,
+          ele: null,
+          info: null
+        },
+        linkBaseStyle: {
+          color: '#666666',           // line 颜色
+          hover: '#FF0000',           // line hover 的颜色
+          textColor: '#666666',       // line 描述文字颜色
+          textHover: '#FF0000',       // line 描述文字 hover 颜色
+          font: '14px Arial',         // line 描述文字 字体设置 参考 canvas font
+          dotted: false,              // 是否是虚线
+          lineDash: [4, 4],           // 虚线时生效
+          background: 'rgba(255,255,255,0.6)'    // 描述文字背景色
+        },
         dataRule: {
           techName: [{ required: true, message: '工艺名称不能为空', trigger: 'blur' }],
           techVersion: [{ required: true, message: '工艺版本不能为空', trigger: 'blur' }],
@@ -476,6 +564,7 @@
         this.$refs.dataForm.validateField(type)
       },
       getLineData (dataAll, dList, lList, id, sortNo) {
+        debugger
         const _l = []
         lList.forEach(l => {
           if (l.start.id === id) {
@@ -490,7 +579,7 @@
               'sortNo': 0,
               'stepId': data.id,
               'stepName': data.meta.name,
-              'workTypeId': data.meta.workTypeId || null,
+              'workTypeId':  v.meta.prop === 'end' ? '4' : (data.meta.workTypeId || null),
               'type': data.meta.type || null,
               'coordinate': data.coordinate.join(',')
             }])
@@ -509,7 +598,7 @@
                 'sortNo': (sortNo + 1),
                 'stepId': v.id,
                 'stepName': v.meta.name,
-                'workTypeId': v.meta.workTypeId || null,
+                'workTypeId': v.meta.prop === 'end' ? '4' : (v.meta.workTypeId || null),
                 'type': v.meta.type || null,
                 'coordinate': v.coordinate.join(',')
               })
@@ -527,7 +616,7 @@
                   'sortNo': (sortNo + 1),
                   'stepId': v.id,
                   'stepName': v.meta.name,
-                  'workTypeId': v.meta.prop === 'end' ? 0 : (v.meta.workTypeId || ''),
+                  'workTypeId': v.meta.prop === 'end' ? 4 : (v.meta.workTypeId || ''),
                   'type': v.meta.type || '',
                   'coordinate': v.coordinate.join(',')
                 })
@@ -574,6 +663,19 @@
               this.$message.error('请先完成流程图!')
               return
             }
+
+            // 填充附件
+            let fList = this.fileList
+            if (fList.length > 0) {
+              this.dataForm.attachList = []
+              for (let i = 0; i < fList.length; i++) {
+                this.dataForm.attachList.push({
+                  fileName: fList[i].name,
+                  url: fList[i].url
+                })
+              }
+            }
+
             this.$http({
               url: this.$http.adornUrl(`/biz-service/technology/submit`),
               method: 'post',
@@ -634,6 +736,7 @@
         this.$refs['nodeSetting'].validate((valid) => {
           if (valid) {
             const conf = this.drawerConf
+            // 节点
             if (this.drawerConf.type === drawerType.node) {
               if (!conf.info.meta) conf.info.meta = {}
               Object.keys(this.nodeSetting).forEach(key => {
@@ -641,6 +744,7 @@
               })
               this.$refs.nodeSetting.resetFields()
             } else {
+              // 连线
               if (!conf.info.meta) conf.info.meta = {}
               Object.keys(this.linkSetting).forEach(key => {
                 this.$set(conf.info.meta, key, this.linkSetting[key])
@@ -653,6 +757,102 @@
       },
       uploadSuccess (fileList) {
         this.fileList = fileList
+      },
+      docMousemove ({ clientX, clientY }) {
+        const conf = this.dragConf
+
+        if (conf.isMove) {
+          conf.ele.style.top = clientY - conf.offsetTop + 'px'
+          conf.ele.style.left = clientX - conf.offsetLeft + 'px'
+        } else if (conf.isDown) {
+          // 鼠标移动量大于 5 时 移动状态生效
+          conf.isMove =
+              Math.abs(clientX - conf.clientX) > 5 || Math.abs(clientY - conf.clientY) > 5
+        }
+      },
+      docMouseup ({ clientX, clientY }) {
+        const conf = this.dragConf
+        conf.isDown = false
+
+        if (conf.isMove) {
+          const {
+            top,
+            right,
+            bottom,
+            left
+          } = this.$refs.flowContainer.getBoundingClientRect()
+
+          // 判断鼠标是否进入 flow container
+          if (clientX > left && clientX < right && clientY > top && clientY < bottom) {
+            // 获取拖动元素左上角相对 super flow 区域原点坐标
+            const coordinate = this.$refs.superFlow.getMouseCoordinate(
+                clientX - conf.offsetLeft,
+                clientY - conf.offsetTop
+            )
+
+            // 添加节点
+            this.$refs.superFlow.addNode({
+              coordinate,
+              ...conf.info
+            })
+          }
+
+          conf.isMove = false
+        }
+
+        if (conf.ele) {
+          conf.ele.remove()
+          conf.ele = null
+        }
+      },
+      nodeItemMouseDown (evt, infoFun) {
+        const {
+          clientX,
+          clientY,
+          currentTarget
+        } = evt
+
+        const {
+          top,
+          left
+        } = evt.currentTarget.getBoundingClientRect()
+
+        const conf = this.dragConf
+        const ele = currentTarget.cloneNode(true)
+
+        Object.assign(this.dragConf, {
+          offsetLeft: clientX - left,
+          offsetTop: clientY - top,
+          clientX: clientX,
+          clientY: clientY,
+          info: infoFun(),
+          ele,
+          isDown: true
+        })
+
+        ele.style.position = 'fixed'
+        ele.style.margin = '0'
+        ele.style.top = clientY - conf.offsetTop + 'px'
+        ele.style.left = clientX - conf.offsetLeft + 'px'
+
+        this.$el.appendChild(this.dragConf.ele)
+      },
+      linkStyle (link) {
+        if (link.meta && link.meta.desc === '1') {
+          return {
+            color: 'red',
+            hover: '#FF00FF',
+            dotted: true
+          }
+        } else {
+          return {}
+        }
+      },
+      nodeMouseUp (evt) {
+        evt.preventDefault()
+      },
+      nodeClick () {
+        console.log(arguments)
       }
     }
   }