Ver Fonte

feature(督办): 督办事项前端功能实现

likai há 3 anos atrás
pai
commit
5785a3b145

+ 2 - 0
.env.development

@@ -7,3 +7,5 @@ VUE_APP_MicroSrvProxy_API=http://127.0.0.1:9981/
 # 登录验证微服务名称
 VUE_APP_AdminPath=dashoo.opms.admin-0.0.1
 
+# 业务接口微服务名称
+VUE_APP_ParentPath=dashoo.opms.parent-0.0.1

+ 3 - 0
.env.production

@@ -3,3 +3,6 @@ VUE_APP_TENANT=default
 # 登录验证微服务名称
 VUE_APP_AdminPath=dashoo.opms.admin-0.0.1
 VUE_APP_MicroSrvProxy_API=http://127.0.0.1:9981/
+
+# 业务接口微服务名称
+VUE_APP_ParentPath=dashoo.opms.parent-0.0.1

+ 1 - 1
src/api/customer/index.js

@@ -7,7 +7,7 @@
  * @FilePath: \opms_frontend\src\api\customer\index.js
  */
 import micro_request from '@/utils/micro_request'
-const basePath = process.env.VUE_APP_CustomerPath
+const basePath = process.env.VUE_APP_ParentPath
 export default {
   // 客户详情
   getDetail(query) {

+ 45 - 0
src/api/plat/task.js

@@ -0,0 +1,45 @@
+import micro_request from '@/utils/micro_request'
+
+const basePath = process.env.VUE_APP_ParentPath
+export default {
+  // 督办列表
+  getTaskList(query) {
+    return micro_request.postRequest(basePath, 'Task', 'GetList', query)
+  },
+  // 创建督办
+  createTask(query) {
+    return micro_request.postRequest(basePath, 'Task', 'Create', query)
+  },
+  // 更新督办状态
+  updateTaskStatus(query) {
+    return micro_request.postRequest(basePath, 'Task', 'ChangeStatus', query)
+  },
+  // 统计各类型督办数量
+  statisticsTaskNumber(query) {
+    return micro_request.postRequest(basePath, 'Task', 'StatisticsTaskNumber', query)
+  },
+  // 数据导出
+  exportTasks(query) {
+    return micro_request.postRequest(basePath, 'Task', 'Export', query)
+  },
+  // 评论列表
+  getTaskCommentList(query) {
+    return micro_request.postRequest(basePath, 'TaskComment', 'GetList', query)
+  },
+  // 添加评论
+  createTaskComment(query) {
+    return micro_request.postRequest(basePath, 'TaskComment', 'Create', query)
+  },
+  // 日志列表
+  getTaskLogList(query) {
+    return micro_request.postRequest(basePath, 'TaskLog', 'GetList', query)
+  },
+  // 督办进展列表
+  getTaskProgressList(query) {
+    return micro_request.postRequest(basePath, 'TaskProgress', 'GetList', query)
+  },
+  // 添加督办进展
+  createTaskProgress(query) {
+    return micro_request.postRequest(basePath, 'TaskProgress', 'Create', query)
+  },
+}

+ 29 - 0
src/utils/base64ToFile.js

@@ -0,0 +1,29 @@
+ 
+const util = {
+  // 创建一个a标签,并做下载点击事件
+  downloadFile: function(blob, fileName) {
+    const link = document.createElement('a')
+    link.href = window.URL.createObjectURL(blob)
+    link.download = fileName
+    // 此写法兼容可火狐浏览器
+    document.body.appendChild(link)
+    const evt = document.createEvent('MouseEvents')
+    evt.initEvent('click', false, false)
+    link.dispatchEvent(evt)
+    document.body.removeChild(link)
+  },
+  // 将Base64文件转为 Blob
+  buildBlobByByte: function(data) {
+    const raw = window.atob(data)
+    const rawLength = raw.length
+    const uInt8Array = new Uint8Array(rawLength)
+    for (let i = 0; i < rawLength; ++i) {
+      uInt8Array[i] = raw.charCodeAt(i)
+    }
+    return new Blob([uInt8Array])
+  },
+}
+// 二进制数组 生成文件
+export default function downloadFileByByte (data, fileName) {
+  return util.downloadFile(util.buildBlobByByte(data), fileName)
+}

+ 98 - 0
src/views/plat/task/components/CommentAdd.vue

@@ -0,0 +1,98 @@
+<template>
+  <el-dialog
+    title="评论"
+    :visible.sync="selfVisible"
+    width="500px"
+    @open="open"
+    @close="close">
+    <el-form ref="form" label-width="80px" :model="form" :rules="rules">
+      <el-row>
+        <el-col :span="24">
+          <el-form-item label="评论" prop="content">
+            <el-input type="textarea" v-model="form.content" placeholder="请输入评论"/>
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+    <template #footer>
+      <el-button @click="selfVisible = false">取 消</el-button>
+      <el-button type="primary" @click="save">确 定</el-button>
+    </template>
+  </el-dialog>
+</template>
+
+<script>
+  import taskApi from '@/api/plat/task'
+
+  export default {
+    name: 'CommentAdd',
+    props: {
+      selfVisible: {
+        type: Boolean,
+        default: false
+      },
+      theTask: {
+        type: Object,
+        default: {}
+      },
+      doRefresh: {
+        type: Function,
+        default: undefined,
+      },
+    },
+    watch: {
+      selfVisible (val) {
+        this.$emit('update:selfVisible', val)
+      }
+    },
+    data() {
+      return {
+        // 新增数据表单
+        form: {
+          taskId: '',
+          content: '',
+          remark: ''
+        },
+        // 校验规则
+        rules: {
+          content: [
+            { required: true, message: '评论不能为空', trigger: 'blur' },
+          ],
+        },
+      }
+    },
+    methods: {
+      // 打开弹窗
+      open() {
+        this.getData()
+        if (this.$refs['form']) {
+          this.$refs['form'].resetFields()
+        }
+        this.form.taskId = ''
+        this.form.content = ''
+        this.form.remark = ''
+      },
+      // 关闭弹窗
+      close() {
+        if (this.$refs['form']) {
+          this.$refs['form'].resetFields()
+        }
+        this.selfVisible = false
+      },
+      // 保存数据
+      save() {
+        this.$refs['form'].validate(async (valid) => {
+          if (valid) {
+            this.selfVisible = false
+            this.form.taskId = this.theTask.id
+            const { msg } = await taskApi.createTaskComment(this.form)
+            this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+            if (this.doRefresh) {
+              this.doRefresh()
+            }
+          }
+        })
+      },
+    }
+  }
+</script>

+ 127 - 0
src/views/plat/task/components/ProgressAdd.vue

@@ -0,0 +1,127 @@
+<template>
+  <el-dialog
+    title="评论"
+    :visible.sync="selfVisible"
+    width="500px"
+    @open="open"
+    @close="close">
+    <el-form ref="form" label-width="80px" :model="form" :rules="rules">
+      <el-row>
+        <el-col :span="12">
+          <el-form-item label="进展时间" prop="progDate">
+            <el-date-picker v-model="form.progDate" style="width: 100%;" type="datetime" value-format="yyyy-MM-dd HH:mm:ss" placeholder="请选择进展时间"/>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="附件" prop="progFile">
+            <!-- 此处附件上传组件不可用,等以后再调整 -->
+            <!-- <el-button size="mini" type="primary">上传</el-button> -->
+            <el-link v-show="form.progFile != ''" @click="showFile(form.progFile)">查看附件</el-link>
+          </el-form-item>
+        </el-col>
+        <el-col :span="24">
+          <el-form-item label="进展说明" prop="progDesc">
+            <el-input type="textarea" v-model="form.progDesc" placeholder="请输入进展说明"/>
+          </el-form-item>
+        </el-col>
+        <el-col :span="24">
+          <el-form-item label="备注" prop="remark">
+            <el-input type="textarea" v-model="form.remark" placeholder="请输入备注"/>
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+    <template #footer>
+      <el-button @click="selfVisible = false">取 消</el-button>
+      <el-button type="primary" @click="save">确 定</el-button>
+    </template>
+  </el-dialog>
+</template>
+
+<script>
+  import taskApi from '@/api/plat/task'
+
+  export default {
+    name: 'CommentAdd',
+    props: {
+      selfVisible: {
+        type: Boolean,
+        default: false
+      },
+      theTask: {
+        type: Object,
+        default: {}
+      },
+      doRefresh: {
+        type: Function,
+        default: undefined,
+      },
+    },
+    watch: {
+      selfVisible (val) {
+        this.$emit('update:selfVisible', val)
+      }
+    },
+    data() {
+      return {
+        // 新增数据表单
+        form: {
+          taskId: '',
+          progDate: '',
+          progDesc: '',
+          progFile: '',
+          remark: ''
+        },
+        // 校验规则
+        rules: {
+          progDesc: [
+            { required: true, message: '进展说明不能为空', trigger: 'blur' },
+          ],
+        },
+      }
+    },
+    methods: {
+      // 打开弹窗
+      open() {
+        this.getData()
+        if (this.$refs['form']) {
+          this.$refs['form'].resetFields()
+        }
+        this.form.taskId = ''
+        this.form.progDate = ''
+        this.form.progDesc = ''
+        this.form.progFile = ''
+        this.form.remark = ''
+      },
+      // 关闭弹窗
+      close() {
+        if (this.$refs['form']) {
+          this.$refs['form'].resetFields()
+        }
+        this.selfVisible = false
+      },
+      // 查看附件
+      showFile(path) {
+        const a = document.createElement('a')
+        a.href = path // 文件链接
+        a.download = path // 文件名,跨域资源download无效
+        a.click()
+        a.remove()
+      },
+      // 保存数据
+      save() {
+        this.$refs['form'].validate(async (valid) => {
+          if (valid) {
+            this.selfVisible = false
+            this.form.taskId = this.theTask.id
+            const { msg } = await taskApi.createTaskProgress(this.form)
+            this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+            if (this.doRefresh) {
+              this.doRefresh()
+            }
+          }
+        })
+      },
+    }
+  }
+</script>

+ 265 - 0
src/views/plat/task/components/TaskAdd.vue

@@ -0,0 +1,265 @@
+<template>
+  <el-dialog
+    title="添加"
+    :visible.sync="selfVisible"
+    width="650px"
+    @open="open"
+    @close="close">
+    <el-form ref="form" label-width="80px" :model="form" :rules="rules">
+      <el-row>
+        <el-col :span="12">
+          <el-form-item label="标题" prop="taskTitle">
+            <el-input v-model="form.taskTitle" placeholder="请输入标题"/>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="类型" prop="taskType">
+            <el-select v-model="form.taskType" style="width: 100%;" placeholder="请选择类型">
+              <el-option v-for="item in types" :key="item.dictCode" :value="item.dictValue" :label="item.dictLabel" />
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="开始时间" prop="taskStartDate">
+            <el-date-picker v-model="form.taskStartDate" style="width: 100%;" type="datetime" value-format="yyyy-MM-dd HH:mm:ss" placeholder="请选择开始时间"/>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="结束时间" prop="taskEndDate">
+            <el-date-picker v-model="form.taskEndDate" style="width: 100%;" type="datetime" value-format="yyyy-MM-dd HH:mm:ss" placeholder="请选择结束时间"/>
+          </el-form-item>
+        </el-col>
+        <el-col :span="24">
+          <el-form-item label="说明" prop="taskDesc">
+            <el-input type="textarea" v-model="form.taskDesc" placeholder="请输入说明"/>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="督办人" prop="supervisorUserId">
+            <el-select v-model="form.supervisorUserId" style="width: 100%;" placeholder="请选择督办人">
+              <el-option v-for="item in users" :key="item.id" :value="item.id" :label="item.userName" />
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="监办人" prop="watchUserId">
+            <el-select v-model="form.watchUserId" style="width: 100%;" placeholder="请选择监办人" clear>
+              <el-option v-for="item in users" :key="item.id" :value="item.id" :label="item.userName" />
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="负责人" prop="mainUserId">
+            <el-select v-model="form.mainUserId" style="width: 100%;" placeholder="请选择负责人">
+              <el-option v-for="item in users" :key="item.id" :value="item.id" :label="item.userName" />
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="团队成员">
+            <el-select v-model="teamIds" style="width: 100%;" clear multiple placeholder="请选择团队成员">
+              <el-option v-for="item in users" :key="item.id" :value="item.id" :label="item.userName" />
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="关联类型" prop="targetType">
+            <el-select v-model="form.targetType" style="width: 100%;" @change="targetTypeChange" placeholder="请选择关联对象类型">
+              <el-option label="客户" value="10" />
+              <el-option label="项目" value="20" />
+              <el-option label="合同" value="30" />
+              <el-option label="回款" value="40" />
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="关联对象" prop="targetId">
+            <el-select v-model="form.targetId" style="width: 100%;" placeholder="请选择关联对象" @change="targetChange">
+              <el-option v-for="item in targets" :key="item.value" :value="item.value" :label="item.label" />
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="24">
+          <el-form-item label="备注" prop="remark">
+            <el-input type="textarea" v-model="form.remark" placeholder="请输入备注"/>
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+    <template #footer>
+      <el-button @click="selfVisible = false">取 消</el-button>
+      <el-button type="primary" @click="save">确 定</el-button>
+    </template>
+  </el-dialog>
+</template>
+
+<script>
+  import taskApi from '@/api/plat/task'
+  import custApi from '@/api/customer/index'
+
+  export default {
+    name: 'TaskAdd',
+    props: {
+      selfVisible: {
+        type: Boolean,
+        default: false
+      },
+      users: {
+        type: Array,
+        default: []
+      },
+      types: {
+        type: Array,
+        default: []
+      },
+      doRefresh: {
+        type: Function,
+        default: undefined,
+      },
+    },
+    watch: {
+      selfVisible (val) {
+        this.$emit('update:selfVisible', val)
+      }
+    },
+    data() {
+      return {
+        // 团队成员
+        teamIds: [],
+        // 客户数据
+        customers: [],
+        // 关联对象
+        targets: [],
+        // 新增数据表单
+        form: {
+          taskTitle: '',
+          taskType: '',
+          taskStatus: '10',
+          isOverdue: '10',
+          taskStartDate: '',
+          taskEndDate: '',
+          taskDesc: '',
+          supervisorUserId: '',
+          watchUserId: '',
+          mainUserId: '',
+          ownerUserId: '',
+          taskLabel: '',
+          targetId: '',
+          targetType: '',
+          targetName: '',
+          remark: ''
+        },
+        // 校验规则
+        rules: {
+          taskTitle: [
+            { required: true, message: '标题不能为空', trigger: 'blur' },
+          ],
+          taskType: [
+            { required: true, message: '类型不能为空', trigger: 'blur' },
+          ],
+          taskStartDate: [
+            { required: true, message: '开始时间不能为空', trigger: 'blur' },
+          ],
+          taskEndDate: [
+            { required: true, message: '结束时间不能为空', trigger: 'blur' },
+          ],
+          supervisorUserId: [
+            { required: true, message: '督办人不能为空', trigger: 'change' },
+          ],
+          mainUserId: [
+            { required: true, message: '负责人不能为空', trigger: 'change' },
+          ],
+        },
+      }
+    },
+    methods: {
+      // 关联类型变化
+      targetTypeChange() {
+        this.targets.splice(0, this.targets.length)
+        this.form.targetId = ''
+        this.form.targetName = ''
+        if (this.form.targetType == '10') {
+          for (let cust of this.customers) {
+            this.targets.push(cust)
+          }
+        }
+      },
+      // 关联对象变化
+      targetChange() {
+        for (let item of this.targets) {
+          if (item.value == this.form.targetId) {
+            this.form.targetName = item.label
+            break
+          }
+        }
+      },
+      // 获取基本数据
+      getData() {
+        this.customers = []
+        custApi.getList({targetType: '10'})
+          .then(res => {
+            if (res.data.list) {
+              for (let cust of res.data.list) {
+                let data = {
+                  value: cust.id,
+                  label: cust.custName
+                }
+                this.customers.push(data)
+              }
+            }
+          }).catch(err => {
+            console.error(err)
+          })
+      },
+      // 打开弹窗
+      open() {
+        this.getData()
+        if (this.$refs['form']) {
+          this.$refs['form'].resetFields()
+        }
+        this.teamIds = []
+        this.form.taskTitle = ''
+        this.form.taskType = ''
+        this.form.taskStatus = '10'
+        this.form.isOverdue = '10'
+        this.form.taskStartDate = ''
+        this.form.taskEndDate = ''
+        this.form.taskDesc = ''
+        this.form.supervisorUserId = ''
+        this.form.watchUserId = ''
+        this.form.mainUserId = ''
+        this.form.ownerUserId = ''
+        this.form.taskLabel = ''
+        this.form.targetId = ''
+        this.form.targetType = ''
+        this.form.targetName = ''
+        this.form.remark = ''
+      },
+      // 关闭弹窗
+      close() {
+        if (this.$refs['form']) {
+          this.$refs['form'].resetFields()
+        }
+        this.selfVisible = false
+      },
+      // 保存数据
+      save() {
+        this.$refs['form'].validate(async (valid) => {
+          if (valid) {
+            if (this.teamIds && this.teamIds.length > 0) {
+              this.form.ownerUserId = this.teamIds.join(',')
+            } else {
+              this.form.ownerUserId = ''
+            }
+            this.selfVisible = false
+            const { msg } = await taskApi.createTask(this.form)
+            this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+            if (this.doRefresh) {
+              this.doRefresh()
+            }
+          }
+        })
+      },
+    }
+  }
+</script>

+ 240 - 0
src/views/plat/task/components/TaskDetail.vue

@@ -0,0 +1,240 @@
+<template>
+  <div>
+    <el-dialog
+      title="督办事项"
+      :visible.sync="selfVisible"
+      @open="open">
+      <el-row :gutter="20">
+        <el-col :span="18">
+          <span style="font-size: 18px; margin-right: 10px;">{{theTask.taskTitle}}</span>
+          <el-button type="primary" v-show="theTask.isOverdue === '10'" @click="changeStatus('IsOverdue', '20')">超期</el-button>
+          <el-button type="primary" v-show="theTask.taskStatus === '10'" @click="changeStatus('TaskStatus', '20')">关闭</el-button>
+          <div style="border-bottom: solid 1px; margin-top: 1px;"></div>
+          <el-row :gutter="20" style="margin-top: 10px;">
+            <el-col :span="8">督办类型:{{typeMap[theTask.taskType]}}</el-col>
+            <el-col :span="8">状态:{{theTask.taskStatus === '10' ? '进行中' : '关闭'}}</el-col>
+            <el-col :span="8">超期:{{theTask.isOverdue === '10' ? '否' : '是'}}</el-col>
+            <el-col :span="8">开始时间:{{parseTime(theTask.taskStartDate)}}</el-col>
+            <el-col :span="8">结束时间:{{parseTime(theTask.taskEndDate)}}</el-col>
+            <el-col :span="24">说明:{{theTask.taskDesc}}</el-col>
+            <el-col :span="8">督办人:{{userMap[theTask.supervisorUserId]}}</el-col>
+            <el-col :span="8">监办人:{{userMap[theTask.watchUserId]}}</el-col>
+            <el-col :span="8">负责人:{{userMap[theTask.mainUserId]}}</el-col>
+            <el-col :span="16">团队成员:{{teamNames}}</el-col>
+            <el-col :span="8">关联类型:
+              <span v-show="theTask.targetType == '10'">客户</span>
+              <span v-show="theTask.targetType == '20'">项目</span>
+              <span v-show="theTask.targetType == '30'">合同</span>
+              <span v-show="theTask.targetType == '40'">回款</span>
+            </el-col>
+            <el-col :span="8">关联对象:{{theTask.targetName}}</el-col>
+            <el-col :span="8">创建人:{{theTask.createdName}}</el-col>
+            <el-col :span="8">创建时间:{{parseTime(theTask.createdTime)}}</el-col>
+            <el-col :span="24">备注:{{theTask.remark}}</el-col>
+            <el-button type="primary" v-show="theTask.taskStatus === '10'" @click="addProgress">添加进展</el-button>
+            <el-table border :data="progressList" height="440">
+              <el-table-column align="center" label="进展说明" prop="progDesc"></el-table-column>
+              <el-table-column align="center" label="时间" prop="progDate">
+                <template #default="{ row }">
+                  {{parseTime(row.progDate)}}
+                </template>
+              </el-table-column>
+              <el-table-column align="center" label="附件" prop="progFile">
+                <template #default="{ row }">
+                  <el-link v-show="row.progFile != ''" @click="showFile(row.progFile)">查看附件</el-link>
+                </template>
+              </el-table-column>
+              <el-table-column align="center" label="备注" prop="remark"></el-table-column>
+            </el-table>
+          </el-row>
+        </el-col>
+        <el-col :span="6">
+          <el-button :type="rightInfo == 'log' ? 'primary' : 'none'" round @click="changeRightInfo('log')">流程动态</el-button>
+          <el-button :type="rightInfo == 'comment' ? 'primary' : 'none'" round @click="changeRightInfo('comment')">评论</el-button>
+          <div style="border-bottom: solid 1px; margin-top: 1px;"></div>
+          <div style="height: 560px; width: 100%;">
+            <div v-for="log in logList" :key="log.id" style="margin-top: 10px;" v-show="rightInfo == 'log'">
+              {{log.nodeName}}
+              <div style="margin-top: 5px;">
+                <span style="margin-top: 5px;">{{log.createdName}}</span><span style="float: right; margin-top: 5px;">{{log.desc}}</span>
+                <div style="margin-top: 5px;">开始处理:{{log.startTime}}</div>
+                <div style="margin-top: 5px;">完成处理:{{log.endTime}}</div>
+              </div>
+            </div>
+            <el-button style="margin-top: 10px;" type="primary" v-show="rightInfo == 'comment'" @click="addComment">评论</el-button>
+            <div v-for="comment in commentList" :key="comment.id" style="margin-top: 10px;" v-show="rightInfo == 'comment'">
+              {{comment.createdName}}
+              <div style="margin-top: 5px;">
+                <span style="margin-top: 5px;">评论时间:{{comment.createdTime}}</span>
+                <div style="margin-top: 5px;">评论:{{comment.content}}</div>
+              </div>
+            </div>
+          </div>
+        </el-col>
+      </el-row>
+    </el-dialog>
+    <!-- 评论添加 -->
+    <commentAdd :theTask="theTask" :doRefresh="getCommentList" :selfVisible.sync="commentAddDialogVisible"></commentAdd>
+    <!-- 进展添加 -->
+    <progressAdd :theTask="theTask" :doRefresh="doRefreshProgressAndLogs" :selfVisible.sync="progressAddDialogVisible"></progressAdd>
+  </div>
+</template>
+
+<script>
+  import taskApi from '@/api/plat/task'
+  import commentAdd from './CommentAdd.vue'
+  import progressAdd from './ProgressAdd.vue'
+
+  export default {
+    name: 'TaskDetail',
+    components: { commentAdd, progressAdd },
+    props: {
+      selfVisible: {
+        type: Boolean,
+        default: false
+      },
+      theTask: {
+        type: Object,
+        default: {}
+      },
+      userMap: {
+        type: Object,
+        default: {}
+      },
+      typeMap: {
+        type: Object,
+        default: {}
+      },
+      doRefresh: {
+        type: Function,
+        default: undefined,
+      },
+    },
+    watch: {
+      selfVisible (val) {
+        this.$emit('update:selfVisible', val)
+      }
+    },
+    data() {
+      return {
+        // 进展新增弹窗
+        progressAddDialogVisible: false,
+        // 团队成员
+        teamNames: '',
+        // 评论新增弹窗
+        commentAddDialogVisible: false,
+        // 右侧栏显示项目
+        rightInfo: 'log',
+        // 督办进展
+        progressList: [],
+        // 日志
+        logList: [],
+        // 评论
+        commentList: [],
+      }
+    },
+    methods: {
+      // 改变督办状态
+      changeStatus(type, status) {
+        this.$confirm('确定修改督办状态?', '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning'
+        }).then(async () => {
+          this.selfVisible = false
+          let data = {
+            taskId: this.theTask.id + '',
+            type: type,
+            oldStatus: this.theTask.taskStatus,
+            nowStatus: status
+          }
+          const { msg } = await taskApi.updateTaskStatus(data)
+          this.$baseMessage(msg, 'success', 'vab-hey-message-success')
+          if (this.doRefresh) {
+            this.doRefresh()
+          }
+        })
+      },
+      // 刷新进展和日志信息
+      doRefreshProgressAndLogs() {
+        this.getProgressList()
+        this.getLogList()
+      },
+      // 添加进展
+      addProgress() {
+        this.progressAddDialogVisible = true
+      },
+      // 查看附件
+      showFile(path) {
+        const a = document.createElement('a')
+        a.href = path // 文件链接
+        a.download = path // 文件名,跨域资源download无效
+        a.click()
+        a.remove()
+      },
+      // 新增评论
+      addComment() {
+        this.commentAddDialogVisible = true
+      },
+      // 打开弹窗
+      open() {
+        this.rightInfo = 'log'
+        // 获取数据信息
+        this.getProgressList()
+        this.getLogList()
+        this.getCommentList()
+        this.teamNames = ''
+        if (this.theTask.ownerUserId != '') {
+          let ids = this.theTask.ownerUserId.split(',')
+          for (let id of ids) {
+            if (this.teamNames == '') {
+              this.teamNames = this.userMap[parseInt(id)]
+            } else {
+              this.teamNames += ',' + this.userMap[parseInt(id)]
+            }
+          }
+        }
+      },
+      // 获取进展信息
+      getProgressList() {
+        this.progressList = []
+        taskApi.getTaskProgressList({ taskId: this.theTask.id + '' })
+          .then(res => {
+            if (res.data.list) {
+              this.progressList = res.data.list
+            }
+          }).catch(err => {
+            console.error(err)
+          })
+      },
+      // 获取日志信息
+      getLogList() {
+        this.logList = []
+        taskApi.getTaskLogList({ taskId: this.theTask.id + '' })
+          .then(res => {
+            if (res.data.list) {
+              this.logList = res.data.list
+            }
+          }).catch(err => {
+            console.error(err)
+          })
+      },
+      // 获取评论信息
+      getCommentList() {
+        this.commentList = []
+        taskApi.getTaskCommentList({ taskId: this.theTask.id + '' })
+          .then(res => {
+            if (res.data.list) {
+              this.commentList = res.data.list
+            }
+          }).catch(err => {
+            console.error(err)
+          })
+      },
+      // 右侧操作栏改变状态
+      changeRightInfo(info) {
+        this.rightInfo = info
+      },
+    }
+  }
+</script>

+ 357 - 0
src/views/plat/task/index.vue

@@ -0,0 +1,357 @@
+<template>
+  <div class="user-management-container">
+    <el-row :gutter="20">
+      <el-col :span="2">
+        <span>操作类型</span>
+        <div style="margin-top: 25px; cursor: pointer;" @click="search('1')">
+          <i class="el-icon-message-solid" style="margin-right: 10px;"></i>我的待办({{statisticsForm.toDoNumber}})
+        </div>
+        <div style="margin-top: 20px; cursor: pointer;" @click="search('2')">
+          <i class="el-icon-video-play" style="margin-right: 10px;"></i>我发起的({{statisticsForm.createNumber}})
+        </div>
+        <div style="margin-top: 20px; cursor: pointer;" @click="search('3')">
+          <i class="el-icon-folder-checked" style="margin-right: 10px;"></i>我处理的({{statisticsForm.completedNumber}})
+        </div>
+        <div style="margin-top: 50px">督办类型</div>
+        <div style="margin-top: 20px; cursor: pointer;" @click="searchType('')">
+          全部
+        </div>
+        <div style="margin-top: 20px; cursor: pointer;" @click="searchType(item.dictValue)" v-for="item in types" :key="item.dictCode">
+          {{item.dictLabel}}
+        </div>
+      </el-col>
+      <el-col :span="22">
+        <el-row :gutter="10" style="margin-bottom:10px">
+          <el-col :span="4">
+            <el-input v-model.trim="queryForm.taskTitle" clearable placeholder="请输入督办标题" />
+          </el-col>
+          <el-col :span="12">
+            <el-button icon="el-icon-search" type="primary" @click="queryData">查询</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" type="primary" @click="handleAdd">添加</el-button>
+          </vab-query-form-left-panel>
+          <vab-query-form-right-panel :span="12">
+            <el-button icon="el-icon-download" @click="exportData"></el-button>
+            <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-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 v-if="item.prop === 'taskType'">
+                {{ typeMap[row.taskType] }}
+              </span>
+              <span v-else-if="item.prop === 'taskStatus'">
+                {{ row.taskStatus === '10' ? '进行中' : '关闭' }}
+              </span>
+              <span v-else-if="item.prop === 'isOverdue'">
+                {{ row.isOverdue === '10' ? '否' : '是' }}
+              </span>
+              <span v-else-if="item.prop === 'mainUserId' || item.prop === 'supervisorUserId'">
+                {{ userMap[row[item.prop]] }}
+              </span>
+              <span v-else-if="item.prop === 'taskStartDate' || item.prop === 'taskEndDate' || item.prop === 'createdTime'">
+                {{ parseTime(row[item.prop]) }}
+              </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="showDetail(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" />
+      </el-col>
+    </el-row>
+    <!-- 新建督办 -->
+    <taskAdd :users="users" :types="types" :selfVisible.sync="addDialogVisible" :doRefresh="doRefresh"></taskAdd>
+    <!-- 查看详情 -->
+    <taskDetail :theTask="theTask" :userMap="userMap" :typeMap="typeMap" :selfVisible.sync="detailDialogVisible" :doRefresh="doRefresh"></taskDetail>
+  </div>
+</template>
+
+<script>
+  import taskApi from '@/api/plat/task'
+  import userApi from '@/api/system/user'
+  import dictApi from '@/api/system/dict'
+  import taskAdd from './components/TaskAdd.vue'
+  import taskDetail from './components/TaskDetail.vue'
+  import TableTool from '@/components/table/TableTool'
+  import downloadFileByByte from '@/utils/base64ToFile'
+
+  export default {
+    name: 'Task',
+    components: { taskAdd, taskDetail, TableTool },
+    data() {
+      return {
+        // 各督办数量统计
+        statisticsForm: {
+          toDoNumber: 0,
+          createNumber: 0,
+          completedNumber: 0
+        },
+        // 督办详情查看
+        detailDialogVisible: false,
+        // 新建弹窗控制
+        addDialogVisible: false,
+        // 展示的督办数据
+        theTask: {},
+        list: [],
+        listLoading: true,
+        layout: 'total, sizes, prev, pager, next, jumper',
+        total: 0,
+        queryForm: {
+          pageNum: 1,
+          pageSize: 10,
+          taskTitle: undefined,
+          taskType: undefined,
+          taskStatus: undefined,
+          mySelf: undefined,
+          isMain: undefined,
+        },
+        // 用户信息
+        userMap: {},
+        users: [],
+        // 类型信息
+        typeMap: {},
+        types: [],
+        // 自定义列表
+        checkList: [],
+        columns: [
+          {
+            label: '督办标题',
+            width: 'auto',
+            prop: 'taskTitle',
+            sortable: true,
+            disableCheck: true,
+          },
+          {
+            label: '督办类型',
+            width: 'auto',
+            prop: 'taskType',
+            sortable: true,
+          },
+          {
+            label: '状态',
+            width: 'auto',
+            prop: 'taskStatus',
+            sortable: true,
+          },
+          {
+            label: '超期',
+            width: 'auto',
+            prop: 'isOverdue',
+            sortable: true,
+          },
+          {
+            label: '督办说明',
+            width: 'auto',
+            prop: 'taskDesc',
+            sortable: true,
+          },
+          {
+            label: '关联对象',
+            width: 'auto',
+            prop: 'targetName',
+            sortable: true,
+          },
+          {
+            label: '负责人',
+            width: 'auto',
+            prop: 'mainUserId',
+            sortable: true,
+          },
+          {
+            label: '督办人',
+            width: 'auto',
+            prop: 'supervisorUserId',
+            sortable: true,
+          },
+          {
+            label: '开始时间',
+            width: 'auto',
+            prop: 'taskStartDate',
+            sortable: true,
+          },
+          {
+            label: '结束时间',
+            width: 'auto',
+            prop: 'taskEndDate',
+            sortable: true,
+          },
+          {
+            label: '创建时间',
+            width: 'auto',
+            prop: 'createdTime',
+            sortable: true,
+          },
+        ],
+      }
+    },
+    computed: {
+      height() {
+        return this.$baseTableHeight(1)
+      },
+      finallyColumns() {
+        return this.columns.filter((item) =>
+          this.checkList.includes(item.label)
+        )
+      },
+    },
+    async created() {
+      this.statistics()
+      await this.initData()
+      this.fetchData()
+    },
+    methods: {
+      // 刷新表数据和数量统计
+      doRefresh() {
+        this.fetchData()
+        this.statistics()
+      },
+      // 统计各类型督办数量
+      statistics() {
+        taskApi.statisticsTaskNumber()
+          .then(res => {
+            if (res.data.list) {
+              this.statisticsForm = res.data.list
+            }
+          }).catch(err => {
+            console.error()
+          })
+      },
+      // 数据导出
+      exportData() {
+        let exportFrom = JSON.parse(JSON.stringify(this.queryForm))
+        exportFrom.columns = this.finallyColumns.map(item => item.label)
+        taskApi.exportTasks(exportFrom)
+          .then(res => {
+            if (res.data.list.content) {
+              downloadFileByByte(res.data.list.content, '督办数据.xlsx')
+            }
+          }).catch(err => {
+            console.error(err)
+          })
+        console.info(this.finallyColumns.map(item => item.label))
+      },
+      // 重置查询数据
+      reset() {
+        this.queryForm.pageNum = 1
+        this.queryForm.pageSize = 10
+        this.queryForm.taskTitle = undefined
+        this.queryForm.taskType = undefined
+        this.queryForm.taskStatus = undefined
+        this.queryForm.mySelf = undefined
+        this.queryForm.isMain = undefined
+        this.queryData()
+      },
+      // 左侧操作栏搜索
+      search(type) {
+        this.queryForm.taskStatus = undefined
+        this.queryForm.mySelf = undefined
+        this.queryForm.isMain = undefined
+        if (type == '1') {
+          this.queryForm.taskStatus = '10'
+          this.queryForm.isMain = '1'
+        }
+        if (type == '2') {
+          this.queryForm.mySelf = '1'
+        }
+        if (type == '3') {
+          this.queryForm.taskStatus = '20'
+          this.queryForm.isMain = '1'
+        }
+        this.queryData()
+      },
+      // 督办类型搜索
+      searchType(type) {
+        this.queryForm.taskType = type
+        this.queryData()
+      },
+      // 初始化数据
+      async initData() {
+        await dictApi.getDictDataList({dictType: 'TaskType'})
+          .then(res => {
+            if (res.data.list) {
+              this.types = res.data.list
+              for (let type of this.types) {
+                this.typeMap[type.dictValue] = type.dictLabel
+              }
+            }
+          }).catch(err => {
+            console.error(err)
+          })
+        await userApi.getList()
+          .then(res => {
+            if (res.data.list) {
+              this.users = res.data.list
+              for (let user of this.users) {
+                this.userMap[user.id] = user.userName
+              }
+            }
+          }).catch(err => {
+            console.error(err)
+          })
+      },
+      // 显示详情数据
+      showDetail(row) {
+        this.theTask = JSON.parse(JSON.stringify(row))
+        this.detailDialogVisible = true
+      },
+      // 处理新增
+      handleAdd() {
+        this.addDialogVisible = true
+      },
+      // 更换页数据大小
+      handleSizeChange(val) {
+        this.queryForm.pageSize = val
+        this.fetchData()
+      },
+      // 更换当前页
+      handleCurrentChange(val) {
+        this.queryForm.pageNum = val
+        this.fetchData()
+      },
+      // 查询
+      queryData() {
+        this.queryForm.pageNum = 1
+        this.fetchData()
+      },
+      // 获取数据
+      fetchData() {
+        this.listLoading = true
+        this.list = []
+        taskApi.getTaskList(this.queryForm)
+          .then(res => {
+            if (res.data.list) {
+              this.list = res.data.list
+            }
+            this.total = res.data.total
+            this.listLoading = false
+          }).catch(err => {
+            this.listLoading = false
+            console.error(err)
+          })
+      },
+    },
+  }
+</script>