Преглед на файлове

feat(工位节拍管理): 添加工序选择弹窗和工位选择功能

yusheng преди 7 месеца
родител
ревизия
8fbf7f040c

+ 110 - 0
src/api/aps/index.js

@@ -0,0 +1,110 @@
+import request from '@/utils/request';
+
+/**
+ * 列表
+ */
+export async function getList(data) {
+  const res = await request.post('/aps/productionplan/page', data);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+// aps生产计划获取产品的多个bom版本
+export async function bomListByPlan(params) {
+  const res = await request.get(`/main/bomCategory/bomListByPlan`, { params });
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+// 根据bom 获取工艺路线
+
+export async function bomRoutingList(id) {
+  const res = await request.get(`/main/bomCategory/bomRoutingList/${id}`);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+//工艺路线工序实例-分页
+export async function taskinstanceList(data) {
+  const res = await request.post(
+    '/main/producerouting/taskinstance/page',
+    data
+  );
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+}
+//工序分页
+export async function producetask(params) {
+  const res = await request.get('/main/producetask/page', { params });
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+}
+
+// 根据产品查询BOM分类
+export async function findBomSalesorderCategoryId(categoryId) {
+  const res = await request.get(
+    `/aps/salesorder/findBomCategoryByCategoryId/${categoryId}`
+  );
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+// 根据计划id查询物料信息
+export async function findMaterialInfoSalesorder(params) {
+  const res = await request.get(
+    '/aps/salesorder/findMaterialInfoBySalesOrderId',
+    {
+      params
+    }
+  );
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+// 根据产品ID查询物料信息
+export async function findMaterialInfoByCategory(params) {
+  const res = await request.get(
+    '/aps/salesorder/findMaterialInfoByCategoryId',
+    {
+      params
+    }
+  );
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+// 根据产品ID查询物料信息
+export async function getMaterialInfo(params) {
+  const res = await request.get('/eom/common/getMaterialInfo', {
+    params
+  });
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+export async function getStockList(params) {
+  const res = await request.get('/wms/outindetailtwo/page', { params });
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+export async function getCurrentList(data) {
+  const res = await request.post('/eom/purchaseorder/getZtDetail', data);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}

+ 346 - 0
src/components/procedure/taskinstanceDialog.vue

@@ -0,0 +1,346 @@
+<!-- 用户编辑弹窗 -->
+<template>
+  <ele-modal
+    width="1060px"
+    :visible="visible"
+    :append-to-body="true"
+    :close-on-click-modal="false"
+    custom-class="ele-dialog-form"
+    title="工序"
+    :maxable="true"
+    :resizable="true"
+    @update:visible="updateVisible(false)"
+  >
+    <header-title title="基本信息"> </header-title>
+    <el-form ref="form" :model="form" :rules="rules" label-width="100px">
+      <el-row>
+        <el-col :span="8">
+          <el-form-item label="加工方式:" prop="produceType">
+            <el-select
+              v-model="form.produceType"
+              style="width: 100%"
+              @change="changeProduceType"
+            >
+              <el-option
+                v-for="item of producedList"
+                :key="item.code"
+                :label="item.name"
+                :value="item.code"
+              ></el-option>
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="BOM版本:" prop="bomCategoryId">
+            <el-select
+              v-model="form.bomCategoryId"
+              style="width: 100%"
+              @change="changeBomId"
+            >
+              <el-option
+                v-for="item of bomVersionList"
+                :key="item.id"
+                :label="item.name + '(V' + item.versions + '.0)'"
+                :value="item.id"
+              ></el-option>
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="工艺路线:" prop="produceRoutingId">
+            <!--  @click.native="openVersion"   -->
+            <el-select
+              v-model="form.produceRoutingId"
+              style="width: 100%"
+              @change="changeRoute"
+            >
+              <el-option
+                v-for="item of routingList"
+                :key="item.id"
+                :label="item.name"
+                :value="item.id"
+              ></el-option>
+            </el-select>
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+
+    <ele-pro-table
+      ref="table"
+      :needPage="false"
+      :columns="columns"
+      :datasource="datasource"
+      @cell-click="cellClick"
+      v-if="form.produceRoutingId"
+      row-key="id"
+    >
+      <template v-slot:action="{ row }">
+        <el-radio class="radio" v-model="radio" :label="row.id"
+          ><i></i
+        ></el-radio>
+      </template>
+    </ele-pro-table>
+    <ele-pro-table
+      ref="table"
+      :columns="columns1"
+      :datasource="datasource"
+      @cell-click="cellClick"
+      v-if="!form.produceRoutingId"
+      row-key="id"
+    >
+      <template v-slot:action="{ row }">
+        <el-radio class="radio" v-model="radio" :label="row.id"
+          ><i></i
+        ></el-radio>
+      </template>
+    </ele-pro-table>
+    <template v-slot:footer>
+      <el-button type="primary" @click="save"> 确定 </el-button>
+      <el-button @click="updateVisible(false)"> 返回 </el-button>
+    </template>
+  </ele-modal>
+</template>
+
+<script>
+  import {
+    bomListByPlan,
+    bomRoutingList,
+    taskinstanceList,
+    producetask
+  } from '@/api/aps';
+
+  export default {
+    components: {},
+    props: {
+      // 弹窗是否打开
+      visible: Boolean
+    },
+    data() {
+      const defaultForm = {
+        produceType: '',
+        bomCategoryId: '',
+        produceRoutingId: ''
+      };
+      return {
+        defaultForm,
+        radio: null,
+        current: {},
+        producedList: [
+          { code: 2, name: '加工(MBOM)' },
+          { code: 3, name: '装配(ABOM)' }
+        ],
+        index: '',
+        bomVersionList: [],
+        routingList: [],
+        // 表单数据
+        form: { ...defaultForm },
+        // 表单验证规则
+        rules: {},
+        versionList: [],
+        // 提交状态
+        loading: false,
+        // 是否是修改
+        isUpdate: false,
+        columns1: [
+          {
+            action: 'action',
+            slot: 'action',
+            align: 'center',
+            label: '选择',
+            width: 80
+          },
+          {
+            prop: 'code',
+            label: '工序编码',
+            // sortable: 'custom',
+            showOverflowTooltip: true,
+            align: 'center',
+            minWidth: 110
+          },
+
+          {
+            prop: 'name',
+            label: '工序名称',
+            showOverflowTooltip: true,
+            align: 'center',
+            minWidth: 110
+          },
+          {
+            align: 'center',
+            prop: 'controlName',
+            label: '工序控制码',
+            showOverflowTooltip: true,
+            minWidth: 110
+          },
+          {
+            prop: 'workCenterName',
+            label: '所属工作中心',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 110
+          }
+        ],
+        columns: [
+          {
+            action: 'action',
+            slot: 'action',
+            align: 'center',
+            label: '选择',
+            width: 80
+          },
+          {
+            prop: 'orderNum',
+            label: '排序',
+            align: 'center',
+            slot: 'orderNum',
+            width: 80
+          },
+          {
+            prop: 'code',
+            label: '工序编码',
+            // sortable: 'custom',
+            showOverflowTooltip: true,
+            align: 'center',
+            minWidth: 110
+          },
+
+          {
+            prop: 'name',
+            label: '工序名称',
+            showOverflowTooltip: true,
+            align: 'center',
+            minWidth: 110
+          },
+          {
+            align: 'center',
+            prop: 'controlName',
+            label: '工序控制码',
+            showOverflowTooltip: true,
+            minWidth: 110
+          },
+          {
+            prop: 'workCenterName',
+            label: '所属工作中心',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 110
+          }
+        ]
+      };
+    },
+    computed: {
+      // 是否开启响应式布局
+      styleResponsive() {
+        return this.$store.state.theme.styleResponsive;
+      }
+    },
+    methods: {
+      open(row, index) {
+        this.data = row;
+        this.index = index;
+      },
+      changeProduceType() {
+        this.form.bomCategoryId = '';
+        this.form['bomCategoryName'] = '';
+        this.form['bomCategoryVersions'] = '';
+
+        this.bomVersionList = [];
+        this.routingList = [];
+        this.form.produceRoutingId = '';
+        this.form.produceRoutingName = '';
+        this.form.produceVersionName = '';
+        this.bomListVersion();
+      },
+      bomListVersion() {
+        let param = {
+          bomType: this.form.produceType,
+          categoryId: this.data.productId
+        };
+        bomListByPlan(param).then((res) => {
+          this.bomVersionList = res || [];
+        });
+      },
+      changeBomId() {
+        this.routingList = [];
+        this.form.produceRoutingId = '';
+        this.form.produceRoutingName = '';
+        this.form.produceVersionName = '';
+        this.bomVersionList.forEach((f) => {
+          if (f.id == this.form.bomCategoryId) {
+            this.$set(this.form, 'bomCategoryName', f.name);
+            this.$set(this.form, 'bomCategoryVersions', f.versions);
+          }
+        });
+        this.getPlanRouting();
+      },
+      getPlanRouting() {
+        bomRoutingList(this.form.bomCategoryId).then((res) => {
+          this.routingList = res || [];
+        });
+      },
+      changeRoute(val) {
+        if (!val) return;
+        this.routingList.forEach((f) => {
+          if (f.id == this.form.produceRoutingId) {
+            this.$set(this.form, 'produceRoutingName', f.name);
+            this.$set(this.form, 'produceVersionName', f.version);
+          }
+        });
+        this.$refs.table.reload();
+      },
+      /* 表格数据源 */
+      async datasource({ page, limit, where, order }) {
+
+        if (this.form.produceRoutingId) {
+          const res = await taskinstanceList({
+            routingId: this.form.produceRoutingId,
+            isDetail: true,
+            pageNum: 1,
+            size: -1
+          });
+
+          let arr = res.list.map((it) => {
+            it.detail.orderNum = it.orderNum;
+            return it.detail;
+          });
+          return {
+            list: arr
+          };
+        } else {
+       
+          return await producetask({
+            pageNum: page,
+            size: limit,
+            ...where
+          });
+          
+        }
+
+        //this.$refs.table.reload()
+      },
+      // 单击获取id
+      cellClick(row) {
+        this.current = row;
+        this.radio = row.id;
+      },
+      /* 保存编辑 */
+      save() {
+        if (!this.radio) return this.$message.warning('请选择工序');
+        this.$emit('saveTaskInstance', {
+          ...this.current,
+          index: this.index,
+          produceRoutingId: this.form.produceRoutingId
+        });
+        this.updateVisible(false);
+      },
+
+      /* 更新visible */
+      updateVisible(value) {
+        this.$emit('update:visible', value);
+      }
+    },
+
+    watch: {}
+  };
+</script>

+ 234 - 0
src/views/factoryModel/station/components/stationBeatDialog.vue

@@ -0,0 +1,234 @@
+<template>
+  <ele-modal
+    custom-class="ele-dialog-form long-dialog-form"
+    :centered="true"
+    :visible.sync="addOrEditDialogFlag"
+    title="选择工位"
+    :append-to-body="true"
+    :close-on-click-modal="false"
+    width="80%"
+    :before-close="cancel"
+    :maxable="true"
+    :resizable="true"
+  >
+    <el-card shadow="never" v-loading="loading">
+      <search-table @search="reload"></search-table>
+      <!-- 数据表格 -->
+      <ele-pro-table
+        ref="table"
+        :columns="columns"
+        :datasource="datasource"
+        :page-size="pageSize"
+        @columns-change="handleColumnChange"
+        :cache-key="cacheKeyUrl"
+        @filter-change="selectType"
+        :initLoad="false"
+        :selection.sync="selection"
+      >
+      </ele-pro-table>
+    </el-card>
+
+    <div slot="footer">
+      <el-button type="primary" @click="save">确定</el-button>
+      <el-button @click="cancel">返回</el-button>
+    </div>
+  </ele-modal>
+</template>
+<script>
+  import searchTable from './search.vue';
+  import { getFactoryworkstation, getFactoryarea } from '@/api/factoryModel';
+  import { listOrganizations } from '@/api/system/organization';
+  import tabMixins from '@/mixins/tableColumnsMixin';
+
+  export default {
+    components: { searchTable },
+    mixins: [tabMixins],
+    computed: {},
+    props: {
+      disabledTableList: {
+        type: Array,
+        default: () => []
+      }
+    },  
+    data() {
+      return {
+        addOrEditDialogFlag: false,
+        cacheKeyUrl: 'ef00833a-factoryModel-station',
+        selection: [],
+        columns: [
+          {
+            width: 45,
+            type: 'index',
+            columnKey: 'index',
+            align: 'center'
+          },
+          {
+            width: 45,
+            type: 'selection',
+            columnKey: 'selection',
+            align: 'center',
+            selectable: (row, index) => {
+              return !this.disabledTableList
+                .map((item) => item.workstationId)
+                .includes(row.id);
+            }
+          },
+          {
+            prop: 'code',
+            label: '工位编码'
+          },
+          {
+            label: '工位名称',
+            prop: 'name'
+          },
+          {
+            label: '责任人',
+            prop: 'leaderName',
+            slot: 'factory'
+          },
+          {
+            label: '所属区域',
+            prop: 'areaName'
+          },
+
+          {
+            label: '所属工厂',
+            prop: 'factoryName'
+          },
+
+          {
+            label: '所属厂房',
+            prop: 'workshopPlanName'
+          },
+          {
+            label: '所属车间',
+            prop: 'workshopName'
+          },
+          {
+            label: '所属产线',
+            prop: 'productionLineName'
+          },
+          {
+            label: '设备名称',
+            prop: 'extInfo.assetName',
+            formatter: (_row, _column, cellValue) => {
+              return typeof cellValue === 'string' ? cellValue : '';
+            }
+          },
+          {
+            label: '设备编码',
+            prop: 'extInfo.assetCode',
+            formatter: (_row, _column, cellValue) => {
+              return typeof cellValue === 'string' ? cellValue : '';
+            }
+          },
+          {
+            label: '节拍时间',
+            prop: 'extInfo.meterTime',
+            slot: 'meterTime'
+          },
+          {
+            //修改此prop名称时,请同步修改columnKey属性和下方selectType方法
+            label: '状态',
+            prop: 'enabled',
+            slot: 'enabled',
+            filters: [
+              { value: 1, text: '生效' },
+              { value: 0, text: '未生效' }
+            ],
+            filterMultiple: false,
+            columnKey: 'enabled'
+          },
+          {
+            columnKey: 'action',
+            label: '操作',
+            width: 220,
+            align: 'center',
+            resizable: false,
+            slot: 'action',
+            showOverflowTooltip: true
+          }
+        ],
+        dict: {
+          groupId: [],
+          factory: [],
+          enabled: {
+            1: '生效',
+            0: '未生效'
+          }
+        },
+        pageSize: this.$store.state.tablePageSize
+      };
+    },
+    mounted() {},
+    created() {
+      this.getGs();
+      this.getFactoryarea();
+    },
+
+    methods: {
+      //初始化
+      async open() {
+        this.addOrEditDialogFlag = true;
+        this.$nextTick(() => {
+          this.reload();
+        });
+      },
+      selectType(value) {
+        let where = {};
+        if (value.enabled.length > 0) {
+          where['enable'] = value.enabled[0];
+        }
+        this.search(where);
+      },
+      datasource({ page, where, limit }) {
+        return getFactoryworkstation({
+          ...where,
+          pageNum: page,
+          size: limit
+        });
+      },
+      search(where) {
+        this.$refs.table.reload({
+          where: where,
+          page: 1
+        });
+      },
+      /* 刷新表格 */
+      reload(where = {}) {
+        this.$refs.table.reload({ page: 1, where });
+      },
+      // 获取公司数据
+      getGs() {
+        listOrganizations().then((list) => {
+          this.dict.groupId = JSON.parse(JSON.stringify(list));
+        });
+      },
+      // 获取工厂数据
+      getFactoryarea() {
+        let par = {
+          type: 1,
+          size: 9999,
+          type: 2
+        };
+        getFactoryarea(par).then((res) => {
+          this.dict.factory = res.list;
+        });
+      },
+      save() {
+        console.log(this.selection,'this.selection')
+        if(!this.selection.length){
+          return this.$message.warning('请选择工位')
+        }
+        this.$emit('save', this.selection);
+        this.addOrEditDialogFlag = false;
+      },
+
+      //关闭弹窗
+      cancel() {
+        this.addOrEditDialogFlag = false;
+      }
+    }
+  };
+</script>
+<style scoped lang="scss"></style>

+ 33 - 2
src/views/productionScheduling/productionShift/components/addOrEditDialog.vue

@@ -35,6 +35,25 @@
             <el-input clearable v-model="form.shiftName" placeholder=" " />
           </el-form-item>
         </el-col>
+        <el-col :span="12">
+          <el-form-item label="所属工厂:" prop="factoryIds">
+            <el-select
+              style="width: 100%"
+              clearable
+              multiple
+              v-model="form.factoryIds"
+              filterable
+              placeholder="请选择所属工厂"
+            >
+              <el-option
+                v-for="item in factoryList"
+                :label="item.name"
+                :value="item.id"
+                :key="item.id"
+              ></el-option>
+            </el-select>
+          </el-form-item>
+        </el-col>
         <el-col :span="12">
           <el-form-item label="出勤开始时间" prop="attendanceStartTime">
             <el-input
@@ -77,8 +96,9 @@
         </el-col>
       </el-row>
     </el-form>
+    <headerTitle title="时间设置"> </headerTitle>
+
     <div style="display: flex">
-      <span style="width: 90px">时间设置:</span>
       <timeTable
         ref="timeTable"
         @setFormtime="setFormtime"
@@ -115,6 +135,7 @@
     getById
   } from '@/api/productionScheduling/productionShift';
   import timeTable from './timeTable.vue';
+  import { getFactoryarea } from '@/api/factoryModel';
 
   export default {
     components: { timeTable },
@@ -205,9 +226,19 @@
       };
     },
     mounted() {},
-    created() {},
+    created() {
+      this.getFactoryList()
+    },
 
     methods: {
+      async getFactoryList() {
+        const { list } = await getFactoryarea({
+          pageNum: 1,
+          size: 999,
+          type: 1
+        });
+        this.factoryList = list || [];
+      },
       //初始化
       async open(row = {}, type) {
         this.addOrEditDialogFlag = true;

+ 45 - 34
src/views/productionScheduling/productionShift/components/timeTable.vue

@@ -103,7 +103,18 @@
             }
           ]"
         >
-          <el-time-select
+          <el-time-picker
+            style="width: 100%"
+            placeholder="开始时间"
+            v-model="row.startTime"
+            format="HH:mm"
+            value-format="HH:mm"
+            @change="changeTime(row, $index, 'startTime')"
+            :disabled="!!form.list[$index - 1]?.endTime"
+          >
+          </el-time-picker>
+
+          <!-- <el-time-select
             placeholder="开始时间"
             v-model="row.startTime"
             @change="changeTime(row, $index, 'startTime')"
@@ -117,7 +128,7 @@
                 : {})
             }"
           >
-          </el-time-select>
+          </el-time-select> -->
         </el-form-item>
       </template>
       <template v-slot:endTime="{ row, $index }">
@@ -132,7 +143,21 @@
             }
           ]"
         >
-          <el-time-select
+          <el-time-picker
+            style="width: 100%"
+            placeholder="结束时间"
+            v-model="row.endTime"
+            format="HH:mm"
+            value-format="HH:mm"
+            :disabled="!row.startTime"
+            @change="changeTime(row, $index, 'endTime')"
+            :picker-options="{
+              selectableRange: row.startTime + ':00-23:59:59'
+            }"
+          >
+          </el-time-picker>
+
+          <!-- <el-time-select
             placeholder="结束时间"
             v-model="row.endTime"
             @change="changeTime(row, $index, 'endTime')"
@@ -143,7 +168,7 @@
               }
             }"
           >
-          </el-time-select>
+          </el-time-select> -->
         </el-form-item>
       </template>
       <template v-slot:columnRequired="{ column }">
@@ -176,11 +201,7 @@
   export default {
     data() {
       return {
-        defTime: {
-          step: '00:01',
-          start: '00:00',
-          end: '23:59'
-        },
+
         columns: [
           {
             width: 60,
@@ -284,9 +305,7 @@
           return;
         }
         this.form.list.push({
-          startTime: this.incrementTime(
-            this.form.list[this.form.list?.length - 1]?.endTime
-          ),
+          startTime: this.form.list[this.form.list?.length - 1]?.endTime || '',
           endTime: '',
           note: '',
           totalDuration: '',
@@ -296,22 +315,24 @@
         });
       },
       changeTime(row, index, type) {
+        // return
         if (type == 'startTime') {
           this.$set(this.form.list[index], 'endTime', '');
+          row.startTime =
+            row.startTime.split(':')[0] +
+            ':' +
+            row.startTime.split(':')[1] +
+            ':00';
         }
-        if (type == 'endTime') {
-          if (!row.startTime) {
-            this.$set(this.form.list[index], 'endTime', '');
-            this.$message.error('请先选择开始时间');
-            return;
-          }
-        }
+
         this.removeFormtime(index);
         if (row.startTime && row.endTime) {
-          let val =
-            (new Date('2025-01-01 ' + row.endTime).getTime() -
-              new Date('2025-01-01 ' + row.startTime).getTime()) /
-            60000;
+          let startTime = new Date(row.startTime).getTime();
+          if (!startTime) {
+            startTime = new Date('2025-01-01 ' + row.startTime).getTime();
+          }
+          let endTime = new Date('2025-01-01 ' + row.endTime).getTime();
+          let val = (endTime - startTime) / 60000;
 
           this.$set(this.form.list[index], 'totalDuration', val);
         } else {
@@ -346,17 +367,7 @@
           });
         }
       },
-      incrementTime(time) {
-        if (!time) {
-          return '';
-        }
-        const [minutes, seconds] = time.split(':').map(Number);
-        let newSeconds = (seconds + 1) % 60;
-        let newMinutes = minutes + Math.floor((seconds + 1) / 60);
-        return `${String(newMinutes).padStart(2, '0')}:${String(
-          newSeconds
-        ).padStart(2, '0')}`;
-      },
+
       remove(index) {
         if (index != 0) {
           this.removeFormtime(index);

+ 77 - 17
src/views/productionScheduling/stationBeat/components/addOrEditDialog.vue

@@ -4,7 +4,7 @@
     :centered="true"
     :visible.sync="addOrEditDialogFlag"
     :title="title"
-    :append-to-body="false"
+    :append-to-body="true"
     :close-on-click-modal="false"
     width="60%"
     :before-close="cancel"
@@ -43,6 +43,7 @@
               v-model="form.bomCategory"
               @change="changeProductType"
               style="width: 100%"
+              clearable
             >
               <el-option
                 v-for="item of bomCategoryList"
@@ -59,6 +60,7 @@
               v-model="form.bomId"
               @change="changeBomId"
               style="width: 100%"
+              clearable
             >
               <el-option
                 v-for="item of bomVersionList"
@@ -75,6 +77,7 @@
               v-model="form.produceRoutingId"
               @change="changeRoutingList"
               style="width: 100%"
+              clearable
             >
               <el-option
                 v-for="item of routingList"
@@ -117,7 +120,11 @@
         <template v-slot:toolbar>
           <div style="display: flex">
             <el-form-item label="工序名称" prop="operationId">
-              <el-select v-model="form.operationId" style="width: 230px">
+              <el-select
+                v-model="form.operationId"
+                style="width: 230px"
+                v-if="form.produceRoutingId"
+              >
                 <el-option
                   v-for="item of taskList"
                   :key="item.sourceTaskId"
@@ -126,6 +133,21 @@
                   @click.native="changeOperationId(item)"
                 ></el-option>
               </el-select>
+              <el-input
+                v-if="!form.produceRoutingId"
+                style="width: 230px"
+                readonly
+                :value="form.operationName"
+                placeholder=" "
+              />
+              <el-button
+                v-if="!form.produceRoutingId"
+                size="small"
+                type="primary"
+                style="margin-left: 10px"
+                @click.native="taskinstanceDialogFlag = true"
+                >选择
+              </el-button>
             </el-form-item>
             <el-form-item label="工序节拍(分)" prop="cycleTime">
               <el-input
@@ -136,6 +158,15 @@
                 placeholder=" "
               />
             </el-form-item>
+            <el-form-item label-width="0">
+              <el-button
+                size="small"
+                type="primary"
+                style="margin-left: 10px"
+                @click.native="$refs.stationBeatDialogRef.open()"
+                >新增工位
+              </el-button>
+            </el-form-item>
           </div>
         </template>
         <template v-slot:stationCycleTime="{ row, $index }">
@@ -188,6 +219,17 @@
       ref="produceRef"
       @changeParent="produceConfirm"
     ></ProductModal>
+    <taskinstanceDialog
+      ref="taskinstanceDialogRef"
+      v-if="taskinstanceDialogFlag"
+      @saveTaskInstance="changeOperationId"
+      :visible.sync="taskinstanceDialogFlag"
+    ></taskinstanceDialog>
+    <stationBeatDialog
+      ref="stationBeatDialogRef"
+      @save="stationBeatAdd"
+      :disabledTableList="this.form.list"
+    ></stationBeatDialog>
   </ele-modal>
 </template>
 <script>
@@ -212,6 +254,8 @@
   };
   import { getFactoryarea } from '@/api/factoryModel';
   import ProductModal from '@/components/productList/product-list.vue';
+  import taskinstanceDialog from '@/components/procedure/taskinstanceDialog.vue';
+  import stationBeatDialog from '@/views/factoryModel/station/components/stationBeatDialog.vue';
   import {
     saveOrUpdate,
     getById,
@@ -221,32 +265,32 @@
     listByRoutingIds,
     selectFactoryWorkstation
   } from '@/api/productionScheduling/stationBeat';
-
   export default {
-    components: { ProductModal },
+    components: { ProductModal, taskinstanceDialog, stationBeatDialog },
     computed: {},
     data() {
       return {
         lodading: false,
         title: '',
         addOrEditDialogFlag: false,
+        taskinstanceDialogFlag: false,
         form: { ...defForm },
         rules: {
           name: [
             { required: true, message: '名称不能为空', trigger: 'change' }
           ],
-          factoriesId: [
-            { required: true, message: '请选择工厂', trigger: 'change' }
-          ],
-          bomId: [
-            { required: true, message: '请选择BOM版本', trigger: 'change' }
-          ],
-          bomCategory: [
-            { required: true, message: '请选择BOM类型', trigger: 'change' }
-          ],
-          produceRoutingId: [
-            { required: true, message: '请选择工艺路线', trigger: 'change' }
-          ],
+          // factoriesId: [
+          //   { required: true, message: '请选择工厂', trigger: 'change' }
+          // ],
+          // bomId: [
+          //   { required: true, message: '请选择BOM版本', trigger: 'change' }
+          // ],
+          // bomCategory: [
+          //   { required: true, message: '请选择BOM类型', trigger: 'change' }
+          // ],
+          // produceRoutingId: [
+          //   { required: true, message: '请选择工艺路线', trigger: 'change' }
+          // ],
           code: [{ required: true, message: '编码不能为空', trigger: 'change' }]
         },
         factoryList: [],
@@ -491,6 +535,7 @@
       },
       async changeOperationId(item) {
         this.form.operationName = item.name;
+        this.form.operationId = item.sourceTaskId || item.id;
         const res = await selectFactoryWorkstation({
           taskId: this.form.operationId
         });
@@ -511,7 +556,22 @@
           };
         });
       },
-
+      stationBeatAdd(data) {
+        data.forEach((item) => {
+          this.form.list.push({
+            deviceId: item.extInfo?.assetId,
+            deviceName: item.extInfo?.assetName,
+            deviceCode: item.extInfo?.assetCode,
+            workstationName: item.name,
+            workstationId: item.id,
+            model: item.model,
+            deviceType: item.deviceType,
+            stationCycleTime: '',
+            remark: '',
+            priority: ''
+          });
+        });
+      },
       save() {
         this.$refs.form.validate((valid) => {
           if (!this.form.list?.length) {

+ 164 - 86
src/views/workforceManagement/schedule/components/ManagementTable.vue

@@ -1,76 +1,84 @@
 <template>
-  <ele-pro-table
-    ref="table"
-    :needPage="false"
-    :columns="columns"
-    :datasource="tableData"
-    @cell-click="cellClick"
-    @row-click="rowClick"
-    @header-click="headerClick"
-    cache-key="systemRoleTable19"
+  <div
+    @mousedown="startSelecting"
+    @mousemove="updateSelection"
+    @mouseup="stopSelecting"
   >
-    <!-- 表头工具栏 -->
-    <template v-slot:toolbar>
-      <el-form class="ele-form-search">
-        <el-row :gutter="15">
-          <el-col :span="4">
-            <el-form-item style="margin-bottom: 0">
-              <el-input
-                clearable
-                :placeholder="type == 'person' ? '搜索人员' : '搜索班组'"
-                v-model.trim="searchKey"
-                @input="search"
-              ></el-input>
-            </el-form-item>
-          </el-col>
-          <el-col :span="4">
-            <el-form-item style="margin-bottom: 0">
-              <el-date-picker
-                v-model="time"
-                type="month"
-                @change="timeChange"
-                value-format="yyyy-MM"
-                placeholder="选择月"
-              >
-              </el-date-picker>
-            </el-form-item>
-          </el-col>
-          <el-col :span="16">
-            <el-form-item class="btn-wrap" style="margin-bottom: 0">
-              <el-button @click="save" >保存</el-button>
-              <el-button @click="rest">重置</el-button>
-              <el-button @click="anbp">{{
-                type == 'person' ? '按班排' : '按人排'
-              }}</el-button>
-            </el-form-item>
-          </el-col>
-        </el-row>
-      </el-form>
-    </template>
-    <template v-slot:item="{ row }">
-      {{ row.item.name }}
-    </template>
-    <template v-slot:bcHeader="{ column }">
-      <div v-html="showheaderName(column.label)"></div>
-    </template>
-    <template v-slot:bc="{ column, row }">
-      <div
-        class="tabel-cell-item"
-        :style="`background-color: ${showColor(el)};`"
-        v-for="el in showName(row, column)"
-        :key="el.id"
-      >
-        {{ el.name }}
-      </div>
-    </template>
-  </ele-pro-table>
+    <ele-pro-table
+      ref="table"
+      :needPage="false"
+      :columns="columns"
+      :datasource="tableData"
+      @cell-click="cellClick"
+      @row-click="rowClick"
+      @header-click="headerClick"
+      cache-key="systemRoleTable19"
+    >
+      <!-- 表头工具栏 -->
+      <template v-slot:toolbar>
+        <el-form class="ele-form-search">
+          <el-row :gutter="15">
+            <el-col :span="4">
+              <el-form-item style="margin-bottom: 0">
+                <el-input
+                  clearable
+                  :placeholder="type == 'person' ? '搜索人员' : '搜索班组'"
+                  v-model.trim="searchKey"
+                  @input="search"
+                ></el-input>
+              </el-form-item>
+            </el-col>
+            <el-col :span="4">
+              <el-form-item style="margin-bottom: 0">
+                <el-date-picker
+                  v-model="time"
+                  type="month"
+                  @change="timeChange"
+                  value-format="yyyy-MM"
+                  placeholder="选择月"
+                >
+                </el-date-picker>
+              </el-form-item>
+            </el-col>
+            <el-col :span="16">
+              <el-form-item class="btn-wrap" style="margin-bottom: 0">
+                <el-button @click="save" type="primary">保存</el-button>
+                <el-button @click="rest">重置</el-button>
+                <el-button @click="anbp" type="primary">{{
+                  type == 'person' ? '按班排' : '按人排'
+                }}</el-button>
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </el-form>
+      </template>
+      <template v-slot:item="{ row }">
+        {{ row.item.name }}
+      </template>
+      <template v-slot:bcHeader="{ column }">
+        <div v-html="showheaderName(column.label)"></div>
+      </template>
+      <template v-slot:bc="{ column, row, $index }">
+        <div
+          class="tabel-cell-item"
+          :style="`background-color: ${showColor(el)};`"
+          v-for="el in showName(row, column)"
+          :key="el.id"
+          ref="tabelItem"
+        >
+          {{ el.name }}
+        </div>
+      </template>
+    </ele-pro-table>
+    <div v-if="isSelecting" class="selection-box" :style="selectionStyle"></div>
+  </div>
 </template>
 <script>
   import * as dayjs from 'dayjs';
   import { debounce } from 'throttle-debounce';
   export default {
     props: ['selectedClasses', 'classesColor'],
-    data () {
+    data() {
       const columnsDefault = {
         person: {
           prop: 'item',
@@ -84,6 +92,11 @@
         }
       };
       return {
+        isSelecting: false,
+        startX: 0,
+        startY: 0,
+        endX: 0,
+        endY: 0,
         type: 'person', // team 班次 person 人员
         columnsDefault,
         tableData: [],
@@ -107,14 +120,68 @@
         }
       };
     },
-    created () {
+    computed: {
+      selectionStyle() {
+        return {
+          position: 'absolute',
+          left: `${Math.min(this.startX, this.endX)}px`,
+          top: `${Math.min(this.startY, this.endY)}px`,
+          width: `${Math.abs(this.endX - this.startX)}px`,
+          height: `${Math.abs(this.endY - this.startY)}px`,
+          border: '2px dashed black'
+        };
+      }
+    },
+    created() {
       this.search = debounce(500, this.search);
       this.time = dayjs(new Date()).format('YYYY-MM');
       this.initColumns();
     },
     methods: {
+      startSelecting(event) {
+        return
+        this.isSelecting = true;
+        this.startX = event.clientX;
+        this.startY = event.clientY;
+        this.endX = this.startX;
+        this.endY = this.startY;
+      },
+      updateSelection(event) {
+        return
+        if (this.isSelecting) {
+          this.endX = event.clientX;
+          this.endY = event.clientY;
+        }
+      },
+      stopSelecting() {
+        return
+        this.isSelecting = false;
+        // 在这里处理框选结果,例如计算被框选的元素
+        this.selectedItems = this.calculateSelectedItems();
+      },
+      calculateSelectedItems() {
+        return this.tableData.filter((item, index) => {
+          console.log(item,'item');
+          console.log(
+            this.$refs[
+              (this.type == 'person' ? item.item?.userId : item.item?.teamId) + index
+            ]
+          );
+          const rect =
+            this.$refs[
+              (this.type == 'person' ? item.item?.userId : item.item?.teamId) + index
+            ].getBoundingClientRect();
+          console.log(rect);
+          return (
+            rect.left < this.endX &&
+            rect.right > this.startX &&
+            rect.top < this.endY &&
+            rect.bottom > this.startY
+          );
+        });
+      },
       // 人员班次数据切换
-      setTableData () {
+      setTableData() {
         switch (this.type) {
           case 'person':
             this.tableData = this.personData;
@@ -127,7 +194,7 @@
         }
       },
       // 初始化columns
-      initColumns () {
+      initColumns() {
         let Month = dayjs(this.time).month() + 1;
         let MonthNum = dayjs(this.time).daysInMonth();
         let columns = [];
@@ -156,33 +223,36 @@
         this.columns = [].concat(this.columns, columns);
       },
       // 设置数据
-      setData (personData, teamData) {
+      setData(personData, teamData) {
         this.personData = personData;
         this.teamData = teamData;
         this.setTableData();
       },
-      showName (row, column) {
+      showName(row, column) {
+
         let list = row[column.property];
         if (list) {
           list.forEach((n) => {
             n.name = n.name.slice(0, 4);
+            n['userId'] = row.item?.userId;
+            n['teamId'] = row.item?.teamId;
           });
           return list;
         } else {
           return [];
         }
       },
-      showheaderName (label) {
+      showheaderName(label) {
         let list = label.split('/');
         let week = this.dict.week[list[1]];
         return `${list[0]}</br>${week}`;
       },
       // 设置颜色
-      showColor (item) {
+      showColor(item) {
         return this.classesColor[item.id];
       },
       // 单元格点击
-      cellClick (row, column) {
+      cellClick(row, column) {
         const selectedClasses = JSON.parse(
           JSON.stringify(this.selectedClasses)
         );
@@ -199,7 +269,7 @@
         }
       },
       // 行点击
-      rowClick (row, column) {
+      rowClick(row, column) {
         const selectedClasses = JSON.parse(
           JSON.stringify(this.selectedClasses)
         );
@@ -224,7 +294,7 @@
         }
       },
       // 列点击
-      headerClick (column) {
+      headerClick(column) {
         const selectedClasses = JSON.parse(
           JSON.stringify(this.selectedClasses)
         );
@@ -245,7 +315,7 @@
         }
       },
       // 按班排同步人员-单元格点击
-      SyncCellClick (row, column) {
+      SyncCellClick(row, column) {
         const selectedClasses = JSON.parse(
           JSON.stringify(this.selectedClasses)
         );
@@ -261,7 +331,7 @@
         }
       },
       // 按班排同步人员-行点击
-      SyncRowClick (row, column) {
+      SyncRowClick(row, column) {
         const selectedClasses = JSON.parse(
           JSON.stringify(this.selectedClasses)
         );
@@ -285,7 +355,7 @@
         }
       },
       // 按班排同步人员-列点击
-      SyncHeaderClick (column) {
+      SyncHeaderClick(column) {
         const selectedClasses = JSON.parse(
           JSON.stringify(this.selectedClasses)
         );
@@ -300,43 +370,43 @@
         }
       },
       // 根据班组id查询人员
-      getid_Person (id) {
+      getid_Person(id) {
         let list = this.personData.filter((n) => {
           return n.item.teamId == id;
         });
         return list;
       },
       // 保存
-      save () {
+      save() {
         this.$emit('save');
       },
       // 重置
-      rest () {
+      rest() {
         this.$emit('rest');
       },
       // 按人/班排
-      anbp () {
+      anbp() {
         this.type = this.type == 'person' ? 'team' : 'person';
         this.initColumns();
         this.setTableData();
       },
       // 切换按班排班
-      changanbp () {
+      changanbp() {
         this.type = 'team';
         this.initColumns();
         this.setTableData();
       },
       // 选择时间
-      timeChange () {
+      timeChange() {
         this.initColumns();
       },
       // 补零
-      setNUm (num) {
+      setNUm(num) {
         let length = 2;
         return (num / Math.pow(10, length)).toFixed(length).substr(2);
       },
       // 搜索
-      search (val) {
+      search(val) {
         switch (this.type) {
           case 'person':
             if (val !== '') {
@@ -395,4 +465,12 @@
     text-align: right;
     padding-right: 20px;
   }
+  :deep(.el-button--medium) {
+    padding: 10px 20px !important;
+  }
+  .selection-box {
+    position: absolute;
+    border: 2px dashed black;
+    pointer-events: none; /* 确保选择框不会干扰鼠标事件 */
+  }
 </style>