Преглед на файлове

feature(工单): 工单管理

ZZH-wl преди 2 години
родител
ревизия
dcc6e752b8

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

@@ -14,4 +14,22 @@ export default {
   getDetail(query) {
     return micro_request.postRequest(basePath, 'WorkOrder', 'GetEntityById', query)
   },
+  doEdit(query) {
+    return micro_request.postRequest(basePath, 'WorkOrder', 'UpdateById', query)
+  },
+  doAdd(query) {
+    return micro_request.postRequest(basePath, 'WorkOrder', 'CreateWorkOrder', query)
+  },
+  createUploadFileOrder(query) {
+    return micro_request.postFormDataRequest(basePath, 'WorkOrderHandler', 'CreateUploadFileOrder', query)
+  },
+
+  // 创建工单反馈
+  createWorkOrderFeedback(query) {
+    return micro_request.postRequest(basePath, 'WorkOrder', 'CreateWorkOrderFeedback', query)
+  },
+  // 反馈记录详情展示
+  getWorkOrderFeedbackByDay(query) {
+    return micro_request.postRequest(basePath, 'WorkOrder', 'GetWorkOrderFeedbackByDay', query)
+  },
 }

+ 12 - 4
src/utils/index.js

@@ -315,10 +315,18 @@ export function getFormData(object) {
   const formData = new FormData()
   Object.keys(object).forEach((key) => {
     const value = object[key]
-    if (Array.isArray(value)) {
-      value.forEach((subValue, i) => formData.append(key + `[${i}]`, subValue))
-    } else {
-      formData.append(key, object[key])
+    if (value) {
+      if (Array.isArray(value)) {
+        console.log(value)
+        // value.forEach((subValue, i) => {
+        //   for (let val in subValue) {
+        //     formData.append(key + `[${i}].` + val, subValue[val])
+        //   }
+        // })
+        value.forEach((subValue, i) => formData.append(key + `[${i}]`, subValue))
+      } else {
+        formData.append(key, object[key])
+      }
     }
   })
   return formData

+ 111 - 0
src/views/proj/business/components/DetailsWorkOrder.vue

@@ -0,0 +1,111 @@
+<template>
+  <div style="height: 100%">
+    <el-table v-loading="listLoading" border :data="list" height="calc(100% - 42px)">
+      <el-table-column
+        v-for="(item, index) in columns"
+        :key="index"
+        align="center"
+        :label="item.label"
+        :min-width="item.width"
+        :prop="item.prop"
+        show-overflow-tooltip>
+        <template #default="{ row }">
+          <span v-if="item.prop === 'orderStatus'">
+            {{ selectDictLabel(orderStatusOptions, row.orderStatus) }}
+          </span>
+          <span v-else-if="item.prop === 'createdTime'">
+            {{ parseTime(row.createdTime, '{y}-{m}-{d}') }}
+          </span>
+          <span v-else>{{ row[item.prop] }}</span>
+        </template>
+      </el-table-column>
+    </el-table>
+  </div>
+</template>
+
+<script>
+  import workOrderApi from '@/api/work/index'
+  import to from 'await-to-js'
+
+  export default {
+    name: 'DetailsContract',
+    data() {
+      return {
+        busId: undefined,
+        queryForm: {
+          nboCode: undefined,
+          pageNum: 1,
+          pageSize: 9999,
+        },
+        listLoading: false,
+        selectRows: [],
+        checkList: [],
+        columns: [
+          {
+            label: '工单名称',
+            width: '160px',
+            prop: 'name',
+            disableCheck: true,
+          },
+          {
+            label: '工单类型',
+            width: 'auto',
+            prop: 'orderTypeName',
+          },
+          {
+            label: '工单状态',
+            width: '80px',
+            prop: 'orderStatus',
+          },
+          {
+            label: '分派人员',
+            width: 'auto',
+            prop: 'assignUserName',
+          },
+          // {
+          //   label: '反馈信息',
+          //   width: 'auto',
+          //   prop: 'feedBack',
+          // },
+          {
+            label: '创建人',
+            width: '80px',
+            prop: 'createdName',
+          },
+          {
+            label: '创建时间',
+            width: '80px',
+            prop: 'createdTime',
+          },
+        ],
+        orderStatusOptions: [],
+        list: [],
+      }
+    },
+    mounted() {
+      this.getOptions()
+    },
+    methods: {
+      getOptions() {
+        Promise.all([this.getDicts('work_order_status')])
+          .then(([workorder]) => {
+            this.orderStatusOptions = workorder.data.values || []
+          })
+          .catch((err) => console.log(err))
+      },
+      open(nboCode) {
+        this.queryForm.nboCode = nboCode
+        this.fetchData()
+      },
+      async fetchData() {
+        this.listLoading = true
+        const params = { ...this.queryForm }
+        const [err, res] = await to(workOrderApi.getList(params))
+        if (err) return (this.listLoading = false)
+        this.list = res.data.list || []
+        this.total = res.data.total
+        this.listLoading = false
+      },
+    },
+  }
+</script>

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

@@ -22,7 +22,7 @@
                 @click="handleToReserve">
                 转为储备
               </el-button>
-              <el-button v-permissions="['proj:business:workOrder']">创建工单</el-button>
+              <el-button v-permissions="['proj:business:workOrder']" @click="createWorkOrder">创建工单</el-button>
               <el-button v-permissions="['proj:business:contract']" @click="createContract">创建合同</el-button>
             </span>
           </h3>
@@ -186,7 +186,9 @@
           <el-tab-pane label="合同记录" name="contract">
             <details-contract ref="detailsContract" :bus-id="id" />
           </el-tab-pane>
-          <el-tab-pane label="工单记录" name="worksheet" />
+          <el-tab-pane label="工单记录" name="workorder">
+            <details-work-order ref="detailsWorkOrder" />
+          </el-tab-pane>
           <el-tab-pane label="归属记录" name="belong">
             <el-table v-loading="belongLoading" border :data="belongs" height="calc(100% - 42px)">
               <el-table-column align="center" label="归属销售" prop="opnContent.saleName" show-overflow-tooltip />
@@ -220,6 +222,8 @@
     <transfer ref="transfer" @fetch-data="init" />
     <!-- 转储备项目 -->
     <to-reserve ref="toReserve" @fetch-data="init" />
+    <!-- 创建工单 -->
+    <work-order-edit ref="work-order-edit" :business-info="details" />
     <!-- 合同 -->
     <contract-edit ref="contract" :business-data="[details]" />
   </div>
@@ -233,12 +237,14 @@
   import Edit from './components/BusinessEdit'
   import FollowAdd from './components/FollowAdd'
   import Transfer from './components/Transfer'
+  import ToReserve from './components/ToReserve.vue'
   import ProductTable from './components/ProductTable'
   import DetailsContact from './components/DetailsContact'
   import DetailsContract from './components/DetailsContract'
   import DetailsRecords from './components/DetailsRecords'
   import DetailsFollow from './components/DetailsFollow'
-  import ToReserve from './components/ToReserve.vue'
+  import DetailsWorkOrder from './components/DetailsWorkOrder'
+  import WorkOrderEdit from '@/views/work/order/components/Edit'
   import ContractEdit from '@/views/contract/components/Edit'
 
   export default {
@@ -254,6 +260,8 @@
       DetailsContract,
       DetailsRecords,
       DetailsFollow,
+      DetailsWorkOrder,
+      WorkOrderEdit,
       ContractEdit,
     },
     data() {
@@ -336,8 +344,8 @@
           await this.getProductData(this.id)
         } else if (tab.name == 'contract') {
           this.$refs.detailsContract.open(this.id)
-        } else if (tab.name == 'worksheet') {
-          return
+        } else if (tab.name == 'workorder') {
+          await this.$refs.detailsWorkOrder.open(this.details.nboCode)
         } else if (tab.name == 'belong') {
           // 获取项目转移记录
           this.belongLoading = true
@@ -391,6 +399,10 @@
           this.back()
         })
       },
+      // 创建工单
+      createWorkOrder() {
+        this.$refs['work-order-edit'].showEdit()
+      },
       // 创建合同
       createContract() {
         this.$refs.contract.init()

+ 1 - 1
src/views/proj/business/index.vue

@@ -436,7 +436,7 @@
       },
     },
     created() {
-      if (this.$route.params) {
+      if (this.$route.params && this.$route.params.length > 0) {
         this.queryForm = this.$route.params
         this.activeName = this.queryForm.activeName ? this.queryForm.activeName : 'all'
       }

+ 208 - 0
src/views/work/order/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>

+ 261 - 0
src/views/work/order/components/DingTalkFromToVue.vue

@@ -0,0 +1,261 @@
+<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
+              :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"
+                :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-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'"
+                v-model="item.props.value"
+                :disabled="item.props.disabled"
+                :placeholder="item.props.placeholder"
+                type="daterange"
+                :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="#"
+                :auto-upload="false"
+                :file-list="item.props.value"
+                :limit="1"
+                :on-change="
+                  (file) => {
+                    return setFile(index, file)
+                  }
+                ">
+                <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"
+                @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, index) in item.children"
+                  :key="index"
+                  align="center"
+                  :label="child.props.label"
+                  :prop="child.props.id"
+                  show-overflow-tooltip>
+                  <template #default="{ row }">
+                    <!--文本框-->
+                    <el-input
+                      v-if="child.componentName === 'TextField'"
+                      v-model="row[child.props.id]"
+                      :placeholder="item.props.placeholder" />
+                    <!--数字文本框-->
+                    <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 SelectUser from '@/components/select/SelectUser'
+
+  export default {
+    name: 'DingTalkFromToVue',
+    components: {
+      SelectUser,
+    },
+    props: {
+      colSpan: {
+        type: Number,
+        default: 24,
+      },
+    },
+    data() {
+      return {
+        dingtalkForm: {},
+        fileList: [],
+        dingRules: {},
+        userItemIndex: {},
+        dingTableFlag: true,
+      }
+    },
+    mounted() {},
+    // 方法集合
+    methods: {
+      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()
+      },
+      // 文件
+      setFile(index, file) {
+        this.$emit('upload', file.raw)
+        this.dingtalkForm.items[index].props.value = file.name
+        // 上传文件一直校验不过问题方案
+        let temp = JSON.parse(JSON.stringify(this.dingtalkForm))
+        this.$refs.dingtalkForm.clearValidate('items.' + index + '.props.value')
+        this.dingtalkForm = temp
+        return true
+      },
+      selectDataToJson(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(' - ')
+          }
+        }
+        this.dingtalkForm = dingtalkForm
+      },
+      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>

+ 216 - 0
src/views/work/order/components/Edit.vue

@@ -0,0 +1,216 @@
+<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-row>
+
+      <!--      钉钉审批流表单-->
+      <DingTalkFromToVue ref="dingTalkFrom" :col-span="12" @upload="handleUploadFile" />
+      <!--      钉钉审批流表单-->
+      <!--      <el-row :gutter="20">-->
+
+      <!--      </el-row>-->
+    </el-form>
+    <template #footer>
+      <el-button @click="close">取 消</el-button>
+      <el-button v-if="!form.id" 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 {
+        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,
+
+          dingtalkForm: 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() {
+        const { data } = await typeApi.getList()
+        this.orderTypeList = data.list
+      },
+      changeOrderType() {
+        let item = this.orderTypeList.find((item) => {
+          return item.id === this.form.orderTypeId
+        })
+        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
+          }
+        }
+        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) {
+        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.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 fileFlag = false
+            let dingtalkForm = this.$refs['dingTalkFrom'].getFormData()
+            for (let item of dingtalkForm.items) {
+              if (item.componentName === 'DDAttachment') {
+                fileFlag = true
+                if (!this.form.file && 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
+              }
+              items.push({
+                componentName: item.componentName,
+                id: item.props.id,
+                name: item.props.label,
+                value: item.props.value,
+                required: item.props.required,
+              })
+            }
+            if (fileFlag) {
+              this.form.dingTalkFormData = JSON.stringify(items)
+              const { msg } = await workApi.createUploadFileOrder(this.form)
+              this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+            } else {
+              this.form.formData = items
+              const { msg } = await workApi.doAdd(this.form)
+              this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+            }
+
+            this.$emit('fetch-data')
+            this.close()
+          }
+        })
+      },
+    },
+  }
+</script>

+ 103 - 0
src/views/work/order/components/Feedback.vue

@@ -0,0 +1,103 @@
+<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="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"
+              @focus="handleSelectUser" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row :gutter="20">
+        <el-col :span="12">
+          <el-form-item label="反馈信息" prop="feedback">
+            <el-input v-model="form.feedback" placeholder="请输入反馈信息" :rows="5" show-word-limit type="textarea" />
+          </el-form-item>
+        </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,
+    },
+    data() {
+      return {
+        form: {
+          orderId: undefined,
+          feedback: undefined,
+          remark: undefined,
+        },
+        rules: {
+          orderId: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
+          feedback: [{ 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 = '反馈'
+        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.createWorkOrderFeedback(this.form)
+            this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+            this.$emit('fetch-data')
+            this.close()
+          }
+        })
+      },
+    },
+  }
+</script>

+ 231 - 0
src/views/work/order/detail.vue

@@ -0,0 +1,231 @@
+<template>
+  <div class="detail">
+    <el-row :gutter="30">
+      <el-col :span="25">
+        <div class="title">
+          <p>工单</p>
+          <h3>
+            {{ detail.name }}
+          </h3>
+        </div>
+        <header>
+          <el-descriptions :colon="false" :column="6" direction="vertical" style="padding-top: 15px">
+            <el-descriptions-item content-class-name="my-content" label="名称" label-class-name="my-label">
+              {{ detail.name }}
+            </el-descriptions-item>
+            <el-descriptions-item content-class-name="my-content" label="工单类型" label-class-name="my-label">
+              {{ detail.orderTypeName }}
+            </el-descriptions-item>
+            <el-descriptions-item content-class-name="my-content" label="工单状态" label-class-name="my-label">
+              {{ selectDictLabel(orderStatusOptions, detail.orderStatus) }}
+            </el-descriptions-item>
+            <el-descriptions-item content-class-name="my-content" label="分派人员" label-class-name="my-label">
+              {{ detail.assignUserName }}
+            </el-descriptions-item>
+            <el-descriptions-item content-class-name="my-content" label="创建人" label-class-name="my-label">
+              {{ detail.createdName }}
+            </el-descriptions-item>
+            <el-descriptions-item content-class-name="my-content" label="创建时间" label-class-name="my-label">
+              {{ parseTime(detail.createdTime, '{y}-{m}-{d}') }}
+            </el-descriptions-item>
+          </el-descriptions>
+        </header>
+        <el-tabs v-model="activeName" @tab-click="handleClick">
+          <el-tab-pane label="详细信息" name="detail">
+            <el-descriptions
+              border
+              :column="2"
+              :content-style="{ width: '35%', 'word-break': 'break-all' }"
+              :label-style="{ width: '15%' }"
+              size="small">
+              <el-descriptions-item
+                v-for="(item, index) in dingFormData"
+                v-show="item.componentName !== 'TableField'"
+                :key="index"
+                :label="item.name">
+                <span v-if="item.componentName === 'DDDateRangeField'">{{ item.value.join(' 至 ') }}</span>
+                <span v-else-if="item.componentName === 'DDAttachment'">
+                  {{ item && item.length > 0 ? item[0].fileName : '' }}
+                </span>
+                <span v-else>{{ item.value }}</span>
+              </el-descriptions-item>
+            </el-descriptions>
+            <div v-if="dingFormTableData.length">
+              <h3 style="margin-top: 10px">{{ dingFormTableName }}</h3>
+              <el-table border :data="dingFormTableData">
+                <el-table-column
+                  v-for="(col, index) in dingFormTableHeader"
+                  :key="index"
+                  align="center"
+                  :label="col.name"
+                  :prop="col.id"
+                  show-overflow-tooltip />
+              </el-table>
+            </div>
+          </el-tab-pane>
+          <el-tab-pane label="反馈记录" name="feedback">
+            <detail-feedback ref="feedback" :work-order-id="id" />
+          </el-tab-pane>
+        </el-tabs>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+  import api from '@/api/work/index'
+  import DetailFeedback from './components/DetailFeedback'
+
+  export default {
+    name: 'WorkOrderDetail',
+    components: { DetailFeedback },
+    data() {
+      return {
+        id: undefined,
+        activeName: 'detail',
+        detail: {},
+        dingFormData: {},
+        dingFormTableName: [],
+        dingFormTableHeader: [],
+        dingFormTableData: [],
+        orderStatusOptions: [],
+      }
+    },
+    mounted() {
+      this.id = parseInt(this.$route.query.id)
+      this.getOptions()
+    },
+    methods: {
+      getOptions() {
+        Promise.all([api.getDetail({ id: this.id }), this.getDicts('work_order_status')])
+          .then(([detail, workOrderStatus]) => {
+            this.detail = detail.data
+            this.orderStatusOptions = workOrderStatus.data.values || []
+
+            this.dingFormData = JSON.parse(this.detail.formData)
+            this.checkDingFormType()
+          })
+          .catch((err) => console.log(err))
+      },
+      checkDingFormType() {
+        for (let item of this.dingFormData) {
+          if (item.componentName === 'TableField') {
+            this.dingFormTableName = item.name
+            if (item.value.length === 0) {
+              continue
+            }
+            this.dingFormTableHeader = item.value[0]
+            this.dingFormTableData = []
+            let tableData = []
+            for (let row of item.value) {
+              let obj = {}
+              for (let col of row) {
+                obj[col.id] = col.value
+              }
+              tableData.push(obj)
+            }
+            this.dingFormTableData = tableData
+          }
+        }
+        this.dingFormData = this.dingFormData.filter((item) => item.componentName !== 'TableField')
+      },
+      async handleClick(tab) {
+        if (tab.name === 'feedback') {
+          await this.$refs.feedback.fetchData()
+        } else if (tab.name === 'worksheet') {
+          return
+        } else {
+          return
+        }
+      },
+    },
+  }
+</script>
+
+<style lang="scss" scoped>
+  $base: '.detail';
+
+  #{$base} {
+    height: calc(100vh - 60px - 12px * 2 - 40px);
+    display: flex;
+    padding: 20px 40px;
+
+    > .el-row {
+      flex: 1;
+
+      > .el-col {
+        height: 100%;
+      }
+    }
+
+    .title {
+      p,
+      h3 {
+        margin: 0;
+      }
+
+      p {
+        font-size: 14px;
+        font-weight: 400;
+        line-height: 22px;
+      }
+
+      h3 {
+        font-size: 24px;
+        font-weight: 500;
+        line-height: 36px;
+        color: #333;
+        display: flex;
+        justify-content: space-between;
+      }
+    }
+
+    header {
+      height: 74px;
+      background: rgba(196, 196, 196, 0.5);
+      border-radius: 4px;
+      display: flex;
+      align-items: center;
+      padding: 0 20px;
+      margin-top: 16px;
+
+      ::v-deep .el-descriptions__body {
+        background: transparent;
+      }
+
+      ::v-deep .my-label {
+        font-size: 14px;
+        font-weight: 600;
+        color: #1d66dc;
+      }
+
+      ::v-deep .my-content {
+        font-size: 14px;
+        font-weight: 600;
+        color: #333;
+        white-space: nowrap;
+        overflow: hidden;
+        text-overflow: ellipsis;
+      }
+    }
+
+    .el-tabs {
+      height: calc(100% - 148px);
+      display: flex;
+      flex-direction: column;
+
+      ::v-deep .el-tabs__content {
+        flex: 1;
+
+        .el-tab-pane {
+          height: 100%;
+        }
+      }
+    }
+
+    .buttons {
+      padding-top: 28px;
+      text-align: right;
+    }
+  }
+</style>

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

@@ -1,38 +1,38 @@
 <template>
   <div class="list-container">
-    <el-row :gutter="10" style="margin-bottom: 10px">
-      <el-col :span="4">
-        <el-input v-model="queryForm.orderTypeDesc" placeholder="工单类型" />
-      </el-col>
-      <el-col :span="4">
-        <el-input v-model="queryForm.orderStatus" placeholder="工单状态" />
-      </el-col>
-      <el-col :span="4">
-        <el-input v-model="queryForm.assignUserName" placeholder="分派人员姓名" />
-      </el-col>
-      <el-col :span="12">
-        <el-button icon="el-icon-plus" type="primary" @click="fetchData">查询</el-button>
-        <el-button icon="el-icon-refresh-right" @click="reset">重置</el-button>
-      </el-col>
-    </el-row>
     <vab-query-form>
+      <vab-query-form-top-panel>
+        <el-form ref="queryForm" :inline="true" :model="queryForm" @submit.native.prevent>
+          <el-form-item prop="name">
+            <el-input v-model="queryForm.name" placeholder="工单名称" />
+          </el-form-item>
+          <el-form-item prop="orderTypeName">
+            <el-input v-model="queryForm.orderTypeName" placeholder="工单类型" />
+          </el-form-item>
+          <el-form-item prop="orderStatus">
+            <el-select v-model="queryForm.orderStatus" clearable placeholder="工单状态" style="width: 100%">
+              <el-option v-for="item in orderStatusOptions" :key="item.key" :label="item.value" :value="item.key" />
+            </el-select>
+          </el-form-item>
+          <el-form-item prop="assignUserName">
+            <el-input v-model="queryForm.assignUserName" placeholder="分派人员姓名" />
+          </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">
-        <el-button
-          v-permissions="['proj:business:edit']"
-          icon="el-icon-plus"
-          size="mini"
-          type="primary"
-          @click="$refs.edit.init()">
-          新建
-        </el-button>
+        <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">
-        <el-button icon="el-icon-download" @click="exportData" />
         <table-tool :check-list.sync="checkList" :columns="columns" />
       </vab-query-form-right-panel>
     </vab-query-form>
 
-    <el-table v-loading="listLoading" border :data="list" :height="height">
+    <el-table v-loading="listLoading" border :data="list" :height="$baseTableHeight(1)">
       <el-table-column
         v-for="(item, index) in finallyColumns"
         :key="index"
@@ -43,29 +43,21 @@
         :sortable="item.sortable"
         :width="item.width">
         <template #default="{ row }">
-          <span v-if="item.prop === 'orderTypeDesc'">
-            {{ row.orderTypeDesc }}
-          </span>
-          <span v-if="item.prop === 'orderStatus'">
-            {{ row.orderStatus }}
-          </span>
-          <span v-if="item.prop === 'assignUserName'">
-            {{ row.assignUserName }}
-          </span>
-          <span v-if="item.prop === 'feedBack'">
-            {{ row.feedBack }}
-          </span>
-          <span v-if="item.prop === 'createName'">
-            {{ row.createName }}
+          <el-button v-if="item.prop === 'name'" style="font-size: 14px" type="text" @click="handleDetail(row)">
+            {{ row.name }}
+          </el-button>
+          <span v-else-if="item.prop === 'orderStatus'">
+            {{ selectDictLabel(orderStatusOptions, row.orderStatus) }}
           </span>
-          <span v-if="item.prop === 'createTime'">
-            {{ row.createTime }}
+          <span v-else-if="item.prop === 'createdTime'">
+            {{ parseTime(row.createdTime, '{y}-{m}-{d}') }}
           </span>
+          <span v-else>{{ row[item.prop] }}</span>
         </template>
       </el-table-column>
       <el-table-column align="center" label="操作" width="85">
         <template #default="{ row }">
-          <el-button type="text" @click="handleDetail(row)">查看</el-button>
+          <el-button type="text" @click="handleFeedback(row)">反 馈</el-button>
         </template>
       </el-table-column>
       <template #empty>
@@ -81,24 +73,30 @@
       :total="total"
       @current-change="handleCurrentChange"
       @size-change="handleSizeChange" />
+    <!--创建工单-->
+    <edit ref="edit" @fetch-data="fetchData" />
+    <!--反馈-->
+    <feedback ref="feedback" @fetch-data="fetchData" />
   </div>
 </template>
 
 <script>
   import to from 'await-to-js'
   import api from '@/api/work/index'
-  import downloadFileByByte from '@/utils/base64ToFile'
   import TableTool from '@/components/table/TableTool'
+  import Edit from './components/Edit'
+  import Feedback from './components/Feedback'
 
   export default {
     name: 'WorkOrder',
-    components: { TableTool },
+    components: { TableTool, Edit, Feedback },
     data() {
       return {
         activeName: 'first',
         layout: 'total, sizes, prev, pager, next, jumper',
         queryForm: {
-          orderTypeDesc: '',
+          name: '',
+          orderTypeName: '',
           orderStatus: '',
           assignUserName: '',
           pageNum: 1,
@@ -110,50 +108,47 @@
         selectRows: [],
         checkList: [],
         columns: [
+          {
+            label: '工单名称',
+            width: '300px',
+            prop: 'name',
+            disableCheck: true,
+          },
           {
             label: '工单类型',
             width: 'auto',
-            prop: 'orderTypeDesc',
-            sortable: true,
-            disableCheck: true,
+            prop: 'orderTypeName',
           },
           {
             label: '工单状态',
             width: 'auto',
             prop: 'orderStatus',
-            sortable: true,
           },
           {
             label: '分派人员',
             width: 'auto',
             prop: 'assignUserName',
-            sortable: true,
-          },
-          {
-            label: '反馈信息',
-            width: 'auto',
-            prop: 'feedBack',
-            sortable: true,
           },
+          // {
+          //   label: '反馈信息',
+          //   width: 'auto',
+          //   prop: 'feedBack',
+          // },
           {
             label: '创建人',
             width: 'auto',
-            prop: 'createName',
-            sortable: true,
+            prop: 'createdName',
           },
           {
             label: '创建时间',
             width: 'auto',
-            prop: 'createTime',
-            sortable: true,
+            prop: 'createdTime',
           },
         ],
+        orderStatusOptions: [],
       }
     },
     computed: {
-      height() {
-        return this.$baseTableHeight(1)
-      },
       finallyColumns() {
         return this.columns.filter((item) => this.checkList.includes(item.label))
       },
@@ -163,9 +158,12 @@
     },
 
     methods: {
-      handleClick(tab) {
-        console.log(tab, 'tab')
-        this.fetchData()
+      getOptions() {
+        Promise.all([this.getDicts('work_order_status')])
+          .then(([workOrderStatus]) => {
+            this.orderStatusOptions = workOrderStatus.data.values || []
+          })
+          .catch((err) => console.log(err))
       },
       async fetchData() {
         this.listLoading = true
@@ -176,27 +174,23 @@
         this.total = res.data.total
         this.listLoading = false
       },
-      exportData() {
-        let exportFrom = JSON.parse(JSON.stringify(this.queryForm))
-        exportFrom.columns = this.finallyColumns.map((item) => item.label)
-        api
-          .deriveList(exportFrom)
-          .then((res) => {
-            if (res.data.list.content) {
-              downloadFileByByte(res.data.list.content, '工单数据.xlsx')
-            }
-          })
-          .catch((err) => {
-            console.error(err)
-          })
+      handleEdit(row) {
+        if (row.id) {
+          this.$refs['edit'].showEdit(row)
+        } else {
+          this.$refs['edit'].showEdit()
+        }
+      },
+      handleFeedback(row) {
+        row.orderId = row.id
+        this.$refs['feedback'].showEdit(row)
       },
       //详情
       handleDetail(row) {
         this.$router.push({
-          path: './detail',
+          name: 'WorkOrderDetail',
           query: {
             id: row.id,
-            privateCus: 1,
           },
         })
       },
@@ -204,7 +198,8 @@
         this.queryForm = {
           pageNum: 1,
           pageSize: 10,
-          orderTypeDesc: '',
+          name: '',
+          orderTypeName: '',
           orderStatus: '',
           assignUserName: '',
         }

+ 75 - 0
src/views/work/orderType/components/Edit.vue

@@ -0,0 +1,75 @@
+<template>
+  <el-dialog :title="title" :visible.sync="dialogFormVisible" @close="close">
+    <el-form ref="form" label-width="100px" :model="form" :rules="rules">
+      <el-form-item label="名称" prop="name">
+        <el-input v-model="form.name" placeholder="请输入名称" />
+      </el-form-item>
+      <el-form-item label="钉钉流程号" prop="workflowCode">
+        <el-input v-model="form.workflowCode" placeholder="请输入钉钉流程编号" />
+      </el-form-item>
+      <el-form-item label="排序" prop="sort">
+        <el-input-number v-model="form.sort" controls-position="right" :min="0" />
+      </el-form-item>
+      <el-form-item label="备注" prop="remark">
+        <el-input v-model="form.remark" placeholder="请输入内容" type="textarea" />
+      </el-form-item>
+      <el-form-item label="表单内容">
+        <el-input v-model="form.formColumn" disabled readonly :rows="5" type="textarea" />
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <el-button @click="close">取 消</el-button>
+      <el-button type="primary" @click="save">确 定</el-button>
+    </template>
+  </el-dialog>
+</template>
+
+<script>
+  import typeApi from '@/api/work/type'
+
+  export default {
+    name: 'WorkOrderTypeEdit',
+    data() {
+      return {
+        form: { name: '', workflowCode: '', sort: 0, remark: '' },
+        rules: {
+          name: [{ required: true, message: '名称不能为空', trigger: 'blur' }],
+          workflowCode: [{ required: true, message: '钉钉流程编号不能为空', trigger: 'blur' }],
+        },
+        title: '',
+        dialogFormVisible: false,
+      }
+    },
+    methods: {
+      showEdit(row) {
+        if (!row) {
+          this.title = '添加'
+        } else {
+          this.title = '编辑'
+          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) {
+            if (this.form.id) {
+              const { msg } = await typeApi.doEdit(this.form)
+              this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+            } else {
+              const { msg } = await typeApi.doAdd(this.form)
+              this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+            }
+            this.$emit('fetch-data')
+            this.close()
+          }
+        })
+      },
+    },
+  }
+</script>

+ 205 - 0
src/views/work/orderType/index.vue

@@ -0,0 +1,205 @@
+<template>
+  <div class="list-container">
+    <el-row :gutter="10" style="margin-bottom: 10px">
+      <el-col :span="4">
+        <el-input v-model="queryForm.name" placeholder="工单类型" />
+      </el-col>
+      <el-col :span="12">
+        <el-button icon="el-icon-plus" type="primary" @click="fetchData">查询</el-button>
+        <el-button icon="el-icon-refresh-right" @click="reset">重置</el-button>
+      </el-col>
+    </el-row>
+    <vab-query-form>
+      <vab-query-form-left-panel :span="12">
+        <el-button icon="el-icon-plus" size="mini" type="primary" @click="handleEdit">新建</el-button>
+        <el-button icon="el-icon-delete" type="danger" @click="handleDelete($event)">删除</el-button>
+      </vab-query-form-left-panel>
+      <vab-query-form-right-panel :span="12">
+        <table-tool :check-list.sync="checkList" :columns="columns" />
+      </vab-query-form-right-panel>
+    </vab-query-form>
+
+    <el-table
+      v-loading="listLoading"
+      border
+      :data="list"
+      :height="$baseTableHeight(1)"
+      @selection-change="setSelectRows">
+      <el-table-column show-overflow-tooltip type="selection" />
+
+      <el-table-column
+        v-for="(item, index) in finallyColumns"
+        :key="index"
+        align="center"
+        :label="item.label"
+        :prop="item.prop"
+        show-overflow-tooltip
+        :sortable="item.sortable"
+        :width="item.width">
+        <template #default="{ row }">
+          <span>{{ row[item.prop] }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column align="center" label="操作" width="120">
+        <template #default="{ row }">
+          <el-button type="text" @click="handleEdit(row)">编辑</el-button>
+          <el-button type="text" @click="handleSyncForm(row)">同步表单</el-button>
+        </template>
+      </el-table-column>
+      <template #empty>
+        <el-image class="vab-data-empty" :src="require('@/assets/empty_images/data_empty.png')" />
+      </template>
+    </el-table>
+
+    <el-pagination
+      background
+      :current-page="queryForm.pageNum"
+      :layout="layout"
+      :page-size="queryForm.pageSize"
+      :total="total"
+      @current-change="handleCurrentChange"
+      @size-change="handleSizeChange" />
+
+    <edit ref="edit" @fetch-data="fetchData" />
+  </div>
+</template>
+
+<script>
+  import to from 'await-to-js'
+  import api from '@/api/work/type'
+  import TableTool from '@/components/table/TableTool'
+  import Edit from './components/Edit'
+
+  export default {
+    name: 'WorkOrderType',
+    components: { TableTool, Edit },
+    data() {
+      return {
+        activeName: 'first',
+        layout: 'total, sizes, prev, pager, next, jumper',
+        queryForm: {
+          name: '',
+          pageNum: 1,
+          pageSize: 10,
+        },
+        total: 0,
+        listLoading: false,
+        list: [],
+        selectRows: [],
+        checkList: [],
+        columns: [
+          {
+            label: '名称',
+            width: 'auto',
+            prop: 'name',
+            disableCheck: true,
+          },
+          {
+            label: '钉钉流程',
+            width: '400px',
+            prop: 'workflowCode',
+          },
+          {
+            label: '排序',
+            width: '80px',
+            prop: 'sort',
+          },
+          {
+            label: '备注',
+            width: 'auto',
+            prop: 'remark',
+          },
+          {
+            label: '创建人',
+            width: '100px',
+            prop: 'createdName',
+          },
+          {
+            label: '创建时间',
+            width: '170px',
+            prop: 'createdTime',
+          },
+        ],
+      }
+    },
+    computed: {
+      finallyColumns() {
+        return this.columns.filter((item) => this.checkList.includes(item.label))
+      },
+    },
+    mounted() {
+      this.fetchData()
+    },
+
+    methods: {
+      async fetchData() {
+        this.listLoading = true
+        const params = { ...this.queryForm }
+        const [err, res] = await to(api.getList(params))
+        if (err) return (this.listLoading = false)
+        this.list = res.data.list || []
+        this.total = res.data.total
+        this.listLoading = false
+      },
+      async handleSyncForm(row) {
+        this.$baseConfirm('你确定要同步当前项表单吗', null, async () => {
+          const { msg } = await api.syncDingTalkForm(row)
+          this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+          await this.fetchData()
+        })
+      },
+      handleEdit(row) {
+        if (row.id) {
+          this.$refs['edit'].showEdit(row)
+        } else {
+          this.$refs['edit'].showEdit()
+        }
+      },
+      handleDelete(row) {
+        console.log(row)
+        if (row.id) {
+          this.$baseConfirm('你确定要删除当前项吗', null, async () => {
+            const { msg } = await api.doDelete({ ids: [row.id] })
+            this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+            await this.fetchData()
+          })
+        } else {
+          if (this.selectRows.length > 0) {
+            const ids = this.selectRows.map((item) => item.id)
+            this.$baseConfirm('你确定要删除选中项吗', null, async () => {
+              const { msg } = await api.doDelete({ ids })
+              this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+              await this.fetchData()
+            })
+          } else {
+            this.$baseMessage('未选中任何行', 'error', 'vab-hey-message-error')
+          }
+        }
+      },
+
+      reset() {
+        this.queryForm = {
+          pageNum: 1,
+          pageSize: 10,
+          name: '',
+        }
+        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>