Sun Xinyuan 1 жил өмнө
parent
commit
c300de6a52

+ 39 - 0
src/api/work/trainHead.js

@@ -0,0 +1,39 @@
+/*
+ * @Author: liuzhenlin 461480418@qq.ocm
+ * @Date: 2023-03-21 16:33:47
+ * @LastEditors: liuzhenlin
+ * @LastEditTime: 2023-07-10 14:44:29
+ * @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 {
+  // 获取列表
+  getList(query) {
+    return micro_request.postRequest(basePath, 'TrainHead', 'GetList', query)
+  },
+  //批量删除
+  DeleteByIds(query) {
+    return micro_request.postRequest(basePath, 'TrainHead', 'DeleteByIds', query)
+  },
+  //详情
+  getDetail(query) {
+    return micro_request.postRequest(basePath, 'TrainHead', 'GetEntityById', query)
+  },
+   //详情
+  getFeedBackDetail(query) {
+    return micro_request.postRequest(basePath, 'TrainHead', 'GetDetailById', query)
+  },
+  UpdateById(query) {
+    return micro_request.postRequest(basePath, 'TrainHead', 'UpdateById', query)
+  },
+  // 新建培训工单
+  Create(query) {
+    return micro_request.postRequest(basePath, 'TrainHead', 'Create', query)
+  },
+  FeedBackTrain(query) {
+    return micro_request.postRequest(basePath, 'TrainHead', 'FeedBackTrain', query)
+  },
+}

+ 3 - 0
src/main.js

@@ -12,6 +12,8 @@ import i18n from './i18n'
 import store from './store'
 import router from './router'
 import '@/vab'
+import VXETable from 'vxe-table'
+import 'vxe-table/lib/style.css'
 
 import { parseTime, translateDataToTree, resetForm, formatPrice, selectDictLabel } from '@/utils'
 import dictApi from '@/api/system/dict'
@@ -25,6 +27,7 @@ Vue.prototype.getDicts = dictApi.getDictDataByType
 Vue.prototype.resetForm = resetForm
 Vue.prototype.selectDictLabel = selectDictLabel
 Vue.prototype.formatPrice = formatPrice
+Vue.use(VXETable)
 
 Vue.config.productionTip = false
 new Vue({

+ 145 - 0
src/views/work/train/head/components/ChangeTime.vue

@@ -0,0 +1,145 @@
+<template>
+  <el-dialog title="改期" :visible.sync="dialogFormVisible" width="600px" @close="close">
+    <el-form v-if="detail" ref="form" label-position="top" :model="form" :rules="rules">
+      <el-row :gutter="20">
+        <!-- 技术支持 -->
+        <el-col v-if="detail.orderTypeName == '技术文件支持'" :span="24">
+          <el-form-item label="期望完成时间" prop="expectTime">
+            <el-date-picker
+              v-model="form.expectTime"
+              placeholder="选择期望完成时间"
+              style="width: 100%"
+              type="date"
+              value-format="yyyy-MM-dd" />
+          </el-form-item>
+        </el-col>
+        <!-- end -->
+        <!-- 售前 -->
+        <el-col v-if="detail.orderTypeName == '售前讲解支持'" :span="24">
+          <el-form-item label="支持时间" prop="supportTime">
+            <el-date-picker
+              v-model="form.supportTime"
+              placeholder="选择支持时间"
+              style="width: 100%"
+              type="datetime"
+              value-format="yyyy-MM-dd HH:mm:ss" />
+          </el-form-item>
+        </el-col>
+        <!-- end -->
+        <!-- 修改试用时间 -->
+        <el-col
+          v-if="detail.orderTypeName == '产品试用申请(硬件)' || detail.orderTypeName == '产品试用申请(软件)'"
+          :span="24">
+          <el-form-item label="试用时间" prop="trialTime">
+            <el-date-picker
+              v-model="form.trialTime"
+              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 to from 'await-to-js'
+  import workOrderApi from '@/api/work/index'
+  export default {
+    name: 'WorkOrderFeedback',
+    data() {
+      return {
+        detail: null,
+        form: {
+          expectTime: '',
+          supportTime: '',
+          trialTime: [],
+        },
+        rules: {
+          expectTime: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+          supportTime: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+          trialTime: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+        },
+        dialogFormVisible: false,
+      }
+    },
+
+    mounted() {},
+    methods: {
+      open(row) {
+        this.detail = row
+        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 = {}
+            if (
+              this.detail.orderTypeName == '产品试用申请(硬件)' ||
+              this.detail.orderTypeName == '产品试用申请(软件)'
+            ) {
+              params = {
+                orderId: this.detail.id,
+                trialTimeEnd: this.form.trialTime[1],
+                trialTimeStart: this.form.trialTime[0],
+              }
+              const [err, res] = await to(workOrderApi.changeTryTime(params))
+              if (err) return
+              if (res.code == 200) {
+                this.$message({
+                  type: 'success',
+                  message: '修改成功!',
+                })
+                this.close()
+                this.$emit('update-detail')
+              }
+            } else if (this.detail.orderTypeName == '技术文件支持') {
+              params = {
+                orderId: this.detail.id,
+                expectTime: this.form.expectTime,
+              }
+              const [err, res] = await to(workOrderApi.changeSupportTime(params))
+              if (err) return
+              if (res.code == 200) {
+                this.$message({
+                  type: 'success',
+                  message: '修改成功!',
+                })
+                this.close()
+                this.$emit('update-detail')
+              }
+            } else if (this.detail.orderTypeName == '售前讲解支持') {
+              params = {
+                orderId: this.detail.id,
+                supportTime: this.form.supportTime,
+              }
+              const [err, res] = await to(workOrderApi.changeSaleTime(params))
+              if (err) return
+              if (res.code == 200) {
+                this.$message({
+                  type: 'success',
+                  message: '修改成功!',
+                })
+                this.close()
+                this.$emit('update-detail')
+              }
+            }
+          }
+        })
+      },
+    },
+  }
+</script>

+ 370 - 0
src/views/work/train/head/components/CreateTrainHead.vue

@@ -0,0 +1,370 @@
+<template>
+  <el-dialog append-to-body :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="name">
+            <el-input v-model="form.name" />
+          </el-form-item>
+        </el-col> -->
+        <el-col :span="24">
+          <el-form-item label="培训主题" prop="trainTitle">
+            <el-input v-model="form.trainTitle" placeholder="请输入培训主题" />
+            <!-- <el-input v-model="form.trainTitle" readonly suffix-icon="el-icon-search" @focus="handleSelectUser" /> -->
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="培训日期" prop="trainDate">
+            <el-date-picker v-model="form.trainDate" type="date" placeholder="选择日期" style="width: 400px" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="培训时间" prop="trainConcreteStartTime">
+            <el-time-select
+              v-model="form.trainConcreteStartTime"
+              :picker-options="{
+                start: '08:30',
+                step: '00:30',
+                end: '18:30',
+              }"
+              placeholder="选择培训开始时间"
+              style="width: 200px" />
+            <el-time-select
+              v-model="form.trainConcreteEndTime"
+              :picker-options="{
+                start: '08:30',
+                step: '00:30',
+                end: '18:30',
+                minTime: form.trainConcreteStartTime,
+              }"
+              placeholder="选择培训结束时间"
+              style="margin-left: 10px; width: 200px" />
+          </el-form-item>
+          <!-- <el-form-item label="培训结束时间" prop="trainConcreteEndTime">
+
+          </el-form-item> -->
+        </el-col>
+      </el-row>
+    </el-form>
+    <el-button icon="el-icon-plus" type="primary" @click="createFeedBack">添加工程师</el-button>
+    <div   style="height: 400px">
+      <vxe-table
+        ref="feedBackRef"
+        border
+        resizable
+        show-overflow
+        :data="form.feedbackList"
+        :edit-config="{ trigger: 'click', mode: 'cell' }"
+        style="height: auto">
+        <vxe-column type="seq" width="60" />
+        <vxe-column field="saleName" title="销售工程师" :edit-render="{}">
+          <template #default="{ row }">
+            {{ row.sale.saleName }}
+          </template>
+          <template #edit="{ row }">
+            <vxe-select v-model="row.sale" style="height: 50px">
+              <vxe-option
+                v-for="(item, index) in userList"
+                :key="item.saleId"
+                :value="userList[index]"
+                :label="item.saleName" />
+            </vxe-select>
+          </template>
+        </vxe-column>
+        <!-- <vxe-column field="distributorName" title="渠道名称">
+
+        <template #default="{ row }">
+          <vxe-input v-model="row.role" type="text" placeholder="请输入昵称" />
+        </template>
+      </vxe-column> -->
+
+        <vxe-column field="distributorList" title="渠道名称" :edit-render="{}">
+          <template #default="{ row }">
+            <span v-for="(item, index) in row.distributorList" :key="item.distributorId">
+              {{ item.distributorName }}
+              <span v-if="index == row.distributorList.length">,</span>
+            </span>
+          </template>
+          <template #edit="{ row }">
+            <vxe-select v-model="row.distributorList" multiple>
+              <vxe-option
+                v-for="(item, index) in sexList"
+                :key="item.distributorId"
+                :value="sexList[index]"
+                :label="item.distributorName" />
+            </vxe-select>
+          </template>
+        </vxe-column>
+        <vxe-column title="操作" fixed="right">
+          <template #default="{ row }">
+            <vxe-button type="text" status="primary" content="删除" @click="DeleteData(row)" />
+          </template>
+        </vxe-column>
+      </vxe-table>
+    </div>
+
+    <!-- <el-table :data="form.feedbackList" border style="width: 100%">
+      <el-table-column align="center" label="序号" width="80">
+        <template #default="{ $index }">
+          {{ $index + 1 }}
+        </template>
+      </el-table-column>
+      <el-table-column prop="saleName" label="销售工程师">
+        <template #default="{ $index }">
+          <el-input v-model="form.feedbackList[$index].saleName" @focus="handleSelectUser($index)" />
+        </template>
+      </el-table-column>
+      <el-table-column prop="distributorName" label="渠道名称">
+        <template slot-scope="scope">
+          <el-input v-model="scope.row.distributorName" />
+        </template>
+      </el-table-column>
+
+      <el-table-column fixed="right" label="操作" width="100">
+        <template slot-scope="scope">
+          <el-button type="text" @click="scope.row" size="small">编辑</el-button>
+          <el-button type="text" @click="deleteFeedBack(scope.row)" size="small">删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table> -->
+
+    <template #footer>
+      <el-button @click="close">取 消</el-button>
+      <el-button :loading="loading" type="primary" @click="save">确 定</el-button>
+    </template>
+
+    <!-- 选择支持人员弹窗 -->
+    <select-user ref="selectUser" @save="selectUser" />
+    <!-- 选择经销商弹窗 -->
+    <select-distributor ref="selectDistributor" :multiple="true" :query-params="queryParams" @save="saveDistributors" />
+  </el-dialog>
+</template>
+
+<script>
+  import api from '@/api/work/trainHead'
+  import to from 'await-to-js'
+
+  import typeApi from '@/api/work/type'
+  import SelectUser from '@/components/select/SelectUser'
+  import { mapGetters } from 'vuex'
+  import SelectDistributor from '@/components/select/SelectDistributor'
+
+  export default {
+    name: 'WorkOrderEdit',
+    components: {
+      SelectUser,
+      SelectDistributor,
+    },
+    props: {
+      businessInfo: {
+        type: Object,
+        default: () => {},
+      },
+    },
+    data() {
+      return {
+        sexList: [
+          { distributorId: 1, distributorName: 'a' },
+          { distributorId: 2, distributorName: 'b' },
+          { distributorId: 3, distributorName: 'c' },
+        ],
+        userList: [
+          { saleId: 1, saleName: '工程师1' },
+          { saleId: 2, saleName: '工程师2' },
+          { saleId: 3, saleName: '工程师3' },
+        ],
+        row: {},
+        index: 0,
+        queryParams: {
+          distType: '',
+        },
+        loading: false,
+        form: {
+          id: undefined,
+          trainTitle: undefined,
+          trainDate: undefined,
+          trainConcreteStartTime: undefined,
+          trainConcreteEndTime: undefined,
+          feedbackList: [],
+        },
+        rules: {
+          trainTitle: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+          trainDate: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+          trainConcreteStartTime: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+          trainConcreteEndTime: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+        },
+        title: '',
+        dialogFormVisible: false,
+        orderTypeList: [],
+        dingtalkForm: undefined,
+      }
+    },
+    computed: {
+      ...mapGetters({
+        userId: 'user/id',
+        nickName: 'user/nickName',
+      }),
+    },
+    mounted() {},
+    methods: {
+      DeleteData(row) {
+        const $table = this.$refs.feedBackRef
+        $table.remove(row)
+      },
+      createFeedBack() {
+        const $table = this.$refs.feedBackRef
+
+        $table.insertAt(
+          {
+            sale: {},
+            distributorList: [],
+          },
+          0
+        )
+        // if (this.form.feedbackList == undefined) {
+        //   this.form.feedbackList = []
+        // }
+        // this.form.feedbackList.push({
+        //   saleId: '',
+        //   saleName: '',
+        //   distributorId: '',
+        //   distributorName: '',
+        // })
+      },
+      deleteFeedBack(row) {
+        // const index = this.form.feedbackList.findIndex((item) => {
+        //   item == row
+        // })
+        // this.form.feedbackList.splice(index, 1)
+        const $table = this.$refs.feedBackRef
+        $table.remove(row)
+      },
+      async getOrderTypeList() {
+        this.orderTypeList.splice(0, this.orderTypeList.length)
+        const { data } = await typeApi.getList()
+        for (let item of data.list) {
+          this.orderTypeList.push(item)
+        }
+        this.changeOrderType()
+      },
+      changeOrderType() {},
+      // handleUploadFile(file) {
+      //   this.form.file = file
+      // },
+      handleSelectUser(rowIndex) {
+        // Object.assign(this.row, rowIndex)
+        this.index = rowIndex
+        // const index = this.form.feedbackList.findIndex(item=>{
+        //   item==row
+        // })
+        this.$refs.selectUser.open()
+      },
+      selectUser(val) {
+        if (val && val.length > 0) {
+          // const $table = this.$refs.feedBackRef
+
+          // const index = this.form.feedbackList.findIndex((item) => {
+          //   item == this.row
+          // })
+
+          this.$refs.feedBackRef.updateData(this.index, { saleId: val[0].id })
+          this.$refs.feedBackRef.updateData(this.index, { saleName: val.map((item) => item.nickName).join() })
+          console.log(' this.$refs.feedBackRef', this.$refs.feedBackRef)
+          let data = this.$refs.feedBackRef.getRecordset()
+          console.log('data', data)
+
+          // this.row.saleId = val[0].id
+          // this.row.saleName = val.map((item) => item.nickName).join()
+
+          // this.$refs.feedBackRef.reload()
+
+          // console.log('index', this.index)
+          // console.log("val-------",val);
+          // console.log("item",this.row);
+          // console.log('this.form.feedbackList[index]', this.form.feedbackList[index])
+          // this.row.saleId = val[0].id
+          // this.row.saleName = val.map((item) => item.nickName).join()
+          // this.form.feedbackList[this.index] = {
+          //   saleId: val[0].id,
+          //   saleName: val.map((item) => item.nickName).join(),
+          //   distributorId: this.row.distributorId,
+          //   distributorName: this.row.distributorName,
+          // }
+          console.log('this.form.feedbackList', this.form.feedbackList)
+
+          console.log('row222', this.row)
+        }
+      },
+      async showEdit(id) {
+        this.getOrderTypeList()
+        this.loading = false
+        if (id == null) {
+          this.title = '添加'
+        } else {
+          const [err, res] = await to(api.getDetail({ id: id }))
+          if (err) return (this.listLoading = false)
+
+          // this.form = res.data || {}
+          this.form.id = res.data.id
+          this.form.trainConcreteEndTime = res.data.trainConcreteEndTime
+          this.form.trainConcreteStartTime = res.data.trainConcreteStartTime
+          this.form.trainDate = res.data.trainDate
+          this.form.trainTitle = res.data.trainTitle
+          this.form.feedbackList = res.data.feedbackList
+          this.form.feedbackList.forEach((item) => {
+            item.sale = {
+              saleId: item.saleId,
+              saleName: item.saleName,
+            }
+          })
+
+          console.log('this.form.feedbackList', this.form.feedbackList)
+        }
+
+        this.dialogFormVisible = true
+      },
+      close() {
+        // this.$refs['dingTalkFrom'].resetForm()
+        // this.$refs['form'].resetFields()
+        this.form = this.$options.data().form
+        this.dialogFormVisible = false
+      },
+      //
+      handleSelectDistributor(type) {
+        this.queryParams.distType = type
+        this.$refs.selectDistributor.open()
+      },
+      // 选中数据
+      saveDistributors() {},
+      save() {
+        // let dingValid = this.$refs['form'].validateForm()
+        this.$refs['form'].validate(async (valid) => {
+          if (valid) {
+            // this.loading = true
+            const $table = this.$refs.feedBackRef
+            let insertList = $table.getInsertRecords()
+            let removeList = $table.getRemoveRecords()
+            let updateList = $table.getUpdateRecords()
+            if (insertList.length > 0) {
+              insertList.map((item) => (item.operate = '10'))
+            }
+            if (updateList.length > 0) {
+              updateList.map((item) => (item.operate = '20'))
+            }
+            if (removeList.length > 0) {
+              removeList.map((item) => (item.operate = '30'))
+            }
+            this.form.feedbackList = [...insertList, ...updateList, ...removeList]
+            this.form.trainConcreteStartTime = '2024-01-11T' + this.form.trainConcreteStartTime + ':00.000Z'
+            this.form.trainConcreteEndTime = '2024-01-11T' + this.form.trainConcreteEndTime + ':00.000Z'
+            const { msg } = await api.Create(this.form)
+            // this.loading = false
+            this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+            this.$emit('fetch-data')
+            this.close()
+          }
+        })
+      },
+    },
+  }
+</script>

+ 297 - 0
src/views/work/train/head/components/Detail.vue

@@ -0,0 +1,297 @@
+<template>
+  <el-dialog append-to-body :title="title" :visible.sync="dialogFormVisible" @close="close">
+    <el-descriptions class="margin-top" :column="2" border>
+      <el-descriptions-item>
+        <template slot="label">培训主题</template>
+        {{ detail.trainTitle }}
+      </el-descriptions-item>
+      <el-descriptions-item>
+        <template slot="label">培训日期</template>
+        {{ detail.trainDate }}
+      </el-descriptions-item>
+      <el-descriptions-item>
+        <template slot="label">培训开始时间</template>
+        {{ detail.trainConcreteStartTime }}
+      </el-descriptions-item>
+      <el-descriptions-item>
+        <template slot="label">培训结束时间</template>
+        {{ detail.trainConcreteEndTime }}
+      </el-descriptions-item>
+    </el-descriptions>
+
+    <vxe-table
+      ref="feedBackRef"
+      class="mt10"
+      border
+      resizable
+      show-overflow
+      :data="detail.feedbackDetailList"
+      :edit-config="{ trigger: 'click', mode: 'cell' }"
+      style="height: 300px">
+      <vxe-column type="seq" width="60" />
+      <vxe-column field="saleName" title="销售工程师" width="100" />
+      <vxe-column field="distributorName" title="渠道名称" width="100" />
+
+      <vxe-column field="trainingPersNum" title="参训人数" width="100">
+        <template #default="{ row }" v-if="type == 'edit'">
+          <vxe-input
+            v-model.number="row.trainingPersNum"
+            type="text"
+            :disabled="type == 'detail'"
+            placeholder="请输入参训人数" />
+        </template>
+        <template #default="{ row }" v-else>
+          {{ row.trainingPersNum }}
+        </template>
+      </vxe-column>
+      <vxe-column field="distributorFeedback" title="经销商反馈">
+        <template #default="{ row }" v-if="type == 'edit'">
+          <vxe-input
+            v-model="row.distributorFeedback"
+            type="text"
+            :disabled="type == 'detail'"
+            placeholder="请输入经销商反馈" />
+        </template>
+        <template #default="{ row }" v-else>
+          {{ row.distributorFeedback }}
+        </template>
+      </vxe-column>
+    </vxe-table>
+
+    <template #footer>
+      <el-button @click="close">取 消</el-button>
+      <el-button v-if="!form.id" :loading="loading" type="primary" @click="save">确 定</el-button>
+    </template>
+
+    <!-- 选择支持人员弹窗 -->
+    <select-user ref="selectUser" @save="selectUser" />
+    <!-- 选择经销商弹窗 -->
+    <select-distributor ref="selectDistributor" :multiple="true" :query-params="queryParams" @save="saveDistributors" />
+  </el-dialog>
+</template>
+
+<script>
+  import api from '@/api/work/trainHead'
+  import to from 'await-to-js'
+  import typeApi from '@/api/work/type'
+  import SelectUser from '@/components/select/SelectUser'
+  import { mapGetters } from 'vuex'
+  import SelectDistributor from '@/components/select/SelectDistributor'
+
+  export default {
+    name: 'WorkOrderEdit',
+    components: {
+      SelectUser,
+      SelectDistributor,
+    },
+    props: {
+      businessInfo: {
+        type: Object,
+        default: () => {},
+      },
+    },
+    data() {
+      return {
+        sexList: [
+          { distributorId: 1, distributorName: 'a' },
+          { distributorId: 2, distributorName: 'b' },
+          { distributorId: 3, distributorName: 'c' },
+        ],
+        userList: [
+          { saleId: 1, saleName: '工程师1' },
+          { saleId: 2, saleName: '工程师2' },
+          { saleId: 3, saleName: '工程师3' },
+        ],
+        type: '',
+        row: {},
+        index: 0,
+        queryParams: {
+          distType: '',
+        },
+        loading: false,
+        form: {
+          id: undefined,
+          trainTitle: undefined,
+          trainDate: undefined,
+          trainConcreteStartTime: undefined,
+          trainConcreteEndTime: undefined,
+          feedbackList: [],
+        },
+        rules: {
+          trainTitle: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+          trainDate: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+          trainConcreteStartTime: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+          trainConcreteEndTime: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+        },
+        title: '',
+        dialogFormVisible: false,
+        orderTypeList: [],
+        dingtalkForm: undefined,
+        detail: {
+          id: '',
+          trainTitle: '',
+          trainDate: '',
+          trainConcreteStartTime: '',
+          trainConcreteEndTime: '',
+          feedbackDetailList: [],
+        },
+      }
+    },
+    computed: {
+      ...mapGetters({
+        userId: 'user/id',
+        nickName: 'user/nickName',
+      }),
+    },
+    mounted() {},
+    methods: {
+      DeleteData(row) {
+        const $table = this.$refs.feedBackRef
+        $table.remove(row)
+      },
+      createFeedBack() {
+        const $table = this.$refs.feedBackRef
+
+        $table.insertAt(
+          {
+            sale: {},
+            distributorList: [],
+          },
+          0
+        )
+        // if (this.form.feedbackList == undefined) {
+        //   this.form.feedbackList = []
+        // }
+        // this.form.feedbackList.push({
+        //   saleId: '',
+        //   saleName: '',
+        //   distributorId: '',
+        //   distributorName: '',
+        // })
+      },
+      deleteFeedBack(row) {
+        // const index = this.form.feedbackList.findIndex((item) => {
+        //   item == row
+        // })
+        // this.form.feedbackList.splice(index, 1)
+        const $table = this.$refs.feedBackRef
+        $table.remove(row)
+      },
+      async getOrderTypeList() {
+        this.orderTypeList.splice(0, this.orderTypeList.length)
+        const { data } = await typeApi.getList()
+        for (let item of data.list) {
+          this.orderTypeList.push(item)
+        }
+        this.changeOrderType()
+      },
+      changeOrderType() {},
+      // handleUploadFile(file) {
+      //   this.form.file = file
+      // },
+      handleSelectUser(rowIndex) {
+        // Object.assign(this.row, rowIndex)
+        this.index = rowIndex
+        // const index = this.form.feedbackList.findIndex(item=>{
+        //   item==row
+        // })
+        this.$refs.selectUser.open()
+      },
+      selectUser(val) {
+        if (val && val.length > 0) {
+          // const $table = this.$refs.feedBackRef
+
+          // const index = this.form.feedbackList.findIndex((item) => {
+          //   item == this.row
+          // })
+
+          this.$refs.feedBackRef.updateData(this.index, { saleId: val[0].id })
+          this.$refs.feedBackRef.updateData(this.index, { saleName: val.map((item) => item.nickName).join() })
+          console.log(' this.$refs.feedBackRef', this.$refs.feedBackRef)
+          let data = this.$refs.feedBackRef.getRecordset()
+          console.log('data', data)
+
+          // this.row.saleId = val[0].id
+          // this.row.saleName = val.map((item) => item.nickName).join()
+
+          // this.$refs.feedBackRef.reload()
+
+          // console.log('index', this.index)
+          // console.log("val-------",val);
+          // console.log("item",this.row);
+          // console.log('this.form.feedbackList[index]', this.form.feedbackList[index])
+          // this.row.saleId = val[0].id
+          // this.row.saleName = val.map((item) => item.nickName).join()
+          // this.form.feedbackList[this.index] = {
+          //   saleId: val[0].id,
+          //   saleName: val.map((item) => item.nickName).join(),
+          //   distributorId: this.row.distributorId,
+          //   distributorName: this.row.distributorName,
+          // }
+          console.log('this.form.feedbackList', this.form.feedbackList)
+
+          console.log('row222', this.row)
+        }
+      },
+      async showEdit(id, type) {
+        console.log('id222', id)
+        console.log('type', type)
+        this.loading = false
+        if (type == 'edit') {
+          this.title = '反馈'
+        } else {
+          this.title = '详情'
+        }
+        this.type = type
+        // this.form.saleId = this.userId
+        // this.form.saleName = this.nickName
+        this.listLoading = true
+        const [err, res] = await to(api.getFeedBackDetail({ id: id }))
+        if (err) return (this.listLoading = false)
+
+        this.detail = res.data || {}
+
+        // this.$nextTick(() => this.$refs.table.doLayout())
+
+        this.dialogFormVisible = true
+      },
+      close() {
+        // this.$refs['dingTalkFrom'].resetForm()
+        // this.$refs['form'].resetFields()
+        this.form = this.$options.data().form
+        this.dialogFormVisible = false
+      },
+      //
+      handleSelectDistributor(type) {
+        this.queryParams.distType = type
+        this.$refs.selectDistributor.open()
+      },
+      // 选中数据
+      saveDistributors() {},
+      async save() {
+        // const $table = this.$refs.feedBackRef
+        // let insertList = $table.getInsertRecords()
+        // let removeList = $table.getRemoveRecords()
+        // let updateList = $table.getUpdateRecords()
+        // if (insertList.length > 0) {
+        //   insertList.map((item) => (item.operate = '10'))
+        // }
+        // if (updateList.length > 0) {
+        //   updateList.map((item) => (item.operate = '20'))
+        // }
+        // if (removeList.length > 0) {
+        //   removeList.map((item) => (item.operate = '30'))
+        // }
+        console.log('detail.feedbackDetailList', this.detail.feedbackDetailList)
+        // this.detail.feedbackDetailList = [...insertList, ...updateList, ...removeList]
+        // console.log(" this.detail.feedbackDetailList", this.detail.feedbackDetailList);
+        // return
+        const { msg } = await api.FeedBackTrain({ feedBackTrainList: this.detail.feedbackDetailList })
+        // this.loading = false
+        this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+        this.$emit('fetch-data')
+        this.close()
+      },
+    },
+  }
+</script>

+ 208 - 0
src/views/work/train/head/components/DetailFeedback.vue

@@ -0,0 +1,208 @@
+<template>
+  <div style="height: 100%">
+    <ul v-if="feedbackList.length" class="feedback">
+      <li v-for="(date, index) in feedbackList" :key="index">
+        <div class="date">
+          <h2>{{ date.feeDate.split('-')[2] }}</h2>
+          <h3>
+            {{ date.feeDate.split('-').splice(0, 2).join('.') }}
+          </h3>
+        </div>
+        <ul class="content">
+          <li v-for="(item, idx) in date.workOrderFeedbackList" :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.feePeople }} 反馈</span>
+                  <span>
+                    <vab-icon icon="time-line" />
+                    {{ item.feeDate }}
+                  </span>
+                </p>
+                <p>{{ item.feedback }}</p>
+                <div class="footer"></div>
+              </div>
+            </div>
+          </li>
+        </ul>
+      </li>
+    </ul>
+    <div v-else class="no-feedback">暂无反馈记录</div>
+  </div>
+</template>
+
+<script>
+  import api from '@/api/work/index'
+  import to from 'await-to-js'
+
+  export default {
+    name: 'Records',
+
+    props: {
+      workOrderId: {
+        type: Number,
+        default: 0,
+      },
+    },
+    data() {
+      return {
+        feedbackList: [],
+      }
+    },
+    mounted() {
+      this.getOptions()
+    },
+    methods: {
+      getOptions() {
+        // Promise.all([this.getDicts('plat_feedback_type')]).then(([feedbackType]) => {
+        //   this.feedbackTypeOptions = feedbackType.data.values || []
+        // })
+      },
+      async fetchData() {
+        let params = {
+          orderId: this.workOrderId,
+          daysBeforeToday: 9999,
+        }
+        let [err, res] = await to(api.getWorkOrderFeedbackByDay(params))
+        if (err) return
+        this.feedbackList = res.data.list || []
+      },
+    },
+  }
+</script>
+
+<style lang="scss" scoped>
+  .feedback {
+    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-feedback {
+    height: 100%;
+    width: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    font-size: 12px;
+    color: rgba(0, 0, 0, 0.65);
+  }
+</style>

+ 410 - 0
src/views/work/train/head/components/DingTalkFromToVue.vue

@@ -0,0 +1,410 @@
+<template>
+  <div class="">
+    <el-form ref="dingtalkForm" label-position="top" :model="dingtalkForm">
+      <el-row :gutter="20">
+        <div v-for="(item, index) in dingtalkForm.items" :key="index">
+          <el-col v-if="item.componentName !== 'TableField'" :span="colSpan">
+            <el-form-item
+              v-if="item.props.label != '支持人员'"
+              :label="item.props.label"
+              :prop="'items.' + index + '.props.value'"
+              :rules="{
+                required: item.props.required,
+                message: '不能为空',
+                trigger: ['blur', 'change'],
+              }">
+              <!--文本框-->
+              <el-input
+                v-if="item.componentName === 'TextField'"
+                v-model="item.props.value"
+                :disabled="item.props.disabled"
+                :placeholder="item.props.placeholder" />
+              <!--文本域-->
+              <el-input
+                v-else-if="item.componentName === 'TextareaField'"
+                v-model="item.props.value"
+                :disabled="item.props.disabled"
+                :placeholder="item.props.placeholder"
+                :rows="5"
+                show-word-limit
+                type="textarea" />
+              <!--下拉选-->
+              <el-select
+                v-else-if="item.componentName === 'DDSelectField'"
+                v-model="item.props.value"
+                clearable
+                :disabled="item.props.disabled || item.props.label == '工单类型'"
+                :placeholder="item.props.placeholder"
+                style="width: 100%"
+                @change="selectWorkOrderType">
+                <el-option
+                  v-for="option in selectDataToJson(item.props.options)"
+                  :key="option.key"
+                  :label="option.value"
+                  :value="option.value" />
+              </el-select>
+              <!--下拉多选-->
+              <el-select
+                v-else-if="item.componentName === 'DDMultiSelectField'"
+                v-model="item.props.value"
+                clearable
+                :disabled="item.props.disabled"
+                multiple
+                :placeholder="item.props.placeholder"
+                style="width: 100%">
+                <el-option
+                  v-for="option in selectDataToJson(item.props.options)"
+                  :key="option.key"
+                  :label="option.value"
+                  :value="option.value" />
+              </el-select>
+              <!--时间选择器-->
+              <el-date-picker
+                v-else-if="item.componentName === 'DDDateField' && item.props.label != '运维时间'"
+                v-model="item.props.value"
+                :disabled="item.props.disabled"
+                :placeholder="item.props.placeholder"
+                :type="item.props.label == '支持时间' ? 'datetime' : 'date'"
+                :value-format="item.props.format" />
+
+              <el-date-picker
+                v-else-if="item.componentName === 'DDDateField' && item.props.label == '运维时间'"
+                v-model="item.props.value"
+                :disabled="item.props.disabled"
+                format="yyyy-MM-dd HH:mm"
+                :placeholder="item.props.placeholder"
+                :type="item.props.label == '运维时间' ? 'datetime' : 'date'"
+                value-format="yyyy-MM-dd HH:mm"
+                :value-format="item.props.format" />
+
+              <!--时间范围选择器-->
+              <el-date-picker
+                v-else-if="item.componentName === 'DDDateRangeField'"
+                v-model="item.props.value"
+                :disabled="item.props.disabled"
+                :end-placeholder="item.props.placeholder[1]"
+                :start-placeholder="item.props.placeholder[0]"
+                style="width: 100%"
+                type="daterange"
+                :value-format="item.props.format" />
+
+              <!--文件-->
+              <el-upload
+                v-else-if="item.componentName === 'DDAttachment'"
+                :ref="item.props.id"
+                :action="uploadFileUrl"
+                :limit="1"
+                :on-remove="
+                  (file, fileList) => {
+                    return removeFile(file, fileList, index)
+                  }
+                "
+                :on-success="
+                  (response, file) => {
+                    return setQuotationFile(response, file, index)
+                  }
+                "
+                :show-file-list="typeof item.props.value == 'string'">
+                <el-button size="mini" type="primary">点击上传</el-button>
+              </el-upload>
+
+              <!--选择用户-->
+              <el-input
+                v-else-if="item.componentName === 'InnerContactField'"
+                v-model="item.props.value"
+                :disabled="item.props.disabled"
+                :placeholder="item.props.placeholder"
+                readonly
+                suffix-icon="el-icon-search"
+                @focus="handleSelectUser(index)" />
+            </el-form-item>
+          </el-col>
+
+          <el-col v-if="item.componentName === 'TableField'" :span="24">
+            <el-form-item :label="item.props.label" :prop="item.props.id" :required="item.props.required">
+              <el-button
+                v-show="item.props.actionName"
+                style="float: right; margin-top: -20px; margin-bottom: 10px"
+                @click="addTableData(index)">
+                {{ item.props.actionName }}
+              </el-button>
+              <!--表格-->
+              <el-table :key="dingTableFlag" :ref="'dingTable' + index" border :data="item.tableData" height="250px">
+                <el-table-column
+                  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-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]"
+                      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'"
+                      v-model="row[child.props.id]"
+                      :min="1"
+                      onkeyup="value=value.replace(/[^\d]/g,'')" />
+                  </template>
+                </el-table-column>
+              </el-table>
+            </el-form-item>
+          </el-col>
+        </div>
+      </el-row>
+    </el-form>
+
+    <!-- 选择人员弹窗 -->
+    <select-user ref="selectUser" @save="selectUser" />
+  </div>
+</template>
+
+<script>
+  import productApi from '@/api/base/product'
+  import workApi from '@/api/work/index'
+  import SelectUser from '@/components/select/SelectUser'
+  import asyncUploadFile from '@/utils/uploadajax'
+  import axios from 'axios'
+  import to from 'await-to-js'
+  import { parseTime } from '@/utils/index'
+  export default {
+    name: 'DingTalkFromToVue',
+    components: {
+      SelectUser,
+    },
+    props: {
+      colSpan: {
+        type: Number,
+        default: 24,
+      },
+    },
+    data() {
+      return {
+        dingtalkForm: {},
+        productList: [],
+        dingRules: {},
+        userItemIndex: {},
+        dingTableFlag: true,
+        uploadFileUrl: process.env.VUE_APP_UPLOAD_FILE_WEED,
+        fileSettings: {
+          // 文件配置信息
+          fileSize: 20971520,
+          fileTypes: '.rar,.zip,.doc,.docx,.pdf',
+        },
+      }
+    },
+    mounted() {
+      this.getProduct()
+    },
+    // 方法集合
+    methods: {
+      selectWorkOrderType(val) {
+        console.log('下拉选择的val是:', val)
+      },
+      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
+        if (!tableData) {
+          this.dingtalkForm.items[index].tableData = []
+        }
+        for (let child of this.dingtalkForm.items[index].children) {
+          newObj[child.props.id] = ''
+        }
+        this.dingtalkForm.items[index].tableData.push(newObj)
+        this.dingTableFlag = !this.dingTableFlag
+      },
+      handleSelectUser(index) {
+        this.userItemIndex = index
+        this.$refs.selectUser.open()
+      },
+      selectUser(val) {
+        let nickName = val.map((item) => item.nickName).join()
+        this.dingtalkForm.items[this.userItemIndex].props.value = nickName
+        this.$forceUpdate()
+      },
+      removeFile(file, list, index) {
+        this.dingtalkForm.items[index].props.value = null
+      },
+      async setQuotationFile(res, file, index) {
+        // 如果上传成功
+        if (res.Code == 200) {
+          console.log(file)
+          const fileName = file.name // 资料存储url
+          const fileUrl = res.Data // 资料存储url
+          const [uploadErr, uploadRes] = await to(workApi.uploadDingtalk({ fileName, fileUrl }))
+          if (uploadErr) return
+          if (uploadRes.code == 200) {
+            this.dingtalkForm.items[index].props.value = uploadRes.data
+            let temp = JSON.parse(JSON.stringify(this.dingtalkForm))
+            this.$refs.dingtalkForm.clearValidate('items.' + index + '.props.value')
+            this.dingtalkForm = temp
+          }
+        } else {
+          this.$message.error('上传文件失败')
+        }
+      },
+      // 文件
+      setFile(index, file) {
+        // this.$emit('upload', file.raw)
+        let flag1 = file.size < this.fileSettings.fileSize
+        if (!flag1) {
+          this.$message.warning('文件过大,请重新选择!')
+          this.dingtalkForm.items[index].props.value = ''
+          return false
+        }
+        // let flag2 = this.fileSettings.fileTypes.split(',').includes('.' + file.name.split('.').pop())
+        // if (!flag2) {
+        //   this.$message.warning('文件类型不符合,请重新选择!')
+        //   this.dingtalkForm.items[index].props.value = ''
+        //   this.$forceUpdate()
+        //   return false
+        // }
+        return true
+        // // 上传文件一直校验不过问题方案
+        // let temp = JSON.parse(JSON.stringify(this.dingtalkForm))
+        // this.$refs.dingtalkForm.clearValidate('items.' + index + '.props.value')
+        // this.dingtalkForm = temp
+      },
+      // 上传
+      uploadrequest(index, option) {
+        let idx = index
+        console.log(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(async (res) => {
+                if (res) {
+                  let fileRes = JSON.parse(res)
+                  const fileName = fileRes.name // 资料存储url
+                  const fileUrl = option.action // 资料存储url
+                  const [uploadErr, uploadRes] = await to(workApi.uploadDingtalk({ fileName, fileUrl }))
+                  if (uploadErr) return
+                  if (uploadRes.code == 200) {
+                    console.log(uploadRes)
+                    _this.dingtalkForm.items[idx].props.value = uploadRes.data
+                    let temp = JSON.parse(JSON.stringify(_this.dingtalkForm))
+                    _this.$refs.dingtalkForm.clearValidate('items.' + idx + '.props.value')
+                    _this.dingtalkForm = temp
+                  }
+                }
+              })
+            } else {
+              _this.$message({
+                type: 'warning',
+                message: '未上传成功!请刷新界面重新上传!',
+              })
+            }
+          })
+          .catch(function () {
+            _this.$message({
+              type: 'warning',
+              message: '未上传成功!请重新上传!',
+            })
+          })
+      },
+      selectDataToJson(data) {
+        console.log('select data is', data)
+        return data.map((item) => {
+          return JSON.parse(item)
+        })
+      },
+      setFormAndRules(dingtalkForm) {
+        for (let index in dingtalkForm.items) {
+          if (dingtalkForm.items[index].componentName === 'DDDateRangeField') {
+            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
+          }
+          if (dingtalkForm.items[index].props.label == '运维时间') {
+            this.$set(dingtalkForm.items[index].props, 'value', parseTime(new Date()).substring(0, 16))
+            dingtalkForm.items[index].props.format = 'yyyy-MM-dd HH:mm'
+          }
+        }
+        this.dingtalkForm = dingtalkForm
+        this.$forceUpdate()
+      },
+      getFormData() {
+        for (let index in this.dingtalkForm.items) {
+          // 表格数据需要特殊处理
+          if (this.dingtalkForm.items[index].componentName === 'TableField') {
+            this.dingtalkForm.items[index].props.value = []
+            let tableData = this.dingtalkForm.items[index].tableData || []
+            for (let row of tableData) {
+              // 循环数据,组装信息
+              let newData = []
+              for (let colKey in row) {
+                let child = this.dingtalkForm.items[index].children.find((val) => {
+                  return val.props.id === colKey //筛选出匹配数据
+                })
+                newData.push({
+                  value: row[colKey],
+                  id: child.props.id,
+                  name: child.props.label,
+                  required: child.props.required,
+                  componentName: child.componentName,
+                })
+              }
+              this.dingtalkForm.items[index].props.value.push(newData)
+            }
+          }
+        }
+        return this.dingtalkForm
+      },
+      validateForm() {
+        let result = false
+        this.$refs['dingtalkForm'].validate((valid) => {
+          result = valid
+        })
+        return result
+      },
+      resetForm() {
+        this.$refs['dingtalkForm'].resetFields()
+      },
+    },
+  }
+</script>
+
+<style lang="scss" scoped></style>

+ 273 - 0
src/views/work/train/head/components/Edit.vue

@@ -0,0 +1,273 @@
+<template>
+  <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-form-item label="工单名称" prop="name">
+            <el-input v-model="form.name" />
+          </el-form-item>
+        </el-col> -->
+        <el-col :span="12">
+          <el-form-item label="工单类型" prop="orderTypeId">
+            <el-select
+              v-model="form.orderTypeId"
+              clearable
+              placeholder="工单类型"
+              style="width: 100%"
+              @change="changeOrderType">
+              <el-option v-for="item in orderTypeList" :key="item.id" :label="item.name" :value="item.id" />
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="支持人员" prop="assignUserName">
+            <el-input v-model="form.assignUserName" readonly suffix-icon="el-icon-search" @focus="handleSelectUser" />
+          </el-form-item>
+        </el-col>
+        <!-- <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 :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>
+
+      <!--      钉钉审批流表单-->
+      <DingTalkFromToVue ref="dingTalkFrom" :col-span="12" />
+      <!--      钉钉审批流表单-->
+      <!--      <el-row :gutter="20">-->
+
+      <!--      </el-row>-->
+    </el-form>
+    <template #footer>
+      <el-button @click="close">取 消</el-button>
+      <el-button v-if="!form.id" :loading="loading" type="primary" @click="save">确 定</el-button>
+    </template>
+
+    <!-- 选择支持人员弹窗 -->
+    <select-user ref="selectUser" @save="selectUser" />
+  </el-dialog>
+</template>
+
+<script>
+  import workApi from '@/api/work/index'
+  import typeApi from '@/api/work/type'
+  import SelectUser from '@/components/select/SelectUser'
+  import DingTalkFromToVue from './DingTalkFromToVue.vue'
+
+  export default {
+    name: 'WorkOrderEdit',
+    components: {
+      SelectUser,
+      DingTalkFromToVue,
+    },
+    props: {
+      businessInfo: {
+        type: Object,
+        default: () => {},
+      },
+    },
+    data() {
+      return {
+        loading: false,
+        form: {
+          nboId: undefined,
+          nboName: undefined,
+          nboCode: undefined,
+          custId: undefined,
+          custName: undefined,
+          name: undefined,
+          orderTypeId: undefined,
+          orderTypeName: undefined,
+          assignUserId: undefined,
+          assignUserName: undefined,
+          formData: undefined,
+          dingTalkFormData: undefined,
+          feedback: undefined,
+          // file: undefined,
+          remark: undefined,
+          endTime: undefined,
+          dingtalkForm: undefined,
+          trialTimeStart: undefined,
+          trialTimeEnd: undefined,
+          expectTime: undefined,
+          supportTime: undefined,
+        },
+        rules: {
+          name: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+          orderTypeId: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+          assignUserName: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+        },
+        title: '',
+        dialogFormVisible: false,
+        orderTypeList: [],
+        dingtalkForm: undefined,
+      }
+    },
+    mounted() {
+      this.getOrderTypeList()
+    },
+    methods: {
+      async getOrderTypeList() {
+        this.orderTypeList.splice(0, this.orderTypeList.length)
+        const { data } = await typeApi.getList()
+        for (let item of data.list) {
+          if (item.name !== '硬件交付工单' && item.name !== '经销商支持') {
+            this.orderTypeList.push(item)
+          }
+        }
+      },
+      changeOrderType() {
+        let item = this.orderTypeList.find((item) => {
+          return item.id === this.form.orderTypeId
+        })
+        console.log(item.formColumn)
+        this.form.orderTypeName = item.name
+        this.form.orderTypeCode = item.workflowCode
+        this.dingtalkForm = JSON.parse(item.formColumn)
+        console.log(this.dingtalkForm)
+        for (let index in this.dingtalkForm.items) {
+          if (this.dingtalkForm.items[index].props.label === '项目编码') {
+            this.dingtalkForm.items[index].props.value = this.form.nboCode
+            this.dingtalkForm.items[index].props.disabled = true
+          }
+          if (this.dingtalkForm.items[index].props.label === '项目名称') {
+            this.dingtalkForm.items[index].props.value = this.form.nboName
+            this.dingtalkForm.items[index].props.disabled = true
+          }
+          if (this.dingtalkForm.items[index].props.label === '客户名称') {
+            this.dingtalkForm.items[index].props.value = this.form.custName
+            this.dingtalkForm.items[index].props.disabled = true
+          }
+          if (this.dingtalkForm.items[index].props.label === '所在省') {
+            this.dingtalkForm.items[index].props.value = this.form.custProvince
+            this.dingtalkForm.items[index].props.disabled = true
+          }
+          if (this.dingtalkForm.items[index].props.label === '所在市') {
+            this.dingtalkForm.items[index].props.value = this.form.custCity
+            this.dingtalkForm.items[index].props.disabled = true
+          }
+          if (this.dingtalkForm.items[index].props.label === '销售工程师') {
+            this.dingtalkForm.items[index].props.value = this.form.saleName
+            this.dingtalkForm.items[index].props.disabled = true
+          }
+          if (this.dingtalkForm.items[index].props.label === '工单名称') {
+            this.dingtalkForm.items[index].props.value = this.form.custName + this.form.orderTypeName
+          }
+        }
+        this.$refs.dingTalkFrom.setFormAndRules(this.dingtalkForm)
+      },
+      // handleUploadFile(file) {
+      //   this.form.file = file
+      // },
+      handleSelectUser() {
+        this.$refs.selectUser.open()
+      },
+      selectUser(val) {
+        if (val && val.length > 0) {
+          this.form.assignUserId = val[0].id
+          this.form.assignUserName = val.map((item) => item.nickName).join()
+        }
+      },
+      showEdit(row) {
+        this.loading = false
+        if (!row) {
+          this.title = '添加'
+        } else {
+          this.title = '编辑'
+          this.form = Object.assign({}, row)
+        }
+        if (this.businessInfo) {
+          this.form.nboId = this.businessInfo.id
+          this.form.nboName = this.businessInfo.nboName
+          this.form.nboCode = this.businessInfo.nboCode
+          this.form.custId = this.businessInfo.custId
+          this.form.custName = this.businessInfo.custName
+          this.form.saleId = this.businessInfo.saleId
+          this.form.saleName = this.businessInfo.saleName
+        }
+        this.dialogFormVisible = true
+      },
+      close() {
+        this.$refs['dingTalkFrom'].resetForm()
+        this.$refs['form'].resetFields()
+        this.form = this.$options.data().form
+        this.dialogFormVisible = false
+      },
+      save() {
+        let dingValid = this.$refs['dingTalkFrom'].validateForm()
+        this.$refs['form'].validate(async (valid) => {
+          if (valid && dingValid) {
+            let items = []
+            let dingtalkForm = this.$refs['dingTalkFrom'].getFormData()
+            for (let item of dingtalkForm.items) {
+              if (item.componentName === 'DDAttachment') {
+                if (!item.props && item.props.required) {
+                  this.$baseMessage('请上传' + item.props.label, 'error', 'vab-hey-message-error')
+                  return
+                }
+              }
+              if (item.props.label === '项目编码') {
+                this.form.nboCode = item.props.value
+              }
+              if (item.props.label === '项目名称') {
+                this.form.nboName = item.props.value
+              }
+              if (item.props.label === '客户名称') {
+                this.form.custName = item.props.value
+              }
+              if (item.props.label === '工单名称') {
+                this.form.name = item.props.value
+              }
+              if (item.props.label === '备注' && !item.props.value) {
+                item.props.value = ''
+              }
+              if (item.props.label === '调研表上传' && !item.props.value) {
+                item.props.value = ''
+              }
+
+              if (item.props.label === '试用开始时间') {
+                this.form.trialTimeStart = item.props.value
+              }
+              if (item.props.label === '试用结束时间') {
+                this.form.trialTimeEnd = item.props.value
+              }
+              if (item.props.label === '期望完成时间') {
+                this.form.expectTime = item.props.value
+              }
+              if (item.props.label === '支持时间') {
+                this.form.supportTime = item.props.value
+              }
+              if (item.props.label === '支持人员') {
+                item.props.value = this.form.assignUserName
+              }
+              items.push({
+                componentName: item.componentName,
+                id: item.props.id,
+                name: item.props.label,
+                value: item.props.value,
+                required: item.props.required,
+              })
+            }
+            this.form.formData = items
+            this.loading = true
+            const { msg } = await workApi.doAdd(this.form)
+            this.loading = false
+            this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+            this.$emit('fetch-data')
+            this.close()
+          }
+        })
+      },
+    },
+  }
+</script>

+ 136 - 0
src/views/work/train/head/components/Feedback.vue

@@ -0,0 +1,136 @@
+<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="id">
+            <el-input v-model="form.id" disabled readonly />
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="工单状态" prop="orderStatus">
+            <el-input
+              disabled
+              readonly
+              suffix-icon="el-icon-search"
+              :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="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-row>
+    </el-form>
+    <template #footer>
+      <el-button @click="close">取 消</el-button>
+      <el-button type="primary" @click="save">确 定</el-button>
+    </template>
+
+    <!-- 选择支持人员弹窗 -->
+    <select-user ref="selectUser" @save="selectUser" />
+  </el-dialog>
+</template>
+
+<script>
+  import workApi from '@/api/work/index'
+  import SelectUser from '@/components/select/SelectUser'
+
+  export default {
+    name: 'WorkOrderFeedback',
+    components: {
+      SelectUser,
+    },
+    props: {
+      orderStatusOptions: {
+        type: Array,
+        default: () => [],
+      },
+    },
+    data() {
+      return {
+        form: {
+          orderId: undefined,
+          finishRemark: undefined,
+        },
+        rules: {
+          orderId: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+          finishRemark: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+        },
+        title: '',
+        dialogFormVisible: false,
+        orderTypeList: [],
+        dingtalkForm: undefined,
+      }
+    },
+    mounted() {},
+    methods: {
+      handleSelectUser() {
+        this.$refs.selectUser.open()
+      },
+      selectUser(val) {
+        if (val && val.length > 0) {
+          this.form.assignUserId = val[0].id
+          this.form.assignUserName = val.map((item) => item.nickName).join()
+        }
+      },
+      showEdit(row) {
+        this.title = row.name
+        this.form = Object.assign({}, row)
+        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 workApi.finishWorkOrder(this.form)
+            this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+            this.$emit('fetch-data')
+            this.close()
+          }
+        })
+      },
+    },
+  }
+</script>

+ 309 - 0
src/views/work/train/head/components/FeedbackRecord.vue

@@ -0,0 +1,309 @@
+<template>
+  <div style="height: 100%; overflow: auto">
+    <div v-if="type == 'try'">
+      <el-table border :data="detail.feedbackTrail">
+        <el-table-column
+          v-for="(item, index) in tryColumns"
+          :key="index"
+          align="center"
+          height="calc(100% - 42px)"
+          :label="item.label"
+          :min-width="item.width"
+          :prop="item.prop"
+          show-overflow-tooltip>
+          <template #default="{ row }">
+            <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 === 'feedbackTrialType'">
+              {{ tryType[row.feedbackTrialType] }}
+            </span>
+            <span v-else-if="item.prop === 'feedbackTrialTime'">
+              {{ parseTime(row.feedbackTrialTime, '{y}-{m}-{d}') }}
+            </span>
+            <span v-else>{{ row[item.prop] }}</span>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+    <div v-if="type == 'support'">
+      <el-empty v-if="!detail.feedbackSupportTime" :image-size="200" />
+      <el-descriptions v-else border :column="1" :label-style="{ width: '130px' }">
+        <el-descriptions-item label="反馈时间">{{ detail.feedbackSupportTime }}</el-descriptions-item>
+        <el-descriptions-item label="本次讲解情况反馈">
+          <span v-html="detail.feedbackSupportContent.replace(/\n/g, '<br>')"></span>
+        </el-descriptions-item>
+      </el-descriptions>
+    </div>
+    <div v-if="type == 'sale'">
+      <el-empty v-if="!detail.feedbackSaleTime" :image-size="200" />
+      <el-descriptions
+        v-else-if="detail.orderTypeName == '技术文件支持'"
+        border
+        :column="1"
+        :label-style="{ width: '130px' }">
+        <el-descriptions-item label="反馈时间">{{ detail.feedbackSaleTime }}</el-descriptions-item>
+        <el-descriptions-item label="用户反馈">
+          <span v-html="detail.feedbackSaleUser.replace(/\n/g, '<br>')"></span>
+        </el-descriptions-item>
+        <el-descriptions-item label="下一步计划">
+          <span v-html="detail.feedbackSaleNext.replace(/\n/g, '<br>')"></span>
+        </el-descriptions-item>
+        <el-descriptions-item label="技术支持满意度">
+          {{ detail.satisfactionRating ? detail.satisfactionRating + '分' : '' }}
+        </el-descriptions-item>
+        <el-descriptions-item label="技术支持提升建议">
+          <span v-html="detail.promotionProposal.replace(/\n/g, '<br>')"></span>
+        </el-descriptions-item>
+      </el-descriptions>
+      <el-descriptions
+        v-else-if="detail.orderTypeName == '售前讲解支持' || detail.orderTypeName == '经销商支持'"
+        border
+        :column="1"
+        :label-style="{ width: '130px' }">
+        <el-descriptions-item label="反馈时间">
+          <span v-html="detail.feedbackSaleTime.replace(/\n/g, '<br>')"></span>
+        </el-descriptions-item>
+        <el-descriptions-item label="会议纪要">
+          <span v-html="detail.feedbackSaleMeeting.replace(/\n/g, '<br>')"></span>
+        </el-descriptions-item>
+        <el-descriptions-item label="客户/经销商反馈">
+          <span v-html="detail.feedbackSaleDist.replace(/\n/g, '<br>')"></span>
+        </el-descriptions-item>
+        <el-descriptions-item label="下一步计划">
+          <span v-html="detail.feedbackSaleNext.replace(/\n/g, '<br>')"></span>
+        </el-descriptions-item>
+        <el-descriptions-item label="技术支持满意度">
+          {{ detail.satisfactionRating ? detail.satisfactionRating + '分' : '' }}
+        </el-descriptions-item>
+        <el-descriptions-item label="技术支持提升建议">
+          <span v-html="detail.promotionProposal.replace(/\n/g, '<br>')"></span>
+        </el-descriptions-item>
+      </el-descriptions>
+      <el-descriptions
+        v-else-if="detail.orderTypeName == '售后运维工单'"
+        border
+        :column="1"
+        :label-style="{ width: '130px' }">
+        <el-descriptions-item label="反馈时间">{{ detail.feedbackSaleTime }}</el-descriptions-item>
+        <el-descriptions-item label="工单反馈">
+          <span v-html="detail.feedbackSaleOrder.replace(/\n/g, '<br>')"></span>
+        </el-descriptions-item>
+      </el-descriptions>
+    </div>
+    <try-feedback-detail ref="tryFB" />
+  </div>
+</template>
+
+<script>
+  import TryFeedbackDetail from '@/views/work/order/components/TryFeedbackDetail'
+
+  export default {
+    name: 'DetailsContract',
+    components: { TryFeedbackDetail },
+
+    props: {
+      detail: {
+        type: [Object, String],
+        required: true,
+      },
+      type: {
+        type: String,
+        required: true,
+      },
+    },
+    data() {
+      return {
+        tryType: {
+          10: '试用启动反馈',
+          20: '试用过程反馈',
+          30: '试用总结反馈',
+        },
+        tryColumns: [
+          {
+            label: '编号',
+            width: '40px',
+            prop: 'id',
+            disableCheck: true,
+          },
+          {
+            label: '试用反馈类型',
+            width: '100px',
+            prop: 'feedbackTrialType',
+            sortable: false,
+            disableCheck: false,
+          },
+          {
+            label: '总结/问题',
+            width: 'auto',
+            prop: 'feedbackTrialContent',
+            sortable: false,
+            disableCheck: false,
+          },
+          {
+            label: '客户/经销商反馈',
+            width: 'auto',
+            prop: 'feedbackTrialDist',
+            sortable: false,
+            disableCheck: false,
+          },
+          {
+            label: '计划',
+            width: 'auto',
+            prop: 'feedbackTrialPlan',
+            sortable: false,
+            disableCheck: false,
+          },
+          {
+            label: '反馈时间',
+            width: '80px',
+            prop: 'feedbackTrialTime',
+            sortable: false,
+            disableCheck: false,
+          },
+        ],
+        list: [],
+      }
+    },
+    mounted() {},
+    methods: {
+      handleDetail(row) {
+        this.$refs.tryFB.open(row)
+      },
+    },
+  }
+</script>
+
+<style lang="scss" scoped>
+  .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);
+  }
+</style>

+ 171 - 0
src/views/work/train/head/components/SaleFeedback.vue

@@ -0,0 +1,171 @@
+<template>
+  <el-dialog title="销售反馈" :visible.sync="dialogFormVisible" width="500px" @close="close">
+    <el-form
+      ref="form"
+      label-position="top"
+      :model="form"
+      :rules="rules"
+      style="max-height: 550px; overflow-y: scroll; overflow-x: hidden">
+      <el-row :gutter="20">
+        <el-col :span="24">
+          <el-form-item label="反馈时间" prop="feedbackSaleTime">
+            <el-date-picker
+              v-model="form.feedbackSaleTime"
+              placeholder="选择反馈时间"
+              style="width: 100%"
+              type="date"
+              value-format="yyyy-MM-dd" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <!-- 售前讲解 -->
+      <el-row v-if="workType == '售前讲解支持' || workType == '经销商支持'">
+        <el-col :span="24">
+          <el-form-item label="会议纪要" prop="feedbackSaleMeeting">
+            <el-input
+              v-model="form.feedbackSaleMeeting"
+              placeholder="会议纪要"
+              :rows="5"
+              show-word-limit
+              type="textarea" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="24">
+          <el-form-item label="客户/经销商反馈" prop="feedbackSaleDist">
+            <el-input
+              v-model="form.feedbackSaleDist"
+              placeholder="客户/经销商反馈"
+              :rows="5"
+              show-word-limit
+              type="textarea" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <!-- END -->
+      <!-- 技术文件 -->
+      <el-row v-if="workType == '技术文件支持'">
+        <el-col :span="24">
+          <el-form-item label="用户反馈" prop="feedbackSaleUser">
+            <el-input
+              v-model="form.feedbackSaleUser"
+              placeholder="用户反馈"
+              :rows="5"
+              show-word-limit
+              type="textarea" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <!-- END -->
+      <el-row v-if="workType == '售前讲解支持' || workType == '技术文件支持' || workType == '经销商支持'">
+        <el-col :span="24">
+          <el-form-item label="下一步计划" prop="feedbackSaleNext">
+            <el-input
+              v-model="form.feedbackSaleNext"
+              placeholder="下一步计划"
+              :rows="5"
+              show-word-limit
+              type="textarea" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <!-- 技术文件 -->
+      <el-row v-if="workType == '售后运维工单'">
+        <el-col :span="24">
+          <el-form-item label="工单反馈" prop="feedbackSaleOrder">
+            <el-input
+              v-model="form.feedbackSaleOrder"
+              placeholder="问题是否已解决、用户是否满意"
+              :rows="5"
+              show-word-limit
+              type="textarea" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <!-- END -->
+      <el-row v-if="workType == '售前讲解支持' || workType == '技术文件支持' || workType == '经销商支持'">
+        <el-col :span="24">
+          <el-form-item label="技术支持满意度" prop="satisfactionRating">
+            <el-radio-group v-model="form.satisfactionRating">
+              <el-radio label="1">1分</el-radio>
+              <el-radio label="2">2分</el-radio>
+              <el-radio label="3">3分</el-radio>
+              <el-radio label="4">4分</el-radio>
+              <el-radio label="5">5分</el-radio>
+            </el-radio-group>
+          </el-form-item>
+        </el-col>
+        <el-col :span="24">
+          <el-form-item label="技术支持提升建议" prop="promotionProposal">
+            <el-input
+              v-model="form.promotionProposal"
+              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 workOrderApi from '@/api/work/index'
+  export default {
+    name: 'WorkOrderFeedback',
+    data() {
+      return {
+        workType: '',
+        form: {
+          feedbackSaleTime: '',
+          feedbackSaleMeeting: '',
+          feedbackSaleDist: '',
+          feedbackSaleUser: '',
+          feedbackSaleNext: '',
+          feedbackSaleOrder: '',
+          orderId: '',
+          satisfactionRating: '',
+          promotionProposal: '',
+        },
+        rules: {
+          feedbackSaleTime: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+          feedbackSaleMeeting: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+          feedbackSaleDist: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+          feedbackSaleUser: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+          feedbackSaleNext: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+          feedbackSaleOrder: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+          satisfactionRating: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+        },
+        dialogFormVisible: false,
+      }
+    },
+    mounted() {},
+    methods: {
+      open(row) {
+        console.log(row)
+        this.form.orderId = row.id
+        this.workType = row.orderTypeName
+        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 workOrderApi.feedbackSale(this.form)
+            this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+            this.$emit('update-detail')
+            this.close()
+          }
+        })
+      },
+    },
+  }
+</script>

+ 77 - 0
src/views/work/train/head/components/SupportFeedback.vue

@@ -0,0 +1,77 @@
+<template>
+  <el-dialog title="支持人员总结" :visible.sync="dialogFormVisible" width="600px" @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="feedbackSupportTime">
+            <el-date-picker
+              v-model="form.feedbackSupportTime"
+              placeholder="选择反馈时间"
+              style="width: 100%"
+              type="date"
+              value-format="yyyy-MM-dd" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="24">
+          <el-form-item label="本次情况反馈" prop="feedbackSupportContent">
+            <el-input
+              v-model="form.feedbackSupportContent"
+              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 workOrderApi from '@/api/work/index'
+  export default {
+    name: 'WorkOrderFeedback',
+    data() {
+      return {
+        workType: '',
+        form: {
+          feedbackSupportContent: '',
+          feedbackSupportTime: '',
+          orderId: '',
+        },
+        rules: {
+          feedbackSupportContent: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+          feedbackSupportTime: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+        },
+        dialogFormVisible: false,
+      }
+    },
+    mounted() {},
+    methods: {
+      open(row) {
+        console.log(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 workOrderApi.feedbackSupport(this.form)
+            this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+            this.$emit('update-detail')
+            this.close()
+          }
+        })
+      },
+    },
+  }
+</script>

+ 143 - 0
src/views/work/train/head/components/TryFeedback.vue

@@ -0,0 +1,143 @@
+<template>
+  <el-dialog title="试用反馈" :visible.sync="dialogFormVisible" width="600px" @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="feedbackTrialTime">
+            <el-date-picker
+              v-model="form.feedbackTrialTime"
+              placeholder="选择反馈时间"
+              style="width: 100%"
+              type="date"
+              value-format="yyyy-MM-dd" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="24">
+          <el-form-item :label="summarizeLabel" prop="feedbackTrialContent">
+            <el-input
+              v-model="form.feedbackTrialContent"
+              :placeholder="summarizeLabel"
+              :rows="5"
+              show-word-limit
+              type="textarea" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="24">
+          <el-form-item label="客户/经销商反馈" prop="feedbackTrialDist">
+            <el-input
+              v-model="form.feedbackTrialDist"
+              placeholder="客户/经销商反馈"
+              :rows="5"
+              show-word-limit
+              type="textarea" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="24">
+          <el-form-item :label="nextPlanLabel" prop="feedbackTrialPlan">
+            <el-input
+              v-model="form.feedbackTrialPlan"
+              :placeholder="nextPlanLabel"
+              :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 workOrderApi from '@/api/work/index'
+  export default {
+    name: 'WorkOrderFeedback',
+    data() {
+      return {
+        workType: '',
+        fbType: '',
+        form: {
+          feedbackTrialTime: '',
+          feedbackTrialContent: '',
+          feedbackTrialDist: '',
+          feedbackTrialPlan: '',
+          workOrderId: '',
+        },
+        rules: {
+          feedbackTrialTime: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+          feedbackTrialContent: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+          feedbackTrialDist: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+          feedbackTrialPlan: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+        },
+        feedbackTrialTypeObj: {
+          start: '10',
+          process: '20',
+          summarize: '30',
+        },
+        dialogFormVisible: false,
+      }
+    },
+    computed: {
+      summarizeLabel() {
+        let str = ''
+        switch (this.fbType) {
+          case 'start':
+            str = this.workType == '产品试用申请(软件)' ? '部署安装总结' : '会议总结'
+            break
+          case 'process':
+            str = '过程问题'
+            break
+          case 'summarize':
+            str = '使用总结'
+            break
+        }
+        return str
+      },
+      nextPlanLabel() {
+        let str = ''
+        switch (this.fbType) {
+          case 'start':
+            str = '跟进计划'
+            break
+          case 'process':
+            str = '跟进计划'
+            break
+          case 'summarize':
+            str = '下一步计划'
+            break
+        }
+        return str
+      },
+    },
+    mounted() {},
+    methods: {
+      open(row, type) {
+        console.log(row, type)
+        this.form.workOrderId = row.id
+        this.workType = row.orderTypeName
+        this.fbType = type
+        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)
+            params.feedbackTrialType = this.feedbackTrialTypeObj[this.fbType]
+            const { msg } = await workOrderApi.feedbackTrail(params)
+            this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+            this.$emit('update-detail')
+            this.close()
+          }
+        })
+      },
+    },
+  }
+</script>

+ 122 - 0
src/views/work/train/head/components/TryFeedbackDetail.vue

@@ -0,0 +1,122 @@
+<template>
+  <el-dialog title="试用反馈" :visible.sync="dialogFormVisible" width="600px" @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="feedbackTrialTime">
+            <el-date-picker
+              v-model="form.feedbackTrialTime"
+              placeholder="选择反馈时间"
+              readonly
+              style="width: 100%"
+              type="date"
+              value-format="yyyy-MM-dd" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="24">
+          <el-form-item :label="summarizeLabel" prop="feedbackTrialContent">
+            <el-input
+              v-model="form.feedbackTrialContent"
+              :placeholder="summarizeLabel"
+              readonly
+              :rows="5"
+              show-word-limit
+              type="textarea" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="24">
+          <el-form-item label="客户/经销商反馈" prop="feedbackTrialDist">
+            <el-input
+              v-model="form.feedbackTrialDist"
+              placeholder="客户/经销商反馈"
+              readonly
+              :rows="5"
+              show-word-limit
+              type="textarea" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="24">
+          <el-form-item :label="nextPlanLabel" prop="feedbackTrialPlan">
+            <el-input
+              v-model="form.feedbackTrialPlan"
+              :placeholder="nextPlanLabel"
+              readonly
+              :rows="5"
+              show-word-limit
+              type="textarea" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+    <template #footer>
+      <el-button @click="close">取 消</el-button>
+    </template>
+  </el-dialog>
+</template>
+
+<script>
+  export default {
+    name: 'WorkOrderFeedback',
+    data() {
+      return {
+        workType: '',
+        fbType: '',
+        form: {},
+        rules: {
+          feedbackTrialTime: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+          feedbackTrialContent: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+          feedbackTrialDist: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+          feedbackTrialPlan: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+        },
+        feedbackTrialTypeObj: {
+          start: '10',
+          process: '20',
+          summarize: '30',
+        },
+        dialogFormVisible: false,
+      }
+    },
+    computed: {
+      summarizeLabel() {
+        let str = ''
+        switch (this.form.feedbackTrialType) {
+          case '10':
+            str = '总结'
+            break
+          case '20':
+            str = '过程问题'
+            break
+          case '30':
+            str = '使用总结'
+            break
+        }
+        return str
+      },
+      nextPlanLabel() {
+        let str = ''
+        switch (this.form.feedbackTrialType) {
+          case '10':
+            str = '跟进计划'
+            break
+          case '20':
+            str = '跟进计划'
+            break
+          case '30':
+            str = '下一步计划'
+            break
+        }
+        return str
+      },
+    },
+    mounted() {},
+    methods: {
+      open(row) {
+        this.form = row
+        this.dialogFormVisible = true
+      },
+      close() {
+        this.dialogFormVisible = false
+      },
+    },
+  }
+</script>

+ 309 - 0
src/views/work/train/head/index.vue

@@ -0,0 +1,309 @@
+<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="trainTitle">
+            <el-input v-model="queryForm.trainTitle" placeholder="培训主题" @keyup.enter.native="fetchData" />
+          </el-form-item>
+          <el-form-item prop="trainDate">
+            <el-date-picker
+              v-model="queryForm.trainDate"
+              end-placeholder="日期结束"
+              range-separator="至"
+              start-placeholder="日期开始"
+              type="daterange"
+              value-format="yyyy-MM-dd" />
+          </el-form-item>
+          <el-form-item>
+            <el-button icon="el-icon-search" type="primary" @click="fetchData">查询</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-left-panel :span="12">
+          <el-button
+            v-permissions="['proj:business:workOrder']"
+            icon="el-icon-plus"
+            type="primary"
+            @click="createOrder(null)">
+            创建工单
+          </el-button>
+        </vab-query-form-left-panel>
+        <!--        <el-button icon="el-icon-plus" size="mini" type="primary" @click="handleEdit">新建</el-button>-->
+      </vab-query-form-left-panel>
+      <vab-query-form-right-panel :span="12">
+        <table-tool :columns="columns" :show-columns.sync="showColumns" table-type="workOrderTable" />
+      </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 === 'id'" style="font-size: 14px" type="text" @click="handleDetail(row)">
+            {{ row.id }}
+          </el-button>
+          <span v-else-if="item.prop === 'createdTime'">
+            {{ parseTime(row.createdTime, '{y}-{m}-{d}') }}
+          </span>
+          <span v-else-if="item.prop === 'trainDate'">
+            {{ parseTime(row.createdTime, '{y}-{m}-{d}') }}
+          </span>
+          <span v-else-if="item.prop === 'trainConcreteTime'">
+            {{ overdue(row.trainConcreteTime) }}
+          </span>
+          <span v-else>{{ row[item.prop] }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column align="center" fixed="right" label="操作" width="200">
+        <template #default="{ row }">
+          <el-button type="text" @click="del(row.id)">删除</el-button>
+          <el-button type="text" @click="createOrder(row.id)">修改</el-button>
+          <el-button type="text" @click="toDetail(row.id,'edit')">反馈</el-button>
+          <el-button type="text" @click="toDetail(row.id,'detail')">详情</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" />
+    <!-- 创建经销商支持工单 -->
+    <createTrainHead ref="createTrainHead" @fetch-data="fetchData" />
+    <Detail ref="DetailRef" @fetch-data="fetchData" />
+  </div>
+</template>
+
+<script>
+  import to from 'await-to-js'
+  import api from '@/api/work/trainHead'
+  import TableTool from '@/components/table/TableTool'
+  import Edit from './components/Edit'
+  import createTrainHead from './components/CreateTrainHead'
+  import Detail from './components/Detail'
+
+  export default {
+    name: 'WorkOrder',
+    components: { TableTool, Edit, createTrainHead, Detail },
+    data() {
+      return {
+        activeName: 'first',
+        layout: 'total, sizes, prev, pager, next, jumper',
+        queryForm: {
+          trainTitle: '',
+          trainDate: [],
+          trainStartDate: '',
+          trainEndDate: '',
+          trainConcreteTime: '',
+          pageNum: 1,
+          pageSize: 10,
+        },
+        total: 0,
+        listLoading: false,
+        list: [],
+        selectRows: [],
+        showColumns: [],
+        productLineOptions: [],
+        columns: [
+          {
+            label: '工单编号',
+            width: '80px',
+            prop: 'id',
+            disableCheck: true,
+          },
+          {
+            label: '培训主题',
+            width: '320px',
+            prop: 'trainTitle',
+            disableCheck: true,
+          },
+          {
+            label: '培训日期',
+            width: '200px',
+            prop: 'trainDate',
+          },
+          {
+            label: '培训具体时间',
+            width: '100px',
+            prop: 'trainConcreteTime',
+          },
+          {
+            label: '创建时间',
+            width: '100px',
+            prop: 'createdTime',
+          },
+        ],
+        orderStatusOptions: [],
+      }
+    },
+    watch: {
+      showColumns: function () {
+        this.$nextTick(() => this.$refs.table.doLayout())
+      },
+    },
+    activated() {
+      this.fetchData()
+    },
+    mounted() {
+      this.getOptions()
+      this.fetchData()
+    },
+
+    methods: {
+      // 创建工单
+      createOrder(id) {
+        this.$refs['createTrainHead'].showEdit(id)
+      },
+      toDetail(id,type) {
+        console.log("id",id);
+        this.$refs['DetailRef'].showEdit(id,type)
+      },
+      async del(id) {
+        const [err, res] = await to(api.DeleteByIds({ ids: [id] }))
+        if (err) return (this.listLoading = false)
+        this.fetchData()
+      },
+      changeProductLine() {},
+      getOptions() {
+        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
+        this.$nextTick(() => this.$refs.table.doLayout())
+      },
+      handleEdit(row) {
+        if (row.id) {
+          this.$refs['edit'].showEdit(row)
+        } else {
+          this.$refs['edit'].showEdit()
+        }
+      },
+      // 关闭工单
+      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({
+          name: 'WorkOrderDetail',
+          query: {
+            id: row.id,
+          },
+        })
+      },
+      handleProjDetail(row) {
+        this.$router.push({
+          path: '/opportunity/detail',
+          query: {
+            id: row.nboId,
+          },
+        })
+      },
+      reset() {
+        this.queryForm = {
+          pageNum: 1,
+          pageSize: 10,
+          name: '',
+          orderTypeName: '',
+          orderStatus: '',
+          assignUserName: '',
+          supportTime: [],
+          productLine: '',
+          nboName: '',
+          custName: '',
+        }
+        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>