Prechádzať zdrojové kódy

feature(*): OMS合同模块、交付工单模块需求优化,大模块功能已完成,暂未优化细节和测试

likai 2 rokov pred
rodič
commit
fc9eb4d865

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

@@ -32,4 +32,10 @@ export default {
   getHardwareUserInfo(query) {
     return micro_request.postRequest(basePath, 'DeliverOrder', 'GetHardwareUserInfo', query)
   },
+  softwareStart(query) {
+    return micro_request.postRequest(basePath, 'DeliverOrder', 'SoftwareStart', query)
+  },
+  getDeliverTimeChangeLogs(query) {
+    return micro_request.postRequest(basePath, 'DeliverOrder', 'GetDeliverTimeChangeLogs', query)
+  },
 }

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

@@ -61,4 +61,13 @@ export default {
   updateProgressProductInfo(query) {
     return micro_request.postRequest(basePath, 'DeliverOrderProgress', 'UpdateProgressProductInfo', query)
   },
+  productSign(query) {
+    return micro_request.postRequest(basePath, 'DeliverOrderProgress', 'ProductSign', query)
+  },
+  completeSoftwareProgress(query) {
+    return micro_request.postRequest(basePath, 'DeliverOrderProgress', 'CompleteSoftwareProgress', query)
+  },
+  changeDeliverTime(query) {
+    return micro_request.postRequest(basePath, 'DeliverOrderProgress', 'ChangeDeliverTime', query)
+  },
 }

+ 21 - 0
src/views/contract/components/DetailsInfo.vue

@@ -81,6 +81,27 @@
       <el-descriptions-item content-class-name="my-content" label="更新时间" label-class-name="my-label">
         {{ parseTime(details.updatedTime, '{y}-{m}-{d}') }}
       </el-descriptions-item>
+      <el-descriptions-item content-class-name="my-content" label="软件运维开始时间" label-class-name="my-label">
+        {{ parseTime(details.softwareMaintenanceBeginTime, '{y}-{m}-{d}') }}
+      </el-descriptions-item>
+      <el-descriptions-item content-class-name="my-content" label="软件运维期限" label-class-name="my-label">
+        {{ details.softwareMaintenanceLimit + '年' }}
+      </el-descriptions-item>
+      <el-descriptions-item content-class-name="my-content" label="软件运维结束时间" label-class-name="my-label">
+        {{ parseTime(details.softwareMaintenanceEndTime, '{y}-{m}-{d}') }}
+      </el-descriptions-item>
+      <el-descriptions-item content-class-name="my-content" label="硬件运维开始时间" label-class-name="my-label">
+        {{ parseTime(details.hardwareMaintenanceBeginTime, '{y}-{m}-{d}') }}
+      </el-descriptions-item>
+      <el-descriptions-item content-class-name="my-content" label="硬件运维期限" label-class-name="my-label">
+        {{ details.hardwareMaintenanceLimit + '年' }}
+      </el-descriptions-item>
+      <el-descriptions-item content-class-name="my-content" label="硬件运维结束时间" label-class-name="my-label">
+        {{ parseTime(details.hardwareMaintenanceEndTime, '{y}-{m}-{d}') }}
+      </el-descriptions-item>
+      <el-descriptions-item content-class-name="my-content" label="运维条款" label-class-name="my-label">
+        {{ details.maintenanceClause }}
+      </el-descriptions-item>
     </el-descriptions>
   </div>
 </template>

+ 22 - 22
src/views/contract/components/DetailsProduct.vue

@@ -54,13 +54,13 @@
       <el-table-column align="center" fixed="right" label="操作" width="120px">
         <!-- approStatus 审核状态 10 待提交审核 20 待审核 30 审核已同意 40 审核已拒绝 50 审核已撤销 -->
         <template slot-scope="scope">
-          <el-button
+          <!-- <el-button
             v-permissions="['contract:detail:product:maintain']"
             :disabled="details.approStatus != '30'"
             type="text"
             @click="maintainOpen(scope.row)">
             维保
-          </el-button>
+          </el-button> -->
           <el-button
             v-permissions="['contract:detail:product:cost']"
             :disabled="details.approStatus != '30'"
@@ -261,31 +261,31 @@
             width: '120px',
             prop: 'directCost',
           },
-          {
-            label: '验收时间',
-            width: '120px',
-            prop: 'acceptTime',
-          },
+          // {
+          //   label: '验收时间',
+          //   width: '120px',
+          //   prop: 'acceptTime',
+          // },
           {
             label: '质保期(天)',
             width: '120px',
             prop: 'maintainPeriod',
           },
-          {
-            label: '运维开始时间',
-            width: '120px',
-            prop: 'maintainStartTime',
-          },
-          {
-            label: '运维期(天)',
-            width: '120px',
-            prop: 'warrantPeriod',
-          },
-          {
-            label: '运维约定条款',
-            width: '120px',
-            prop: 'maintainRemark',
-          },
+          // {
+          //   label: '运维开始时间',
+          //   width: '120px',
+          //   prop: 'maintainStartTime',
+          // },
+          // {
+          //   label: '运维期(天)',
+          //   width: '120px',
+          //   prop: 'warrantPeriod',
+          // },
+          // {
+          //   label: '运维约定条款',
+          //   width: '120px',
+          //   prop: 'maintainRemark',
+          // },
         ],
         productLineOptions: [],
         maintainVisible: false,

+ 34 - 0
src/views/contract/components/Edit.vue

@@ -155,6 +155,34 @@
           </el-form-item>
         </el-col>
       </el-row>
+      <el-row :gutter="20">
+        <el-col :span="12">
+          <el-form-item label="软件运维期限(年)" prop="softwareMaintenanceLimit">
+            <el-input-number
+              v-model.number="editForm.softwareMaintenanceLimit"
+              controls-position="right"
+              :min="0"
+              placeholder="请输入软件运维期限"
+              :step="1"
+              step-strictly
+              style="width: 100%" />
+          </el-form-item>
+          <el-form-item label="硬件运维期限(年)" prop="hardwareMaintenanceLimit">
+            <el-input-number
+              v-model="editForm.hardwareMaintenanceLimit"
+              controls-position="right"
+              :min="0"
+              placeholder="请输入软件运维期限"
+              :step="1"
+              style="width: 100%" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="运维条款" prop="maintenanceClause">
+            <el-input v-model="editForm.maintenanceClause" placeholder="请输入运维条款" :rows="5" type="textarea" />
+          </el-form-item>
+        </el-col>
+      </el-row>
       <el-row :gutter="20">
         <el-col :span="12">
           <el-form-item label="备注" prop="remark">
@@ -273,6 +301,9 @@
           custSignatoryName: '', //客户签约人name
           remark: '', //备注
           serviceFeeAgreement: '', // 运维服务费约定
+          softwareMaintenanceLimit: 1, // 软件运维期限(年)
+          hardwareMaintenanceLimit: 1, // 硬件运维期限(年)
+          maintenanceClause: '', // 运维条款
         },
         editRules: {
           contractName: [{ required: true, trigger: 'blur', message: '请输入合同名称' }],
@@ -582,6 +613,9 @@
           distributorName: '', //经销商name
           remark: '', //备注
           serviceFeeAgreement: '', //运维服务费约定
+          softwareMaintenanceLimit: 1, // 软件运维期限(年)
+          hardwareMaintenanceLimit: 1, // 硬件运维期限(年)
+          maintenanceClause: '', // 运维条款
         }
         this.productData = []
         this.stepActive = 0

+ 17 - 0
src/views/contract/index.vue

@@ -173,6 +173,9 @@
           <span v-else-if="item.label === '开票金额'">
             {{ formatPrice(row.invoiceAmount) }}
           </span>
+          <span v-else-if="item.prop === 'softwareMaintenanceLimit' || item.prop === 'hardwareMaintenanceLimit'">
+            {{ row[item.prop] + '年' }}
+          </span>
           <el-button
             v-else-if="item.prop === 'contractCode'"
             style="font-size: 14px"
@@ -383,6 +386,20 @@
             sortable: false,
             disableCheck: false,
           },
+          {
+            label: '软件运维期限',
+            width: '120px',
+            prop: 'softwareMaintenanceLimit',
+            sortable: false,
+            disableCheck: false,
+          },
+          {
+            label: '硬件运维期限',
+            width: '120px',
+            prop: 'hardwareMaintenanceLimit',
+            sortable: false,
+            disableCheck: false,
+          },
         ],
       }
     },

+ 142 - 0
src/views/work/deliver/components/DetailsRecords.vue

@@ -0,0 +1,142 @@
+<!--
+ * @Author: liuzl 461480418@qq.com
+ * @Date: 2023-01-09 17:42:13
+ * @LastEditors: wanglj
+ * @LastEditTime: 2023-02-21 17:14:52
+ * @Description: file content
+ * @FilePath: \opms_frontend\src\views\contract\components\DetailsRecords.vue
+-->
+<template>
+  <ul class="records">
+    <li v-for="(value, key) in dynamicsList" :key="key">
+      <div class="date">
+        {{ value.createdTime.split(' ')[0] }}
+        <h2>{{ value.createdTime.split(' ')[0].split('-')[2] }}</h2>
+        <h3>{{ value.createdTime.split(' ')[0].split('-').splice(0, 2).join('.') }}</h3>
+      </div>
+      <ul class="content">
+        <li>
+          <vab-icon class="user-avatar" icon="account-circle-fill" />
+          <div class="text">
+            <p class="action">{{ value.opnPeople }} {{ value.opnType }}</p>
+            <p>{{ parseTime(value.opnDate, '{y}-{m}-{d}') }}</p>
+            <p v-if="value.opnContent">
+              变更:
+              <span>{{ value.opnContent }}</span>
+            </p>
+            <p v-if="value.remark">
+              原因:
+              <span>{{ value.remark }}</span>
+            </p>
+            <!-- 
+            <template v-else-if="item.opnContent.cuctName">
+              <p>
+                联系人名称:
+                <span>{{ item.opnContent.cuctName }}</span>
+              </p>
+              <p>职务:{{ item.opnContent.postion }}</p>
+              <p>手机:{{ item.opnContent.telephone }}</p>
+            </template> -->
+          </div>
+        </li>
+      </ul>
+    </li>
+  </ul>
+</template>
+
+<script>
+  export default {
+    name: 'DetailsRecords',
+    props: {
+      dynamicsList: {
+        type: Array,
+        default: () => [],
+      },
+    },
+    data() {
+      return {}
+    },
+
+    mounted() {},
+
+    methods: {},
+  }
+</script>
+
+<style lang="scss" scoped>
+  .records {
+    margin: 0;
+    padding: 10px 20px;
+    list-style: none;
+    height: calc(100% - 60px);
+    width: 450px;
+    margin-top: 6px;
+    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;
+        }
+      }
+    }
+  }
+</style>

+ 71 - 0
src/views/work/deliver/components/changeDeliverTime.vue

@@ -0,0 +1,71 @@
+<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="deliveryTime">
+            <el-date-picker
+              v-model="form.deliveryTime"
+              placeholder="要求交付时间"
+              type="date"
+              value-format="yyyy-MM-dd HH:mm:ss" />
+          </el-form-item>
+          <el-form-item label="变更原因" prop="changeReason">
+            <el-input v-model="form.changeReason" 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/deliverWork'
+
+  export default {
+    name: 'ChangeDeliverTime',
+    data() {
+      return {
+        form: {
+          deliverOrderId: '',
+          progressId: '',
+          deliveryTime: '',
+          changeReason: '',
+        },
+        rules: {
+          deliveryTime: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+          changeReason: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+        },
+        dialogFormVisible: false,
+      }
+    },
+    mounted() {},
+    methods: {
+      open(row) {
+        this.form.deliverOrderId = row.deliverOrderId
+        this.form.progressId = row.id
+        this.form.deliveryTime = ''
+        this.form.changeReason = ''
+        this.dialogFormVisible = true
+      },
+      close() {
+        this.$refs['form'].resetFields()
+        this.dialogFormVisible = false
+      },
+      save() {
+        this.$refs['form'].validate(async (valid) => {
+          if (valid) {
+            const { msg } = await deliverApi.changeDeliverTime(this.form)
+            this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+            this.$emit('fetch-data')
+            this.close()
+          }
+        })
+      },
+    },
+  }
+</script>

+ 14 - 0
src/views/work/deliver/components/deliver.vue

@@ -39,6 +39,16 @@
               value-format="yyyy-MM-dd HH:mm:ss" />
           </el-form-item>
         </el-col>
+        <el-col :span="24">
+          <el-form-item label="选择实际发货时间" prop="realDeliveryTime">
+            <el-date-picker
+              v-model="form.realDeliveryTime"
+              placeholder="请选择日期"
+              style="width: 100%"
+              type="date"
+              value-format="yyyy-MM-dd HH:mm:ss" />
+          </el-form-item>
+        </el-col>
         <!-- <el-col :span="24">
           <el-form-item label="备注" prop="remark">
             <el-input v-model="form.remark" placeholder="请输入备注" />
@@ -72,6 +82,7 @@
       return {
         form: {
           date: '',
+          realDeliveryTime: '',
           expressName: '',
           expressCode: '',
           remark: '',
@@ -81,6 +92,7 @@
           expressCode: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
           expressName: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
           date: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+          realDeliveryTime: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
         },
         dialogFormVisible: false,
         planId: 0,
@@ -121,6 +133,7 @@
         this.title = '发货'
         this.progress = row
         this.form.date = ''
+        this.form.realDeliveryTime = ''
         this.form.expressName = ''
         this.form.expressCode = ''
         this.form.remark = ''
@@ -202,6 +215,7 @@
               deliverWorkApi.deliverGoods({
                 id: this.progress.id,
                 estimatedArrivalTime: this.form.date,
+                realDeliveryTime: this.form.realDeliveryTime,
                 expressName: this.form.expressName,
                 expressCode: this.form.expressCode,
                 // remark: this.form.remark,

+ 23 - 3
src/views/work/deliver/components/detailWork.vue

@@ -23,21 +23,36 @@
               {{ theDeliverOrder.orderCode }}
             </el-form-item>
           </el-col>
-          <el-col v-if="theProgress.progressType != ''" :span="12">
+          <el-col v-if="theProgress.progressType != '' && theProgress.progressType != '40'" :span="12">
             <el-form-item label="要求发货时间:" style="margin: 0px">
               {{ parseTime(theDeliverOrder.requiredDeliveryTime, '{y}-{m}-{d}') }}
             </el-form-item>
           </el-col>
-          <el-col v-if="theProgress.progressType != ''" :span="12">
+          <el-col v-if="theProgress.progressType != '' && theProgress.progressType != '40'" :span="12">
             <el-form-item label="收货信息:" style="margin: 0px">
               {{ theDeliverOrder.receivingInfo }}
             </el-form-item>
           </el-col>
-          <el-col v-if="theProgress.progressType != ''" :span="24">
+          <el-col v-if="theProgress.progressType != '' && theProgress.progressType != '40'" :span="24">
             <el-form-item label="特殊要求说明:" style="margin: 0px">
               {{ theDeliverOrder.specialRequirements }}
             </el-form-item>
           </el-col>
+          <el-col v-if="theProgress.progressType == '40'" :span="12">
+            <el-form-item label="要求交付时间:" style="margin: 0px">
+              {{ parseTime(theDeliverOrder.softwareRequiredDeliveryTime, '{y}-{m}-{d}') }}
+            </el-form-item>
+          </el-col>
+          <el-col v-if="theProgress.progressType == '40'" :span="12">
+            <el-form-item label="是否延期:" style="margin: 0px">
+              {{ theDeliverOrder.isDelay == '20' ? '是' : '否' }}
+            </el-form-item>
+          </el-col>
+          <el-col v-if="theProgress.progressType == '40'" :span="24">
+            <el-form-item label="特殊事项说明:" style="margin: 0px">
+              {{ theDeliverOrder.softwareSpecialRequirements }}
+            </el-form-item>
+          </el-col>
           <!-- <el-col :span="12">
           <el-form-item label="任务标题:" style="margin: 0px">
             {{ form.progressTitle }}
@@ -143,6 +158,11 @@
               {{ parseTime(theProgress.arrivalTime, '{y}-{m}-{d}') }}
             </el-form-item>
           </el-col>
+          <el-col v-if="theProgress && theProgress.isSign == 20" :span="12">
+            <el-form-item label="签收时间:" style="margin: 0px">
+              {{ parseTime(theProgress.productSigningTime, '{y}-{m}-{d}') }}
+            </el-form-item>
+          </el-col>
           <el-col :span="24" style="margin-top: 10px">
             <el-button
               v-if="theProgress && theProgress.assembleFileName != ''"

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

@@ -13,6 +13,7 @@
               <el-option label="发货任务单" value="10" />
               <el-option label="组装任务单" value="20" />
               <el-option label="部署安装单" value="30" />
+              <el-option label="软件交付验收任务单" value="40" />
             </el-select>
           </el-form-item>
         </el-col>

+ 164 - 0
src/views/work/deliver/components/productSign.vue

@@ -0,0 +1,164 @@
+<template>
+  <el-dialog :title="title" :visible.sync="dialogFormVisible" width="500px">
+    <el-form ref="form" label-position="top" :model="form" :rules="rules">
+      <el-row :gutter="20">
+        <el-col :span="24">
+          <el-form-item label="选择签收日期" prop="signTime">
+            <el-date-picker
+              v-model="form.signTime"
+              placeholder="请选择日期"
+              style="width: 100%"
+              type="date"
+              value-format="yyyy-MM-dd HH:mm:ss" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+    <template #footer>
+      <el-button @click="dialogFormVisible = false">取 消</el-button>
+      <el-button type="primary" @click="sign">确 定</el-button>
+    </template>
+  </el-dialog>
+</template>
+
+<script>
+  import deliverWorkApi from '@/api/work/deliverWork'
+  import to from 'await-to-js'
+  import axios from 'axios'
+  import asyncUploadFile from '@/utils/uploadajax'
+
+  export default {
+    name: 'ProductSign',
+    components: {},
+    props: {
+      // orderStatusOptions: {
+      //   type: Array,
+      //   default: () => [],
+      // },
+    },
+    data() {
+      return {
+        form: {
+          signTime: '',
+        },
+        progress: {},
+        rules: {
+          expressCode: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+          expressName: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+          signTime: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+          realDeliveryTime: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+        },
+        dialogFormVisible: false,
+        planId: 0,
+        title: '',
+        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',
+        },
+      }
+    },
+    mounted() {},
+    methods: {
+      // 打开弹窗
+      open(row) {
+        this.title = '签收'
+        this.progress = row
+        this.form.signTime = ''
+        if (this.$refs['form']) {
+          this.$refs['form'].resetFields()
+        }
+        if (this.$refs.uploadRef) {
+          this.$refs.uploadRef.clearFiles() //去掉文件列表
+        }
+        this.dialogFormVisible = true
+      },
+      // 上传附件
+      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(function (res) {
+            if (res.data && res.data.fid && res.data.fid !== '') {
+              option.action = `${process.env.VUE_APP_PROTOCOL}${res.data.publicUrl}/${res.data.fid}`
+              asyncUploadFile(option).then(() => {
+                _this.form.fileName = option.file.name
+                _this.form.fileUrl = `${process.env.VUE_APP_PROTOCOL}${res.data.publicUrl}/${res.data.fid}` // 资料存储url
+              })
+            } else {
+              _this.$message({
+                type: 'warning',
+                message: '未上传成功!请刷新界面重新上传!',
+              })
+            }
+          })
+          .catch(function () {
+            _this.$message({
+              type: 'warning',
+              message: '未上传成功!请重新上传!',
+            })
+          })
+      },
+      // 查看附件
+      showFile(url) {
+        let fileName = this.form.fileName
+        const xhr = new XMLHttpRequest()
+        xhr.open('GET', url, true)
+        xhr.responseType = 'blob' // 通过文件下载url拿到对应的blob对象
+        xhr.onload = () => {
+          if (xhr.status === 200) {
+            let link = document.createElement('a')
+            let body = document.querySelector('body')
+            link.href = window.URL.createObjectURL(xhr.response)
+            link.download = fileName
+            link.click()
+            this.$message.success('下载成功')
+            body.removeChild(link)
+            window.URL.revokeObjectURL(link.href)
+          }
+        }
+
+        xhr.send()
+      },
+      // 完成
+      async sign() {
+        this.$refs['form'].validate(async (valid) => {
+          if (valid) {
+            const [err, res] = await to(
+              deliverWorkApi.productSign({
+                id: this.progress.id,
+                signTime: this.form.signTime,
+              })
+            )
+            if (err) return
+            if (res.code == 200) {
+              this.$baseMessage(res.msg, 'success', 'vab-hey-message-success')
+              this.$emit('fetch-data')
+            }
+            this.dialogFormVisible = false
+          }
+        })
+      },
+    },
+  }
+</script>

+ 248 - 0
src/views/work/deliver/components/softwareComplete.vue

@@ -0,0 +1,248 @@
+<template>
+  <el-dialog :title="title" :visible.sync="dialogFormVisible" width="750px">
+    <el-form ref="form" label-position="top" :model="form" :rules="rules">
+      <el-row :gutter="20">
+        <el-col :span="24">
+          <el-form-item label="验收报告" prop="softwareCheckFileUrl">
+            <el-upload
+              ref="uploadRef"
+              action="#"
+              :before-upload="
+                (file) => {
+                  return beforeAvatarUpload(file)
+                }
+              "
+              :http-request="uploadRequest"
+              :limit="1">
+              <el-button size="mini" type="primary">点击上传</el-button>
+            </el-upload>
+            <el-button v-show="form.softwareCheckFileUrl != ''" @click="showFile(form.softwareCheckFileUrl)">
+              查看
+            </el-button>
+          </el-form-item>
+        </el-col>
+        <el-col :span="24">
+          <el-form-item label="选择验收时间" prop="softwareCheckTime">
+            <el-date-picker
+              v-model="form.softwareCheckTime"
+              placeholder="请选择日期"
+              style="width: 100%"
+              type="date"
+              value-format="yyyy-MM-dd HH:mm:ss" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="24">
+          <el-form-item label="已完成工作内容" prop="completedContent">
+            <el-input
+              v-model="form.completedContent"
+              placeholder="请输入已完成工作内容"
+              :rows="4"
+              show-word-limit
+              type="textarea" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="24">
+          <el-form-item label="已完成工作量" prop="completedWork">
+            <el-input v-model="form.completedWork" placeholder="请输入已完成工作量" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="24">
+          <el-form-item label="未完成工作内容" prop="uncompletedContent">
+            <el-input
+              v-model="form.uncompletedContent"
+              placeholder="请输入未完成工作内容"
+              :rows="4"
+              show-word-limit
+              type="textarea" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="24">
+          <el-form-item label="已完成工作量" prop="uncompletedWork">
+            <el-input v-model="form.uncompletedWork" placeholder="请输入已完成工作量" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+    <template #footer>
+      <el-button @click="dialogFormVisible = false">取 消</el-button>
+      <el-button type="primary" @click="complete">确 定</el-button>
+    </template>
+  </el-dialog>
+</template>
+
+<script>
+  import deliverWorkApi from '@/api/work/deliverWork'
+  import orderWorkApi from '@/api/work/deliver'
+  import to from 'await-to-js'
+  import axios from 'axios'
+  import asyncUploadFile from '@/utils/uploadajax'
+
+  export default {
+    name: 'SoftwareComplete',
+    components: {},
+    props: {
+      // orderStatusOptions: {
+      //   type: Array,
+      //   default: () => [],
+      // },
+    },
+    data() {
+      return {
+        contractId: '',
+        form: {
+          softwareCheckTime: '',
+          softwareCheckFileUrl: '',
+          softwareCheckFileName: '',
+          completedContent: '',
+          completedWork: '',
+          uncompletedContent: '',
+          uncompletedWork: '',
+        },
+        progress: {},
+        rules: {
+          softwareCheckFileUrl: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+          softwareCheckTime: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+          completedContent: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+          completedWork: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+          uncompletedContent: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+          uncompletedWork: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+        },
+        dialogFormVisible: false,
+        planId: 0,
+        title: '',
+        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',
+        },
+      }
+    },
+    mounted() {},
+    methods: {
+      // 打开弹窗
+      open(row) {
+        this.title = '完成'
+        this.contractId = ''
+        this.progress = row
+        this.form.softwareCheckTime = ''
+        this.form.softwareCheckFileUrl = ''
+        this.form.softwareCheckFileName = ''
+        this.form.completedContent = ''
+        this.form.completedWork = ''
+        this.form.uncompletedContent = ''
+        this.form.uncompletedWork = ''
+        if (this.$refs['form']) {
+          this.$refs['form'].resetFields()
+        }
+        if (this.$refs.uploadRef) {
+          this.$refs.uploadRef.clearFiles() //去掉文件列表
+        }
+        this.getDeliverInfo()
+        this.dialogFormVisible = true
+      },
+      // 获取产品
+      async getDeliverInfo() {
+        const [err, res] = await to(orderWorkApi.getDeliverOrder({ id: this.progress.deliverOrderId }))
+        if (err) return
+        if (res.code == 200 && res.data) {
+          this.contractId = res.data.contractId
+        }
+      },
+      // 上传附件
+      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(function (res) {
+            if (res.data && res.data.fid && res.data.fid !== '') {
+              option.action = `${process.env.VUE_APP_PROTOCOL}${res.data.publicUrl}/${res.data.fid}`
+              asyncUploadFile(option).then(() => {
+                _this.form.softwareCheckFileName = option.file.name
+                _this.form.softwareCheckFileUrl = `${process.env.VUE_APP_PROTOCOL}${res.data.publicUrl}/${res.data.fid}` // 资料存储url
+              })
+            } else {
+              _this.$message({
+                type: 'warning',
+                message: '未上传成功!请刷新界面重新上传!',
+              })
+            }
+          })
+          .catch(function () {
+            _this.$message({
+              type: 'warning',
+              message: '未上传成功!请重新上传!',
+            })
+          })
+      },
+      // 查看附件
+      showFile(url) {
+        let fileName = this.form.softwareCheckFileName
+        const xhr = new XMLHttpRequest()
+        xhr.open('GET', url, true)
+        xhr.responseType = 'blob' // 通过文件下载url拿到对应的blob对象
+        xhr.onload = () => {
+          if (xhr.status === 200) {
+            let link = document.createElement('a')
+            let body = document.querySelector('body')
+            link.href = window.URL.createObjectURL(xhr.response)
+            link.download = fileName
+            link.click()
+            this.$message.success('下载成功')
+            body.removeChild(link)
+            window.URL.revokeObjectURL(link.href)
+          }
+        }
+
+        xhr.send()
+      },
+      // 完成
+      async complete() {
+        this.$refs['form'].validate(async (valid) => {
+          if (valid) {
+            // 10发货任务单、20组装任务单、30部署安装单、40软件交付验收任务单
+            const [err, res] = await to(
+              deliverWorkApi.completeSoftwareProgress({
+                id: this.progress.id,
+                contractId: this.contractId,
+                deliverOrderId: this.progress.deliverOrderId,
+                softwareCheckTime: this.form.softwareCheckTime,
+                softwareCheckFileUrl: this.form.softwareCheckFileUrl,
+                softwareCheckFileName: this.form.softwareCheckFileName,
+                completedContent: this.form.completedContent,
+                completedWork: this.form.completedWork,
+                uncompletedContent: this.form.uncompletedContent,
+                uncompletedWork: this.form.uncompletedWork,
+              })
+            )
+            if (err) return
+            if (res.code == 200) {
+              this.$baseMessage(res.msg, 'success', 'vab-hey-message-success')
+              this.$emit('fetch-data')
+            }
+            this.dialogFormVisible = false
+          }
+        })
+      },
+    },
+  }
+</script>

+ 93 - 0
src/views/work/deliver/components/softwareStart.vue

@@ -0,0 +1,93 @@
+<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="startDate">
+            <el-date-picker
+              v-model="form.startDate"
+              placeholder="计划开始时间"
+              type="date"
+              value-format="yyyy-MM-dd HH:mm:ss" />
+          </el-form-item>
+          <el-form-item label="计划结束时间" prop="endDate">
+            <el-date-picker
+              v-model="form.endDate"
+              placeholder="计划结束时间"
+              type="date"
+              value-format="yyyy-MM-dd HH:mm:ss" />
+          </el-form-item>
+          <el-form-item label="要求交付时间" prop="softwareRequiredDeliveryTime">
+            <el-date-picker
+              v-model="form.softwareRequiredDeliveryTime"
+              placeholder="要求交付时间"
+              type="date"
+              value-format="yyyy-MM-dd HH:mm:ss" />
+          </el-form-item>
+          <el-form-item label="特殊事项说明" prop="softwareSpecialRequirements">
+            <el-input
+              v-model="form.softwareSpecialRequirements"
+              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: 'SoftwareStart',
+    data() {
+      return {
+        form: {
+          orderId: '',
+          softwareRequiredDeliveryTime: '',
+          softwareSpecialRequirements: '',
+          startDate: '',
+          endDate: '',
+        },
+        rules: {
+          softwareRequiredDeliveryTime: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+          startDate: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+          endDate: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+        },
+        dialogFormVisible: false,
+      }
+    },
+    mounted() {},
+    methods: {
+      open(row) {
+        this.form.orderId = row.id
+        this.form.softwareRequiredDeliveryTime = ''
+        this.form.softwareSpecialRequirements = ''
+        this.form.startDate = ''
+        this.form.endDate = ''
+        this.dialogFormVisible = true
+      },
+      close() {
+        this.$refs['form'].resetFields()
+        this.dialogFormVisible = false
+      },
+      save() {
+        this.$refs['form'].validate(async (valid) => {
+          if (valid) {
+            const { msg } = await deliverApi.softwareStart(this.form)
+            this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+            this.$emit('fetch-data')
+            this.close()
+          }
+        })
+      },
+    },
+  }
+</script>

+ 16 - 2
src/views/work/deliver/index.vue

@@ -98,12 +98,19 @@
           <el-button v-if="row.orderType != '20' && !row.deliverManName" type="text" @click="handleUpdateUser(row)">
             成员
           </el-button>
+          <!-- 软件的启动 -->
           <el-button
+            v-if="row.orderType == '10' && row.orderStatus == 10 && userId == row.saleId"
+            type="text"
+            @click="handleSoftwareStart(row)">
+            启动
+          </el-button>
+          <!-- <el-button
             v-if="(row.orderStatus == 10 || row.orderStatus == 15) && row.orderType != '20'"
             type="text"
             @click="handleFinish(row)">
             完成
-          </el-button>
+          </el-button> -->
         </template>
       </el-table-column>
       <template #empty>
@@ -129,6 +136,8 @@
     <start ref="startOrder" @fetch-data="restFetchData" />
     <!-- 创建工单 -->
     <order-edit ref="order-edit" @fetch-data="restFetchData" />
+    <!-- 软件交付工单启动 -->
+    <softwareStart ref="softwareStart" @fetch-data="restFetchData" />
   </div>
 </template>
 
@@ -138,13 +147,14 @@
   import TableTool from '@/components/table/TableTool'
   import Finish from './components/finish'
   import start from './components/start'
+  import softwareStart from './components/softwareStart'
   import UpdateUser from './components/updateUser'
   import { mapGetters } from 'vuex'
   import orderEdit from '@/views/work/deliver/components/Edit'
 
   export default {
     name: 'WorkOrder',
-    components: { TableTool, Finish, start, UpdateUser, orderEdit },
+    components: { TableTool, Finish, start, UpdateUser, orderEdit, softwareStart },
     data() {
       return {
         activeName: 'first',
@@ -301,6 +311,10 @@
       handleStart(row) {
         this.$refs['startOrder'].showEdit(row)
       },
+      // 软件交付的启动
+      handleSoftwareStart(row) {
+        this.$refs['softwareStart'].open(row)
+      },
       handleFinish(row) {
         this.$refs['finish'].showEdit(row)
       },

+ 70 - 0
src/views/work/deliver/plan.vue

@@ -237,6 +237,17 @@
                 @click="handleInspectGoods(row)">
                 验收
               </el-button>
+              <el-button
+                v-if="
+                  row.deliverStatus >= 30 &&
+                  row.progressType == '10' &&
+                  row.isSign != '20' &&
+                  userId == row.principalPersonId
+                "
+                type="text"
+                @click="handleProductSign(row)">
+                签收
+              </el-button>
               <el-button
                 v-if="
                   row.progressStatus == 20 &&
@@ -283,6 +294,18 @@
                 @click="handleComplete(row)">
                 完成
               </el-button>
+              <el-button
+                v-if="row.progressStatus == 20 && row.progressType == '40' && userId == row.principalPersonId"
+                type="text"
+                @click="handleSoftwareComplete(row)">
+                完成
+              </el-button>
+              <el-button
+                v-if="row.progressStatus == 20 && row.progressType == '40' && userId == row.principalPersonId"
+                type="text"
+                @click="handleChangeDeliverTime(row)">
+                变更交付时间
+              </el-button>
               <!-- 部署安装任务审核 -->
               <el-button
                 v-show="row.progressStatus == 30 && row.progressType == '30'"
@@ -307,6 +330,9 @@
           @current-change="handleCurrentChange"
           @size-change="handleSizeChange" />
       </div>
+      <div class="info-side">
+        <detailsRecords :dynamics-list="dynamicsList" />
+      </div>
       <edit-plan ref="plan" @fetch-data="fetchPlanList" />
       <edit-work ref="editWork" @fetch-data="fetchWorkListPage1" />
       <select-status ref="status" @fetch-data="fetchPlanList" />
@@ -324,6 +350,12 @@
       <ConfirmInstall ref="ConfirmInstall" @fetch-data="fetchWorkListPage1" />
       <!-- 安装审核 -->
       <AuditInstall ref="AuditInstall" @fetch-data="fetchWorkListPage1" />
+      <!-- 签收 -->
+      <productSign ref="productSign" @fetch-data="fetchWorkListPage1" />
+      <!-- 软件完成 -->
+      <softwareComplete ref="softwareComplete" @fetch-data="fetchWorkListPage1" />
+      <!-- 变更要求交付时间 -->
+      <changeDeliverTime ref="changeDeliverTime" @fetch-data="getProgressAndLogs" />
     </div>
   </div>
 </template>
@@ -344,6 +376,10 @@
   import StartInstall from '@/views/work/deliver/components/startInstall'
   import ConfirmInstall from '@/views/work/deliver/components/confirmInstall'
   import AuditInstall from '@/views/work/deliver/components/auditInstall'
+  import productSign from '@/views/work/deliver/components/productSign'
+  import softwareComplete from '@/views/work/deliver/components/softwareComplete'
+  import detailsRecords from './components/detailsRecords'
+  import changeDeliverTime from './components/changeDeliverTime'
   import { mapGetters } from 'vuex'
 
   export default {
@@ -360,9 +396,14 @@
       StartInstall,
       ConfirmInstall,
       AuditInstall,
+      productSign,
+      softwareComplete,
+      detailsRecords,
+      changeDeliverTime,
     },
     data() {
       return {
+        dynamicsList: [],
         canInstallProgress: false,
         orderType: '',
         saleId: '',
@@ -492,6 +533,7 @@
     },
     async mounted() {
       this.id = parseInt(this.$route.query.id)
+      this.getDynamicsList()
       await this.getOrderDetails()
       this.fetchPlanList()
       this.getInstallProgress()
@@ -532,6 +574,8 @@
           return '组装任务单'
         } else if (type == '30') {
           return '部署安装单'
+        } else if (type == '40') {
+          return '软件交付验收任务单'
         }
         return '未知类型'
       },
@@ -539,6 +583,14 @@
       handleComplete(row) {
         this.$refs.complete.open(row)
       },
+      // 软件交付完成
+      handleSoftwareComplete(row) {
+        this.$refs.softwareComplete.open(row)
+      },
+      // 变更要求交付时间
+      handleChangeDeliverTime(row) {
+        this.$refs.changeDeliverTime.open(row)
+      },
       // 发货
       handleDeliverGoods(row) {
         this.$refs.deliver.open(row)
@@ -547,6 +599,10 @@
       handleInspectGoods(row) {
         this.$refs.inspect.open(row)
       },
+      // 签收
+      handleProductSign(row) {
+        this.$refs.productSign.open(row)
+      },
       // 确认到货
       async handleConfirmArrival(row) {
         this.$prompt('你确定货物已到达吗', '提示', {
@@ -690,6 +746,10 @@
         this.queryForm.pageNum = 1
         this.fetchWorkList()
       },
+      getProgressAndLogs() {
+        this.fetchWorkListPage1()
+        this.getDynamicsList()
+      },
       // 查询计划
       async fetchPlanList() {
         this.listLoading = true
@@ -717,6 +777,15 @@
           this.orderType = res.data.orderType
         }
       },
+      async getDynamicsList() {
+        this.dynamicsList = []
+        const [err, res] = await to(orderWorkApi.getDeliverTimeChangeLogs({ deliverOrderId: this.id }))
+        if (err) return
+
+        if (res.code == 200 && res.data) {
+          this.dynamicsList = res.data
+        }
+      },
       // 查询工作项
       async fetchWorkList() {
         this.listLoading = true
@@ -740,6 +809,7 @@
   .left-scroll {
     height: 100%;
     overflow: auto;
+    width: 350px;
   }
   .tree-side {
     border-right: 0;

+ 3 - 0
src/views/work/deliver/progress.vue

@@ -16,6 +16,7 @@
               <el-option label="发货任务单" value="10" />
               <el-option label="组装任务单" value="20" />
               <el-option label="部署安装单" value="30" />
+              <el-option label="软件交付验收任务单" value="40" />
             </el-select>
           </el-form-item>
           <el-form-item prop="progressStatus">
@@ -435,6 +436,8 @@
           return '组装任务单'
         } else if (type == '30') {
           return '部署安装单'
+        } else if (type == '40') {
+          return '软件交付验收任务单'
         }
         return '未知类型'
       },