Bläddra i källkod

feature:工单修改列表字段,增加关闭和完成功能,增加动态以及反馈提交

liuzl 2 år sedan
förälder
incheckning
cfaa726020

+ 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)
+  },
 }

+ 41 - 6
src/views/work/order/components/DingTalkFromToVue.vue

@@ -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]"
+                      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]"
-                      :placeholder="item.props.placeholder" />
+                      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

+ 11 - 1
src/views/work/order/components/Edit.vue

@@ -29,6 +29,16 @@
             <el-input v-model="form.remark" placeholder="请输入内容" :rows="2" show-word-limit type="textarea" />
           </el-form-item>
         </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: {

+ 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()

+ 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>

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

@@ -48,21 +48,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'">
+            {{ 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 +90,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 +123,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 +145,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: '产品线',
+            width: 'auto',
+            prop: 'productLine',
           },
           {
-            label: '创建时间',
+            label: '负责人',
             width: 'auto',
-            prop: 'createdTime',
+            prop: 'assignUserName',
+          },
+          {
+            label: '是否超期',
+            width: '80px',
+            prop: 'endTime',
+          },
+          {
+            label: '申请人',
+            width: 'auto',
+            prop: 'createdName',
           },
         ],
         orderStatusOptions: [],
+        productLineOptions: [],
       }
     },
     watch: {
@@ -162,17 +188,32 @@
       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 }
@@ -194,6 +235,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({