yusheng 2 anni fa
parent
commit
adf7968da8

+ 88 - 0
src/api/bpm/components/saleManage/businessOpportunity.js

@@ -0,0 +1,88 @@
+import request from '@/utils/request';
+
+/**
+ * 获取信息详情
+ */
+export async function getDetail(id) {
+  const res = await request.get(`/eom/businessopportunity/getById/${id}`, {});
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+/**
+ * 更新信息
+ */
+export async function UpdateInformation(data) {
+  const res = await request.put(`/eom/businessopportunity/update`, data);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+/**
+ * 查询客户联系人列表
+ */
+export async function getcontactlink(params) {
+  const res = await request.get(`/eom/contactlink/list/`, {params});
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
+
+// 生产主管指派技术员
+export async function assignTechnician(data) {
+  const res = await request({
+    url: '/bpm/businessOpportunityApprove/assignTechnician',
+    method: 'post',
+    data: data
+  });
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+/**
+ * 更新信息
+ */
+export async function updateTech(data) {
+  const res = await request.put(`/eom/businessopportunity/updateTech`, data);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+/**
+ * 生产主管复核
+ */
+export async function productionSupervisorReApprove(data) {
+  const res = await request.put(`/bpm/businessOpportunityApprove/productionSupervisorReApprove`, data);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+/**
+ * 销售主管审批
+ */
+export async function salesManagerApprove(data) {
+  const res = await request.put(`/bpm/businessOpportunityApprove/salesManagerApprove`, data);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+/**
+ * 销售员补充
+ */
+export async function salesmanApprove(data) {
+  const res = await request.put(`/bpm/businessOpportunityApprove/salesmanApprove`, data);
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}

+ 4 - 0
src/enum/dict.js

@@ -44,6 +44,10 @@ export default {
   流程实例的结果:'bpm_process_instance_result',
   结算方式: 'settlement_mode',
   质保期单位:'date_unit',
+  商机阶段:'business_stage_code',
+  影响力:'influence_level_code',
+  态度:'attittude_code',
+  商机来源:'business_opport_code',
 
 };
 

+ 441 - 0
src/views/bpm/handleTask/components/businessOpportunity/addOpportunityDialog.vue

@@ -0,0 +1,441 @@
+<template>
+  <div
+
+  >
+    <headerTitle title="基本信息"></headerTitle>
+    <el-form
+      label-width="120px"
+      ref="form"
+      :model="form"
+      :rules="rules"
+      style="margin-top: 30px"
+    >
+      <el-row>
+        <el-col :span="8">
+          <el-form-item label="选择客户" prop="contactName">
+            <el-input
+              placeholder="请选择"
+              v-model="form.contactName"
+              @click.native="handParent"
+              maxlength="50"
+            ></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="	商机名称" prop="name">
+            <el-input
+              placeholder="请输入"
+              v-model="form.name"
+              maxlength="50"
+            ></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="选择负责人" prop="responsibleName">
+            <el-input
+              placeholder="请选择"
+              v-model="userInfo.name"
+              disabled
+              maxlength="50"
+            ></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="商机来源" prop="sourceCode">
+            <DictSelection
+              dictName="商机来源"
+              clearable
+              v-model="form.sourceCode"
+            >
+            </DictSelection>
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="赢单率" prop="winRate">
+            <el-input placeholder="请输入内容" v-model="form.winRate">
+              <template slot="append">%</template>
+            </el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="预算" prop="budget">
+            <el-input placeholder="请输入内容" v-model="form.budget">
+              <template slot="append">万元</template>
+            </el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="预计结单日期" prop="expectedClosingDate">
+            <el-date-picker
+              v-model="form.expectedClosingDate"
+              value-format="yyyy-MM-dd"
+              placeholder="请选择"
+              type="date"
+              style="width: 200px"
+              class="filter-item"
+            ></el-date-picker>
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="商机阶段" prop="stageCode">
+            <DictSelection
+              dictName="商机阶段"
+              clearable
+              v-model="form.stageCode"
+            >
+            </DictSelection>
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item prop="files" label="附件">
+            <fileUpload
+              v-model="form.files"
+              module="main"
+              :showLib="false"
+              :limit="5"
+            />
+          </el-form-item>
+        </el-col>
+        <el-col :span="16">
+          <el-form-item label="备注" prop="remark">
+            <el-input
+              placeholder="请输入"
+              v-model="form.remark"
+              type="textarea"
+              maxlength="300"
+            ></el-input>
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+    <headerTitle title="产品清单"></headerTitle>
+    <inventoryTable ref="inventoryTable"></inventoryTable>
+    <headerTitle title="竞品"></headerTitle>
+    <businessAddTable
+      ref="businessAddTable"
+      @timeAll="getDetailTable"
+      :delDetailIds="delDetailIds"
+    ></businessAddTable>
+
+    <headerTitle title="关键人信息"></headerTitle>
+    <personnelAddTable
+      ref="personnelAddTable"
+      :contactId="form.contactId"
+      @timeAll="getPersonnelAddTable"
+      :delDetailIds="PersonnelDetailIds"
+    ></personnelAddTable>
+   
+    <head-list ref="headRef" @changeParent="changeHead"></head-list>
+    <parentList
+      ref="parentRef"
+      classType="1"
+      @changeParent="changeParent"
+    ></parentList>
+  </div>
+</template>
+
+<script>
+  import store from '@/store';
+  import dictMixins from '@/mixins/dictMixins';
+  import businessAddTable from './businessAddTable.vue';
+  import personnelAddTable from './personnelAddTable.vue';
+  import headList from '@/components/headList';
+  import parentList from '@/views/bpm/handleTask/components/saleOrder/parentList.vue';
+  import {
+    getDetail
+  } from '@/api/bpm/components/saleManage/businessOpportunity';
+
+  import { copyObj } from '@/utils/util';
+  import inventoryTable from '@/views/bpm/handleTask/components/saleOrder/inventoryTable.vue';
+  import fileUpload from '@/components/upload/fileUpload';
+
+  export default {
+    props: {
+      businessId: {
+        default: ''
+      }
+    },
+    mixins: [dictMixins],
+    components: {
+      fileUpload,
+      businessAddTable,
+      personnelAddTable,
+      headList,
+      parentList,
+      inventoryTable
+    },
+    data() {
+      let formDef = {
+        id: '',
+        contactName: '',
+        contactId: '',
+        budget: null,
+        expectedClosingDate: '',
+        sourceCode: '',
+        sourceName: '',
+        stageCode: '',
+        stageName: '',
+        winRate: null,
+        name: '',
+        remark: '',
+        responsibleName: '',
+        responsibleId: '',
+        source: '',
+        status: 1,
+        files: []
+      };
+
+      return {
+        delDetailIds: [],
+        PersonnelDetailIds: [],
+        visible: false,
+        title: '',
+        row: {},
+        userInfo: {},
+        activeName: 'base',
+        formDef,
+        form: copyObj(formDef),
+        // removeBankList: [],
+        // removeLinkList: [],
+        tableBankData: [],
+        tableLinkData: [],
+
+        rules: {
+          contactName: [
+            { required: true, message: '请选择客户', trigger: 'change' }
+          ],
+          name: [
+            { required: true, message: '请输入商机名称', trigger: 'blur' }
+          ],
+          expectedClosingDate: [
+            { required: true, message: '请选择预计结单日期', trigger: 'blur' }
+          ],
+          sourceCode: [
+            { required: true, message: '请选择商机来源名称', trigger: 'change' }
+          ],
+          stageCode: [
+            { required: true, message: '请选择商机阶段名称', trigger: 'change' }
+          ],
+          winRate: [
+            {
+              required: false,
+              trigger: 'blur',
+              validator: (_rule, value, callback) => {
+                console.log(_rule);
+                const reg = /^\d+(\.\d{1,2})?$/; // 限制为最多两位小数的正数
+                if (!value) {
+                  callback(); // 验证通过
+                  return;
+                }
+                if (reg.test(value) && parseFloat(value) <= 100) {
+                  callback(); // 验证通过
+                } else {
+                  value = value && value.replace(/[^0-9.]/g, ''); // 只保留数字和小数点
+                  const [integerPart, decimalPart] = value && value.split('.');
+                  if (decimalPart && decimalPart.length > 2) {
+                    callback(new Error('小数位数不能超过两位'));
+                  } else {
+                    callback(new Error('只能输入小于100的数字'));
+                  }
+                }
+              }
+            }
+          ],
+          budget: [
+            {
+              required: false,
+              trigger: 'blur',
+              validator: (_rule, value, callback) => {
+                const reg = /^\d{1,13}(\.\d{1,2})?$/; // 限制为最多两位小数的13位以内的正数
+                if (!value) {
+                  callback(); // 验证通过
+                  return;
+                }
+                if (reg.test(value)) {
+                  callback(); // 验证通过
+                } else {
+                  value = value && value.replace(/[^0-9.]/g, ''); // 只保留数字和小数点
+                  const [integerPart, decimalPart] = value && value.split('.');
+                  if (decimalPart && decimalPart.length > 2) {
+                    callback(new Error('小数位数不能超过两位'));
+                  } else if (integerPart.length > 13) {
+                    callback(new Error('整数位数不能超过13位'));
+                  } else {
+                    callback(new Error('只能输入数字'));
+                  }
+                }
+              }
+            }
+          ]
+        },
+
+        // 提交状态
+        loading: false,
+        // 是否是修改
+        isUpdate: false,
+
+        // 组织机构树形结构数据
+        groupTreeData: [],
+        // 组织机构平铺数据
+        groupData: []
+      };
+    },
+    mounted() {
+      this.$nextTick(() => {
+        this.userInfo = store.state.user.info;
+      });
+    },
+    created() {
+      this.getDetailData(this.businessId);
+    },
+    methods: {
+      //获取竞品
+      getDetailTable(val) {
+        this.form.competAnalysisList = val;
+      },
+      //获取关键人信息
+      getPersonnelAddTable(val) {
+        this.form.partyList = val;
+      },
+      //选择客户回调
+      changeParent(obj) {
+        this.$set(this.form, 'contactId', obj.id);
+        this.$set(this.form, 'contactName', obj.name);
+      },
+      //选择负责人回调
+      changeHead(obj) {
+        this.$set(this.form, 'responsibleId', obj.id);
+        this.$set(this.form, 'responsibleName', obj.name);
+      },
+      handParent() {
+        let item = {
+          id: this.form.contactId
+        };
+        this.$refs.parentRef.open(item);
+      },
+      handHead() {
+        let item = {
+          id: this.form.responsibleId
+        };
+        this.$refs.headRef.open(item);
+      },
+ 
+      //获取详情
+      async getDetailData(id) {
+        this.loading = true;
+        const data = await getDetail(id);
+        this.loading = false;
+        if (data) {
+          this.$nextTick(() => {
+            this.form = data;
+            this.$refs.businessAddTable &&
+              this.$refs.businessAddTable.putTableValue(
+                data.competAnalysisList
+              );
+            this.$refs.personnelAddTable &&
+              this.$refs.personnelAddTable.putTableValue(data.partyList);
+            this.$refs.inventoryTable &&
+              this.$refs.inventoryTable.putTableValue(data.productList);
+          });
+        }
+      },
+
+      salesmanChange(val, info) {
+        this.otherForm.salesmanName = info.name;
+      },
+      settlementModeChange(info) {
+        this.otherForm.settlementModeName = info.dictValue;
+      },
+      ifChiefChange(value, idx) {
+        if (value === 1) {
+          this.tableLinkData.forEach((e) => (e.ifChief = 0));
+          this.tableLinkData[idx].ifChief = 1;
+        }
+      },
+
+      getValidate() {
+        return Promise.all([
+          new Promise((resolve, reject) => {
+            this.$refs.form.validate((valid) => {
+              if (!valid) {
+                reject(false);
+              } else {
+                resolve(true);
+              }
+            });
+          }),
+          new Promise((resolve, reject) => {
+            this.$refs.inventoryTable.validateForm((valid) => {
+              if (!valid) {
+                reject(false);
+              } else {
+                resolve(true);
+              }
+            });
+          }),
+          new Promise((resolve, reject) => {
+            this.$refs.businessAddTable.validateForm((valid) => {
+              if (!valid) {
+                reject(false);
+              } else {
+                resolve(true);
+              }
+            });
+          }),
+          new Promise((resolve, reject) => {
+            this.$refs.personnelAddTable.validateForm((valid) => {
+              if (!valid) {
+                reject(false);
+              } else {
+                resolve(true);
+              }
+            });
+          })
+        ]);
+      },
+      async getTableValue() {
+        try {
+          await this.getValidate();
+          // 表单验证通过,执行保存操作
+
+      
+          let _sourceName = this.getDictValue('商机来源', this.form.sourceCode),
+            _stageName = this.getDictValue('商机阶段', this.form.stageCode);
+          this.form = Object.assign({}, this.form, {
+            responsibleId: this.userInfo.userId,
+            responsibleName: this.userInfo.name,
+            sourceName: _sourceName,
+            stageName: _stageName,
+            files: this.form.files||[]
+          });
+        
+          if (this.$refs.inventoryTable.getTableValue().length == 0) {
+            this.$message.warning('产品清单不能为空');
+
+            return;
+          }
+          const commitData = {
+            opportunity: this.form,
+            competAnalysisList: this.$refs.businessAddTable.getTableValue(),
+            partyList: this.$refs.personnelAddTable.getTableValue(),
+            productList: this.$refs.inventoryTable.getTableValue()
+          };
+          return commitData;
+        } catch (error) {
+          console.log(error);
+          // 表单验证未通过,不执行保存操作
+        }
+      },
+      cancel() {
+        this.$nextTick(() => {
+          this.activeName = 'base';
+          // 关闭后,销毁所有的表单数据
+          this.$refs['otherForm'] && this.$refs['otherForm'].resetFields();
+          this.$refs['formRef'] && this.$refs['formRef'].resetFields();
+          this.form = copyObj(this.formDef);
+          this.visible = false;
+        });
+      }
+    }
+  };
+</script>

+ 185 - 0
src/views/bpm/handleTask/components/businessOpportunity/businessAddTable.vue

@@ -0,0 +1,185 @@
+<template>
+  <el-form ref="form" :model="form" :rules="rules">
+    <ele-pro-table
+      ref="table"
+      :needPage="false"
+      :columns="columns"
+      :datasource="form.datasource"
+      cache-key="systemRoleTable17"
+      class="time-form"
+    >
+      <!-- 表头工具栏 -->
+      <template v-slot:toolbar>
+        <el-button
+          size="small"
+          type="primary"
+          icon="el-icon-plus"
+          class="ele-btn-icon"
+          @click="handlAdd"
+        >
+          新增
+        </el-button>
+      </template>
+      <template v-slot:name="scope">
+        <el-form-item
+          style="margin-bottom: 20px"
+          :prop="'datasource.' + scope.$index + '.name'"
+          :rules="{
+            required: true,
+            message: '请输入',
+            trigger: 'change'
+          }"
+        >
+          <el-input v-model="scope.row.name" placeholder="请输入"></el-input>
+        </el-form-item>
+      </template>
+      <template v-slot:analysis="scope">
+        <el-form-item
+          style="margin-bottom: 20px"
+          :prop="'datasource.' + scope.$index + '.analysis'"
+          :rules="{
+            required: true,
+            message: '请输入',
+            trigger: 'change'
+          }"
+        >
+          <el-input
+            v-model="scope.row.analysis"
+            type="textarea"
+            placeholder="请输入"
+          ></el-input>
+        </el-form-item>
+      </template>
+      <template v-slot:headerName="{ column }">
+        <span class="is-required">{{ column.label }}</span>
+      </template>
+      <template v-slot:headerAnalysis="{ column }">
+        <span class="is-required">{{ column.label }}</span>
+      </template>
+
+      <!-- 操作列 -->
+      <template v-slot:action="{ row }">
+        <el-popconfirm
+          class="ele-action"
+          title="确定要删除吗?"
+          @confirm="remove(row)"
+        >
+          <template v-slot:reference>
+            <el-link type="danger" :underline="false" icon="el-icon-delete">
+              删除
+            </el-link>
+          </template>
+        </el-popconfirm>
+      </template>
+    </ele-pro-table>
+  </el-form>
+</template>
+<script>
+  export default {
+    props: {
+      delDetailIds: Array
+    },
+    data() {
+      const defaultForm = {
+        key: null,
+        name: '',
+        analysis: ''
+      };
+      return {
+        defaultForm,
+        form: {
+          datasource: []
+        },
+        rules: {},
+        columns: [
+          {
+            width: 45,
+            type: 'index',
+            columnKey: 'index',
+            align: 'center',
+            fixed: 'left'
+          },
+
+          {
+            width: 300,
+            prop: 'name',
+            label: '竞争对手名称',
+            slot: 'name',
+            headerSlot:'headerName'
+
+          },
+          {
+            prop: 'analysis',
+            label: '竞品分析',
+            slot: 'analysis',
+            headerSlot:'headerAnalysis'
+          },
+          {
+            width: 100,
+            columnKey: 'action',
+            label: '操作',
+            align: 'center',
+            resizable: false,
+            slot: 'action',
+            showOverflowTooltip: true
+          }
+        ]
+      };
+    },
+    computed: {
+      canHandl() {
+        return this.form.datasource.length;
+      }
+    },
+    methods: {
+      // 返回列表数据
+      getTableValue() {
+        return this.form.datasource;
+      },
+      //修改回显
+      putTableValue(data) {
+        if (data && data?.length) {
+          this.form.datasource = data;
+        }
+      },
+      remove(row) {
+        let index = this.form.datasource.findIndex((n) => n.key == row.key);
+        if (index !== -1) {
+          this.form.datasource.splice(index, 1);
+          this.setSort();
+          if (row.id) {
+            this.delDetailIds.push(row.id);
+          }
+        }
+      },
+      // 清空表格
+      restTable() {
+        this.form.datasource = [];
+      },
+      // 重新排序
+      setSort() {
+        this.form.datasource.forEach((n, index) => {
+          n.key = index + 1;
+        });
+      },
+      // 添加
+      handlAdd() {
+        let item = JSON.parse(JSON.stringify(this.defaultForm));
+        item.key = this.form.datasource.length + 1;
+        this.form.datasource.push(item);
+      },
+
+      validateForm(callback) {
+        //开始表单校验
+        this.$refs.form.validate((valid) => {
+          callback(valid);
+        });
+      }
+    }
+  };
+</script>
+<style lang="scss" scoped>
+  .time-form .el-form-item {
+    margin-bottom: 0 !important;
+  }
+</style>

+ 394 - 0
src/views/bpm/handleTask/components/businessOpportunity/opportunityDetailDialog.vue

@@ -0,0 +1,394 @@
+<template>
+  <div>
+    <el-card
+      shadow="never"
+      header="基本信息"
+      body-style="padding: 22px 20px 0 0;"
+    >
+      <el-form
+        label-width="120px"
+        ref="form"
+        :model="form"
+        :rules="rules"
+        style="margin-top: 30px"
+      >
+        <el-row>
+          <el-col :span="8">
+            <el-form-item label="客户名称:" prop="contactName">
+              {{ detailData.contactName }}
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="	商机名称:" prop="name">
+              {{ detailData.name }}
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="负责人名称:" prop="responsibleName">
+              {{ detailData.responsibleName }}
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="商机来源:" prop="sourceCode">
+              {{ detailData.sourceName }}
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="赢单率:" prop="winRate">
+              {{ detailData.winRate + '%' }}
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="预算:" prop="budget">
+              {{ detailData.budget + '万元' }}
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="预计结单日期:" prop="expectedClosingDate">
+              {{ detailData.expectedClosingDate }}
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="商机阶段:" prop="stageCode">
+              {{ detailData.stageName }}
+            </el-form-item>
+          </el-col>
+          <el-col :span="8">
+            <el-form-item label="附件:" prop="files">
+              <div v-if="detailData.files && detailData.files?.length">
+                <el-link
+                  v-for="link in detailData.files"
+                  :key="link.id"
+                  type="primary"
+                  :underline="false"
+                  @click="downloadFile(link)"
+                >
+                  {{ link.name }}</el-link
+                >
+              </div>
+            </el-form-item>
+          </el-col>
+          <el-col :span="16">
+            <el-form-item label="备注:" prop="remark">
+              {{ detailData.remark }}
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+    </el-card>
+
+    <el-card
+      shadow="never"
+      header="产品清单"
+      body-style="padding: 22px 20px 0 20px;"
+    >
+      <inventoryTable
+        :taskDefinitionKey="taskDefinitionKey"
+        ref="inventoryTable"
+        v-if="taskDefinitionKey === 'technicianApprove'"
+      ></inventoryTable>
+      <ele-pro-table
+        v-if="taskDefinitionKey !== 'technicianApprove'"
+        ref="table"
+        :needPage="false"
+        :columns="competAnalysisListcolumns"
+        :toolkit="[]"
+        :datasource="detailData.productList"
+        row-key="id"
+      >
+        <template v-slot:toolbar>
+          <div class="headbox">
+            <span class="amount">总计:{{ form.totalPrice }}元</span>
+          </div>
+        </template>
+        <template v-slot:technicalDrawings="{ row }">
+          <div v-if="row.technicalDrawings && row.technicalDrawings?.length">
+            <el-link
+              v-for="link in row.technicalDrawings"
+              :key="link.id"
+              type="primary"
+              :underline="false"
+              @click="downloadFile(link)"
+            >
+              {{ link.name }}</el-link
+            >
+          </div>
+        </template>
+      </ele-pro-table>
+    </el-card>
+    <el-card
+      shadow="never"
+      header="竞品对手"
+      body-style="padding: 22px 20px 0 20px;"
+    >
+      <ele-pro-table
+        ref="table"
+        :needPage="false"
+        :columns="competAnalysisListcolumns1"
+        :datasource="detailData.competAnalysisList"
+        row-key="id"
+      >
+      </ele-pro-table>
+    </el-card>
+    <el-card
+      shadow="never"
+      header="关键人信息"
+      body-style="padding: 22px 20px 0 20px;"
+    >
+      <ele-pro-table
+        ref="table"
+        :needPage="false"
+        :columns="partyListcolumns"
+        :datasource="detailData.partyList"
+        row-key="id"
+      >
+      </ele-pro-table>
+    </el-card>
+  </div>
+</template>
+
+<script>
+  import { getDetail } from '@/api/bpm/components/saleManage/businessOpportunity';
+  import { getFile } from '@/api/system/file';
+  import dictMixins from '@/mixins/dictMixins';
+  import { copyObj } from '@/utils/util';
+  import inventoryTable from '@/views/bpm/handleTask/components/saleOrder/inventoryTable.vue';
+
+  export default {
+    mixins: [dictMixins],
+    components:{
+      inventoryTable
+    },
+    props: {
+      businessId: {
+        default: ''
+      },
+      taskDefinitionKey: {
+        default: ''
+      }
+    },
+    data() {
+      return {
+        visible: false,
+        title: '详情',
+        row: {},
+        activeName: 'base',
+        form: {},
+        rules: {},
+        detailData: {},
+        competAnalysisListcolumns1: [
+          {
+            prop: 'name',
+            label: '竞争对手名称',
+            // sortable: 'custom',
+            showOverflowTooltip: true,
+            align: 'center',
+            width: 300
+          },
+          {
+            prop: 'analysis',
+            label: '竞品分析',
+            showOverflowTooltip: true,
+            align: 'center'
+          }
+        ],
+        partyListcolumns: [
+          {
+            prop: 'linkName',
+            label: '关键人名称',
+            // sortable: 'custom',
+            showOverflowTooltip: true,
+            align: 'center',
+            minWidth: 110
+          },
+          {
+            prop: 'linkMobilePhone',
+            label: '联系方式',
+            showOverflowTooltip: true,
+            align: 'center',
+            minWidth: 110
+          },
+          {
+            prop: 'linkPost',
+            label: '职位',
+            showOverflowTooltip: true,
+            align: 'center',
+            minWidth: 110
+          },
+          {
+            prop: 'attitudeName',
+            label: '态度',
+            showOverflowTooltip: true,
+            align: 'center',
+            minWidth: 110
+          },
+          {
+            prop: 'influenceName',
+            label: '影响力',
+            showOverflowTooltip: true,
+            align: 'center',
+            minWidth: 110
+          },
+          {
+            prop: 'goal',
+            label: '项目目标/期望',
+            showOverflowTooltip: true,
+            align: 'center',
+            minWidth: 110
+          }
+        ],
+        competAnalysisListcolumns: [
+          {
+            width: 45,
+            type: 'index',
+            columnKey: 'index',
+            align: 'center',
+            fixed: 'left'
+          },
+          {
+            width: 200,
+            prop: 'productName',
+            label: '名称',
+            slot: 'productName'
+          },
+          {
+            width: 120,
+            prop: 'productCode',
+            label: '编码',
+            slot: 'productCode'
+          },
+          {
+            width: 200,
+            prop: 'productCategoryName',
+            label: '类型',
+            slot: 'productCategoryName'
+          },
+          {
+            width: 160,
+            prop: 'productBrand',
+            label: '牌号',
+            slot: 'productBrand'
+          },
+          {
+            width: 120,
+            prop: 'modelType',
+            label: '型号',
+            slot: 'modelType'
+          },
+          {
+            width: 120,
+            prop: 'specification',
+            label: '规格',
+            slot: 'specification'
+          },
+
+          {
+            width: 160,
+            prop: 'singlePrice',
+            label: '单价',
+            slot: 'singlePrice'
+          },
+          {
+            width: 120,
+            prop: 'totalCount',
+            label: '数量',
+            slot: 'totalCount'
+          },
+          {
+            width: 120,
+            prop: 'totalPrice',
+            label: '销售总金额',
+            slot: 'totalPrice',
+            formatter: (_row, _column, cellValue) => {
+              return _row.totalPrice + '元';
+            }
+          },
+          {
+            width: 120,
+            prop: 'measuringUnit',
+            label: '计量单位',
+            slot: 'measuringUnit'
+          },
+          {
+            width: 120,
+            prop: 'deliveryDays',
+            label: '交期(天)',
+            slot: 'deliveryDays'
+          },
+          {
+            width: 200,
+            prop: 'guaranteePeriod',
+            label: '质保期',
+            slot: 'guaranteePeriod',
+            formatter: (_row, _column, cellValue) => {
+              return _row.guaranteePeriod + _row.guaranteePeriodUnitName;
+            }
+          },
+          // {
+          //   width: 120,
+          //   prop: 'guaranteePeriodUnitCode',
+          //   label: '',
+          //   slot: 'guaranteePeriodUnitCode'
+          // },
+          {
+            width: 120,
+            prop: 'technicalAnswerName',
+            label: '技术答疑人',
+            slot: 'technicalAnswerName'
+          },
+          {
+            width: 220,
+            prop: 'technicalParams',
+            label: '技术参数',
+            slot: 'technicalParams'
+          },
+          {
+            width: 240,
+            prop: 'technicalDrawings',
+            label: '技术图纸',
+            slot: 'technicalDrawings',
+            formatter: (_row, _column, cellValue) => {
+              return _row.guaranteePeriod + _row.guaranteePeriodUnitName;
+            }
+          },
+          {
+            width: 220,
+            prop: 'remark',
+            label: '备注',
+            slot: 'remark'
+          }
+        ]
+      };
+    },
+    created() {
+      this.getDetailData(this.businessId);
+    },
+    methods: {
+      downloadFile(file) {
+        getFile({ objectName: file.storePath }, file.name);
+      },
+      async getDetailData(id) {
+        this.loading = true;
+        const data = await getDetail(id);
+
+        this.loading = false;
+        if (data) {
+          this.detailData = data;
+          this.$refs.inventoryTable &&
+            this.$refs.inventoryTable.putTableValue(data.productList);
+        }
+      },
+      getTableValue() {
+        return this.$refs.inventoryTable.getTableValue();
+      }
+    }
+  };
+</script>
+
+<style scoped lang="scss">
+  .ele-dialog-form {
+    .el-form-item {
+      margin-bottom: 10px;
+    }
+  }
+</style>

+ 341 - 0
src/views/bpm/handleTask/components/businessOpportunity/personnelAddTable.vue

@@ -0,0 +1,341 @@
+<template>
+  <el-form ref="form" :model="form" :rules="rules">
+    <ele-pro-table
+      ref="table"
+      :needPage="false"
+      :columns="columns"
+      :datasource="form.datasource"
+      cache-key="systemRoleTable17"
+      class="time-form"
+    >
+      <!-- 表头工具栏 -->
+      <template v-slot:toolbar>
+        <el-button
+          size="small"
+          type="primary"
+          icon="el-icon-plus"
+          class="ele-btn-icon"
+          @click="handlAdd"
+        >
+          新增
+        </el-button>
+      </template>
+      <template v-slot:linkId="scope">
+        <el-form-item
+          style="margin-bottom: 20px"
+          :prop="'datasource.' + scope.$index + '.linkId'"
+          :rules="{
+            required: true,
+            message: '请选择',
+            trigger: 'change'
+          }"
+        >
+          <el-select
+            v-model="scope.row.linkId"
+            placeholder="请选择"
+            @change="changePersonel(scope.row, $event)"
+          >
+            <el-option
+              v-for="item in linkNameOptions"
+              :key="item.id"
+              :label="item.linkName"
+              :value="item.id"
+            >
+            </el-option>
+          </el-select>
+        </el-form-item>
+      </template>
+      <template v-slot:linkPost="scope">
+        <el-form-item
+          style="margin-bottom: 20px"
+          :prop="'datasource.' + scope.$index + '.linkPost'"
+        >
+          <el-input
+            v-model="scope.row.linkPost"
+            disabled
+            placeholder="请输入"
+          ></el-input>
+        </el-form-item>
+      </template>
+      <template v-slot:linkMobilePhone="scope">
+        <el-form-item
+          style="margin-bottom: 20px"
+          :prop="'datasource.' + scope.$index + '.linkMobilePhone'"
+        >
+          <el-input
+            v-model="scope.row.linkMobilePhone"
+            disabled
+            placeholder="请输入"
+          ></el-input>
+        </el-form-item>
+      </template>
+      <template v-slot:goal="scope">
+        <el-form-item
+          style="margin-bottom: 20px"
+          :prop="'datasource.' + scope.$index + '.goal'"
+          :rules="{
+            required: true,
+            message: '请输入',
+            trigger: 'change'
+          }"
+        >
+          <el-input v-model="scope.row.goal" placeholder="请输入"></el-input>
+        </el-form-item>
+      </template>
+      <template v-slot:influenceCode="scope">
+        <el-form-item
+          style="margin-bottom: 20px"
+          :prop="'datasource.' + scope.$index + '.influenceCode'"
+          :rules="{
+            required: true,
+            message: '请选择',
+            trigger: 'change'
+          }"
+        >
+          <DictSelection
+            dictName="影响力"
+            clearable
+            v-model="scope.row.influenceCode"
+          >
+          </DictSelection>
+        </el-form-item>
+      </template>
+      <template v-slot:attitudeCode="scope">
+        <el-form-item
+          style="margin-bottom: 20px"
+          :prop="'datasource.' + scope.$index + '.attitudeCode'"
+          :rules="{
+            required: true,
+            message: '请选择',
+            trigger: 'change'
+          }"
+        >
+          <DictSelection
+            dictName="态度"
+            clearable
+            v-model="scope.row.attitudeCode"
+          >
+          </DictSelection>
+        </el-form-item>
+      </template>
+      <template v-slot:headerLinkId="{ column }">
+        <span class="is-required">{{ column.label }}</span>
+      </template>
+      <template v-slot:headerGoal="{ column }">
+        <span class="is-required">{{ column.label }}</span>
+      </template>
+      <template v-slot:headerInfluenceCode="{ column }">
+        <span class="is-required">{{ column.label }}</span>
+      </template>
+      <template v-slot:headerAttitudeCode="{ column }">
+        <span class="is-required">{{ column.label }}</span>
+      </template>
+
+      <!-- 操作列 -->
+      <template v-slot:action="{ row }">
+        <el-popconfirm
+          class="ele-action"
+          title="确定要删除吗?"
+          @confirm="remove(row)"
+        >
+          <template v-slot:reference>
+            <el-link type="danger" :underline="false" icon="el-icon-delete">
+              删除
+            </el-link>
+          </template>
+        </el-popconfirm>
+      </template>
+    </ele-pro-table>
+  </el-form>
+</template>
+<script>
+  import {
+    getcontactlink
+  } from '@/api/bpm/components/saleManage/businessOpportunity';
+
+  import dictMixins from '@/mixins/dictMixins';
+  export default {
+    props: {
+      delDetailIds: Array,
+      contactId: {
+        type: String,
+        default: ''
+      }
+    },
+    mixins: [dictMixins],
+    data() {
+      const defaultForm = {
+        key: null,
+        linkId: '',
+        linkPost: '',
+        linkMobilePhone: '',
+        goal: '',
+        influenceCode: '',
+        influenceName: '',
+        attitudeCode: '',
+        attitudeName: ''
+      };
+      return {
+        newContactId: '',
+        linkNameOptions: [],
+        defaultForm,
+        form: {
+          datasource: []
+        },
+        rules: {},
+        columns: [
+          {
+            width: 45,
+            type: 'index',
+            columnKey: 'index',
+            align: 'center',
+            fixed: 'left'
+          },
+
+          {
+            prop: 'linkId',
+            label: '姓名',
+            slot: 'linkId',
+            headerSlot:"headerLinkId"
+          },
+          {
+            prop: 'linkPost',
+            label: '职位',
+            slot: 'linkPost'
+          },
+          {
+            prop: 'linkMobilePhone',
+            label: '联系方式',
+            slot: 'linkMobilePhone'
+          },
+          {
+            prop: 'goal',
+            label: '项目目标/期望',
+            slot: 'goal',
+            headerSlot:"headerGoal"
+
+          },
+          {
+            prop: 'influenceCode',
+            label: '影响力',
+            slot: 'influenceCode',
+            headerSlot:"headerInfluenceCode"
+
+          },
+          {
+            prop: 'attitudeCode',
+            label: '态度',
+            slot: 'attitudeCode',
+            headerSlot:"headerAttitudeCode"
+
+          },
+          {
+            width: 100,
+            columnKey: 'action',
+            label: '操作',
+            align: 'center',
+            resizable: false,
+            slot: 'action',
+            showOverflowTooltip: true
+          }
+        ]
+      };
+    },
+    computed: {
+      canHandl() {
+        return this.form.datasource.length;
+      }
+    },
+    watch: {
+      contactId(val) {
+        if (val) {
+          this.newContactId = val;
+          this.getContactIdInfo();
+        }
+      }
+    },
+    methods: {
+      //初始数据
+      async getContactIdInfo() {
+        const data = await getcontactlink({ contactId: this.newContactId });
+        if (data && data?.length) {
+          this.linkNameOptions = data;
+        }
+      },
+      //修改回显
+      putTableValue(data) {
+        if (data && data?.length) {
+          this.form.datasource = data;
+        }
+      },
+      // 返回列表数据
+      getTableValue() {
+        let _datasource = this.form.datasource || [];
+        if (_datasource.length === 0) return;
+        _datasource.forEach((v, index) => {
+          if (_datasource[index].influenceCode) {
+            _datasource[index].influenceName = this.getDictValue(
+              '影响力',
+              _datasource[index].influenceCode
+            );
+          }
+          if (_datasource[index].attitudeCode) {
+            _datasource[index].attitudeName = this.getDictValue(
+              '态度',
+              _datasource[index].attitudeCode
+            );
+          }
+        });
+        return _datasource;
+      },
+      //选择关键人
+      changePersonel(row, val) {
+        console.log(row, val);
+        let selectVal = this.linkNameOptions.find((v) => v.id === val);
+        row.linkPost = selectVal.post;
+        row.linkMobilePhone = selectVal.phone;
+      },
+      remove(row) {
+        let index = this.form.datasource.findIndex((n) => n.key == row.key);
+        if (index !== -1) {
+          this.form.datasource.splice(index, 1);
+          this.setSort();
+          if (row.id) {
+            this.delDetailIds.push(row.id);
+          }
+        }
+      },
+      // 清空表格
+      restTable() {
+        this.form.datasource = [];
+      },
+      // 重新排序
+      setSort() {
+        this.form.datasource.forEach((n, index) => {
+          n.key = index + 1;
+        });
+      },
+      // 添加
+      handlAdd() {
+        if (!this.newContactId) {
+          return this.$message.error('请先选择客户');
+        }
+        let item = JSON.parse(JSON.stringify(this.defaultForm));
+        item.key = this.form.datasource.length + 1;
+        this.form.datasource.push(item);
+      },
+
+      validateForm(callback) {
+        //开始表单校验
+        this.$refs.form.validate((valid) => {
+          callback(valid);
+        });
+      }
+    }
+  };
+</script>
+<style lang="scss" scoped>
+  .time-form .el-form-item {
+    margin-bottom: 0 !important;
+  }
+</style>

+ 238 - 0
src/views/bpm/handleTask/components/businessOpportunity/submit.vue

@@ -0,0 +1,238 @@
+<template>
+  <el-col :span="16" :offset="6">
+    <el-form label-width="100px" ref="formRef" :model="form">
+      <el-form-item
+        label="技术员"
+        prop="technicianId"
+        style="margin-bottom: 20px"
+        :rules="{
+          required: true,
+          message: '请选择',
+          trigger: 'change'
+        }"
+        v-if="taskDefinitionKey == 'productionSupervisorApprove1'"
+      >
+        <el-select
+          v-model="form.technicianId"
+          clearable
+          style="width: 100%"
+          :filterable="true"
+        >
+          <el-option
+            v-for="item in userOptions"
+            :key="item.id"
+            :label="item.name"
+            :value="item.id"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="审批建议" prop="reason" style="margin-bottom: 20px">
+        <el-input
+          type="textarea"
+          v-model="form.reason"
+          placeholder="请输入审批建议"
+        />
+      </el-form-item>
+    </el-form>
+    <div style="margin-left: 10%; margin-bottom: 20px; font-size: 14px">
+      <el-button
+        icon="el-icon-edit-outline"
+        type="success"
+        size="mini"
+        @click="handleAudit(1)"
+        >通过
+      </el-button>
+      <el-button
+        icon="el-icon-circle-close"
+        type="danger"
+        size="mini"
+        @click="handleAudit(0)"
+        v-if="
+          [
+            'productionSupervisorApprove2',
+            'salesManagerApprove',
+            'salesmanApprove'
+          ].includes(taskDefinitionKey)
+        "
+        >驳回
+      </el-button>
+
+      <!-- <el-button
+        icon="el-icon-circle-close"
+        type="danger"
+        size="mini"
+        @click="handleBackList"
+        >退回
+      </el-button> -->
+      <!-- <el-button
+        icon="el-icon-circle-close"
+        type="danger"
+        size="mini"
+        @click="handleAudit(0)"
+        v-if="taskDefinitionKey != 'productionSupervisorApprove1'"
+        >不通过
+      </el-button>
+      <el-button
+        icon="el-icon-edit-outline"
+        type="primary"
+        size="mini"
+        v-if="taskDefinitionKey != 'productionSupervisorApprove1'"
+        @click="handleUpdateAssignee"
+        >转办
+      </el-button> -->
+    </div>
+  </el-col>
+</template>
+
+<script>
+  import {
+    assignTechnician,
+    updateTech,
+    productionSupervisorReApprove,
+    UpdateInformation,
+    salesManagerApprove,
+    salesmanApprove
+  } from '@/api/bpm/components/saleManage/businessOpportunity';
+  import { approveTask, } from '@/api/bpm/task';
+  import { listAllUserBind } from '@/api/system/organization';
+
+  // 流程实例的详情页,可用于审批
+  export default {
+    name: '',
+    components: {
+      //   Parser
+    },
+    props: {
+      businessId: {
+        default: ''
+      },
+      taskId: {
+        default: ''
+      },
+      id: {
+        default: ''
+      },
+      id: {
+        default: ''
+      },
+      taskDefinitionKey: {
+        default: ''
+      }
+    },
+    data() {
+      return {
+        form: {
+          technicianId: '',
+          reason: ''
+        },
+        userOptions: []
+      };
+    },
+    created() {
+      this.userOptions = [];
+      listAllUserBind().then((data) => {
+        this.userOptions.push(...data);
+      });
+    },
+    methods: {
+      /** 处理转办审批人 */
+      handleUpdateAssignee() {
+        this.$emit('handleUpdateAssignee');
+      },
+      /** 退回 */
+      handleBackList() {
+        this.$emit('handleBackList');
+      },
+
+      async handleAudit(status) {
+        //生产主管审批选择技术员
+        if (this.taskDefinitionKey === 'productionSupervisorApprove1') {
+          if (!this.form.technicianId) {
+            this.$message.warning(`请选择技术人员!`);
+            return;
+          }
+
+          await assignTechnician({
+            businessId: this.businessId,
+            id: this.taskId,
+            reason: this.form.reason,
+            technicianId: this.form.technicianId
+          });
+          this.$emit('handleAudit', {status});
+        }
+        //技术员修改
+        if (this.taskDefinitionKey === 'technicianApprove') {
+          this.$emit('getTableValue', async (data) => {
+            let arr = data.map((item) => {
+              return {
+                id: item.id,
+                technicalAnswerId: item.technicalAnswerId,
+                technicalAnswerName: item.technicalAnswerName,
+                technicalDrawings: item.technicalDrawings,
+                technicalParams: item.technicalParams
+              };
+            });
+            try {
+              await updateTech(arr);
+              await this._approveTask();
+              this.$emit('handleAudit', {status});
+            } catch (error) {}
+          });
+        }
+        //生产主管审批
+        if (this.taskDefinitionKey === 'productionSupervisorApprove2') {
+          try {
+            await productionSupervisorReApprove({
+              id: this.taskId,
+              reason: this.form.reason,
+              status
+            });
+            this.$emit('handleAudit', {status,title: status === 0 ? '驳回' : ''});
+          } catch (error) {}
+        }
+        //销售员补充
+        if (this.taskDefinitionKey === 'salesmanApprove') {
+          if (status === 0) {
+            await this.salesmanApproveHttp(status);
+            this.$emit('handleAudit',{status,title: '驳回'});
+            return;
+          }
+          this.$emit('getTableValue', async (data) => {
+            let arr = await data;
+            if (arr) {
+              try {
+                await UpdateInformation(arr);
+                await this.salesmanApproveHttp(status);
+                this.$emit('handleAudit', {status});
+              } catch (error) {}
+            }
+          });
+        }
+        //销售主管审批
+        if (this.taskDefinitionKey === 'salesManagerApprove') {
+          try {
+            await salesManagerApprove({
+              id: this.taskId,
+              businessId: this.businessId,
+              reason: this.form.reason,
+              status
+            });
+            this.$emit('handleAudit', {status,title: status === 0 ? '驳回' : ''});
+          } catch (error) {}
+        }
+      },
+      salesmanApproveHttp(status) {
+        return salesmanApprove({
+          id: this.taskId,
+          reason: this.form.reason,
+          status
+        });
+      },
+      _approveTask() {
+        return approveTask({ id: this.taskId, reason: this.form.reason });
+      }
+    }
+  };
+</script>
+
+<style lang="scss"></style>

+ 1 - 1
src/views/bpm/handleTask/index.vue

@@ -382,7 +382,7 @@
           // );
           // const bizpath = formCustomCreatePath.pcView;
           // const subpath = formCustomCreatePath.pcHandle;
-
+console.log(this.listData.pcViewRouter)
           Vue.component('async-biz-form-component', (resolve) => {
             require([`@/views${this.listData.pcViewRouter}`], resolve);
           });