695593266@qq.com 10 bulan lalu
induk
melakukan
28e5d37615

+ 18 - 0
src/api/productionPlan/index.js

@@ -372,3 +372,21 @@ export async function planIsReview(data) {
   }
   return Promise.reject(new Error(res.data.message));
 }
+
+//生产前准备
+export async function completenessCheck(data) {
+  const res = await request.post(`/aps/productionplan/preRelease`, data);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+//任务派单
+export async function taskReleaseWork(data) {
+  const res = await request.post(`/aps/assign/saveEventAssign`, data);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}

+ 10 - 10
src/components/selectionDialog/processRoute.vue

@@ -40,10 +40,10 @@
 <script>
   import { routeList } from '@/api/saleOrder';
   import { reviewStatus } from '@/enum/dict';
-  import routeSearch from './routeSearch.vue'
+  import routeSearch from './routeSearch.vue';
   export default {
     components: {
-        routeSearch
+      routeSearch
     },
     props: {},
     data() {
@@ -102,13 +102,13 @@
             // ],
             filterMultiple: false,
             columnKey: 'status',
-            formatter:(row)=>{
-              if(row.status == -1){
-                return '草稿'
-              }else if(row.status == 0){
-                return '失效'
-              }else{
-                return '生效'
+            formatter: (row) => {
+              if (row.status == -1) {
+                return '草稿';
+              } else if (row.status == 0) {
+                return '失效';
+              } else {
+                return '生效';
               }
             }
           },
@@ -134,7 +134,7 @@
             label: '创建时间',
             showOverflowTooltip: true,
             minWidth: 110
-          },
+          }
         ],
 
         radio: null

+ 382 - 0
src/views/productionPlan/components/checkProductionPreparations.vue

@@ -0,0 +1,382 @@
+<template>
+  <ele-modal
+    width="65vw"
+    :visible.sync="visible"
+    v-if="visible"
+    :close-on-click-modal="false"
+    row-key="code"
+    custom-class="ele-dialog-form"
+    title="生产前准备"
+    @closed="onClose"
+    append-to-body
+    :maxable="true"
+  >
+    <ele-pro-table
+      ref="table"
+      :columns="columns"
+      :initLoad="false"
+      :datasource="datasource"
+      :selection.sync="selection"
+      row-key="id"
+      @columns-change="handleColumnChange"
+      :cacheKey="cacheKeyUrl"
+      :page-size="20"
+    >
+      <template v-slot:toolbar>
+        <el-button type="primary" size="mini" @click="batchKittingComplete"
+          >批量齐套检查</el-button
+        >
+      </template>
+
+      <template v-slot:preType="{ row }">
+        <span>{{ getTypeLabel(row.preType) }}</span>
+      </template>
+
+      <template v-slot:statusStr="{ row }">
+        <span v-if="row.statusStr == '缺料'" style="color: red">{{
+          row.statusStr
+        }}</span>
+        <span v-else style="color: green">{{ row.statusStr }}</span>
+      </template>
+
+      <template v-slot:action="{ row }">
+        <el-link
+          type="primary"
+          :underline="false"
+          @click="kittingComplete(row)"
+        >
+          齐套检查
+        </el-link>
+
+        <el-link
+          type="primary"
+          :underline="false"
+          @click="taskAssignment"
+          v-if="row.statusStr == '缺料' && row.preType == '6'"
+        >
+          任务派单
+        </el-link>
+      </template>
+    </ele-pro-table>
+
+    <el-dialog
+      title="任务派单"
+      v-if="dialogVisible"
+      :visible.sync="dialogVisible"
+      width="30%"
+      :before-close="handleClose"
+      append-to-body
+    >
+      <el-form ref="form" :model="form" label-width="100px" :rules="rules">
+        <el-form-item label="分管部门:">
+          <ele-tree-select
+            clearable
+            :data="groupList"
+            filterable
+            v-model="form.executeGroupId"
+            valueKey="id"
+            labelKey="name"
+            placeholder="请选择"
+            @change="change_principalDep"
+            default-expand-all
+          />
+        </el-form-item>
+
+        <el-form-item label="技术员:" prop="executorId">
+          <el-select
+            v-model="form.executorId"
+            placeholder="请选择"
+            style="width: 100%"
+            @change="leaderListChange"
+          >
+            <el-option
+              v-for="item in leaderList"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+            >
+            </el-option>
+          </el-select>
+        </el-form-item>
+
+        <el-form-item label="备注">
+          <el-input
+            type="textarea"
+            :rows="4"
+            placeholder="请输入内容"
+            v-model="form.remake"
+          >
+          </el-input>
+        </el-form-item>
+      </el-form>
+      <span slot="footer" class="dialog-footer">
+        <el-button @click="dialogVisible = false">取 消</el-button>
+        <el-button type="primary" @click="batchConfirm">确 定</el-button>
+      </span>
+    </el-dialog>
+
+    <kitting-complete ref="kittingCompleteRef"></kitting-complete>
+  </ele-modal>
+</template>
+
+<script>
+  import { completenessCheck, taskReleaseWork } from '@/api/productionPlan';
+  import { listOrganizations, getUserPage } from '@/api/system/organization';
+  import kittingComplete from './kittingComplete.vue';
+  export default {
+    components: { kittingComplete },
+    data() {
+      // 1主材料 2辅材料 3工装 4工艺文件 5NC代码 6人员
+      return {
+        visible: false,
+        cacheKeyUrl: 'checkProductionPreparationsKey',
+        selection: [],
+        planId: '',
+        typeList: [
+          {
+            value: 1,
+            label: '主材料齐套'
+          },
+          {
+            value: 2,
+            label: '辅材料齐套'
+          },
+          {
+            value: 3,
+            label: '工装齐套'
+          },
+          {
+            value: 4,
+            label: '工艺文件齐套'
+          },
+          {
+            value: 5,
+            label: 'NC代码齐套'
+          },
+          {
+            value: 6,
+            label: '人员齐套'
+          }
+        ],
+        groupList: [],
+        leaderList: [],
+        form: {
+          executeGroupName: '',
+          executeGroupId: '',
+          executorName: '',
+          executorId: '',
+          remake: ''
+        },
+        dialogVisible: false,
+        rules: {
+          executorId: [
+            { required: true, message: '请选择技术人员', trigger: 'blur' }
+          ]
+        },
+        itemData: {}
+      };
+    },
+
+    created() {
+      this.getGs();
+    },
+
+    computed: {
+      columns() {
+        return [
+          {
+            width: 45,
+            type: 'selection',
+            columnKey: 'selection',
+            align: 'center',
+            fixed: 'left',
+            reserveSelection: true
+          },
+          {
+            width: 55,
+            type: 'index',
+            columnKey: 'index',
+            align: 'center',
+            label: '序号',
+            fixed: 'left'
+          },
+          {
+            prop: 'preType',
+            slot: 'preType',
+            align: 'center',
+            label: '名称'
+          },
+          {
+            prop: 'statusStr',
+            slot: 'statusStr',
+            align: 'center',
+            label: '状态'
+          },
+          {
+            prop: 'approveDate',
+            align: 'center',
+            label: '校验时间'
+          },
+          {
+            prop: 'approveName',
+            align: 'center',
+            label: '校验人',
+            showOverflowTooltip: true
+          },
+          {
+            columnKey: 'action',
+            label: '操作',
+            align: 'center',
+            resizable: false,
+            slot: 'action',
+            fixed: 'right',
+            showOverflowTooltip: true
+          }
+        ];
+      }
+    },
+
+    methods: {
+      datasource({ page, where, limit }) {
+        let pageNum = page;
+        let size = limit;
+        where.planId = this.planId;
+        const res = completenessCheck({ ...where, pageNum, size });
+
+        return res;
+      },
+
+      reload(where) {
+        this.$nextTick(() => {
+          if (this.$refs.table && this.$refs.table.reload)
+            this.$refs.table.reload({ page: 1, where: where });
+        });
+      },
+
+      async getGs() {
+        await listOrganizations().then((list) => {
+          this.groupList = this.$util.toTreeData({
+            data: list,
+            idField: 'id',
+            parentIdField: 'parentId'
+          });
+        });
+      },
+
+      getTypeLabel(value) {
+        const item = this.typeList.find((it) => it.value === value);
+        return item ? item.label : '';
+      },
+
+      open(item) {
+        this.visible = true;
+        this.itemData = item;
+        this.planId = item.id;
+
+        if (this.planId) {
+          this.reload();
+        }
+      },
+
+      handleColumnChange() {},
+
+      onClose() {
+        this.planId = '';
+        this.visible = false;
+      },
+
+      handleClose() {
+        this.dialogVisible = false;
+      },
+      kittingComplete(item) {
+        if (item.preType == '5' || item.preType == '6') {
+          this.$confirm('是否确认齐套?', '提示', {
+            confirmButtonText: '齐套',
+            cancelButtonText: '不齐套',
+            type: 'warning'
+          })
+            .then(() => {})
+            .catch(() => {});
+        } else {
+          const list = [this.itemData];
+          this.$refs.kittingCompleteRef.open(list);
+        }
+      },
+      taskAssignment() {
+        this.dialogVisible = true;
+      },
+
+      change_principalDep(id) {
+        const node = this.findNodeById(this.groupList, id);
+        this.form.executeGroupName = node ? node.name : '';
+        this.form.executorId = '';
+        this.form.executorName = '';
+
+        this.getUserPage();
+      },
+
+      leaderListChange(id) {
+        const item = this.leaderList.find((it) => it.id === id);
+        this.form.executorName = item.name;
+      },
+
+      findNodeById(list, id) {
+        for (const item of list) {
+          if (item.id === id) return item;
+          if (item.children) {
+            const found = this.findNodeById(item.children, id);
+            if (found) return found;
+          }
+        }
+        return null;
+      },
+
+      async getUserPage() {
+        if (!this.form.executeGroupId) return (this.leaderList = []);
+        let par = {
+          groupId: this.form.executeGroupId,
+          size: 999
+        };
+        await getUserPage(par).then((res) => {
+          this.leaderList = res.list;
+        });
+      },
+
+      async batchConfirm() {
+        const valid = await this.$refs.form.validate().catch(() => false);
+        if (!valid) return;
+
+        const loading = this.$loading({
+          lock: true,
+          text: '派单中...',
+          spinner: 'el-icon-loading',
+          background: 'rgba(0, 0, 0, 0.7)'
+        });
+
+        await taskReleaseWork({
+          ...this.form,
+          productionPlanId: this.itemData.id,
+          eventType: 1
+        })
+          .then(() => {
+            loading.close();
+            this.form = {
+              executeGroupName: '',
+              executeGroupId: '',
+              executorName: '',
+              executorId: '',
+              remake: ''
+            };
+            this.dialogVisible = false;
+            this.reload();
+          })
+          .catch(() => {
+            loading.close();
+          });
+      },
+      batchKittingComplete() {}
+    }
+  };
+</script>
+
+<style></style>

+ 1284 - 0
src/views/productionPlan/components/kittingComplete.vue

@@ -0,0 +1,1284 @@
+<template>
+  <div>
+    <ele-modal
+      width="90vw"
+      :visible.sync="visible"
+      v-if="visible"
+      :close-on-click-modal="false"
+      row-key="code"
+      custom-class="ele-dialog-form"
+      :title="'齐套性检查'"
+      :maxable="true"
+      append-to-body
+    >
+      <el-form
+        :inline="true"
+        class="demo-form-inline"
+        label-width="107px"
+        style="margin-bottom: 16px"
+      >
+        <el-form-item label="选择日期:">
+          <!-- <el-date-picker
+            v-model="startTime"
+            type="date"
+            placeholder="选择日期"
+            value-format="yyyy-MM-dd"
+            size="large"
+            :picker-options="pickerOptions"
+          >
+          </el-date-picker> -->
+          <el-date-picker
+            v-model="startTime"
+            type="daterange"
+            value-format="yyyy-MM-dd"
+            start-placeholder="开始日期"
+            end-placeholder="结束日期"
+            :clearable="false"
+            :picker-options="pickerOptions"
+          >
+          </el-date-picker>
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" @click="onSubmit">查询</el-button>
+        </el-form-item>
+      </el-form>
+      <div v-loading="loading">
+        <el-form label-width="107px" :model="planInfo" class="plan_form">
+          <el-row :gutter="20" type="flex" style="flex-wrap: wrap">
+            <el-col :span="6">
+              <el-form-item label="名称:">
+                <el-input readonly v-model="planInfo.productName"></el-input>
+              </el-form-item>
+            </el-col>
+            <el-col :span="6">
+              <el-form-item label="计划编号:">
+                <el-input readonly v-model="planInfo.code"></el-input>
+              </el-form-item>
+            </el-col>
+            <el-col :span="6">
+              <el-form-item label="规格:">
+                <el-input readonly v-model="planInfo.specification"></el-input>
+              </el-form-item>
+            </el-col>
+            <el-col :span="6">
+              <el-form-item label="型号:">
+                <el-input readonly v-model="planInfo.model"></el-input>
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <el-row>
+            <el-col :span="6">
+              <el-form-item label="编码:">
+                <el-input readonly v-model="planInfo.productCode"></el-input>
+              </el-form-item>
+            </el-col>
+            <el-col :span="6">
+              <el-form-item label="牌号:">
+                <el-input readonly v-model="planInfo.brandNo"></el-input>
+              </el-form-item>
+            </el-col>
+            <el-col :span="6">
+              <el-form-item label="计划数量:">
+                <el-input readonly v-model="planInfo.productNum"></el-input>
+              </el-form-item>
+            </el-col>
+            <el-col :span="6" v-if="produceType != 2">
+              <el-form-item label="未发总数:">
+                <el-input
+                  readonly
+                  v-model="kitComInfo.totalUnshippedQuantity"
+                ></el-input>
+              </el-form-item>
+            </el-col>
+
+            <el-col :span="6">
+              <el-form-item label="库存数量:">
+                <el-input
+                  readonly
+                  v-model="kitComInfo.inventoryQuantity"
+                ></el-input>
+              </el-form-item>
+            </el-col>
+            <el-col :span="6" v-if="produceType != 2">
+              <el-form-item label="在制总数:">
+                <el-input
+                  readonly
+                  v-model="kitComInfo.totalNumberProduction"
+                ></el-input>
+              </el-form-item>
+            </el-col>
+            <el-col :span="6" v-if="produceType != 2">
+              <el-form-item label="最终缺料数量:">
+                <el-input
+                  readonly
+                  v-model="kitComInfo.finalFilledShortCount"
+                ></el-input>
+              </el-form-item>
+            </el-col>
+            <el-col :span="6">
+              <el-form-item label="最终状态:">
+                <el-input
+                  :class="
+                    kitComInfo.finalFilledShortState == '缺料'
+                      ? 'statusRed'
+                      : 'statusGreen'
+                  "
+                  readonly
+                  v-model="kitComInfo.finalFilledShortState"
+                ></el-input>
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </el-form>
+        <div class="form-wrapper">
+          <div
+            class="left_tree"
+            v-show="leftShow"
+            :style="{ width: leftWidth }"
+          >
+            <el-tree
+              ref="treeRef"
+              :expand-on-click-node="false"
+              :data="cardList"
+              :props="treeProps"
+              node-key="id"
+              highlight-current
+              @node-click="handleNodeClick"
+            ></el-tree>
+          </div>
+          <div :style="{ width: rightWidth }">
+            <div>
+              <ele-pro-table
+                ref="table"
+                :needPage="false"
+                :columns="columns"
+                :init-load="false"
+                :datasource="datasourceList"
+                row-key="code"
+                :cache-key="`ProductionPlanTable`"
+                border
+                height="40vh"
+                key="id"
+                :selection.sync="selection"
+              >
+                <template v-slot:default>
+                  <div class="tool">
+                    <div class="tool_l">
+                      <el-button
+                        v-if="produceType == 2"
+                        type="primary"
+                        @click="bulkPurchase"
+                        size="medium"
+                        >下发采购计划</el-button
+                      >
+                      <el-button
+                        v-if="produceType != 2 && formInline.bomType != 1"
+                        type="primary"
+                        @click="issuePlan"
+                        size="medium"
+                        >下发生产计划</el-button
+                      >
+                      <!-- <el-button
+                        type="primary"
+                        @click="isAllChecked"
+                        size="medium"
+                        >一键勾选缺料</el-button
+                      > -->
+                    </div>
+                    <div class="tool_r">
+                      <el-form
+                        :inline="true"
+                        :model="formInline"
+                        class="demo-form-inline"
+                      >
+                        <el-form-item label="BOM类型">
+                          <el-select
+                            size="medium"
+                            v-model="formInline.bomType"
+                            placeholder="BOM类型"
+                            class="select-type"
+                            @change="bomTypeChange"
+                          >
+                            <el-option
+                              v-for="item in bomListType"
+                              :key="item.bomType"
+                              :label="item.bomName"
+                              :value="item.bomType"
+                            >
+                            </el-option>
+                          </el-select>
+                        </el-form-item>
+                        <el-form-item label="BOM版本">
+                          <el-select
+                            class="select-type"
+                            size="medium"
+                            v-model="formInline.bomId"
+                            placeholder="BOM版本"
+                            @change="bomVChange"
+                          >
+                            <el-option
+                              v-for="item in bomListV"
+                              :key="item.bomId"
+                              :label="item.versions"
+                              :value="item.bomId"
+                            >
+                            </el-option>
+                          </el-select>
+                        </el-form-item>
+                        <el-form-item label="属性类型" v-if="produceType == 2">
+                          <el-select
+                            size="medium"
+                            v-model="formInline.attributeType"
+                            placeholder="属性类型"
+                            class="select-type"
+                            @change="attributeChange"
+                          >
+                            <el-option
+                              v-for="item in attributeList"
+                              :key="item.value"
+                              :label="item.label"
+                              :value="item.value"
+                            >
+                            </el-option>
+                          </el-select>
+                        </el-form-item>
+                        <el-form-item label="最终状态">
+                          <el-select
+                            class="select-type"
+                            size="medium"
+                            v-model="formInline.finalState"
+                            placeholder="最终状态"
+                            @change="finalChange"
+                          >
+                            <el-option
+                              v-for="item in finalStateList"
+                              :key="item.value"
+                              :label="item.label"
+                              :value="item.value"
+                            >
+                            </el-option>
+                          </el-select>
+                        </el-form-item>
+                      </el-form>
+                    </div>
+                  </div>
+                </template>
+
+                <!-- <template v-slot:toolbar>
+                  <el-button
+                    v-if="produceType == 2"
+                    type="primary"
+                    @click="bulkPurchase"
+                    size="medium"
+                    >下发采购计划</el-button
+                  >
+                  <el-button
+                    v-if="produceType == 1"
+                    type="primary"
+                    @click="issuePlan"
+                    size="medium"
+                    >下发生产计划</el-button
+                  >
+                </template> -->
+                <!-- 默认按订单 -->
+                <template v-slot:toolbar v-if="produceType != 2">
+                  <el-form :inline="true">
+                    <el-form-item label="定额计算方法:">
+                      <el-radio-group
+                        v-model="quota_calculation"
+                        @change="selectCalculate"
+                      >
+                        <el-radio :label="1">按订单数量</el-radio>
+                        <el-radio :label="2">按缺料数量</el-radio>
+                      </el-radio-group>
+                    </el-form-item>
+                  </el-form>
+                </template>
+                <template v-slot:toolkit>
+                  <!-- 定额计算方法: 按订单数量 按缺料数量 -->
+                  <el-form :inline="true">
+                    <el-form-item label="基本数量">
+                      <el-input size="medium" v-model="baseCount" disabled>
+                        <template slot="append">{{ baseUnit }}</template>
+                      </el-input>
+                    </el-form-item>
+                  </el-form>
+                </template>
+                <template v-slot:inventoryQuantity="{ row }">
+                  <el-link
+                    type="primary"
+                    :underline="false"
+                    @click="stockDetail(row)"
+                  >
+                    {{ row.inventoryQuantity }}
+                  </el-link>
+                </template>
+                <template v-slot:inTransitNum="{ row }">
+                  <el-link
+                    type="primary"
+                    :underline="false"
+                    @click="currentDetail(row)"
+                  >
+                    {{ row.inTransitNum }}
+                  </el-link>
+                </template>
+                <template v-slot:inventoryStatusText="{ row }">
+                  <div
+                    :class="
+                      row.inventoryStatusText == '缺料'
+                        ? 'statusRed'
+                        : 'statusGreen'
+                    "
+                  >
+                    {{ row.inventoryStatusText }}
+                  </div>
+                </template>
+                <template v-slot:inTransitStatusText="{ row }">
+                  <div
+                    :class="
+                      row.inTransitStatusText == '缺料'
+                        ? 'statusRed'
+                        : 'statusGreen'
+                    "
+                  >
+                    {{ row.inTransitStatusText }}
+                  </div>
+                </template>
+                <template v-slot:finalStateText="{ row }">
+                  <div
+                    :class="
+                      row.finalStateText == '缺料' ? 'statusRed' : 'statusGreen'
+                    "
+                  >
+                    {{ row.finalStateText }}
+                  </div>
+                </template>
+
+                <template v-slot:numberProductionStateText="{ row }">
+                  <div
+                    :class="
+                      row.numberProductionStateText == '缺料'
+                        ? 'statusRed'
+                        : 'statusGreen'
+                    "
+                  >
+                    {{ row.numberProductionStateText }}
+                  </div>
+                </template>
+                <template v-slot:productFinalStateText="{ row }">
+                  <div
+                    :class="
+                      row.productFinalStateText == '缺料'
+                        ? 'statusRed'
+                        : 'statusGreen'
+                    "
+                  >
+                    {{ row.productFinalStateText }}
+                  </div>
+                </template>
+
+                <template v-slot:finishCount="{ row }">
+                  <div>
+                    <div v-if="row.finishCount > 0">{{ row.finishCount }}</div>
+                    <div v-else>-</div>
+                  </div>
+                </template>
+                <!-- <template v-slot:action="{ row }">
+                  <el-link
+                    type="primary"
+                    :underline="false"
+                    @click="issuePlan(row)"
+                  >
+                    下发生产计划
+                  </el-link>
+                </template> -->
+              </ele-pro-table>
+            </div>
+          </div>
+        </div>
+      </div>
+      <div slot="footer">
+        <el-button plain @click="cancel">取消</el-button>
+        <!-- <el-button type="primary" @click="confirm">确定</el-button> -->
+      </div>
+    </ele-modal>
+
+    <stockDetailDialog ref="stockDetailDialog" />
+    <currentDetailDialog ref="currentDetailDialog" />
+    <purchaseDialog ref="purchaseDialogRef" :orderType="2" @success="success" />
+    <factoryAdd
+      ref="factoryAddRef"
+      :isAdd="false"
+      :factoryType="3"
+      @close="factoryClose"
+    ></factoryAdd>
+  </div>
+</template>
+
+<script>
+  import {
+    findMaterialInfoByPlanId,
+    findBomCategoryByCategoryId,
+    findBomCategoryByCategoryIdV2,
+    bomVersionList,
+    listBomType,
+    getProductKitting
+  } from '@/api/productionPlan/index.js';
+  import factoryAdd from './factoryAdd';
+  import stockDetailDialog from './stockDetailDialog.vue';
+  import currentDetailDialog from './currentDetailDialog.vue';
+  import producePlan from '@/views/materialPlan/components/producePlan.vue';
+  import purchaseDialog from './purchaseDialog.vue';
+  import { issueProductionPlan } from '@/api/productionPlan/index.js';
+  export default {
+    components: {
+      stockDetailDialog,
+      currentDetailDialog,
+      producePlan,
+      purchaseDialog,
+      factoryAdd
+    },
+    computed: {
+      fieldShow() {
+        return this.produceType != 2 && this.quota_calculation === 2;
+      },
+      columns() {
+        return [
+          {
+            width: 45,
+            type: 'selection',
+            columnKey: 'selection',
+            align: 'center',
+            slot: 'selection',
+            fixed: 'left'
+          },
+          {
+            columnKey: 'index',
+            label: '序号',
+            type: 'index',
+            width: 55,
+            align: 'center',
+            fixed: 'left'
+          },
+          {
+            prop: 'batchNo',
+            label: '批次号',
+            align: 'center',
+            minWidth: 100,
+            showOverflowTooltip: true
+          },
+          {
+            prop: 'bomCode',
+            label: 'BOM编码',
+            align: 'center',
+            minWidth: 100,
+            showOverflowTooltip: true
+          },
+          {
+            prop: 'code',
+            label: '物料编码',
+            align: 'center',
+            minWidth: 100,
+            showOverflowTooltip: true
+          },
+          {
+            prop: 'name',
+            label: '物料名称',
+            align: 'center',
+            minWidth: 100,
+            showOverflowTooltip: true
+          },
+          {
+            prop: 'level',
+            label: '层级',
+            align: 'center',
+            minWidth: 50
+          },
+          {
+            prop: 'supplierName',
+            label: '供应商',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 140
+          },
+          {
+            prop: 'demandQuantity',
+            label: '定额数量',
+            showOverflowTooltip: true,
+            align: 'center'
+          },
+          {
+            prop: 'unit',
+            label: '单位',
+            showOverflowTooltip: true,
+            align: 'center',
+            width: 60
+          },
+          {
+            prop: 'totalUnshippedQuantity',
+            label: '未发总数',
+            showOverflowTooltip: true,
+            align: 'center',
+            show: this.fieldShow
+            // show: this.produceType == 1
+          },
+          {
+            slot: 'inventoryQuantity',
+            prop: 'inventoryQuantity',
+            label: '库存数量',
+            showOverflowTooltip: true,
+            align: 'center'
+          },
+          {
+            prop: 'secureInventory',
+            label: '安全库存',
+            showOverflowTooltip: true,
+            align: 'center'
+          },
+          {
+            prop: 'lockQuantity',
+            label: '锁库数量',
+            showOverflowTooltip: true,
+            align: 'center'
+          },
+          {
+            slot: 'inventoryStatusText',
+            prop: 'inventoryStatusText',
+            label: '库存状态',
+            align: 'center',
+            minWidth: 80
+          },
+          {
+            slot: 'inTransitNum',
+            prop: 'inTransitNum',
+            label: '在途数量',
+            align: 'center',
+            minWidth: 80
+          },
+          {
+            prop: 'inTransitOrdersNum',
+            label: '在途已关联数量	',
+            align: 'center',
+            minWidth: 120
+          },
+          {
+            slot: 'inTransitStatusText',
+            prop: 'inTransitStatusText',
+            label: '在途状态',
+            align: 'center',
+            minWidth: 80
+          },
+          {
+            prop: 'totalNumberProduction',
+            label: '在制总数',
+            showOverflowTooltip: true,
+            align: 'center',
+            // show: this.produceType == 1
+            show: this.fieldShow
+          },
+          {
+            prop: 'numberProductionStateText',
+            slot: 'numberProductionStateText',
+            label: '在制齐套',
+            showOverflowTooltip: true,
+            align: 'center',
+            // show: this.produceType == 1
+            show: this.fieldShow
+          },
+          // {
+          //   prop: 'productFinalStateText',
+          //   slot: 'productFinalStateText',
+          //   label: '成品的最终齐套',
+          //   showOverflowTooltip: true,
+          //   align: 'center',
+          //   minWidth: 160,
+          //   show: this.produceType == 1
+          // },
+          {
+            label: '最终可用数量',
+            prop: 'finalAvailableQuantity',
+            align: 'center',
+            minWidth: 110
+          },
+          {
+            label: '最终缺料数量',
+            prop: 'finalShortageQuantity',
+            align: 'center',
+            minWidth: 120
+          },
+
+          {
+            label: '下发状态',
+            align: 'center',
+            minWidth: 90,
+            fixed: 'right',
+            formatter: (row) => {
+              return row.hasIssued ? '已下发' : '未下发';
+            }
+          },
+          {
+            slot: 'finalStateText',
+            prop: 'finalStateText',
+            label: '最终状态',
+            align: 'center',
+            minWidth: 80,
+            fixed: 'right'
+          }
+          // {
+          //   columnKey: 'action',
+          //   label: '操作',
+          //   width: 120,
+          //   align: 'center',
+          //   resizable: false,
+          //   fixed: 'right',
+          //   show: this.produceType == 1,
+          //   slot: 'action'
+          // }
+        ];
+      }
+    },
+    data() {
+      return {
+        visible: false,
+        kitComInfo: {},
+        // requiredFormingNum: 0,
+        formInline: {
+          finalState: 0,
+          attributeType: 2,
+          bomId: '',
+          time: ''
+        },
+        finalStateList: [
+          {
+            label: '全部',
+            value: 0
+          },
+          {
+            label: '齐套',
+            value: 1
+          },
+          {
+            label: '缺料',
+            value: 2
+          }
+        ],
+        ids: [],
+        leftShow: false,
+        leftWidth: '0%',
+        rightWidth: '100%',
+        cardList: [],
+        treeProps: {
+          label: 'code',
+          children: 'children'
+        },
+        planInfo: {
+          salesCode: ''
+        },
+        datasourceAllList: [], // 表格物料全部数据
+        datasourceList: [], // 表格物料数据
+        bomListV: [], // BOM版本
+        bomListType: [], // BOM 类型
+        planId: '',
+        selectNodeId: '',
+        loading: false,
+        produceType: 1,
+        selection: [],
+        attributeList: [
+          {
+            value: 'all',
+            label: '全部'
+          },
+          {
+            value: 2,
+            label: '采购件'
+          },
+          {
+            value: 3,
+            label: '外协件'
+          }
+        ],
+        baseCount: '',
+        baseUnit: '',
+        quota_calculation: 1,
+        pickerOptions: {
+          // 限制最大可选日期为今天
+          disabledDate(time) {
+            const today = new Date();
+            return time.getTime() > today.getTime();
+          }
+        },
+        startTime: []
+      };
+    },
+    watch: {
+      leftShow(newVal, oldVal) {
+        if (newVal) {
+          this.leftWidth = '230px';
+          this.rightWidth = 'calc(100% - 240px)';
+        } else {
+          this.leftWidth = '0';
+          this.rightWidth = '100%';
+        }
+      }
+    },
+    mounted() {},
+    methods: {
+      async handleNodeClick(data) {
+        this.planInfo = data;
+        try {
+          await this.getKitting();
+          await this.getBomData(data);
+          this.selectNodeId = data.id;
+        } catch (e) {
+          console.log(e, '错误');
+        }
+      },
+      async getKitting() {
+        let { startTime, endTime } = this.getTimeData();
+        let params = {
+          categoryId: this.planInfo.categoryId,
+          startTime,
+          endTime
+        };
+        const res = await getProductKitting(params);
+        if (res) {
+          this.kitComInfo = res;
+        }
+      },
+      getTimeData() {
+        let startTime = '';
+        let endTime = '';
+        if (this.startTime.length > 0) {
+          startTime = this.startTime[0];
+          endTime = this.startTime[1];
+        }
+        return { startTime, endTime };
+      },
+      initTime() {
+        // 获取当前日期(今天)
+        const today = new Date(); // 注意:月份是0-based,8月对应7
+        // 计算三个月前的日期
+        const threeMonthsAgo = new Date(today);
+        threeMonthsAgo.setMonth(today.getMonth() - 3);
+        let start = this.formatDate(threeMonthsAgo);
+        let end = this.formatDate(today);
+        this.startTime = [start, end];
+      },
+      formatDate(date) {
+        const year = date.getFullYear();
+        const month = String(date.getMonth() + 1).padStart(2, '0');
+        const day = String(date.getDate()).padStart(2, '0');
+        return `${year}-${month}-${day}`;
+      },
+      selectCalculate(val) {
+        this.attributeChange(this.formInline.attributeType);
+      },
+      reload(where) {
+        this.$nextTick(() => {
+          this.$refs.table.reload({ page: 1, where });
+        });
+      },
+      stockDetail(row) {
+        this.$refs.stockDetailDialog.open({ bomCode: row.code });
+      },
+      currentDetail(row) {
+        this.$refs.currentDetailDialog.open({ bomCode: row.code });
+      },
+      // 选择BOM类型
+      async bomTypeChange(e, type) {
+        this.formInline.bomType = e;
+        const params = {
+          categoryId: this.planInfo.categoryId,
+          bomType: e,
+          isTemp: 0
+        };
+        const res = await bomVersionList(params);
+        if (!res || res.length == 0) {
+          this.bomListV = [];
+          return;
+        }
+        let list = [];
+        res.forEach((el) => {
+          if (el.status != 1) return;
+          let obj = {
+            bomId: el.id,
+            id: el.id,
+            versions: `V${el.versions}.0`
+          };
+          list.push(obj);
+        });
+        this.bomListV = list;
+        if (type == 'init') {
+          let obj = list.find((el) => el.bomId == this.planInfo.bomCategoryId);
+          let id = obj ? obj.bomId : list[0].bomId;
+          this.formInline.bomId = id;
+          this.bomVChange(id);
+        } else {
+          let id = list[0].bomId;
+          this.formInline.bomId = id;
+          this.bomVChange(id);
+        }
+        // let data = this.bomListType.find((el) => el.id === e);
+        // if (!data) return;
+        // this.bomListV = data.bomList;
+        // let idx = 0;
+        // if (type == 'init') {
+        //   idx = data.bomList.findIndex(
+        //     (item) => item.bomId == this.planInfo.bomCategoryId
+        //   );
+        // }
+        // const index = idx != -1 ? idx : 0;
+        // await this.bomVChange(data.bomList[index].bomId);
+      },
+      // 选择BOM版本
+      async bomVChange(e) {
+        this.formInline.bomId = e;
+        // return
+        // let bomData = list.find((el) => el.bomId == e);
+        // if (!bomData) return;
+        // let data = this.bomListV.find((el) => el.bomId === e);
+        this.getMaterialData({ bomId: e }, this.formInline.attributeType);
+      },
+      // 查询采购价跟外协件两个类型数据
+      async dataMerging() {
+        if (!this.formInline.bomId) {
+          return;
+        }
+        let kitting = this.quota_calculation == 1 ? false : true;
+        let { startTime, endTime } = this.getTimeData();
+        let params1 = {
+          planId: this.planId,
+          bomVersionId: this.formInline.bomId,
+          produceType: 2,
+          startTime,
+          endTime,
+          kitting: kitting
+        };
+        let params2 = {
+          planId: this.planId,
+          bomVersionId: this.formInline.bomId,
+          produceType: 3,
+          startTime,
+          endTime,
+          kitting: kitting
+        };
+        this.loading = true;
+        try {
+          // 直接调用接口函数,因为其返回Promise,无需额外包装
+          let p1 = findMaterialInfoByPlanId(params1);
+          let p2 = findMaterialInfoByPlanId(params2);
+          // 使用Promise.all并行执行多个Promise
+          let results = await Promise.all([p1, p2]);
+          this.loading = false;
+          // 这里可以继续对results做后续处理,比如解构等
+          const [result1, result2] = results;
+          // 示例:可以接着处理result1和result2,比如合并数据等
+          const mergeData = this.handleMergedData(result1, result2);
+          this.resultProcess(mergeData);
+        } catch (error) {
+          this.loading = false;
+          console.error('接口请求出错:', error);
+        }
+      },
+      handleMergedData(result1, result2) {
+        // 处理合并后的数据逻辑
+        return [...result1, ...result2];
+      },
+      // 切换属性
+      async attributeChange(e) {
+        if (e == 'all') {
+          this.dataMerging();
+          return;
+        }
+        let data = this.bomListV.find(
+          (el) => el.bomId === this.formInline.bomId
+        );
+        if (!data) return;
+        await this.getMaterialData(data, e);
+      },
+      // 获取物料数据信息
+      async getMaterialData(data, attributeType) {
+        if (attributeType == 'all') {
+          this.dataMerging();
+          return;
+        }
+        let kitting = this.quota_calculation == 1 ? false : true;
+        let produceType =
+          this.produceType == 1
+            ? 1
+            : this.produceType == 4
+            ? 4
+            : attributeType == 2
+            ? 2
+            : attributeType == 3
+            ? 3
+            : '';
+        let { startTime, endTime } = this.getTimeData();
+        let params = {
+          planId: this.planId,
+          bomVersionId: data.bomId,
+          produceType: produceType,
+          startTime,
+          endTime,
+          kitting: kitting
+        };
+        this.loading = true;
+        try {
+          const result = await findMaterialInfoByPlanId(params);
+          this.resultProcess(result);
+          this.loading = false;
+        } catch (err) {
+          this.loading = false;
+        }
+      },
+      // 结果数据处理
+      resultProcess(result) {
+        this.datasourceList = [];
+        if (!result || result.length == 0) {
+          this.baseCount = '';
+          this.baseUnit = '';
+          return;
+        }
+        let first = result[0];
+        this.baseCount = first.baseCount;
+        this.baseUnit = first.measuringUnit;
+        result.forEach((item) => {
+          item.batchNo = this.planInfo.batchNo;
+          item.salesCode = this.planInfo.salesCode;
+          item.finalState =
+            item.finalStateText == '齐套'
+              ? 1
+              : item.finalStateText == '缺料'
+              ? 2
+              : '';
+        });
+        this.datasourceAllList = result;
+        this.finalChange(this.formInline.finalState);
+      },
+      // 选择状态
+      finalChange(e) {
+        if (e === 0 || !e) {
+          this.datasourceList = this.datasourceAllList;
+          return;
+        }
+        this.datasourceList = this.datasourceAllList.filter(
+          (item) => item.finalState === e
+        );
+      },
+      isAllChecked() {
+        let datasourceList = this.datasourceList.filter(
+          (el) => el.finalStateText == '缺料' && !el.hasIssued
+        );
+        if (datasourceList.length) {
+          this.$refs.table.setSelectedRowKeys(
+            datasourceList.map((el) => el.code)
+          );
+        }
+      },
+      // 批量采购
+      async bulkPurchase() {
+        if (this.selection.length == 0) {
+          return this.$message.warning('请至少选择一条数据');
+        }
+        for (let i = 0; i < this.selection.length; i++) {
+          let el = this.selection[i];
+          if (el.finalState == 1) {
+            return this.$message.warning('必须选择最终状态为缺料的数据');
+          }
+          if (el.hasIssued) {
+            return this.$message.warning('必须选择下发状态为未下发的数据');
+          }
+        }
+        let arr = JSON.parse(JSON.stringify(this.selection));
+        let obj = this.bomListType.find(
+          (el) => el.bomType === this.formInline.bomType
+        );
+        let data = {
+          ...this.planInfo,
+          bomId: this.formInline.bomId,
+          bomType: obj.bomType
+        };
+        this.$refs.purchaseDialogRef.open(arr, data);
+      },
+      success() {
+        this.bomVChange(this.formInline.bomId);
+      },
+      async open(dataList, produceType) {
+        this.initTime();
+        this.produceType = produceType;
+        this.visible = true;
+        this.$nextTick(() => {
+          this.$refs.table.reRenderTable();
+        });
+        // this.$refs.table.reRenderTable();
+        this.cardList = dataList;
+        if (dataList.length === 1) {
+          this.leftShow = false;
+        } else {
+          this.leftShow = true;
+          const firstNodeKey = dataList[0].id;
+          this.$nextTick(() => {
+            this.$refs.treeRef.setCurrentKey(firstNodeKey);
+          });
+        }
+        this.handleNodeClick(dataList[0]);
+      },
+      cancel() {
+        this.clearData();
+        this.visible = false;
+      },
+      clearData() {
+        this.formInline = {
+          finalState: 0,
+          attributeType: 2,
+          bomId: ''
+        };
+        this.baseCount = '';
+        this.baseUnit = '';
+        this.bomListV = [];
+        this.bomListType = [];
+        this.datasourceList = [];
+        this.datasourceAllList = [];
+      },
+      // 获取bom 数据
+      async getBomData(data) {
+        // this.loading = true;
+        try {
+          this.planId = data.id;
+          let params = { categoryId: data.categoryId };
+          const res = await listBomType(params);
+          if (!res || res.length === 0) return;
+          let bomMap = {
+            1: 'PBOM',
+            2: 'MBOM',
+            3: 'ABOM'
+          };
+          let list = res.map((item) => {
+            return {
+              bomType: item.bomType,
+              id: item.id,
+              bomId: item.id,
+              bomName: bomMap[item.bomType]
+            };
+          });
+          let bom = list.find((el) => el.bomType == data.produceType);
+          let bomType = bom ? bom.bomType : list[0].bomType;
+          this.bomListType = list;
+          this.formInline.bomType = bomType;
+          this.bomTypeChange(bomType, 'init');
+          // console.log(res, 'res 333');
+          // const params = {
+          //   categoryId: data.categoryId,
+          //   bomType: data.produceType,
+          //   isTemp: 0
+          // };
+          // const res = bomVersionList(params);
+          return;
+          // const res = await findBomCategoryByCategoryIdV2(
+          //   data.bomCategoryId,
+          //   this.produceType
+          // );
+          // this.loading = false;
+          // if (!res || res.length == 0) {
+          //   this.bomListD = [];
+          //   this.datasourceList = [];
+          //   return;
+          // }
+          // let obj = {};
+          // let bomMap = {
+          //   1: 'PBOM',
+          //   2: 'MBOM',
+          //   3: 'ABOM'
+          // };
+          // if (this.produceType == 1) {
+          //   delete bomMap[1];
+          // }
+          // let result = res.filter((item) => item.bomType != 1);
+          // result.map((el) => {
+          //   el.bomName = bomMap[el.bomType];
+          //   let OBMINFO = {
+          //     bomId: el.bomId,
+          //     bomType: el.type,
+          //     versions: `V${el.versions}.0`
+          //   };
+          //   if (obj[el.bomType]) {
+          //     obj[el.bomType].bomList.unshift(OBMINFO);
+          //   } else {
+          //     obj[el.bomType] = { ...el, bomList: [OBMINFO] };
+          //   }
+          // });
+          // let list = Object.values(obj);
+          // if (list.length == 0) return;
+          // this.bomListType = list;
+          // let bomObj = res.find(
+          //   (item) => item.bomId == this.planInfo.bomCategoryId
+          // );
+          // console.log(bomObj, 'bomObj');
+          // let idx = 0;
+          // if (bomObj && bomObj.bomId) {
+          //   idx = list.findIndex((item) => item.bomType == bomObj.bomType);
+          // }
+          // const index = idx != -1 ? idx : 0;
+          // await this.bomTypeChange(list[index].id, 'init');
+          // this.loading = false;
+        } catch (err) {
+          this.$message.error(err.message);
+          this.loading = false;
+        }
+      },
+
+      issuePlan() {
+        if (this.selection.length == 0) {
+          return this.$message.warning('请至少选择一条数据');
+        }
+        for (let i = 0; i < this.selection.length; i++) {
+          let el = this.selection[i];
+          if (el.finalState == 1) {
+            return this.$message.warning('必须选择最终状态为缺料的数据');
+          }
+          // if (el.hasIssued) {
+          //   return this.$message.warning('必须选择下发状态为未下发的数据');
+          // }
+        }
+        let obj = this.bomListType.find(
+          (el) => el.bomType === this.formInline.bomType
+        );
+        const addPOList = this.fieldProcessing(this.selection);
+        // this.$alert('确定要将所选的数据下发生产计划吗', {
+        //   confirmButtonText: '确定',
+        //   callback: (action) => {
+        //     let param = {
+        //       addPOList: addPOList,
+        //       produceType: obj.bomType,
+        //       id: this.planInfo.id
+        //     };
+        //     batchSave(param).then((res) => {
+        //       this.$message.success('操作成功');
+        //       this.bomVChange(this.formInline.bomId);
+        //     });
+        //   }
+        // });
+
+        this.$confirm('确定要将所选的数据下发生产计划吗?', '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消'
+        })
+          .then(() => {
+            let param = {
+              addPOList: addPOList,
+              produceType: obj.bomType,
+              id: this.planInfo.id
+            };
+            issueProductionPlan(param).then((res) => {
+              this.$message.success('操作成功');
+              this.bomVChange(this.formInline.bomId);
+            });
+          })
+          .catch(() => {});
+
+        // this.$refs.factoryAddRef.open();
+        // this.$refs.factoryAddRef.confirmChoose([row]);
+      },
+
+      // 字段处理
+      fieldProcessing(arr) {
+        // return;
+        let list = arr.map((item) => {
+          return {
+            id: item.resourceBomId,
+            categoryId: item.categoryId,
+            productCode: item.code,
+            productName: item.name,
+            specification: item.specification,
+            produceRoutingId: item.produceRoutingId || '',
+            produceRoutingName: item.produceRoutingName || '',
+            requiredFormingNum: item.finalShortageQuantity,
+            startTime: this.planInfo.startTime,
+            endTime: this.planInfo.endTime,
+            unit: item.unit,
+            brandNo: item.brandNo || '',
+            batchNo: this.planInfo.batchNo,
+            model: item.model,
+            resourceType: 0
+          };
+        });
+        return list || [];
+      },
+      factoryClose(val) {
+        // this.factoryObj = {};
+      },
+      onSubmit() {
+        this.getKitting();
+        this.attributeChange(this.formInline.attributeType);
+      }
+      // confirm() {
+      //   this.visible = false;
+      // },
+      // async merge() {
+      //   this.reload({ planIds: this.ids, productType: 1, merge: 2 });
+      // },
+      // tabClick() {
+      //   this.reload({ planIds: this.ids, productType: 1 });
+      // }
+    }
+  };
+</script>
+
+<style lang="scss" scoped>
+  .mt20 {
+    margin-top: 20px;
+  }
+
+  .el-form-item {
+    margin-bottom: 0 !important;
+  }
+  .optionButton {
+    display: flex;
+    justify-content: flex-end;
+    padding-bottom: 3px;
+  }
+  .statusRed {
+    color: red;
+    :deep(.el-input__inner) {
+      color: red;
+    }
+  }
+
+  .statusGreen {
+    color: green;
+    :deep(.el-input__inner) {
+      color: green;
+    }
+  }
+  .planInfo {
+  }
+  .form-wrapper {
+    display: flex;
+  }
+  .planInfo {
+    // display: flex;
+    font-size: 16px;
+  }
+  .plan_form {
+    .el-col {
+      margin-bottom: 16px;
+    }
+  }
+
+  .left_tree {
+    max-height: 600px;
+    min-height: 300px;
+    overflow: auto;
+    border: 1px solid #ccc;
+    margin-right: 10px;
+  }
+
+  .tool {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 5px 10px;
+    border-left: 1px solid #ededed;
+    border-right: 1px solid #ededed;
+  }
+</style>

+ 23 - 4
src/views/productionPlan/index.vue

@@ -150,12 +150,15 @@
           v-if="$hasPermission('aps:productionplan:changerequest')"
           >变更申请</el-button
         >
-        <el-button
+        <!-- <el-button
           class="my-btn"
           size="mini"
           v-if="$hasPermission('aps:productionplan:changerequest')"
           >补料申请</el-button
-        >
+        > -->
+        <!-- <el-button type="primary" size="mini" @click="productionPreparations"
+          >生产前准备</el-button
+        > -->
         <el-button
           type="info"
           icon="el-icon-upload2"
@@ -378,6 +381,14 @@
           >
             流程
           </el-link>
+
+          <el-link
+            type="primary"
+            :underline="false"
+            @click="productionPreparations(row)"
+          >
+            生产前准备
+          </el-link>
         </template>
       </ele-pro-table>
     </el-card>
@@ -417,6 +428,8 @@
     />
 
     <processDetail ref="processDetailRef" />
+
+    <checkProductionPreparations ref="checkProductionPreparationsRef" />
   </div>
 </template>
 
@@ -445,6 +458,7 @@
   import importDialog from '@/components/upload/import-dialog.vue';
   import { parameterGetByCode } from '@/api/mainData/index';
   import processDetail from './components/detail/processDetail.vue';
+  import checkProductionPreparations from './components/checkProductionPreparations.vue';
 
   import {
     findBomCategoryByCategoryId,
@@ -462,7 +476,8 @@
       homogeneityInspectDialog,
       homogeneityInspectInstallDialog,
       importDialog,
-      processDetail
+      processDetail,
+      checkProductionPreparations
     },
     props: {
       timeDimensionPlanType: { type: Number, default: 1 },
@@ -1287,8 +1302,12 @@
         return this.selection.findIndex((item) => item.id == row.id) >= 0;
       },
 
+      //生产前准备
+      productionPreparations(item) {
+        this.$refs.checkProductionPreparationsRef.open(item);
+      },
+
       goDetail({ id }) {
-        console.log('尽然详情');
         this.$router.push({
           path: '/productionPlan/detail',
           query: { id }