浏览代码

Merge branch 'develop' of http://code.dashoo.cn/chengjian/opms_frontend into develop

ZZH-wl 2 年之前
父节点
当前提交
d0b45ba706
共有 29 个文件被更改,包括 2945 次插入106 次删除
  1. 42 0
      src/api/base/partner.js
  2. 29 0
      src/api/work/deliver.js
  3. 31 0
      src/api/work/deliverPlan.js
  4. 34 0
      src/api/work/deliverWork.js
  5. 24 0
      src/api/work/index.js
  6. 110 0
      src/views/base/partners/components/PartnerEdit.vue
  7. 512 0
      src/views/base/partners/detail.vue
  8. 450 0
      src/views/base/partners/index.vue
  9. 6 2
      src/views/contract/components/DetailsCollection.vue
  10. 13 13
      src/views/plat/task/all.vue
  11. 4 4
      src/views/plat/task/components/TaskAdd.vue
  12. 1 1
      src/views/plat/task/components/TaskDetail.vue
  13. 4 4
      src/views/plat/task/detail.vue
  14. 15 15
      src/views/plat/task/index.vue
  15. 8 2
      src/views/proj/business/components/BusinessEdit.vue
  16. 18 6
      src/views/proj/business/components/FollowAdd.vue
  17. 5 2
      src/views/proj/business/detail.vue
  18. 48 8
      src/views/work/order/components/DingTalkFromToVue.vue
  19. 18 5
      src/views/work/order/components/Edit.vue
  20. 46 13
      src/views/work/order/components/Feedback.vue
  21. 113 0
      src/views/work/order/deliver/components/editPlan.vue
  22. 128 0
      src/views/work/order/deliver/components/editWork.vue
  23. 59 0
      src/views/work/order/deliver/components/finish.vue
  24. 72 0
      src/views/work/order/deliver/components/selectStatus.vue
  25. 105 0
      src/views/work/order/deliver/components/updateUser.vue
  26. 273 0
      src/views/work/order/deliver/index.vue
  27. 512 0
      src/views/work/order/deliver/plan.vue
  28. 168 10
      src/views/work/order/detail.vue
  29. 97 21
      src/views/work/order/index.vue

+ 42 - 0
src/api/base/partner.js

@@ -0,0 +1,42 @@
+import micro_request from '@/utils/micro_request'
+
+const basePath = process.env.VUE_APP_ParentPath
+export default {
+  getCompanyList(query) {
+    return micro_request.postRequest(basePath, 'PartnerCompany', 'List', query)
+  },
+  addCompany(query) {
+    return micro_request.postRequest(basePath, 'PartnerCompany', 'Add', query)
+  },
+  updateCompany(query) {
+    return micro_request.postRequest(basePath, 'PartnerCompany', 'Update', query)
+  },
+  deleteCompany(query) {
+    return micro_request.postRequest(basePath, 'PartnerCompany', 'Delete', query)
+  },
+  getCompany(query) {
+    return micro_request.postRequest(basePath, 'PartnerCompany', 'Get', query)
+  },
+
+  getCompanyConcatList(query) {
+    return micro_request.postRequest(basePath, 'PartnerCompanyContact', 'List', query)
+  },
+  addCompanyConcat(query) {
+    return micro_request.postRequest(basePath, 'PartnerCompanyContact', 'Add', query)
+  },
+  updateCompanyConcat(query) {
+    return micro_request.postRequest(basePath, 'PartnerCompanyContact', 'Update', query)
+  },
+  deleteCompanyConcat(query) {
+    return micro_request.postRequest(basePath, 'PartnerCompanyContact', 'Delete', query)
+  },
+  getCompanyConcat(query) {
+    return micro_request.postRequest(basePath, 'PartnerCompanyContact', 'Get', query)
+  },
+  downloadFile(query) {
+    return micro_request.postRequest(basePath, 'PartnerCompanyContact', 'ExcelTemplate', query)
+  },
+  exportFile(query) {
+    return micro_request.postRequest(basePath, 'PartnerCompanyContact', 'ExcelUpload', query)
+  },
+}

+ 29 - 0
src/api/work/deliver.js

@@ -0,0 +1,29 @@
+/*
+ * @Author: liuzhenlin 461480418@qq.ocm
+ * @Date: 2023-03-21 16:33:47
+ * @LastEditors: liuzhenlin
+ * @LastEditTime: 2023-06-20 11:09:40
+ * @Description: file content
+ * @FilePath: \订单全流程管理系统\src\api\work\index.js
+ */
+import micro_request from '@/utils/micro_request'
+
+const basePath = process.env.VUE_APP_ParentPath
+export default {
+  // 工单列表
+  addDeliverOrder(query) {
+    return micro_request.postRequest(basePath, 'DeliverOrder', 'Add', query)
+  },
+  getDeliverOrderList(query) {
+    return micro_request.postRequest(basePath, 'DeliverOrder', 'List', query)
+  },
+  updateDeliverOrder(query) {
+    return micro_request.postRequest(basePath, 'DeliverOrder', 'Update', query)
+  },
+  getDeliverOrder(query) {
+    return micro_request.postRequest(basePath, 'DeliverOrder', 'Get', query)
+  },
+  finish(query) {
+    return micro_request.postRequest(basePath, 'DeliverOrder', 'Finish', query)
+  },
+}

+ 31 - 0
src/api/work/deliverPlan.js

@@ -0,0 +1,31 @@
+/*
+ * @Author: liuzhenlin 461480418@qq.ocm
+ * @Date: 2023-03-21 16:33:47
+ * @LastEditors: liuzhenlin
+ * @LastEditTime: 2023-06-20 11:09:40
+ * @Description: file content
+ * @FilePath: \订单全流程管理系统\src\api\work\index.js
+ */
+import micro_request from '@/utils/micro_request'
+
+const basePath = process.env.VUE_APP_ParentPath
+export default {
+  addPlan(query) {
+    return micro_request.postRequest(basePath, 'DeliverOrderPlan', 'Add', query)
+  },
+  getList(query) {
+    return micro_request.postRequest(basePath, 'DeliverOrderPlan', 'List', query)
+  },
+  updatePlan(query) {
+    return micro_request.postRequest(basePath, 'DeliverOrderPlan', 'Update', query)
+  },
+  getPlan(query) {
+    return micro_request.postRequest(basePath, 'DeliverOrderPlan', 'Get', query)
+  },
+  startPlan(query) {
+    return micro_request.postRequest(basePath, 'DeliverOrderPlan', 'Start', query)
+  },
+  finishPlan(query) {
+    return micro_request.postRequest(basePath, 'DeliverOrderPlan', 'Finish', query)
+  },
+}

+ 34 - 0
src/api/work/deliverWork.js

@@ -0,0 +1,34 @@
+/*
+ * @Author: liuzhenlin 461480418@qq.ocm
+ * @Date: 2023-03-21 16:33:47
+ * @LastEditors: liuzhenlin
+ * @LastEditTime: 2023-06-20 11:09:40
+ * @Description: file content
+ * @FilePath: \订单全流程管理系统\src\api\work\index.js
+ */
+import micro_request from '@/utils/micro_request'
+
+const basePath = process.env.VUE_APP_ParentPath
+export default {
+  add(query) {
+    return micro_request.postRequest(basePath, 'DeliverOrderProgress', 'Add', query)
+  },
+  list(query) {
+    return micro_request.postRequest(basePath, 'DeliverOrderProgress', 'List', query)
+  },
+  update(query) {
+    return micro_request.postRequest(basePath, 'DeliverOrderProgress', 'Update', query)
+  },
+  get(query) {
+    return micro_request.postRequest(basePath, 'DeliverOrderProgress', 'Get', query)
+  },
+  delete(query) {
+    return micro_request.postRequest(basePath, 'DeliverOrderProgress', 'Delete', query)
+  },
+  startWork(query) {
+    return micro_request.postRequest(basePath, 'DeliverOrderProgress', 'Start', query)
+  },
+  finishWork(query) {
+    return micro_request.postRequest(basePath, 'DeliverOrderProgress', 'Finish', query)
+  },
+}

+ 24 - 0
src/api/work/index.js

@@ -1,3 +1,11 @@
+/*
+ * @Author: liuzhenlin 461480418@qq.ocm
+ * @Date: 2023-03-21 16:33:47
+ * @LastEditors: liuzhenlin
+ * @LastEditTime: 2023-06-20 11:09:40
+ * @Description: file content
+ * @FilePath: \订单全流程管理系统\src\api\work\index.js
+ */
 import micro_request from '@/utils/micro_request'
 
 const basePath = process.env.VUE_APP_ParentPath
@@ -32,4 +40,20 @@ export default {
   getWorkOrderFeedbackByDay(query) {
     return micro_request.postRequest(basePath, 'WorkOrder', 'GetWorkOrderFeedbackByDay', query)
   },
+  // 工单动态
+  dynamicsList(query) {
+    return micro_request.postRequest(basePath, 'WorkOrder', 'DynamicsList', query)
+  },
+  // 添加工单动态
+  addDynamics(query) {
+    return micro_request.postRequest(basePath, 'WorkOrder', 'AddDynamics', query)
+  },
+  // 完成工单
+  finishWorkOrder(query) {
+    return micro_request.postRequest(basePath, 'WorkOrder', 'Finish', query)
+  },
+  // 关闭工单
+  closeWorkOrder(query) {
+    return micro_request.postRequest(basePath, 'WorkOrder', 'Close', query)
+  },
 }

+ 110 - 0
src/views/base/partners/components/PartnerEdit.vue

@@ -0,0 +1,110 @@
+<template>
+  <el-dialog append-to-body :title="title" :visible.sync="dialogFormVisible" @close="close">
+    <el-form ref="form" :model="form" :rules="rules">
+      <el-row :gutter="20">
+        <el-col :span="12">
+          <el-form-item label="姓名" prop="name">
+            <el-input v-model="form.name" placeholder="请输入姓名" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="电话" prop="contactWay">
+            <el-input v-model="form.contactWay" maxlength="11" placeholder="请输入电话" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row :gutter="20">
+        <el-col :span="12">
+          <el-form-item label="岗位" prop="post">
+            <el-input v-model="form.post" placeholder="请输入岗位" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="负责区域" prop="territory">
+            <el-input v-model="form.territory" placeholder="请输入负责区域" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row :gutter="20">
+        <el-col :span="24">
+          <el-form-item label="备注" prop="remark">
+            <el-input v-model="form.remark" placeholder="请输入备注" type="textarea" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+    <span slot="footer">
+      <el-button @click="close">取 消</el-button>
+      <el-button type="primary" @click="save">确 定</el-button>
+    </span>
+  </el-dialog>
+</template>
+<script>
+  import partnerApi from '@/api/base/partner'
+  export default {
+    name: 'UserEdit',
+    data() {
+      return {
+        form: {
+          contactWay: '',
+          name: '',
+          partnerId: null,
+          post: '',
+          remark: '',
+          territory: '',
+          id: null,
+        },
+        rules: {
+          name: [{ required: true, trigger: 'blur', message: '请输入姓名' }],
+          territory: [{ required: true, trigger: 'blur', message: '请输入负责区域' }],
+        },
+        //省份
+        title: '',
+        dialogFormVisible: false,
+      }
+    },
+    mounted() {},
+    methods: {
+      open(row) {
+        console.log(isNaN(row))
+        if (isNaN(row)) {
+          this.title = '编辑'
+          this.form = Object.assign({ ...row })
+        } else {
+          this.title = '新建'
+          this.form.partnerId = row
+        }
+        this.dialogFormVisible = true
+      },
+      close() {
+        this.$refs['form'].resetFields()
+        this.form = {
+          contactWay: '',
+          name: '',
+          partnerId: null,
+          post: '',
+          remark: '',
+          territory: '',
+          id: null,
+        }
+        this.dialogFormVisible = false
+      },
+      save() {
+        this.$refs['form'].validate(async (valid) => {
+          if (valid) {
+            let params = { ...this.form }
+            if (!this.form.id) {
+              const { msg } = await partnerApi.addCompanyConcat(params)
+              this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+            } else {
+              const { msg } = await partnerApi.updateCompanyConcat(params)
+              this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+            }
+            this.$emit('fetch-data')
+            this.close()
+          }
+        })
+      },
+    },
+  }
+</script>

+ 512 - 0
src/views/base/partners/detail.vue

@@ -0,0 +1,512 @@
+<!--
+ * @Author: wanglj 471442253@qq.com
+ * @Date: 2022-12-26 09:30:47
+ * @LastEditors: liuzhenlin
+ * @LastEditTime: 2023-06-30 18:22:00
+ * @Description: file content
+ * @FilePath: \订单全流程管理系统\src\views\base\partners\detail.vue
+-->
+<template>
+  <div class="detail">
+    <div class="side-layout">
+      <div>
+        <div class="title">
+          <p>合作伙伴</p>
+          <h3>
+            <div>{{ detail.title }}</div>
+            <el-button @click="handleFollow">添加跟进</el-button>
+          </h3>
+        </div>
+        <header>
+          <el-descriptions :colon="false" :column="7" direction="vertical">
+            <el-descriptions-item content-class-name="my-content" label="负责区域" label-class-name="my-label">
+              {{ detail.territory }}
+            </el-descriptions-item>
+            <el-descriptions-item content-class-name="my-content" label="联系人" label-class-name="my-label">
+              {{ detail.name }}
+            </el-descriptions-item>
+            <el-descriptions-item content-class-name="my-content" label="联系方式" label-class-name="my-label">
+              {{ detail.contactWay }}
+            </el-descriptions-item>
+            <el-descriptions-item content-class-name="my-content" label="岗位" label-class-name="my-label">
+              {{ detail.post }}
+            </el-descriptions-item>
+            <el-descriptions-item content-class-name="my-content" label="备注" label-class-name="my-label">
+              {{ detail.remark }}
+            </el-descriptions-item>
+          </el-descriptions>
+        </header>
+        <el-tabs v-model="activeName">
+          <el-tab-pane label="跟进记录" name="follow">
+            <ul v-if="followList.length" class="follow">
+              <li v-for="(date, index) in followList" :key="index">
+                <div class="date">
+                  <h2>{{ date.followDay.split('-')[2] }}</h2>
+                  <h3>
+                    {{ date.followDay.split('-').splice(0, 2).join('.') }}
+                  </h3>
+                </div>
+                <ul class="content">
+                  <li v-for="(item, idx) in date.followupList" :key="idx">
+                    <!-- <el-avatar class="user-avatar"
+              :src="avatar" />-->
+                    <div class="text-container">
+                      <vab-icon class="user-avatar" icon="account-circle-fill" />
+                      <div class="text">
+                        <p class="action">
+                          <span>{{ item.createdName }} 跟进({{ formatType(item.followType) }})</span>
+                          <span>
+                            <vab-icon icon="time-line" />
+                            {{ item.followDate }}
+                          </span>
+                        </p>
+                        <p>{{ item.followContent }}</p>
+                        <div class="footer">
+                          <p></p>
+                          <div>
+                            <el-button icon="el-icon-edit" size="mini" @click="postComments(item)">发表评论</el-button>
+                            <el-button size="mini" @click="showDetail(item)">
+                              <vab-icon icon="arrow-right-circle-fill" />
+                              详情
+                            </el-button>
+                            <el-button size="mini" @click="showComment(item)">评论({{ item.commentNumber }})</el-button>
+                          </div>
+                        </div>
+                      </div>
+                    </div>
+                    <transition name="height">
+                      <ul v-if="item.showComment" class="comments">
+                        <li v-for="comment in item.comments" :key="comment.id">
+                          <vab-icon class="user-avatar" icon="account-circle-fill" />
+                          <div class="text">
+                            <p>{{ comment.createdName }}</p>
+                            <p>{{ comment.content }}</p>
+                            <p>{{ comment.createdTime }}</p>
+                          </div>
+                        </li>
+                      </ul>
+                    </transition>
+                  </li>
+                </ul>
+              </li>
+            </ul>
+            <div v-else class="no-follow">暂无跟进记录</div>
+          </el-tab-pane>
+        </el-tabs>
+      </div>
+    </div>
+    <!-- 跟进详情 -->
+    <FollowDetail ref="followDetail" />
+    <!-- 添加跟进记录 -->
+    <follow-add ref="follow-add" @fetch-data="getFollowList" />
+  </div>
+</template>
+
+<script>
+  import follow from '@/api/customer/follow'
+  import to from 'await-to-js'
+  import FollowDetail from '../components/FollowDetail'
+  import partnerApi from '@/api/base/partner'
+  import FollowAdd from '@/views/proj/business/components/FollowAdd'
+
+  export default {
+    name: 'CustomerDetail',
+    components: {
+      FollowDetail,
+      FollowAdd,
+    },
+    data() {
+      return {
+        id: '',
+        detail: {
+          custCode: '', //客户编码
+          abbrName: '', //助记名
+          level: '', //客户级别
+          indusTry: '', //客户类型
+          custStatus: '', //客户状态
+          followUpDate: '', //最后跟进时间
+        },
+        activeName: 'follow',
+        followList: [],
+        bidInfoTypeOptions: {},
+      }
+    },
+    mounted() {
+      this.id = this.$route.query.id
+      this.init()
+      this.getFollowList()
+    },
+    methods: {
+      // 添加跟进记录
+      handleFollow() {
+        this.followup = {
+          targetId: this.detail.id,
+          targetType: '70',
+          targetName: this.detail.name,
+          custId: '',
+          custName: '',
+        }
+        this.$refs['follow-add'].showEdit(this.followup)
+      },
+      async getFollowList() {
+        let params = {
+          targetId: '' + this.id,
+          targetType: '70',
+          DaysBeforeToday: 99999,
+        }
+        const [err, res] = await to(follow.getListByDay(params))
+        if (err) return
+        this.followList = res.data.list || []
+      },
+      // 发表评论
+      postComments(row) {
+        this.$PostComment({ form: row, visible: true })
+          .then(() => {
+            this.getFollowList()
+          })
+          .catch(() => {})
+      },
+      async init() {
+        const [err, res] = await to(partnerApi.getCompanyConcat({ id: parseInt(this.id) }))
+        if (err) return
+        if (res.code == 200) {
+          this.detail = res.data
+        }
+      },
+
+      formatType(val) {
+        let str = ''
+        if (val == 10) str = '电话'
+        else if (val == 20) str = '邮件'
+        else if (val == 30) str = '拜访'
+        return str
+      },
+      // 跟进记录详情
+      showDetail(row) {
+        this.$refs.followDetail.init({ ...row })
+      },
+      // 展开评论
+      showComment(row) {
+        if (!row.comments.length) return this.$message.warning('暂无评论')
+        row.showComment = !row.showComment
+        this.$forceUpdate()
+      },
+    },
+  }
+</script>
+
+<style lang="scss" scoped>
+  $base: '.detail';
+  #{$base} {
+    height: calc(100vh - 60px - 12px * 2 - 40px);
+    display: flex;
+    padding: 20px 40px;
+
+    > .el-row {
+      flex: 1;
+      width: 100%;
+
+      > .el-col {
+        height: 100%;
+      }
+    }
+
+    .title {
+      p,
+      h3 {
+        margin: 0;
+      }
+
+      p {
+        font-size: 14px;
+        font-weight: 400;
+        line-height: 22px;
+      }
+
+      h3 {
+        font-size: 24px;
+        font-weight: 500;
+        line-height: 36px;
+        color: #333;
+        display: flex;
+        justify-content: space-between;
+      }
+    }
+
+    header {
+      height: 74px;
+      background: rgba(196, 196, 196, 0.5);
+      border-radius: 4px;
+      display: flex;
+      align-items: center;
+      padding: 0 20px;
+      margin-top: 16px;
+
+      ::v-deep .el-descriptions__body {
+        background: transparent;
+      }
+
+      ::v-deep .my-label {
+        font-size: 14px;
+        font-weight: 600;
+        color: #1d66dc;
+      }
+
+      ::v-deep .my-content {
+        font-size: 14px;
+        font-weight: 600;
+        color: #333;
+        white-space: nowrap;
+        overflow: hidden;
+        text-overflow: ellipsis;
+      }
+    }
+
+    .el-tabs {
+      height: calc(100% - 148px);
+      display: flex;
+      flex-direction: column;
+
+      ::v-deep .el-tabs__content {
+        flex: 1;
+
+        .el-tab-pane {
+          height: 100%;
+
+          .el-descriptions {
+            table-layout: fixed;
+
+            .is-bordered {
+              table-layout: fixed;
+            }
+          }
+        }
+      }
+    }
+
+    .buttons {
+      padding-top: 28px;
+      text-align: right;
+    }
+
+    .records {
+      margin: 0;
+      padding: 10px 20px;
+      list-style: none;
+      height: calc(100% - 60px);
+      overflow-y: auto;
+
+      > li {
+        display: flex;
+
+        & + li {
+          margin-top: 10px;
+        }
+      }
+
+      .date {
+        width: 100px;
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+
+        h2,
+        h3 {
+          margin: 0;
+        }
+
+        h2 {
+          font-size: 26px;
+          line-height: 32px;
+        }
+      }
+
+      .content {
+        flex: 1;
+        list-style: none;
+
+        li {
+          display: flex;
+
+          & + li {
+            margin-top: 10px;
+          }
+        }
+
+        .user-avatar {
+          font-size: 40px;
+        }
+
+        .text {
+          flex: 1;
+          padding-left: 20px;
+
+          p {
+            font-weight: 500;
+            margin: 0;
+            line-height: 20px;
+
+            span {
+              color: #1d66dc;
+            }
+          }
+
+          p:nth-child(2) {
+            margin-bottom: 10px;
+          }
+
+          .action {
+            font-weight: bold;
+            color: #333;
+          }
+        }
+      }
+    }
+
+    .follow {
+      height: 100%;
+      padding: 10px 20px;
+      overflow: auto;
+
+      > li {
+        display: flex;
+
+        + li {
+          margin-top: 10px;
+        }
+      }
+
+      .date {
+        width: 100px;
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+
+        h2,
+        h3 {
+          margin: 0;
+        }
+
+        h2 {
+          font-size: 26px;
+          line-height: 32px;
+        }
+      }
+
+      .content {
+        flex: 1;
+        list-style: none;
+
+        > li {
+          border: 1px solid rgb(215, 232, 244);
+          background: rgb(247, 251, 254);
+          border-radius: 4px;
+          padding: 8px;
+          overflow: hidden;
+
+          .text-container {
+            display: flex;
+          }
+
+          .comments {
+            padding-left: 60px;
+            margin-top: 10px;
+            max-height: 190px;
+            overflow: auto;
+
+            li {
+              display: flex;
+              border-top: 1px solid #e3e5e7;
+
+              .text {
+                flex: 1;
+                padding: 0 10px;
+
+                p {
+                  font-weight: 500;
+                  margin: 0;
+                  line-height: 32px;
+                }
+
+                p:first-child {
+                  line-height: 30px;
+                  font-weight: bold;
+                }
+
+                p:last-child {
+                  font-size: 12px;
+                  color: #9499a0;
+                  text-align: right;
+                }
+              }
+            }
+          }
+
+          + li {
+            margin-top: 10px;
+          }
+        }
+
+        .user-avatar {
+          font-size: 40px;
+        }
+
+        .text {
+          flex: 1;
+          padding-left: 20px;
+          padding-right: 10px;
+
+          p {
+            font-weight: 500;
+            margin: 0;
+            line-height: 32px;
+
+            span {
+              color: #1d66dc;
+            }
+          }
+
+          .action {
+            display: flex;
+            justify-content: space-between;
+
+            span:first-child {
+              font-weight: bold;
+              color: #333;
+            }
+          }
+
+          .footer {
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+          }
+        }
+      }
+    }
+
+    .no-follow {
+      height: 100%;
+      width: 100%;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      font-size: 12px;
+      color: rgba(0, 0, 0, 0.65);
+    }
+  }
+
+  .height-enter-active,
+  .height-leave-active {
+    transition: all 0.5s;
+  }
+
+  .height-enter-to,
+  .height-leave {
+    height: 190px;
+  }
+
+  .height-enter, .height-leave-to /* .fade-leave-active below version 2.1.8 */ {
+    height: 0;
+  }
+</style>

+ 450 - 0
src/views/base/partners/index.vue

@@ -0,0 +1,450 @@
+<template>
+  <div class="user-management-container">
+    <div class="side-layout">
+      <div class="tree-side">
+        <div class="head-container">
+          <el-row style="padding-right: 10px" type="flex">
+            <el-col><span style="font-size: 25px">公司</span></el-col>
+            <el-button type="text" @click="addCompany()">新增</el-button>
+          </el-row>
+        </div>
+        <el-tree
+          ref="tree"
+          class="filter-tree"
+          :data="companyList"
+          default-expand-all
+          :default-expanded-keys="[1]"
+          highlight-current
+          node-key="id"
+          :props="defaultProps"
+          @node-click="handleNodeClick">
+          <span slot-scope="{ node }" class="custom-tree-node">
+            <span>
+              <span>{{ node.label }}</span>
+            </span>
+            <span>
+              <!-- <el-button icon="el-icon-more" type="text" /> -->
+              <el-dropdown @command="(command) => treeHandle(command, node)">
+                <span class="el-dropdown-link">
+                  <i class="el-icon-more"></i>
+                  <!-- <i class="el-icon-arrow-down el-icon--right"></i> -->
+                </span>
+                <el-dropdown-menu slot="dropdown">
+                  <el-dropdown-item v-permissions="['partner/contact/edit']" command="edit">编辑</el-dropdown-item>
+                  <el-dropdown-item v-permissions="['partner/contact/del']" command="del">删除</el-dropdown-item>
+                </el-dropdown-menu>
+              </el-dropdown>
+            </span>
+          </span>
+        </el-tree>
+      </div>
+      <div class="tree-table">
+        <vab-query-form>
+          <vab-query-form-top-panel>
+            <el-form :inline="true" :model="queryForm" @submit.native.prevent>
+              <el-form-item>
+                <el-input
+                  v-model.trim="queryForm.name"
+                  clearable
+                  placeholder="联系人"
+                  @keyup.enter.native="queryData" />
+              </el-form-item>
+              <el-form-item>
+                <el-button icon="el-icon-search" type="primary" @click="queryData">查询</el-button>
+                <el-button icon="el-icon-refresh-right" @click="reset">重置</el-button>
+              </el-form-item>
+            </el-form>
+          </vab-query-form-top-panel>
+          <vab-query-form-left-panel>
+            <el-button
+              v-permissions="['base:product:add']"
+              icon="el-icon-plus"
+              type="primary"
+              @click="$refs.edit.open(queryForm.partnerId)">
+              添加
+            </el-button>
+            <el-button
+              v-permissions="['base:product:import']"
+              icon="el-icon-download"
+              type="primary"
+              @click="downloadTemplate">
+              下载模板
+            </el-button>
+            <el-upload
+              ref="uploadRef"
+              action="#"
+              :before-upload="
+                (file) => {
+                  return beforeAvatarUpload(file)
+                }
+              "
+              :file-list="fileList"
+              :http-request="uploadrequest"
+              :show-file-list="false"
+              style="margin: 0 10px 10px 0 !important">
+              <el-button v-permissions="['base:product:import']" icon="el-icon-upload2" type="primary">导入</el-button>
+            </el-upload>
+          </vab-query-form-left-panel>
+          <vab-query-form-right-panel>
+            <table-tool :columns="columns" :show-columns.sync="showColumns" table-type="productTable" />
+          </vab-query-form-right-panel>
+        </vab-query-form>
+
+        <el-table ref="table" v-loading="listLoading" border :data="list" :height="$baseTableHeight(2)">
+          <el-table-column align="center" label="序号" show-overflow-tooltip width="80">
+            <template #default="{ $index }">
+              {{ $index + 1 }}
+            </template>
+          </el-table-column>
+          <el-table-column
+            v-for="(item, index) in showColumns"
+            :key="index"
+            align="center"
+            :label="item.label"
+            :min-width="item.minWidth"
+            :prop="item.prop"
+            show-overflow-tooltip
+            :sortable="item.sortable"
+            :width="item.width">
+            <template #default="{ row }">
+              <span>{{ row[item.prop] }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column align="center" fixed="right" label="操作" show-overflow-tooltip width="120">
+            <template #default="{ row }">
+              <el-button v-permissions="['partner/company/edit']" type="text" @click="$refs.edit.open(row)">
+                编辑
+              </el-button>
+              <el-button type="text" @click="handleDetail(row)">详情</el-button>
+              <el-button v-permissions="['partner/company/del']" type="text" @click="handleDelete(row)">删除</el-button>
+            </template>
+          </el-table-column>
+          <template #empty>
+            <el-image class="vab-data-empty" :src="require('@/assets/empty_images/data_empty.png')" />
+          </template>
+        </el-table>
+        <el-pagination
+          background
+          :current-page="queryForm.pageNum"
+          :layout="layout"
+          :page-size="queryForm.pageSize"
+          :total="total"
+          @current-change="handleCurrentChange"
+          @size-change="handleSizeChange" />
+      </div>
+    </div>
+    <partner-edit ref="edit" @fetch-data="fetchData" />
+  </div>
+</template>
+
+<script>
+  import partnerApi from '@/api/base/partner'
+  import TableTool from '@/components/table/TableTool'
+  import PartnerEdit from './components/PartnerEdit'
+  import downloadFileByByte from '@/utils/base64ToFile'
+  import to from 'await-to-js'
+  import axios from 'axios'
+  import asyncUploadFile from '@/utils/uploadajax'
+  export default {
+    name: 'Product',
+    components: { TableTool, PartnerEdit },
+    data() {
+      return {
+        list: [],
+        listLoading: false,
+        layout: 'total, sizes, prev, pager, next, jumper',
+        total: 0,
+        queryForm: {
+          pageNum: 1,
+          pageSize: 10,
+          name: '',
+          partnerId: null,
+        },
+        showColumns: [],
+        columns: [
+          {
+            label: '负责区域',
+            width: 'auto',
+            prop: 'territory',
+            sortable: false,
+            disableCheck: true,
+          },
+          {
+            label: '联系人',
+            prop: 'name',
+            width: '160px',
+            sortable: false,
+            disableCheck: true,
+          },
+          {
+            label: '联系方式',
+            width: '160px',
+            prop: 'contactWay',
+            sortable: false,
+          },
+
+          {
+            label: '岗位',
+            width: '160px',
+            prop: 'post',
+            sortable: false,
+          },
+          {
+            label: '备注',
+            width: 'auto',
+            prop: 'remark',
+            sortable: false,
+          },
+        ],
+        defaultProps: {
+          key: 'id',
+          label: 'partnerName',
+        },
+        fileList: [],
+        fileSettings: {
+          // 文件配置信息
+          fileSize: 52428800,
+          fileTypes: '.doc,.docx,.zip,.xls,.xlsx,.rar,.jpg,.jpeg,.gif,.png,.jfif,.txt',
+          pictureSize: 52428800,
+          pictureTypes: '.jpg,.jpeg,.gif,.png,.jfif,.txt',
+          types: '.doc,.docx,.zip,.xls,.xlsx,.rar,.jpg,.jpeg,.gif,.png,.jfif,.mp4,.txt',
+          videoSize: 104857600,
+          videoType: '.mp4',
+        },
+        companyList: [], //产品分类
+      }
+    },
+    watch: {
+      showColumns: function () {
+        this.$nextTick(() => this.$refs.table.doLayout())
+      },
+    },
+    created() {
+      this.getCompanyList()
+    },
+    methods: {
+      async treeHandle(command, node) {
+        if (command == 'edit') {
+          this.addCompany(node)
+        } else if (command == 'del') {
+          this.$confirm(`确认删除销售区域 ${node.label}?`, '提示', {
+            confirmButtonText: '确定',
+            cancelButtonText: '取消',
+            type: 'warning',
+          })
+            .then(async () => {
+              const [err, res] = await to(partnerApi.deleteCompany({ id: [node.data.id] }))
+              if (err) return
+              if (res.code == 200) {
+                this.$baseMessage(res.msg, 'success', 'vab-hey-message-success')
+                this.getCompanyList()
+              }
+            })
+            .catch(() => {
+              this.$message({
+                type: 'info',
+                message: '已取消删除',
+              })
+            })
+        }
+      },
+      async getCompanyList() {
+        const [err, res] = await to(partnerApi.getCompanyList({}))
+        if (err) return
+        if (res.code == 200) {
+          this.companyList = res.data.list || []
+          if (res.data.list.length > 0) {
+            //默认第一选中
+            this.$nextTick(() => {
+              this.$refs.tree.setCurrentKey(res.data.list[0].id)
+              this.queryForm.partnerId = res.data.list[0].id
+              this.fetchData()
+            })
+          }
+        }
+      },
+      async fetchData() {
+        console.log(1111111)
+        this.listLoading = true
+        const [err, res] = await to(partnerApi.getCompanyConcatList(this.queryForm))
+        this.listLoading = false
+        console.log(this.listLoading)
+        if (err) return
+        if (res.code == 200) {
+          this.total = res.data.total
+          this.list = res.data.list || []
+        }
+      },
+
+      addCompany(node) {
+        console.log(node)
+        let defVal = ''
+        if (node) {
+          defVal = node.label
+        }
+        this.$prompt('请输入公司名称', '提示', {
+          inputValue: defVal, // 设置默认值
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          inputPattern: /\S+/,
+          inputErrorMessage: '公司名称不能为空',
+        })
+          .then(async ({ value }) => {
+            if (node) {
+              const params = {
+                id: node.data.id,
+                partnerName: value,
+              }
+              const [err, res] = await to(partnerApi.updateCompany(params))
+              if (err) return
+              if (res.code == 200) {
+                this.$baseMessage(res.msg, 'success', 'vab-hey-message-success')
+                this.getCompanyList()
+              }
+            } else {
+              const [err, res] = await to(partnerApi.addCompany({ partnerName: value }))
+              if (err) return
+              if (res.code == 200) {
+                this.$baseMessage(res.msg, 'success', 'vab-hey-message-success')
+                this.getCompanyList()
+              }
+            }
+            // 当用户点击确定按钮时,执行的逻辑
+          })
+          .catch(() => {
+            // 当用户点击取消按钮时,执行的逻辑
+            console.log('取消输入')
+          })
+      },
+      // 节点单击事件
+      handleNodeClick(data) {
+        console.log(data)
+        this.queryForm.partnerId = data.id
+        this.fetchData()
+      },
+      reset() {
+        this.queryForm = {
+          pageNum: 1,
+          pageSize: 10,
+          partnerId: null,
+          name: '',
+        }
+        this.fetchData()
+      },
+
+      //详情
+      handleDetail(row) {
+        this.$router.push({
+          name: 'PartnersDetail',
+          query: {
+            id: row.id,
+          },
+        })
+      },
+
+      handleDelete(row) {
+        this.$baseConfirm('你确定要删除当前项吗', null, async () => {
+          const { msg } = await partnerApi.deleteCompanyConcat({ id: [row.id] })
+          this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+          await this.fetchData()
+        })
+      },
+      handleSizeChange(val) {
+        this.queryForm.pageSize = val
+        this.fetchData()
+      },
+      handleCurrentChange(val) {
+        this.queryForm.pageNum = val
+        this.fetchData()
+      },
+      queryData() {
+        this.queryForm.pageNum = 1
+        this.fetchData()
+      },
+      downloadTemplate() {
+        partnerApi
+          .downloadFile({})
+          .then((res) => {
+            if (res.code == 200) {
+              downloadFileByByte(res.data, '合作伙伴导入模板.xlsx')
+            }
+          })
+          .catch((err) => {
+            console.error(err)
+          })
+      },
+
+      // 上传图片
+      beforeAvatarUpload(file) {
+        let flag1 = file.size < this.fileSettings.fileSize
+        if (!flag1) {
+          this.$message.warning('文件过大,请重新选择!')
+          return false
+        }
+        let flag2 = this.fileSettings.fileTypes.split(',').includes('.' + file.name.split('.').pop())
+        if (!flag2) {
+          this.$message.warning('文件类型不符合,请重新选择!')
+          return false
+        }
+        return true
+      },
+      // 上传
+      uploadrequest(option) {
+        let _this = this
+        let url = process.env.VUE_APP_UPLOAD_WEED
+        axios
+          .post(url)
+          .then((res) => {
+            if (res.data && res.data.fid && res.data.fid !== '') {
+              option.action = `${process.env.VUE_APP_PROTOCOL}${res.data.publicUrl}/${res.data.fid}`
+              // let file_name = option.file.name
+              // let index = file_name.lastIndexOf('.')
+              // let file_extend = ''
+              // if (index > 0) {
+              //   // 截取名称中的扩展名
+              //   file_extend = file_name.substr(index + 1)
+              // }
+              // let uploadform = {
+              //   fileName: file_name, // 资料名称
+              //
+              //   size: option.file.size.toString(), // 资料大小
+              //   fileType: file_extend, // 资料文件类型
+              // }
+              let fileUrl = `${process.env.VUE_APP_PROTOCOL}${res.data.publicUrl}/${res.data.fid}` // 资料存储url
+              asyncUploadFile(option).then(async () => {
+                const params = {
+                  excelUrl: fileUrl,
+                  partnerId: this.queryForm.partnerId,
+                }
+                const [err, res] = await to(partnerApi.exportFile(params))
+                if (err) return
+                if (res.code == 200) {
+                  _this.$message({
+                    type: 'success',
+                    message: '上传成功',
+                  })
+                  this.fetchData()
+                }
+              })
+            } else {
+              _this.$message({
+                type: 'warning',
+                message: '未上传成功!请刷新界面重新上传!',
+              })
+            }
+          })
+          .catch(function () {
+            _this.$message({
+              type: 'warning',
+              message: '未上传成功!请重新上传!',
+            })
+          })
+      },
+    },
+  }
+</script>
+<style>
+  .el-form-item--small {
+    margin: 0 0 10px 0 !important;
+  }
+</style>

+ 6 - 2
src/views/contract/components/DetailsCollection.vue

@@ -95,7 +95,7 @@
           show-overflow-tooltip>
           <template #default="{ row }">
             <span v-if="item.prop == 'collectionType'">
-              {{ collectionTypeOption.filter((item) => item.key == row.collectionType)[0].value || '-' }}
+              {{ selectDictLabel(collectionTypeOption, row.collectionType) }}
             </span>
             <span v-else-if="item.prop == 'approStatus'">
               {{ row.approStatus == '10' ? '未回款' : '已回款' }}
@@ -116,7 +116,7 @@
       ref="collection"
       :collection-type-data="collectionTypeOption"
       :details="details"
-      @collectionSave="getCollectionList()" />
+      @collectionSave="collectionSave()" />
   </div>
 </template>
 
@@ -253,6 +253,10 @@
         if (err) return
         this.collectionPlanData = res.data.list
       },
+      collectionSave() {
+        this.getCollectionList()
+        this.getCollectionPlaneList()
+      },
       // 删除回款计划
       handleDel(row, type) {
         this.$confirm('确认删除?', '提示', {

+ 13 - 13
src/views/plat/task/all.vue

@@ -252,24 +252,24 @@
             prop: 'taskDesc',
             sortable: false,
           },
-          {
-            label: '督办类型',
-            width: '120px',
-            prop: 'taskType',
-            sortable: false,
-          },
+          // {
+          //   label: '督办类型',
+          //   width: '120px',
+          //   prop: 'taskType',
+          //   sortable: false,
+          // },
           {
             label: '状态',
             width: '120px',
             prop: 'taskStatus',
             sortable: false,
           },
-          {
-            label: '事项来源',
-            width: '120px',
-            prop: 'source',
-            sortable: false,
-          },
+          // {
+          //   label: '事项来源',
+          //   width: '120px',
+          //   prop: 'source',
+          //   sortable: false,
+          // },
           {
             label: '负责人',
             width: '120px',
@@ -366,7 +366,7 @@
           })
       },
       // 单元格样式控制
-      tableRowClassName({ row, rowIndex }) {
+      tableRowClassName({ row }) {
         if (this.taskMap[row.id]) {
           return 'warning-row'
         }

+ 4 - 4
src/views/plat/task/components/TaskAdd.vue

@@ -8,13 +8,13 @@
             <el-input v-model="form.taskTitle" placeholder="请输入标题" />
           </el-form-item>
         </el-col>
-        <el-col :span="12">
+        <!-- <el-col :span="12">
           <el-form-item label="类型" prop="taskType">
             <el-select v-model="form.taskType" placeholder="请选择类型" style="width: 100%">
               <el-option v-for="item in types" :key="item.dictCode" :label="item.dictLabel" :value="item.dictValue" />
             </el-select>
           </el-form-item>
-        </el-col>
+        </el-col> -->
         <!-- <el-col :span="12">
           <el-form-item label="开始时间" prop="taskStartDate">
             <el-date-picker
@@ -35,13 +35,13 @@
               value-format="yyyy-MM-dd HH:mm:ss" />
           </el-form-item>
         </el-col>
-        <el-col :span="12">
+        <!-- <el-col :span="12">
           <el-form-item label="事项来源" prop="source">
             <el-select v-model="form.source" placeholder="请选择事项来源" style="width: 100%">
               <el-option v-for="item in sourceOptions" :key="item.key" :label="item.value" :value="item.key" />
             </el-select>
           </el-form-item>
-        </el-col>
+        </el-col> -->
         <el-col :span="24">
           <el-form-item label="督办内容" prop="taskDesc">
             <el-input v-model="form.taskDesc" placeholder="请输入督办内容" type="textarea" />

+ 1 - 1
src/views/plat/task/components/TaskDetail.vue

@@ -15,7 +15,7 @@
           </el-button>
           <div style="border-bottom: solid 1px; margin-top: 1px"></div>
           <el-row :gutter="20" style="margin-top: 10px">
-            <el-col :span="8">督办类型:{{ typeMap[theTask.taskType] }}</el-col>
+            <!-- <el-col :span="8">督办类型:{{ typeMap[theTask.taskType] }}</el-col> -->
             <el-col :span="8">状态:{{ theTask.taskStatus === '10' ? '进行中' : '关闭' }}</el-col>
             <el-col :span="8">
               超期:{{ isNotOverdue(parseTime(theTask.taskEndDate, '{y}-{m}-{d} 23:59:59'), theTask) ? '否' : '是' }}

+ 4 - 4
src/views/plat/task/detail.vue

@@ -19,9 +19,9 @@
         </div>
         <header>
           <el-descriptions :colon="false" :column="5" direction="vertical">
-            <el-descriptions-item content-class-name="my-content" label="督办类型" label-class-name="my-label">
+            <!-- <el-descriptions-item content-class-name="my-content" label="督办类型" label-class-name="my-label">
               {{ typeMap[theTask.taskType] }}
-            </el-descriptions-item>
+            </el-descriptions-item> -->
             <el-descriptions-item content-class-name="my-content" label="状态" label-class-name="my-label">
               <span v-if="theTask.taskStatus === '10'">发起</span>
               <span v-else-if="theTask.taskStatus === '20'">进行中</span>
@@ -61,9 +61,9 @@
               <span v-show="theTask.targetType == '30'">合同</span>
               <span v-show="theTask.targetType == '40'">回款</span>
             </el-descriptions-item> -->
-            <el-descriptions-item content-class-name="my-content" label="事项来源" label-class-name="my-label">
+            <!-- <el-descriptions-item content-class-name="my-content" label="事项来源" label-class-name="my-label">
               {{ selectDictLabel(sourceOptions, theTask.source) }}
-            </el-descriptions-item>
+            </el-descriptions-item> -->
           </el-descriptions>
         </header>
         <!--  -->

+ 15 - 15
src/views/plat/task/index.vue

@@ -43,7 +43,7 @@
           <i class="el-icon-s-promotion" style="margin-right: 10px"></i>
           抄送我的
         </div>
-        <div class="type" style="margin-top: 50px">事项来源</div>
+        <!-- <div class="type" style="margin-top: 50px">事项来源</div>
         <div
           class="el-icon-s-management"
           :style="{
@@ -65,7 +65,7 @@
           }"
           @click="searchSource(item.key)">
           {{ item.value }}
-        </div>
+        </div> -->
       </div>
       <div class="tree-table">
         <vab-query-form>
@@ -253,24 +253,24 @@
             prop: 'taskDesc',
             sortable: false,
           },
-          {
-            label: '督办类型',
-            width: '120px',
-            prop: 'taskType',
-            sortable: false,
-          },
+          // {
+          //   label: '督办类型',
+          //   width: '120px',
+          //   prop: 'taskType',
+          //   sortable: false,
+          // },
           {
             label: '状态',
             width: '120px',
             prop: 'taskStatus',
             sortable: false,
           },
-          {
-            label: '事项来源',
-            width: '120px',
-            prop: 'source',
-            sortable: false,
-          },
+          // {
+          //   label: '事项来源',
+          //   width: '120px',
+          //   prop: 'source',
+          //   sortable: false,
+          // },
           {
             label: '负责人',
             width: '120px',
@@ -352,7 +352,7 @@
     },
     methods: {
       // 单元格样式控制
-      tableRowClassName({ row, rowIndex }) {
+      tableRowClassName({ row }) {
         if (!this.isNotOverdue(this.parseTime(row['taskEndDate'], '{y}-{m}-{d} 23:59:59'), row)) {
           return 'danger-row'
         }

+ 8 - 2
src/views/proj/business/components/BusinessEdit.vue

@@ -194,7 +194,10 @@
         </el-col>
         <el-col :span="8">
           <el-form-item label="采购方式" prop="purchasingWay">
-            <el-input v-model="form.purchasingWay" />
+            <!-- <el-input v-model="form.purchasingWay" /> -->
+            <el-select v-model="form.purchasingWay" placeholder="请选择" style="width: 100%">
+              <el-option v-for="item in purchasingWayOptions" :key="item.key" :label="item.value" :value="item.key" />
+            </el-select>
           </el-form-item>
         </el-col>
         <el-col :span="8">
@@ -428,6 +431,7 @@
         customerInfo: {},
         productData: [],
         isBid: true,
+        purchasingWayOptions: [],
       }
     },
     computed: {
@@ -456,11 +460,13 @@
           this.getDicts('proj_sales_model'),
           this.getDicts('sys_product_line'),
           this.getDicts('sys_yes_no'),
-        ]).then(([nboSource, salesModel, productLine, yesOrNo]) => {
+          this.getDicts('proj_purchasing_way'),
+        ]).then(([nboSource, salesModel, productLine, yesOrNo, purchasingWay]) => {
           this.nboSourceOptions = nboSource.data.values || []
           this.salesModelOptions = salesModel.data.values || []
           this.productLineOptions = productLine.data.values || []
           this.yesOrNoOptions = yesOrNo.data.values || []
+          this.purchasingWayOptions = purchasingWay.data.values || []
         })
       },
       nextStep() {

+ 18 - 6
src/views/proj/business/components/FollowAdd.vue

@@ -110,6 +110,8 @@
       :query-params="queryContact"
       :title="distrTitle"
       @save="selectContact" />
+
+    <select-user ref="selectUser" :query-params="queryContact" @save="selectContact" />
   </el-dialog>
 </template>
 
@@ -119,6 +121,7 @@
   import followApi from '@/api/customer/follow'
   import SelectContact from '@/components/select/SelectCustomerContact'
   import SelectDistributor from '@/components/select/SelectDistributorContact'
+  import SelectUser from '@/components/select/SelectUser'
   import asyncUploadFile from '@/utils/uploadajax'
 
   export default {
@@ -126,6 +129,7 @@
     components: {
       SelectContact,
       SelectDistributor,
+      SelectUser,
     },
     data() {
       return {
@@ -142,8 +146,8 @@
           targetId: '',
           targetName: '',
           targetType: '',
-          custId: 0,
-          custName: '',
+          custId: null,
+          custName: null,
           contactsId: '',
           contactsName: '',
           reminders: '',
@@ -189,9 +193,7 @@
       }
     },
     watch: {},
-    created() {
-      this.getOptions()
-    },
+    created() {},
     methods: {
       getOptions() {
         Promise.all([this.getDicts('plat_follow_type'), this.getDicts('plat_follow_content_type')]).then(
@@ -205,6 +207,9 @@
         console.log(this.form.targetType)
         if (this.form.targetType == '50') {
           this.$refs.selectDistributor.open()
+        }
+        if (this.form.targetType == '70') {
+          this.$refs.selectUser.open()
         } else {
           if (!this.queryContact.custId) {
             this.$message.warning('请先选择客户')
@@ -217,6 +222,8 @@
         if (val && val.length > 0) {
           if (this.form.targetType == '50') {
             this.form.contactsName = val.map((item) => item.name).join()
+          } else if (this.form.targetType == '70') {
+            this.form.contactsName = val.map((item) => item.nickName).join()
           } else {
             this.form.contactsName = val.map((item) => item.cuctName).join()
           }
@@ -225,6 +232,7 @@
         console.log(this.form)
       },
       showEdit(row, title = null) {
+        this.getOptions()
         this.title = '添加跟进记录'
         this.form = Object.assign(this.form, row)
         console.log(this.form)
@@ -236,6 +244,10 @@
           }
         } else if (this.form.targetType == '50') {
           this.queryContact.distId = this.form.targetId
+        } else if (this.form.targetType == '70') {
+          this.queryContact.distId = this.form.targetId
+          this.form.custId = null
+          this.form.custName = null
         }
         this.distrTitle = title
         this.dialogFormVisible = true
@@ -252,12 +264,12 @@
           if (valid) {
             this.loading = true
             const [err, res] = await to(followApi.addFollowUp(this.form))
+            this.loading = false
             if (err) {
               this.$baseMessage(res.msg, 'error')
             } else {
               this.$baseMessage(res.msg, 'success')
             }
-            this.loading = false
             this.$emit('fetch-data')
             this.close()
           } else {

+ 5 - 2
src/views/proj/business/detail.vue

@@ -134,7 +134,7 @@
 
               <!--  B-A  -->
               <el-descriptions-item label="采购方式">
-                {{ details.purchasingWay }}
+                {{ selectDictLabel(purchasingWayOptions, details.purchasingWay) }}
               </el-descriptions-item>
               <el-descriptions-item label="采购时间">
                 {{ parseTime(details.purchasingTime, '{y}-{m}-{d}') }}
@@ -293,6 +293,7 @@
         nboPhaseOptions: [],
         nboStatusOptions: [],
         approStatusOptions: [],
+        purchasingWayOptions: [],
       }
     },
     computed: {
@@ -315,14 +316,16 @@
           this.getDicts('proj_sales_model'),
           this.getDicts('sys_product_line'),
           this.getDicts('sys_yes_no'),
+          this.getDicts('proj_purchasing_way'),
           this.getRecord(),
-        ]).then(([details, nboType, nboSource, salesModel, productLine, yesOrNo]) => {
+        ]).then(([details, nboType, nboSource, salesModel, productLine, yesOrNo, purchasingWay]) => {
           if (details.data) this.details = details.data
           this.nboTypeOptions = nboType.data.values || []
           this.nboSourceOptions = nboSource.data.values || []
           this.salesModelOptions = salesModel.data.values || []
           this.productLineOptions = productLine.data.values || []
           this.yesOrNoOptions = yesOrNo.data.values || []
+          this.purchasingWayOptions = purchasingWay.data.values || []
         })
       },
       async getRecord() {

+ 48 - 8
src/views/work/order/components/DingTalkFromToVue.vue

@@ -32,7 +32,7 @@
                 v-else-if="item.componentName === 'DDSelectField'"
                 v-model="item.props.value"
                 clearable
-                :disabled="item.props.disabled"
+                :disabled="item.props.disabled || item.props.label == '工单类型'"
                 :placeholder="item.props.placeholder"
                 style="width: 100%">
                 <el-option
@@ -62,7 +62,7 @@
                 v-model="item.props.value"
                 :disabled="item.props.disabled"
                 :placeholder="item.props.placeholder"
-                type="daterange"
+                :type="item.props.label == '支持时间' ? 'datetime' : 'date'"
                 :value-format="item.props.format" />
               <!--时间范围选择器-->
               <el-date-picker
@@ -114,18 +114,32 @@
               <!--表格-->
               <el-table :key="dingTableFlag" :ref="'dingTable' + index" border :data="item.tableData" height="250px">
                 <el-table-column
-                  v-for="(child, index) in item.children"
-                  :key="index"
+                  v-for="(child, idx) in item.children"
+                  :key="idx"
                   align="center"
                   :label="child.props.label"
                   :prop="child.props.id"
                   show-overflow-tooltip>
                   <template #default="{ row }">
                     <!--文本框-->
-                    <el-input
-                      v-if="child.componentName === 'TextField'"
+                    <el-select
+                      v-if="child.props.label === '产品型号'"
                       v-model="row[child.props.id]"
-                      :placeholder="item.props.placeholder" />
+                      filterable
+                      :placeholder="item.props.placeholder"
+                      value-key="prodCode"
+                      @change="(val) => selectLabel(val, item.children, row)">
+                      <el-option v-for="prod in productList" :key="prod.id" :label="prod.prodCode" :value="prod" />
+                    </el-select>
+                    <el-select
+                      v-if="child.props.label === '产品名称'"
+                      v-model="row[child.props.id]"
+                      filterable
+                      :placeholder="item.props.placeholder"
+                      value-key="prodName"
+                      @change="(val) => selectLabel(val, item.children, row)">
+                      <el-option v-for="prod in productList" :key="prod.id" :label="prod.prodName" :value="prod" />
+                    </el-select>
                     <!--数字文本框-->
                     <el-input
                       v-if="child.componentName === 'NumberField'"
@@ -147,6 +161,7 @@
 </template>
 
 <script>
+  import productApi from '@/api/base/product'
   import SelectUser from '@/components/select/SelectUser'
 
   export default {
@@ -164,14 +179,34 @@
       return {
         dingtalkForm: {},
         fileList: [],
+        productList: [],
         dingRules: {},
         userItemIndex: {},
         dingTableFlag: true,
       }
     },
-    mounted() {},
+    mounted() {
+      this.getProduct()
+    },
     // 方法集合
     methods: {
+      selectLabel(val, children, row) {
+        const codeId = children.find((item) => item.props.label == '产品型号').props.id
+        const nameId = children.find((item) => item.props.label == '产品名称').props.id
+        row[codeId] = val.prodCode
+        row[nameId] = val.prodName
+      },
+      getProduct() {
+        let params = {
+          pageNum: 1,
+          pageSize: 999,
+        }
+        Promise.all([productApi.getList({ ...params })])
+          .then(([product]) => {
+            this.productList = product.data.list || []
+          })
+          .catch((err) => console.log(err))
+      },
       addTableData(index) {
         let newObj = {}
         let tableData = this.dingtalkForm.items[index].tableData
@@ -214,6 +249,11 @@
             dingtalkForm.items[index].props.placeholder = JSON.parse(dingtalkForm.items[index].props.label)
             dingtalkForm.items[index].props.label = dingtalkForm.items[index].props.placeholder.join(' - ')
           }
+          // console.log(dingtalkForm.items[index].props.label, dingtalkForm.items[index].componentName,"=================")
+          if (dingtalkForm.items[index].props.label == '工单类型') {
+            console.log(dingtalkForm.items[index].props.options)
+            dingtalkForm.items[index].props.value = JSON.parse(dingtalkForm.items[index].props.options[0]).value
+          }
         }
         this.dingtalkForm = dingtalkForm
       },

+ 18 - 5
src/views/work/order/components/Edit.vue

@@ -2,11 +2,11 @@
   <el-dialog append-to-body :title="title" :visible.sync="dialogFormVisible" @close="close">
     <el-form ref="form" label-position="top" label-width="100px" :model="form" :rules="rules">
       <el-row :gutter="20">
-        <el-col :span="12">
+        <!-- <el-col :span="12">
           <el-form-item label="工单名称" prop="name">
             <el-input v-model="form.name" />
           </el-form-item>
-        </el-col>
+        </el-col> -->
         <el-col :span="12">
           <el-form-item label="工单类型" prop="orderTypeId">
             <el-select
@@ -24,11 +24,21 @@
             <el-input v-model="form.assignUserName" readonly suffix-icon="el-icon-search" @focus="handleSelectUser" />
           </el-form-item>
         </el-col>
-        <el-col :span="12">
+        <!-- <el-col :span="12">
           <el-form-item label="备注" prop="remark">
             <el-input v-model="form.remark" placeholder="请输入内容" :rows="2" show-word-limit type="textarea" />
           </el-form-item>
-        </el-col>
+        </el-col> -->
+        <!-- <el-col :span="12">
+          <el-form-item label="结束时间" prop="remark">
+            <el-date-picker
+              v-model="form.endTime"
+              placeholder="选择结束时间"
+              style="width: 100%"
+              type="datetime"
+              value-format="yyyy-MM-dd HH:mm:ss" />
+          </el-form-item>
+        </el-col> -->
       </el-row>
 
       <!--      钉钉审批流表单-->
@@ -84,7 +94,7 @@
           feedback: undefined,
           file: undefined,
           remark: undefined,
-
+          endTime: undefined,
           dingtalkForm: undefined,
         },
         rules: {
@@ -196,6 +206,9 @@
               if (item.props.label === '客户名称') {
                 this.form.custName = item.props.value
               }
+              if (item.props.label === '工单名称') {
+                this.form.name = item.props.value
+              }
               items.push({
                 componentName: item.componentName,
                 id: item.props.id,

+ 46 - 13
src/views/work/order/components/Feedback.vue

@@ -3,32 +3,60 @@
     <el-form ref="form" label-position="top" :model="form" :rules="rules">
       <el-row :gutter="20">
         <el-col :span="12">
-          <el-form-item label="工单类型" prop="orderTypeName">
-            <el-input v-model="form.orderTypeName" disabled readonly />
+          <el-form-item label="工单编号" prop="id">
+            <el-input v-model="form.id" disabled readonly />
           </el-form-item>
         </el-col>
         <el-col :span="12">
-          <el-form-item label="分派人员" prop="assignUserName">
+          <el-form-item label="工单状态" prop="orderStatus">
             <el-input
-              v-model="form.assignUserName"
               disabled
               readonly
               suffix-icon="el-icon-search"
-              @focus="handleSelectUser" />
+              :value="selectDictLabel(orderStatusOptions, form.orderStatus)" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row :gutter="20">
+        <el-col :span="12">
+          <el-form-item label="工单类型" prop="orderTypeName">
+            <el-input v-model="form.orderTypeName" disabled readonly />
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="分派人员" prop="assignUserName">
+            <el-input v-model="form.assignUserName" disabled readonly suffix-icon="el-icon-search" />
           </el-form-item>
         </el-col>
       </el-row>
       <el-row :gutter="20">
         <el-col :span="12">
-          <el-form-item label="反馈信息" prop="feedback">
-            <el-input v-model="form.feedback" placeholder="请输入反馈信息" :rows="5" show-word-limit type="textarea" />
+          <el-form-item label="关联客户" prop="custName">
+            <el-input v-model="form.custName" disabled readonly />
           </el-form-item>
         </el-col>
         <el-col :span="12">
+          <el-form-item label="关联项目" prop="nboName">
+            <el-input v-model="form.nboName" disabled readonly suffix-icon="el-icon-search" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row :gutter="20">
+        <el-col :span="24">
+          <el-form-item label="完成信息" prop="finishRemark">
+            <el-input
+              v-model="form.finishRemark"
+              placeholder="请输入完成信息"
+              :rows="5"
+              show-word-limit
+              type="textarea" />
+          </el-form-item>
+        </el-col>
+        <!-- <el-col :span="12">
           <el-form-item label="备注" prop="remark">
             <el-input v-model="form.remark" placeholder="请输入内容" :rows="5" show-word-limit type="textarea" />
           </el-form-item>
-        </el-col>
+        </el-col> -->
       </el-row>
     </el-form>
     <template #footer>
@@ -50,16 +78,21 @@
     components: {
       SelectUser,
     },
+    props: {
+      orderStatusOptions: {
+        type: Array,
+        default: () => [],
+      },
+    },
     data() {
       return {
         form: {
           orderId: undefined,
-          feedback: undefined,
-          remark: undefined,
+          finishRemark: undefined,
         },
         rules: {
           orderId: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
-          feedback: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+          finishRemark: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
         },
         title: '',
         dialogFormVisible: false,
@@ -79,7 +112,7 @@
         }
       },
       showEdit(row) {
-        this.title = '反馈'
+        this.title = row.name
         this.form = Object.assign({}, row)
         this.dialogFormVisible = true
       },
@@ -91,7 +124,7 @@
       save() {
         this.$refs['form'].validate(async (valid) => {
           if (valid) {
-            const { msg } = await workApi.createWorkOrderFeedback(this.form)
+            const { msg } = await workApi.finishWorkOrder(this.form)
             this.$baseMessage(msg, 'success', 'vab-hey-message-success')
             this.$emit('fetch-data')
             this.close()

+ 113 - 0
src/views/work/order/deliver/components/editPlan.vue

@@ -0,0 +1,113 @@
+<template>
+  <el-dialog :title="title" :visible.sync="dialogFormVisible" width="500px" @close="close">
+    <el-form ref="form" label-position="top" :model="form" :rules="rules">
+      <el-row :gutter="20">
+        <el-col :span="24">
+          <el-form-item label="计划标题" prop="planTitle">
+            <el-input v-model="form.planTitle" placeholder="请输入计划标题" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row :gutter="20">
+        <el-col :span="24">
+          <el-form-item label="时间范围" prop="date">
+            <el-date-picker
+              v-model="form.date"
+              end-placeholder="结束日期"
+              range-separator="至"
+              start-placeholder="开始日期"
+              style="width: 100%"
+              type="datetimerange"
+              value-format="yyyy-MM-dd HH:mm:ss" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+    <template #footer>
+      <el-button @click="close">取 消</el-button>
+      <el-button type="primary" @click="save">确 定</el-button>
+    </template>
+  </el-dialog>
+</template>
+
+<script>
+  import planApi from '@/api/work/deliverPlan'
+
+  export default {
+    name: 'WorkOrderFeedback',
+    components: {},
+    props: {
+      // orderStatusOptions: {
+      //   type: Array,
+      //   default: () => [],
+      // },
+    },
+    data() {
+      return {
+        form: {
+          deliverOrderId: 0,
+          planTitle: undefined,
+          date: [],
+        },
+        rules: {
+          planTitle: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+          date: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+        },
+        dialogFormVisible: false,
+        planId: 0,
+        title: '',
+      }
+    },
+    mounted() {},
+    methods: {
+      open(planId) {
+        console.log('planId', planId)
+        if (planId) {
+          this.planId = planId
+          this.title = '编辑'
+          this.getPlanDetail()
+        } else {
+          this.title = '新建计划'
+          this.planId = 0
+        }
+        this.form.deliverOrderId = parseInt(this.$route.query.id)
+        this.dialogFormVisible = true
+      },
+      close() {
+        this.$refs['form'].resetFields()
+        this.form = this.$options.data().form
+        this.dialogFormVisible = false
+      },
+      async getPlanDetail() {
+        const { data, code } = await planApi.getPlan({ id: this.planId })
+        if (code == 200) {
+          this.form.date = [data.planStartDate, data.planEndDate]
+          this.form.planTitle = data.planTitle
+        }
+      },
+      save() {
+        this.$refs['form'].validate(async (valid) => {
+          if (valid) {
+            console.log(this.form.date)
+            const params = {
+              deliverOrderId: this.form.deliverOrderId,
+              planTitle: this.form.planTitle,
+              planStartDate: this.form.date[0],
+              planEndDate: this.form.date[1],
+            }
+            if (this.planId) {
+              params.id = this.planId
+              const { msg } = await planApi.updatePlan(params)
+              this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+            } else {
+              const { msg } = await planApi.addPlan(params)
+              this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+            }
+            this.$emit('fetch-data')
+            this.close()
+          }
+        })
+      },
+    },
+  }
+</script>

+ 128 - 0
src/views/work/order/deliver/components/editWork.vue

@@ -0,0 +1,128 @@
+<template>
+  <el-dialog :title="title" :visible.sync="dialogFormVisible" @close="close">
+    <el-form ref="form" label-position="top" :model="form" :rules="rules">
+      <el-row :gutter="20">
+        <el-col :span="12">
+          <el-form-item label="任务标题" prop="progressTitle">
+            <el-input v-model="form.progressTitle" placeholder="请输入任务标题" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="任务内容" prop="progressContext">
+            <el-input v-model="form.progressContext" placeholder="请输入任务内容" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row :gutter="20">
+        <el-col :span="12">
+          <el-form-item label="优先级" prop="progressLevel">
+            <el-select v-model="form.progressLevel" clearable placeholder="优先级" style="width: 100%">
+              <el-option label="最高" value="10" />
+              <el-option label="普通" value="20" />
+              <el-option label="较低" value="30" />
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="时间范围" prop="date">
+            <el-date-picker
+              v-model="form.date"
+              end-placeholder="结束日期"
+              range-separator="至"
+              start-placeholder="开始日期"
+              style="width: 100%"
+              type="datetimerange"
+              value-format="yyyy-MM-dd HH:mm:ss" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row :gutter="20">
+        <el-col :span="24">
+          <el-form-item label="备注" prop="remark">
+            <el-input v-model="form.remark" placeholder="请输入备注信息" :rows="5" show-word-limit type="textarea" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+    <template #footer>
+      <el-button @click="close">取 消</el-button>
+      <el-button type="primary" @click="save">确 定</el-button>
+    </template>
+
+    <!-- 选择分派人员弹窗 -->
+  </el-dialog>
+</template>
+
+<script>
+  import deliverWorkApi from '@/api/work/deliverWork'
+  export default {
+    name: 'WorkOrderFeedback',
+    components: {},
+    data() {
+      return {
+        form: {
+          progressTitle: undefined,
+          progressContext: '',
+          date: [],
+          progressLevel: '10',
+          remark: undefined,
+        },
+        rules: {
+          progressTitle: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+          progressContext: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+          date: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+        },
+        dialogFormVisible: false,
+        planId: 0,
+        workId: 0,
+        title: '',
+      }
+    },
+    mounted() {},
+    methods: {
+      open(planId, row) {
+        this.planId = planId
+        if (row) {
+          this.workId = row.id
+          this.title = '编辑'
+          this.form = {
+            progressTitle: row.progressTitle,
+            progressContext: row.progressContext,
+            date: [row.startDate, row.endDate],
+            progressLevel: row.progressLevel,
+            remark: row.remark,
+          }
+        } else {
+          this.title = '新建工作项'
+          this.workId = 0
+        }
+        this.dialogFormVisible = true
+      },
+      close() {
+        this.$refs['form'].resetFields()
+        this.form = this.$options.data().form
+        this.dialogFormVisible = false
+      },
+      save() {
+        this.$refs['form'].validate(async (valid) => {
+          if (valid) {
+            let params = Object.assign(this.form, { planId: this.planId })
+            params.startDate = this.form.date[0]
+            params.endDate = this.form.date[1]
+            if (this.workId) {
+              params.id = this.workId
+              const { msg } = await deliverWorkApi.update(params)
+              this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+            } else {
+              const { msg } = await deliverWorkApi.add(params)
+              this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+            }
+
+            this.$emit('fetch-data')
+            this.close()
+          }
+        })
+      },
+    },
+  }
+</script>

+ 59 - 0
src/views/work/order/deliver/components/finish.vue

@@ -0,0 +1,59 @@
+<template>
+  <el-dialog title="完成" :visible.sync="dialogFormVisible" @close="close">
+    <el-form ref="form" label-position="top" :model="form" :rules="rules">
+      <el-row :gutter="20">
+        <el-col :span="24">
+          <el-form-item label="完成信息" prop="finishRemark">
+            <el-input v-model="form.finishRemark" placeholder="完成信息" :rows="5" show-word-limit type="textarea" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+    <template #footer>
+      <el-button @click="close">取 消</el-button>
+      <el-button type="primary" @click="save">确 定</el-button>
+    </template>
+  </el-dialog>
+</template>
+
+<script>
+  import deliverApi from '@/api/work/deliver'
+
+  export default {
+    name: 'WorkOrderFeedback',
+    data() {
+      return {
+        form: {
+          orderId: undefined,
+          finishRemark: undefined,
+        },
+        rules: {
+          orderId: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+        },
+        dialogFormVisible: false,
+      }
+    },
+    mounted() {},
+    methods: {
+      showEdit(row) {
+        this.form.orderId = row.id
+        this.dialogFormVisible = true
+      },
+      close() {
+        this.$refs['form'].resetFields()
+        this.form = this.$options.data().form
+        this.dialogFormVisible = false
+      },
+      save() {
+        this.$refs['form'].validate(async (valid) => {
+          if (valid) {
+            const { msg } = await deliverApi.finish(this.form)
+            this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+            this.$emit('fetch-data')
+            this.close()
+          }
+        })
+      },
+    },
+  }
+</script>

+ 72 - 0
src/views/work/order/deliver/components/selectStatus.vue

@@ -0,0 +1,72 @@
+<template>
+  <el-dialog title="修改状态" :visible.sync="dialogFormVisible" width="300px" @close="close">
+    <el-form ref="form" label-position="top" :model="form">
+      <el-row :gutter="20">
+        <el-col :span="24">
+          <el-form-item label="状态" prop="planTitle">
+            <el-select v-model="form.planStatus" placeholder="请选择">
+              <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
+            </el-select>
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+    <template #footer>
+      <el-button @click="close">取 消</el-button>
+      <el-button :disabled="!form.planStatus" type="primary" @click="save">确 定</el-button>
+    </template>
+  </el-dialog>
+</template>
+
+<script>
+  import planApi from '@/api/work/deliverPlan'
+
+  export default {
+    name: 'WorkOrderFeedback',
+    components: {},
+    props: {},
+    data() {
+      return {
+        options: [
+          {
+            value: '20',
+            label: '开始计划',
+          },
+          {
+            value: '30',
+            label: '关闭计划',
+          },
+        ],
+        form: {
+          id: 0,
+          planStatus: null,
+        },
+        dialogFormVisible: false,
+      }
+    },
+    mounted() {},
+    methods: {
+      open(planId) {
+        this.form.id = planId
+        this.dialogFormVisible = true
+      },
+      close() {
+        this.$refs['form'].resetFields()
+        this.form = this.$options.data().form
+        this.dialogFormVisible = false
+      },
+
+      async save() {
+        if (this.form.planStatus == 20) {
+          const { msg } = await planApi.startPlan(this.form)
+          this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+        } else {
+          const { msg } = await planApi.finishPlan(this.form)
+          this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+        }
+        this.$emit('fetch-data')
+        this.close()
+      },
+    },
+  }
+</script>

+ 105 - 0
src/views/work/order/deliver/components/updateUser.vue

@@ -0,0 +1,105 @@
+<template>
+  <el-dialog title="更新成员信息" :visible.sync="dialogFormVisible" @close="close">
+    <el-form ref="form" label-position="top" :model="form" :rules="rules">
+      <el-row :gutter="20">
+        <el-col :span="12">
+          <el-form-item label="项目经理" prop="projectManId">
+            <el-input
+              v-model="form.projectManName"
+              readonly
+              suffix-icon="el-icon-search"
+              @focus="handleSelectUser('projectManId', 'projectManName')" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="交付经理" prop="deliverManId">
+            <el-input
+              v-model="form.deliverManName"
+              readonly
+              suffix-icon="el-icon-search"
+              @focus="handleSelectUser('deliverManId', 'deliverManName')" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+    <template #footer>
+      <el-button @click="close">取 消</el-button>
+      <el-button :disabled="!form.deliverManId && !form.projectManId" type="primary" @click="save">确 定</el-button>
+    </template>
+
+    <!-- 选择分派人员弹窗 -->
+    <select-user ref="selectUser" @save="selectUser" />
+  </el-dialog>
+</template>
+
+<script>
+  import deliverApi from '@/api/work/deliver'
+  import SelectUser from '@/components/select/SelectUser'
+
+  export default {
+    name: 'WorkOrderFeedback',
+    components: {
+      SelectUser,
+    },
+    props: {},
+    data() {
+      return {
+        form: {
+          deliverManId: undefined,
+          deliverManName: undefined,
+          projectManId: undefined,
+          projectManName: undefined,
+          id: 0,
+        },
+        rules: {
+          // deliverManId: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+          // deliverManName: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+        },
+        dialogFormVisible: false,
+        orderTypeList: [],
+        dingtalkForm: undefined,
+        curKey: '',
+        curName: '',
+      }
+    },
+    mounted() {},
+    methods: {
+      handleSelectUser(key, name) {
+        this.curKey = key
+        this.curName = name
+        this.$refs.selectUser.open()
+      },
+      selectUser(val) {
+        if (val && val.length > 0) {
+          this.form[this.curKey] = val[0].id
+          this.form[this.curName] = val.map((item) => item.nickName).join()
+        }
+      },
+      open(row) {
+        this.form = {
+          deliverManId: row.deliverManId,
+          deliverManName: row.deliverManName,
+          projectManId: row.projectManId,
+          projectManName: row.projectManName,
+          id: row.id,
+        }
+        this.dialogFormVisible = true
+      },
+      close() {
+        this.$refs['form'].resetFields()
+        this.form = this.$options.data().form
+        this.dialogFormVisible = false
+      },
+      save() {
+        this.$refs['form'].validate(async (valid) => {
+          if (valid) {
+            const { msg } = await deliverApi.updateDeliverOrder(this.form)
+            this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+            this.$emit('fetch-data')
+            this.close()
+          }
+        })
+      },
+    },
+  }
+</script>

+ 273 - 0
src/views/work/order/deliver/index.vue

@@ -0,0 +1,273 @@
+<template>
+  <div class="list-container">
+    <vab-query-form>
+      <vab-query-form-top-panel>
+        <el-form ref="queryForm" :inline="true" :model="queryForm" @submit.native.prevent>
+          <el-form-item prop="contractCode">
+            <el-input v-model="queryForm.contractCode" placeholder="合同编号" @keyup.enter.native="restFetchData" />
+          </el-form-item>
+          <el-form-item prop="custName">
+            <el-input v-model="queryForm.custName" placeholder="客户名称" @keyup.enter.native="restFetchData" />
+          </el-form-item>
+          <el-form-item prop="orderType">
+            <el-select
+              v-model="queryForm.orderType"
+              clearable
+              placeholder="交付类型"
+              style="width: 100%"
+              @keyup.enter.native="restFetchData">
+              <el-option label="软件" value="10" />
+              <el-option label="硬件" value="20" />
+            </el-select>
+          </el-form-item>
+          <el-form-item prop="projectManName">
+            <el-input v-model="queryForm.projectManName" placeholder="项目经理" @keyup.enter.native="restFetchData" />
+          </el-form-item>
+
+          <el-form-item>
+            <el-button icon="el-icon-search" type="primary" @click="restFetchData">查询</el-button>
+            <el-button icon="el-icon-refresh-right" @click="reset">重置</el-button>
+          </el-form-item>
+        </el-form>
+      </vab-query-form-top-panel>
+      <vab-query-form-left-panel :span="12" />
+      <vab-query-form-right-panel :span="12">
+        <table-tool :columns="columns" :show-columns.sync="showColumns" table-type="deliverWorkOrderTable" />
+      </vab-query-form-right-panel>
+    </vab-query-form>
+
+    <el-table ref="table" v-loading="listLoading" border :data="list" :height="$baseTableHeight(2)">
+      <el-table-column align="center" label="序号" show-overflow-tooltip width="80">
+        <template #default="{ $index }">
+          {{ $index + 1 }}
+        </template>
+      </el-table-column>
+      <el-table-column
+        v-for="(item, index) in showColumns"
+        :key="index"
+        align="center"
+        :label="item.label"
+        :prop="item.prop"
+        show-overflow-tooltip
+        :sortable="item.sortable"
+        :width="item.width">
+        <template #default="{ row }">
+          <el-button v-if="item.prop === 'orderCode'" style="font-size: 14px" type="text" @click="handleDetail(row)">
+            {{ row.orderCode }}
+          </el-button>
+          <span v-else-if="item.prop === 'orderType'">
+            {{ row.orderType == '10' ? '软件' : '硬件' }}
+          </span>
+          <span v-else-if="item.prop === 'product'">
+            {{ selectDictLabel(productLineOptions, row.product) }}
+          </span>
+          <span v-else-if="item.prop === 'orderStatus'">
+            {{ selectDictLabel(deliveryStatusOptions, row.orderStatus) }}
+          </span>
+          <span v-else-if="item.prop === 'createdTime'">
+            {{ parseTime(row.createdTime, '{y}-{m}-{d}') }}
+          </span>
+          <span v-else>{{ row[item.prop] }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column align="center" label="操作" width="90">
+        <template #default="{ row }">
+          <el-button type="text" @click="handleUpdateUser(row)">成员</el-button>
+          <el-button v-if="row.orderStatus != 20" type="text" @click="handleFinish(row)">完成</el-button>
+        </template>
+      </el-table-column>
+      <template #empty>
+        <el-image class="vab-data-empty" :src="require('@/assets/empty_images/data_empty.png')" />
+      </template>
+    </el-table>
+
+    <el-pagination
+      background
+      :current-page="queryForm.pageNum"
+      :layout="layout"
+      :page-size="queryForm.pageSize"
+      :total="total"
+      @current-change="handleCurrentChange"
+      @size-change="handleSizeChange" />
+    <!--创建工单-->
+    <!-- <edit ref="edit" @fetch-data="fetchData" /> -->
+    <!--反馈-->
+    <finish ref="finish" @fetch-data="restFetchData" />
+    <!-- 更新项目经理交付经理 -->
+    <update-user ref="updateUser" @fetch-data="restFetchData" />
+  </div>
+</template>
+
+<script>
+  import to from 'await-to-js'
+  import deliverApi from '@/api/work/deliver'
+  import TableTool from '@/components/table/TableTool'
+  import Finish from './components/finish'
+  import UpdateUser from './components/updateUser'
+
+  export default {
+    name: 'WorkOrder',
+    components: { TableTool, Finish, UpdateUser },
+    data() {
+      return {
+        activeName: 'first',
+        layout: 'total, sizes, prev, pager, next, jumper',
+        queryForm: {
+          contractCode: '',
+          custName: '',
+          orderType: '',
+          projectManName: '',
+          pageNum: 1,
+          pageSize: 10,
+        },
+        total: 0,
+        listLoading: false,
+        list: [],
+        selectRows: [],
+        showColumns: [],
+        columns: [
+          {
+            label: '交付工单号',
+            width: '160px',
+            prop: 'orderCode',
+          },
+          {
+            label: '关联合同',
+            width: 'auto',
+            prop: 'contractCode',
+          },
+          {
+            label: '交付状态',
+            width: '160px',
+            prop: 'orderStatus',
+            disableCheck: true,
+          },
+          {
+            label: '交付类型',
+            width: '160px',
+            prop: 'orderType',
+          },
+          {
+            label: '关联客户',
+            width: 'auto',
+            prop: 'custName',
+          },
+          {
+            label: '关联项目',
+            width: 'auto',
+            prop: 'projectName',
+          },
+          {
+            label: '项目经理',
+            width: '160px',
+            prop: 'projectManName',
+          },
+          {
+            label: '交付经理',
+            width: '160px',
+            prop: 'deliverManName',
+          },
+          {
+            label: '产品线',
+            width: 'auto',
+            prop: 'product',
+          },
+          {
+            label: '完成信息',
+            width: '160px',
+            prop: 'finishRemark',
+          },
+          {
+            label: '创建时间',
+            width: 'auto',
+            prop: 'createdTime',
+          },
+        ],
+        deliveryStatusOptions: [],
+        productLineOptions: [],
+      }
+    },
+    watch: {
+      showColumns: function () {
+        this.$nextTick(() => this.$refs.table.doLayout())
+      },
+    },
+    activated() {
+      this.fetchData()
+    },
+    mounted() {
+      this.getOptions()
+      this.fetchData()
+      // deliverApi.addDeliverOrder({ contractId: 47 })
+    },
+
+    methods: {
+      getOptions() {
+        Promise.all([this.getDicts('delivery_status'), this.getDicts('sys_product_line')])
+          .then(([deliveryStatus, productLine]) => {
+            this.deliveryStatusOptions = deliveryStatus.data.values || []
+            this.productLineOptions = productLine.data.values || []
+          })
+          .catch((err) => console.log(err))
+      },
+      restFetchData() {
+        this.queryForm.pageNum = 1
+        this.fetchData()
+      },
+      async fetchData() {
+        this.listLoading = true
+        const params = { ...this.queryForm }
+        const [err, res] = await to(deliverApi.getDeliverOrderList({ ...params }))
+        this.listLoading = false
+        if (err) return (this.listLoading = false)
+        if (res.code == 200 && res.data) {
+          this.list = res.data.list || []
+          this.total = res.data.total
+        }
+        this.$nextTick(() => this.$refs.table.doLayout())
+      },
+      handleFinish(row) {
+        this.$refs['finish'].showEdit(row)
+      },
+      handleUpdateUser(row) {
+        this.$refs['updateUser'].open(row)
+      },
+      //详情
+      handleDetail(row) {
+        this.$router.push({
+          name: 'DeliveryPlan',
+          query: {
+            id: row.id,
+          },
+        })
+      },
+      reset() {
+        this.queryForm = {
+          pageNum: 1,
+          pageSize: 10,
+          name: '',
+          orderTypeName: '',
+          orderStatus: '',
+          assignUserName: '',
+        }
+        this.fetchData()
+      },
+
+      handleSizeChange(val) {
+        this.queryForm.pageSize = val
+        this.fetchData()
+      },
+      handleCurrentChange(val) {
+        this.queryForm.pageNum = val
+        this.fetchData()
+      },
+      setSelectRows(val) {
+        this.selectRows = val
+      },
+    },
+  }
+</script>
+
+<style lang="scss" scoped>
+  $base: '.list';
+</style>

+ 512 - 0
src/views/work/order/deliver/plan.vue

@@ -0,0 +1,512 @@
+<template>
+  <div class="user-management-container">
+    <div class="side-layout">
+      <div class="tree-side">
+        <div class="head-container">
+          <el-button
+            v-show="workOrderStatus != 20"
+            v-permissions="['order:delivery:plan:add']"
+            class="add-btn"
+            icon="el-icon-plus"
+            type="text"
+            @click="openAddPlan()">
+            新建计划
+          </el-button>
+        </div>
+        <div class="left-scroll">
+          <ul class="plan-wrap">
+            <li
+              v-for="(v, i) in planList"
+              :key="i"
+              class="plan-item"
+              :class="v.id == curPlanId ? 'actived' : ''"
+              @click="selectPlanItem(v)">
+              <div class="flex-between" style="margin-bottom: 20px">
+                <span>{{ v.planTitle }}</span>
+                <div class="plan-status" :class="'plan-status-' + v.planStatus" @click="openChangeStatus(v)">
+                  {{ plasnStatusObj[v.planStatus] }}
+                </div>
+              </div>
+              <div class="flex-between">
+                <div>{{ parseTime(v.planStartDate, '{m}-{d}') }} ~ {{ parseTime(v.planEndDate, '{m}-{d}') }}</div>
+                <el-button
+                  v-show="workOrderStatus != 20 && v.planStatus != 30"
+                  style="padding: 0"
+                  type="text"
+                  @click="openAddPlan(v.id)">
+                  编辑
+                </el-button>
+              </div>
+            </li>
+          </ul>
+        </div>
+      </div>
+      <div class="tree-table">
+        <vab-query-form>
+          <vab-query-form-top-panel>
+            <el-form :inline="true" :model="queryForm" @submit.native.prevent>
+              <el-form-item>
+                <el-input
+                  v-model.trim="queryForm.progressTitle"
+                  clearable
+                  placeholder="任务标题"
+                  @keyup.enter.native="fetchWorkListPage1" />
+              </el-form-item>
+              <el-form-item>
+                <el-select
+                  v-model="queryForm.progressStatus"
+                  clearable
+                  placeholder="状态"
+                  style="width: 100%"
+                  @change="fetchWorkListPage1">
+                  <el-option label="未开始" value="10" />
+                  <el-option label="已开始" value="20" />
+                  <el-option label="已完成" value="30" />
+                </el-select>
+              </el-form-item>
+              <el-form-item>
+                <el-select
+                  v-model="queryForm.progressLevel"
+                  clearable
+                  placeholder="级别"
+                  style="width: 100%"
+                  @change="fetchWorkListPage1">
+                  <el-option label="最高" value="10" />
+                  <el-option label="普通" value="20" />
+                  <el-option label="较低" value="30" />
+                </el-select>
+              </el-form-item>
+              <el-form-item>
+                <el-button icon="el-icon-search" type="primary" @click="fetchWorkList">查询</el-button>
+                <el-button icon="el-icon-refresh-right" @click="reset">重置</el-button>
+              </el-form-item>
+            </el-form>
+          </vab-query-form-top-panel>
+          <vab-query-form-left-panel>
+            <el-button
+              v-show="workOrderStatus != 20 && curPlanStatus != 30"
+              v-permissions="['order:delivery:work:add']"
+              icon="el-icon-plus"
+              type="primary"
+              @click="openAddWork">
+              新建工作项
+            </el-button>
+          </vab-query-form-left-panel>
+          <vab-query-form-right-panel>
+            <table-tool :columns="columns" :show-columns.sync="showColumns" table-type="productTable" />
+          </vab-query-form-right-panel>
+        </vab-query-form>
+
+        <el-table ref="table" v-loading="listLoading" border :data="tabaleList" :height="$baseTableHeight(2)">
+          <el-table-column align="center" label="序号" show-overflow-tooltip width="80">
+            <template #default="{ $index }">
+              {{ $index + 1 }}
+            </template>
+          </el-table-column>
+          <el-table-column
+            v-for="(item, index) in showColumns"
+            :key="index"
+            align="center"
+            :label="item.label"
+            :min-width="item.minWidth"
+            :prop="item.prop"
+            show-overflow-tooltip
+            :sortable="item.sortable"
+            :width="item.width">
+            <template #default="{ row }">
+              <el-button
+                v-if="item.prop === 'progressTitle'"
+                style="font-size: 14px"
+                type="text"
+                @click="handleWorkDetail(row)">
+                {{ row.progressTitle }}
+              </el-button>
+              <span v-else-if="item.prop === 'progressLevel'">
+                <el-select
+                  v-model="row.progressLevel"
+                  :disabled="workOrderStatus == 20 || curPlanStatus == 30 || row.progressStatus == 30"
+                  placeholder="请选择"
+                  @change="changeprogressLevel($event, row)">
+                  <el-option label="最高" value="10" />
+                  <el-option label="普通" value="20" />
+                  <el-option label="较低" value="30" />
+                </el-select>
+              </span>
+              <span v-else-if="item.prop === 'progressStatus'">
+                {{ plasnStatusObj[row.progressStatus] }}
+              </span>
+              <span v-else-if="item.prop === 'startDate'">
+                {{ parseTime(row.startDate, '{y}-{m}-{d}') }}
+              </span>
+              <span v-else-if="item.prop === 'endDate'">
+                {{ parseTime(row.endDate, '{y}-{m}-{d}') }}
+              </span>
+              <span v-else-if="item.prop === 'reaStartDate'">
+                {{ parseTime(row.reaStartDate, '{y}-{m}-{d}') }}
+              </span>
+              <span v-else-if="item.prop === 'reaEndDate'">
+                {{ parseTime(row.reaEndDate, '{y}-{m}-{d}') }}
+              </span>
+              <span v-else>{{ row[item.prop] }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column align="center" fixed="right" label="操作" show-overflow-tooltip width="120">
+            <template #default="{ row }">
+              <el-button
+                v-if="row.progressStatus == 10 && workOrderStatus != 20 && curPlanStatus != 30"
+                type="text"
+                @click="handleStartWork(row)">
+                开始
+              </el-button>
+              <el-button
+                v-if="row.progressStatus == 20 && workOrderStatus != 20 && curPlanStatus != 30"
+                type="text"
+                @click="handleFinishWork(row)">
+                完成
+              </el-button>
+              <el-button v-show="workOrderStatus != 20 && curPlanStatus != 30" type="text" @click="handleDelete(row)">
+                删除
+              </el-button>
+            </template>
+          </el-table-column>
+          <template #empty>
+            <el-image class="vab-data-empty" :src="require('@/assets/empty_images/data_empty.png')" />
+          </template>
+        </el-table>
+        <el-pagination
+          background
+          :current-page="queryForm.pageNum"
+          :layout="layout"
+          :page-size="queryForm.pageSize"
+          :total="total"
+          @current-change="handleCurrentChange"
+          @size-change="handleSizeChange" />
+      </div>
+      <edit-plan ref="plan" @fetch-data="fetchPlanList" />
+      <edit-work ref="work" @fetch-data="fetchWorkListPage1" />
+      <select-status ref="status" @fetch-data="fetchPlanList" />
+    </div>
+  </div>
+</template>
+
+<script>
+  import orderWorkApi from '@/api/work/deliver'
+  import planApi from '@/api/work/deliverPlan'
+  import deliverWorkApi from '@/api/work/deliverWork'
+  import TableTool from '@/components/table/TableTool'
+  import EditPlan from '@/views/work/order/deliver/components/editPlan'
+  import EditWork from '@/views/work/order/deliver/components/editWork'
+  import selectStatus from './components/selectStatus'
+  import to from 'await-to-js'
+  export default {
+    name: 'Product',
+    components: { EditPlan, EditWork, TableTool, selectStatus },
+    data() {
+      return {
+        planList: [], //左侧工单列表
+        curPlanId: -1,
+        tabaleList: [],
+        plasnStatusObj: {
+          10: '未开始',
+          20: '已开始',
+          30: '已关闭',
+        },
+        progressLevelOptions: {
+          10: '最高',
+          20: '普通',
+          30: '较低',
+        },
+        listLoading: true,
+        layout: 'total, sizes, prev, pager, next, jumper',
+        total: 0,
+        selectRows: '',
+        queryForm: {
+          pageNum: 1,
+          pageSize: 10,
+          progressStatus: '',
+          progressLevel: '',
+          progressTitle: '',
+        },
+        showColumns: [],
+        columns: [
+          {
+            label: '任务标题',
+            width: '160px',
+            prop: 'progressTitle',
+            sortable: false,
+            disableCheck: true,
+          },
+          {
+            label: '任务内容',
+            prop: 'progressContext',
+            minWidth: '160px',
+            sortable: false,
+            disableCheck: true,
+          },
+          {
+            label: '优先级',
+            width: '120px',
+            prop: 'progressLevel',
+            sortable: false,
+          },
+          {
+            label: '状态',
+            width: '120px',
+            prop: 'progressStatus',
+            sortable: false,
+          },
+          {
+            label: '计划开始时间',
+            width: '120px',
+            prop: 'startDate',
+            sortable: false,
+            disableCheck: true,
+          },
+
+          {
+            label: '计划结束时间',
+            width: '120px',
+            prop: 'endDate',
+            sortable: false,
+          },
+          {
+            label: '实际开始时间',
+            width: '120px',
+            prop: 'reaStartDate',
+            sortable: false,
+            disableCheck: true,
+          },
+
+          {
+            label: '实际结束时间',
+            width: '120px',
+            prop: 'reaEndDate',
+            sortable: false,
+          },
+          {
+            label: '备注',
+            width: '120px',
+            prop: 'remark',
+            sortable: false,
+          },
+        ],
+        defaultProps: {
+          key: 'key',
+          label: 'value',
+        },
+        prodClassOptions: [], //产品分类
+        id: 0,
+        curPlanStatus: 0,
+        workOrderStatus: 0,
+      }
+    },
+    watch: {
+      showColumns: function () {
+        this.$nextTick(() => this.$refs.table.doLayout())
+      },
+    },
+    mounted() {
+      this.id = parseInt(this.$route.query.id)
+      this.getOrderDetails()
+      this.fetchPlanList()
+    },
+    methods: {
+      openChangeStatus(v) {
+        if (this.workOrderStatus == 20 || v.planStatus == 30) return
+        this.$refs.status.open(v.id)
+      },
+      selectPlanItem(row) {
+        if (this.curPlanId != row.id) {
+          this.curPlanId = row.id
+          this.curPlanStatus = row.planStatus
+
+          this.queryForm.pageNum = 1
+          this.fetchWorkListPage1()
+        }
+      },
+      reset() {
+        this.queryForm = {
+          pageNum: 1,
+          pageSize: 10,
+          progressStatus: '',
+          progressLevel: '',
+          progressTitle: '',
+        }
+        this.fetchWorkListPage1()
+      },
+      async changeprogressLevel(val, row) {
+        console.log(val, row)
+        const params = {
+          id: row.id,
+          progressLevel: val,
+        }
+        const [err, res] = await to(deliverWorkApi.update(params))
+        if (err) return
+        if (res.code == 200) {
+          this.$baseMessage(res.msg, 'success', 'vab-hey-message-success')
+        }
+        this.fetchWorkList()
+      },
+      // 新建计划
+      openAddPlan(id = null) {
+        this.$refs.plan.open(id)
+      },
+      // 新增工作项
+      openAddWork() {
+        this.$refs.work.open(this.curPlanId)
+      },
+      //工作项详情
+      handleWorkDetail(row) {
+        if (this.workOrderStatus == 20 || this.curPlanStatus == 30 || row.progressStatus == 30) return
+        this.$refs['work'].open(this.planId, row)
+      },
+      // 开始工作项
+      async handleStartWork(row) {
+        this.$prompt('你确定要开始当前任务吗', '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+        })
+          .then(async ({ value }) => {
+            // 当用户点击确定按钮时,执行的逻辑
+            await this.fetchWorkList()
+            console.log('输入的值为:', value)
+            const [err, res] = await to(deliverWorkApi.startWork({ id: row.id, remark: value }))
+            if (err) return
+            if (res.code == 200) {
+              this.$baseMessage(res.msg, 'success', 'vab-hey-message-success')
+              this.fetchWorkList()
+            }
+          })
+          .catch(() => {
+            // 当用户点击取消按钮时,执行的逻辑
+            console.log('取消输入')
+          })
+      },
+      // 完成工作项
+      async handleFinishWork(row) {
+        this.$prompt('你确定要完成当前任务吗', '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+        })
+          .then(async ({ value }) => {
+            // 当用户点击确定按钮时,执行的逻辑
+            await this.fetchWorkList()
+            console.log('输入的值为:', value)
+            const [err, res] = await to(deliverWorkApi.finishWork({ id: row.id, remark: value }))
+            if (err) return
+            if (res.code == 200) {
+              this.$baseMessage(res.msg, 'success', 'vab-hey-message-success')
+              this.fetchWorkList()
+            }
+          })
+          .catch(() => {
+            // 当用户点击取消按钮时,执行的逻辑
+            console.log('取消输入')
+          })
+      },
+      handleDelete(row) {
+        this.$baseConfirm('你确定要删除当前项吗', null, async () => {
+          const { msg } = await deliverWorkApi.delete({ id: [row.id] })
+          this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+          await this.fetchWorkList()
+        })
+      },
+      handleSizeChange(val) {
+        this.queryForm.pageSize = val
+        this.fetchWorkList()
+      },
+      handleCurrentChange(val) {
+        this.queryForm.pageNum = val
+        this.fetchWorkList()
+      },
+      fetchWorkListPage1() {
+        this.queryForm.pageNum = 1
+        this.fetchWorkList()
+      },
+      // 查询计划
+      async fetchPlanList() {
+        this.listLoading = true
+        const [err, res] = await to(planApi.getList({ deliverOrderId: this.id }))
+        this.listLoading = false
+        if (err) return
+        if (res.code == 200 && res.data) {
+          this.planList = res.data.list
+          if (this.curPlanId === -1 && this.planList.length > 0) {
+            this.curPlanId = this.planList[0].id
+            // this.workOrderStatus = this.planList[0].orderStatus || 10
+            this.curPlanStatus = this.planList[0].planStatus
+          }
+          this.fetchWorkList()
+        }
+      },
+      async getOrderDetails() {
+        const [err, res] = await to(orderWorkApi.getDeliverOrder({ id: this.id }))
+        if (err) return
+        if (res.code == 200 && res.data) {
+          this.workOrderStatus = res.data.orderStatus || 10
+        }
+      },
+      // 查询工作项
+      async fetchWorkList() {
+        this.listLoading = true
+        const params = Object.assign(this.queryForm, { planId: this.curPlanId })
+        const [err, res] = await to(deliverWorkApi.list(params))
+        this.listLoading = false
+        if (err) return
+        if (res.code == 200 && res.data) {
+          this.tabaleList = res.data.list
+          this.total = res.data.total
+        }
+      },
+    },
+  }
+</script>
+<style lang="scss" scoped>
+  .el-form-item--small {
+    margin: 0 0 10px 0 !important;
+  }
+  .left-scroll {
+    height: 100%;
+    overflow: auto;
+  }
+  .tree-side {
+    border-right: 0;
+    .plan-item {
+      cursor: pointer;
+      padding: 10px 10px 10px 10px;
+      border-radius: 4px;
+      .flex-between {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        margin-bottom: 10px;
+      }
+    }
+    .actived {
+      background: rgb(135 206 235 / 40%);
+    }
+    .plan-status {
+      border: 1px solid;
+      border-radius: 30px;
+      padding: 5px 10px;
+      line-height: 1;
+      font-size: 12px;
+    }
+    .plan-status-10 {
+      color: #409eff;
+      border-color: #409eff;
+    }
+    .plan-status-20 {
+      color: #67c23a;
+      border-color: #67c23a;
+    }
+    .plan-status-30 {
+      color: #f56c6c;
+      border-color: #f56c6c;
+    }
+    .add-btn {
+      font-size: 14px;
+      font-weight: bold;
+    }
+  }
+</style>

+ 168 - 10
src/views/work/order/detail.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="detail">
-    <el-row :gutter="30">
-      <el-col :span="25">
+    <div class="side-layout">
+      <div class="info">
         <div class="title">
           <p>工单</p>
           <h3>
@@ -51,7 +51,7 @@
               </el-descriptions-item>
             </el-descriptions>
             <div v-if="dingFormTableData.length">
-              <h3 style="margin-top: 10px">{{ dingFormTableName }}</h3>
+              <h3 style="margin-top: 10px; margin-bottom: 10px">{{ dingFormTableName }}</h3>
               <el-table border :data="dingFormTableData">
                 <el-table-column
                   v-for="(col, index) in dingFormTableHeader"
@@ -62,25 +62,74 @@
                   show-overflow-tooltip />
               </el-table>
             </div>
+            <div v-if="detail.finishRemark">
+              <h3 style="margin-top: 10px; margin-bottom: 10px">完成信息</h3>
+              <el-row>
+                <el-col :span="24" style="margin-bottom: 10px">
+                  <div>{{ detail.finishByName }}于 {{ detail.finishTime }} 完成</div>
+                </el-col>
+                <el-col :span="24">
+                  <div>
+                    {{ detail.finishRemark }}
+                  </div>
+                </el-col>
+              </el-row>
+            </div>
           </el-tab-pane>
-          <el-tab-pane label="反馈记录" name="feedback">
+          <!-- <el-tab-pane label="反馈记录" name="feedback">
             <detail-feedback ref="feedback" :work-order-id="id" />
-          </el-tab-pane>
+          </el-tab-pane> -->
         </el-tabs>
-      </el-col>
-    </el-row>
+      </div>
+      <div class="info-side">
+        <ul class="records">
+          <li v-for="(value, key) in records" :key="key">
+            <div class="date">
+              <h2>{{ key.split('-')[2] }}</h2>
+              <h3>{{ key.split('-').splice(0, 2).join('.') }}</h3>
+            </div>
+            <ul class="content">
+              <li v-for="(item, index) in records[key]" :key="index">
+                <!-- <el-avatar class="user-avatar"
+                           :src="avatar" /> -->
+                <vab-icon class="user-avatar" icon="account-circle-fill" />
+                <div class="text">
+                  <p class="action">{{ item.opnPeople }} {{ item.opnType }}</p>
+                  <div class="feedback-content">{{ JSON.parse(item.opnContent).content }}</div>
+                  <p>{{ item.opnDate }}</p>
+                </div>
+              </li>
+            </ul>
+          </li>
+        </ul>
+        <el-input
+          v-model="feedBackContent"
+          maxlength="30"
+          placeholder="请输入反馈内容"
+          show-word-limit
+          style="margin-bottom: 7px"
+          type="textarea" />
+        <el-row>
+          <el-col style="text-align: right">
+            <el-button type="primary" @click="handleSubFeedBack">提交反馈</el-button>
+          </el-col>
+        </el-row>
+      </div>
+    </div>
   </div>
 </template>
 
 <script>
+  import to from 'await-to-js'
   import api from '@/api/work/index'
-  import DetailFeedback from './components/DetailFeedback'
+  // import DetailFeedback from './components/DetailFeedback'
 
   export default {
     name: 'WorkOrderDetail',
-    components: { DetailFeedback },
+    // components: { DetailFeedback },
     data() {
       return {
+        feedBackContent: '',
         id: undefined,
         activeName: 'detail',
         detail: {},
@@ -89,13 +138,45 @@
         dingFormTableHeader: [],
         dingFormTableData: [],
         orderStatusOptions: [],
+        records: [],
       }
     },
     mounted() {
       this.id = parseInt(this.$route.query.id)
       this.getOptions()
+      this.getDynamics()
     },
     methods: {
+      async getDynamics() {
+        const [err, res] = await to(api.dynamicsList({ orderId: this.id }))
+        if (err) return
+        if (res.data.list) {
+          let obj = res.data.list
+          const keys = Object.keys(obj).reverse()
+          let records = {}
+          for (const item of keys) {
+            records[item] = obj[item]
+          }
+          this.records = records
+        }
+      },
+      async handleSubFeedBack() {
+        let params = {
+          content: this.feedBackContent,
+          orderId: this.id,
+        }
+        const [err, res] = await to(api.addDynamics({ ...params }))
+        if (err) return
+        console.log(res)
+        if (res.code == 200) {
+          this.feedBackContent = ''
+          this.$message({
+            type: 'success',
+            message: '提交成功!',
+          })
+          this.getDynamics()
+        }
+      },
       getOptions() {
         Promise.all([api.getDetail({ id: this.id }), this.getDicts('work_order_status')])
           .then(([detail, workOrderStatus]) => {
@@ -131,7 +212,7 @@
       },
       async handleClick(tab) {
         if (tab.name === 'feedback') {
-          await this.$refs.feedback.fetchData()
+          // await this.$refs.feedback.fetchData()
         } else if (tab.name === 'worksheet') {
           return
         } else {
@@ -227,5 +308,82 @@
       padding-top: 28px;
       text-align: right;
     }
+    .records {
+      margin: 0;
+      padding: 10px 20px;
+      list-style: none;
+      height: calc(100% - 90px);
+      overflow-y: auto;
+
+      > li {
+        display: flex;
+
+        & + li {
+          margin-top: 10px;
+        }
+      }
+
+      .date {
+        width: 100px;
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+
+        h2,
+        h3 {
+          margin: 0;
+        }
+
+        h2 {
+          font-size: 26px;
+          line-height: 32px;
+        }
+      }
+
+      .content {
+        flex: 1;
+        list-style: none;
+
+        li {
+          display: flex;
+
+          & + li {
+            margin-top: 10px;
+          }
+        }
+
+        .user-avatar {
+          font-size: 40px;
+        }
+
+        .text {
+          flex: 1;
+          padding-left: 20px;
+
+          p {
+            font-weight: 500;
+            margin: 0;
+            line-height: 20px;
+
+            span {
+              color: #1d66dc;
+            }
+          }
+
+          p:nth-child(2) {
+            margin-bottom: 10px;
+          }
+
+          .action {
+            font-weight: bold;
+            color: #333;
+          }
+        }
+      }
+    }
+    .feedback-content {
+      margin: 10px 0;
+      color: #000;
+    }
   }
 </style>

+ 97 - 21
src/views/work/order/index.vue

@@ -38,6 +38,11 @@
     </vab-query-form>
 
     <el-table ref="table" v-loading="listLoading" border :data="list" :height="$baseTableHeight(2)">
+      <el-table-column align="center" label="序号" show-overflow-tooltip width="80">
+        <template #default="{ $index }">
+          {{ $index + 1 }}
+        </template>
+      </el-table-column>
       <el-table-column
         v-for="(item, index) in showColumns"
         :key="index"
@@ -48,21 +53,30 @@
         :sortable="item.sortable"
         :width="item.width">
         <template #default="{ row }">
-          <el-button v-if="item.prop === 'name'" style="font-size: 14px" type="text" @click="handleDetail(row)">
-            {{ row.name }}
+          <el-button v-if="item.prop === 'id'" style="font-size: 14px" type="text" @click="handleDetail(row)">
+            {{ row.id }}
           </el-button>
           <span v-else-if="item.prop === 'orderStatus'">
             {{ selectDictLabel(orderStatusOptions, row.orderStatus) }}
           </span>
+          <span v-else-if="item.prop === 'productLine'">
+            {{ row.productLineName ? row.productLineName : selectDictLabel(productLineOptions, row.productLine) }}
+          </span>
           <span v-else-if="item.prop === 'createdTime'">
             {{ parseTime(row.createdTime, '{y}-{m}-{d}') }}
           </span>
+          <span v-else-if="item.prop === 'endTime'">
+            {{ overdue(row.endTime) }}
+          </span>
           <span v-else>{{ row[item.prop] }}</span>
         </template>
       </el-table-column>
-      <el-table-column align="center" label="操作" width="85">
+      <el-table-column align="center" label="操作" width="100">
         <template #default="{ row }">
-          <el-button type="text" @click="handleFeedback(row)">反 馈</el-button>
+          <el-button v-if="row.orderStatus == 30" type="text" @click="handleFeedback(row)">完成</el-button>
+          <el-button v-if="row.orderStatus == 10 || row.orderStatus == 30" type="text" @click="handleClose(row.id)">
+            关闭
+          </el-button>
         </template>
       </el-table-column>
       <template #empty>
@@ -81,7 +95,7 @@
     <!--创建工单-->
     <edit ref="edit" @fetch-data="fetchData" />
     <!--反馈-->
-    <feedback ref="feedback" @fetch-data="fetchData" />
+    <feedback ref="feedback" :order-status-options="orderStatusOptions" @fetch-data="fetchData" />
   </div>
 </template>
 
@@ -114,14 +128,20 @@
         showColumns: [],
         columns: [
           {
-            label: '工单名称',
-            width: '300px',
+            label: '工单编号',
+            width: '160px',
+            prop: 'id',
+            disableCheck: true,
+          },
+          {
+            label: '工单标题',
+            width: '160px',
             prop: 'name',
             disableCheck: true,
           },
           {
             label: '工单类型',
-            width: 'auto',
+            width: '160px',
             prop: 'orderTypeName',
           },
           {
@@ -130,27 +150,38 @@
             prop: 'orderStatus',
           },
           {
-            label: '分派人员',
+            label: '关联客户',
             width: 'auto',
-            prop: 'assignUserName',
+            prop: 'custName',
           },
-          // {
-          //   label: '反馈信息',
-          //   width: 'auto',
-          //   prop: 'feedBack',
-          // },
           {
-            label: '创建人',
+            label: '关联项目',
             width: 'auto',
-            prop: 'createdName',
+            prop: 'nboName',
           },
           {
-            label: '创建时间',
+            label: '产品线',
             width: 'auto',
-            prop: 'createdTime',
+            prop: 'productLine',
+          },
+          {
+            label: '负责人',
+            width: 'auto',
+            prop: 'assignUserName',
+          },
+          {
+            label: '是否超期',
+            width: '80px',
+            prop: 'endTime',
+          },
+          {
+            label: '申请人',
+            width: 'auto',
+            prop: 'createdName',
           },
         ],
         orderStatusOptions: [],
+        productLineOptions: [],
       }
     },
     watch: {
@@ -162,22 +193,47 @@
       this.fetchData()
     },
     mounted() {
+      this.getOptions()
       this.fetchData()
     },
 
     methods: {
       getOptions() {
-        Promise.all([this.getDicts('work_order_status')])
-          .then(([workOrderStatus]) => {
+        Promise.all([this.getDicts('work_order_status'), this.getDicts('sys_product_line')])
+          .then(([workOrderStatus, productLine]) => {
             this.orderStatusOptions = workOrderStatus.data.values || []
+            this.productLineOptions = productLine.data.values || []
           })
           .catch((err) => console.log(err))
       },
+      overdue(endTime) {
+        if (endTime) {
+          var oDate1 = new Date()
+          var oDate2 = new Date(endTime)
+          if (oDate1.getTime() > oDate2.getTime()) {
+            return '是' //第一个大
+          } else {
+            return '否' //第二个大
+          }
+        } else {
+          return '否'
+        }
+      },
       async fetchData() {
         this.listLoading = true
         const params = { ...this.queryForm }
         const [err, res] = await to(api.getList(params))
         if (err) return (this.listLoading = false)
+        if (res.data.list) {
+          res.data.list.filter((i) => {
+            let idata = JSON.parse(i.formData)
+            idata.filter((field) => {
+              if (field.name == '产品线') {
+                i.productLineName = field.value
+              }
+            })
+          })
+        }
         this.list = res.data.list || []
         this.total = res.data.total
         this.listLoading = false
@@ -194,6 +250,26 @@
         row.orderId = row.id
         this.$refs['feedback'].showEdit(row)
       },
+      // 关闭工单
+      handleClose(id) {
+        this.$confirm('确认关闭?', '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning',
+        })
+          .then(async () => {
+            const [err, res] = await to(api.closeWorkOrder({ orderId: id }))
+            if (err) return
+            if (res.code == 200) {
+              this.fetchData()
+              this.$message({
+                type: 'success',
+                message: '关闭成功!',
+              })
+            }
+          })
+          .catch(() => {})
+      },
       //详情
       handleDetail(row) {
         this.$router.push({