浏览代码

任务管理修改

ysc 2 年之前
父节点
当前提交
8dff1a0c47

+ 9 - 0
src/api/task/task.js

@@ -97,3 +97,12 @@ export function confirmComment(parmas) {
     params: parmas
   })
 }
+
+//查询子任务列表
+export function childrenTask(parmas) {
+  return request({
+    url: '/task/task/children',
+    method: 'get',
+    params: parmas
+  })
+}

+ 1 - 1
src/views/task/components/project.vue

@@ -267,7 +267,7 @@ export default {
     },
     /** 详情按钮操作 */
     handleDetail(row) {
-      this.$router.push({path: '/task/projectView', query: {projectId: row.id}});
+      this.$router.push({path: '/task/projectTable', query: {projectId: row.id}});
     }
 
   }

+ 124 - 0
src/views/task/projectTable.vue

@@ -0,0 +1,124 @@
+<template>
+  <div class="app-container">
+    <el-form v-model="queryParams" size="mini" :inline="true">
+      <el-form-item>
+        <el-radio-group v-model="viewType" size="mini">
+          <el-radio-button label="表格视图"></el-radio-button>
+          <el-radio-button label="甘特图视图"></el-radio-button>
+        </el-radio-group>
+      </el-form-item>
+      <el-form-item label="项目">
+        <el-select v-model="queryParams.projectIds" multiple collapse-tags size="mini" @change="selectProjectChange">
+          <el-option v-for="(item,index) in projectList" :label="item.projectName" :key="index"
+                     :value="item.id"></el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-tag type="info" size="mini"><i class="el-icon-info"/>请选择要查看的项目</el-tag>
+      </el-form-item>
+    </el-form>
+
+    <el-table :data="tableData"
+              border
+              :span-method="objectSpanMethod"
+              size="mini">
+      <el-table-column label="项目" prop="projectName">
+        <template slot-scope="scope">
+          <div>{{ scope.row.projectName }}</div>
+          <el-tag size="mini">{{ '进度:' + scope.row.progressValue + '%' }}</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="模块" prop="moduleName"/>
+      <el-table-column label="任务名称" prop="taskName" min-width="150"/>
+      <el-table-column label="进度(%)" prop="progressValue" width="70"/>
+      <el-table-column label="周期" width="160">
+        <template slot-scope="scope">
+          <span>{{ scope.row.beginDate + '至' + scope.row.endDate }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="负责人" prop="executorName"/>
+      <el-table-column label="预计工时(小时)" prop="planHours" width="110"/>
+      <el-table-column label="已用工时(小时)" prop="useHours" width="110"/>
+    </el-table>
+
+  </div>
+</template>
+
+<script>
+import {getProjectTree, listProject, projectTaskList} from "@/api/task/project";
+
+export default {
+  name: "projectView",
+  data() {
+    return {
+      viewType: '表格视图',
+      queryParams: {
+        pageSize: 1000,
+        pageNum: 1,
+        projectIds: []
+      },
+      tableData: [],
+      projectList: []
+    }
+  },
+  created() {
+    let queryProjectId = this.$route.query.projectId;
+    if (queryProjectId) {
+      this.queryParams.projectIds.push(parseInt(queryProjectId))
+    }
+    this.initData()
+  },
+  methods: {
+    initData() {
+      listProject({type: '2'}).then(res => {
+        this.projectList = res.data
+        this.getList()
+      })
+    },
+    selectProjectChange(val) {
+      this.getList()
+    },
+    getList() {
+      if (this.queryParams.projectIds.length === 0) {
+        return
+      }
+      projectTaskList(this.queryParams).then(res => {
+          this.tableData = res.data
+        }
+      );
+    },
+    objectSpanMethod({row, column, rowIndex, columnIndex}) {
+      if (columnIndex === 0) {
+        if (row.moduleNum) {
+          return {
+            rowspan: row.moduleNum,
+            colspan: 1
+          };
+        } else {
+          return {
+            rowspan: 0,
+            colspan: 0
+          };
+        }
+      }
+      if (columnIndex === 1) {
+        if (row.taskNum) {
+          return {
+            rowspan: row.taskNum,
+            colspan: 1
+          };
+        } else {
+          return {
+            rowspan: 0,
+            colspan: 0
+          };
+        }
+      }
+    }
+  }
+}
+</script>
+
+<style scoped>
+
+</style>

+ 344 - 72
src/views/task/projectView.vue

@@ -1,119 +1,391 @@
 <template>
   <div class="app-container">
-    <el-form v-model="queryParams" size="mini" :inline="true">
-      <el-form-item>
-        <el-radio-group v-model="viewType" size="mini">
-          <el-radio-button label="表格视图"></el-radio-button>
-          <el-radio-button label="甘特图视图"></el-radio-button>
-        </el-radio-group>
+    <el-form :model="queryParams" ref="queryForm" size="mini" :inline="true">
+      <el-form-item label="所属项目" prop="projectId">
+        <el-cascader
+          :options="projectTree"
+          @change="selectProjectChange"
+          :props="{ expandTrigger: 'hover',value:'id',label:'name', checkStrictly: true }"
+          collapse-tags
+          :show-all-levels="false" clearable></el-cascader>
       </el-form-item>
-      <el-form-item label="项目">
-        <el-select v-model="queryParams.projectIds" multiple collapse-tags size="mini" @change="selectProjectChange">
-          <el-option v-for="(item,index) in projectList" :label="item.projectName" :key="index"
-                     :value="item.id"></el-option>
-        </el-select>
+      <el-form-item label="日期" prop="month">
+        <el-date-picker
+          v-model="queryParams.rangeDate"
+          type="daterange"
+          value-format="yyyy-MM-dd"
+          @change="getList"
+          range-separator="至"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          :clearable="false"
+          style="width: 220px">
+        </el-date-picker>
       </el-form-item>
-      <el-form-item>
-        <el-tag type="info" size="mini"><i class="el-icon-info"/>请选择要查看的项目</el-tag>
+      <el-form-item prop="status">
+        <el-radio-group v-model="queryParams.status" size="mini" @change="getList">
+          <el-radio-button
+            v-for="dict in dict.type.task_status"
+            :key="dict.value"
+            :label="dict.value"
+          >{{ dict.label }}
+          </el-radio-button>
+        </el-radio-group>
       </el-form-item>
     </el-form>
 
-    <el-table :data="tableData"
-              border
-              :span-method="objectSpanMethod"
-              size="mini">
-      <el-table-column label="项目" prop="projectName"/>
-      <el-table-column label="模块" prop="moduleName"/>
-      <el-table-column label="任务名称" prop="taskName" min-width="150"/>
-      <el-table-column label="进度" prop="progressValue" width="60"/>
-      <el-table-column label="周期" width="160">
+    <el-table
+      class="view-table"
+      :data="tableData"
+      size="mini"
+      @cell-mouse-enter="cellMouseEnter"
+      @cell-mouse-leave="cellMouseLeave"
+      :cell-style="cellStyle"
+      @cell-click="cellClick"
+      @cell-dblclick="cellDbClick"
+      @row-click="rowClick"
+      height="calc(100vh - 140px)"
+      border>
+      <el-table-column fixed prop="id" label="编号" align="center" width="50"/>
+      <el-table-column fixed prop="taskName" label="任务名称" align="center" class="task-name-cell" width="180"/>
+      <el-table-column fixed prop="executorName" label="主负责人" align="center" width="90">
+        <template slot-scope="scope">
+          <div style="line-height: 15px">{{ scope.row.executorName }}</div>
+          <el-tag size="mini" :type="statusMap[scope.row.status].type">
+            {{ statusMap[scope.row.status].name + ' ' + scope.row.progressValue + '%' }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column fixed prop="" label="上月延期" width="40">
         <template slot-scope="scope">
-          <span>{{ scope.row.beginDate + '~' + scope.row.endDate }}</span>
+          <span v-if="scope.row.formLastMonth" style="color: red">是</span>
+        </template>
+      </el-table-column>
+      <el-table-column align="center" v-for="item in tableHeaders" :key="item.day" :prop="item.day" width="37">
+        <template slot="header" slot-scope="scope">
+          <div style="width:28px;font-size: 10px">{{ item.day }}</div>
+          <div v-if="item.week=='周六'" style="width: 24px;font-size: 12px;color: #1c84c6">{{ item.week }}</div>
+          <div v-else-if="item.week=='周日'" style="width: 24px;font-size: 12px;color: #1c84c6">{{ item.week }}</div>
+          <div v-else-if="item.week=='今日'" style="width: 24px;font-size: 12px;color: #008000">{{ item.week }}</div>
+          <div v-else style="width: 24px;font-size: 12px">{{ item.week }}</div>
+        </template>
+        <template slot-scope="scope">
+          <span v-if="scope.row[item.day].comment" class="comment-badge"></span>
+          <el-popover
+            v-if="scope.row[item.day].value!=''"
+            placement="top"
+            width="680"
+            trigger="click">
+            <el-table :data="feedbacks" border size="mini">
+              <el-table-column width="75" label="反馈状态">
+                <template slot-scope="scope">
+                  <div>{{ getFeedbackTypeName(scope.row.feedbackType) }}</div>
+                </template>
+              </el-table-column>
+              <el-table-column width="60" property="userName" label="反馈人"></el-table-column>
+              <el-table-column width="55" label="完成度">
+                <template slot-scope="scope">
+                  <div>{{ scope.row.feedbackType === '4' ? '' : scope.row.value + '%' }}</div>
+                </template>
+              </el-table-column>
+              <el-table-column width="110" property="createTime" label="反馈时间">
+                <template slot-scope="scope">
+                  <div>{{ parseTime(scope.row.createTime) }}</div>
+                </template>
+              </el-table-column>
+              <el-table-column label="反馈备注">
+                <template slot-scope="scope">
+                  <div>
+                    <span>{{ scope.row.description }}</span>
+                    <span v-for="(file,index) in scope.row.fileList">
+                                <a :href="file.url" style="color: darkgreen">
+                                  <span v-html="file.fileName"></span>
+                                </a>
+                              </span>
+                  </div>
+                </template>
+              </el-table-column>
+            </el-table>
+            <div slot="reference" style="font-size: 10px">{{ scope.row[item.day].value }}</div>
+          </el-popover>
         </template>
       </el-table-column>
-      <el-table-column label="负责人" prop="executorName"/>
-      <el-table-column label="预计工时(小时)" prop="planHours"/>
-      <el-table-column label="已用工时(小时)" prop="useHours"/>
     </el-table>
 
+    <el-dialog title="添加评论" :visible.sync="open" width="680px" append-to-body :close-on-click-modal="false">
+      <el-form ref="form" :model="form" :rules="rules" size="mini" label-width="100px">
+        <el-form-item label="任务名称">
+          <div>{{ form.taskName }}</div>
+        </el-form-item>
+        <el-form-item label="所属项目">
+          <div>{{ form.projectName }}</div>
+        </el-form-item>
+        <el-form-item label="反馈时间">
+          <div>{{ form.feedbackDate }}</div>
+        </el-form-item>
+        <el-form-item label="附件" prop="fileUrl">
+          <file-upload @getFileUrl="getFileUrl" @removeFile="removeFile"></file-upload>
+        </el-form-item>
+        <el-form-item label="描述" prop="description">
+          <el-input v-model="form.description" type="textarea" autosize/>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm" size="mini">确 定</el-button>
+        <el-button @click="cancel" size="mini">取 消</el-button>
+      </div>
+    </el-dialog>
+
+    <el-dialog title="任务详情" :visible.sync="openDetail" width="680px" append-to-body :close-on-click-modal="false">
+      <task-detail :detailForm="detailForm"></task-detail>
+    </el-dialog>
+
   </div>
 </template>
 
 <script>
-import {getProjectTree, listProject, projectTaskList} from "@/api/task/project";
+import {getTask, listView, addTaskFeedback, getFeedbackList, confirmComment} from "@/api/task/task";
+import {getProjectList, getProjectTree} from "@/api/task/project";
+import DateUtil from "@/utils/date"
+import TaskDetail from "./components/taskDetail"
+import FileUpload from "@/components/FileUpload"
+
+const statusMap = {
+  '0': {name: '待查看', type: 'info'},
+  '1': {name: '未开始', type: 'info'},
+  '2': {name: '进行中', type: ''},
+  '3': {name: '延期', type: 'danger'},
+  '4': {name: '完成', type: 'success'},
+  '5': {name: '终止', type: 'warning'}
+}
 
 export default {
-  name: "projectView",
+  components: {TaskDetail, FileUpload},
+  dicts: ['task_status',],
   data() {
     return {
-      viewType: '表格视图',
       queryParams: {
-        pageSize: 1000,
-        pageNum: 1,
-        projectIds: []
+        projectId: undefined,
+        month: undefined,
+        status: '0'
       },
+      projectTree: [],
+      tableHeaders: [],
       tableData: [],
-      projectList: []
+      statusMap: statusMap,
+      open: false,
+      form: {},
+      feedbacks: [],
+      rules: {
+        description: [
+          {required: true, message: "描述不能为空", trigger: "blur"},
+          {min: 3, max: 500, message: '长度在 3 到 500 个字符', trigger: 'blur'}
+        ]
+      },
+      openDetail: false,
+      detailForm: {},
+      cellColorMap: {
+        '1': '#fefeff',
+        '2': '#a6a9ad',
+        '3': '#f56c6c',
+        '4': '#67c23a',
+        '5': '#e6a23c',
+      }
     }
   },
   created() {
-    let queryProjectId = this.$route.query.projectId;
-    if (queryProjectId) {
-      this.queryParams.projectIds.push(parseInt(queryProjectId))
-    }
-    this.initData()
+    this.initData();
   },
   methods: {
     initData() {
-      listProject({type: '2'}).then(res => {
-        this.projectList = res.data
-        this.getList()
+      let today = new Date();
+      this.queryParams.rangeDate = [DateUtil.beforeDay(today, 13), DateUtil.afterDay(today, 15)]
+      this.getList()
+      getProjectTree().then(res => {
+        this.projectTree = res.data
       })
     },
     selectProjectChange(val) {
+      this.queryParams.projectId = val[val.length - 1]
       this.getList()
     },
     getList() {
-      if (this.queryParams.projectIds.length === 0) {
+      if (this.queryParams.rangeDate && this.queryParams.rangeDate.length === 2) {
+        this.queryParams.startDate = this.queryParams.rangeDate[0]
+        this.queryParams.endDate = this.queryParams.rangeDate[1]
+      }
+      let dayDiff = DateUtil.dayDiff(this.queryParams.startDate, this.queryParams.endDate);
+      if (dayDiff > 366) {
+        this.$message.warning("时间跨度不可超过一年")
         return
       }
-      projectTaskList(this.queryParams).then(res => {
-          this.tableData = res.data
-        }
-      );
-    },
-    objectSpanMethod({row, column, rowIndex, columnIndex}) {
-      if (columnIndex === 0) {
-        if (row.moduleNum) {
-          return {
-            rowspan: row.moduleNum,
-            colspan: 1
-          };
-        } else {
-          return {
-            rowspan: 0,
-            colspan: 0
-          };
-        }
+      listView(this.queryParams).then(res => {
+        this.tableHeaders = res.data.headers
+        this.tableData = res.data.data
+      })
+    },
+    cellMouseEnter(row, column, cell, event) {
+      if (column.property != 'taskName') {
+        return
       }
-      if (columnIndex === 1) {
-        if (row.taskNum) {
-          return {
-            rowspan: row.taskNum,
-            colspan: 1
-          };
-        } else {
-          return {
-            rowspan: 0,
-            colspan: 0
-          };
+      cell.style.color = '#306FB1';
+      cell.style.textDecoration = 'underline';
+    },
+    cellMouseLeave(row, column, cell, event) {
+      if (column.property != 'taskName') {
+        return
+      }
+      cell.style.color = '#606266';
+      cell.style.textDecoration = 'none';
+    },
+    cellStyle({row, column, rowIndex, columnIndex}) {
+      let style = {
+        fontSize: '12px',
+        padding: '2px 0'
+      }
+      if (row[column.property]) {
+        style.background = this.cellColorMap[row[column.property].color]
+      }
+      return style
+    },
+
+    cellClick(row, column, cell, event) {
+      if (!row[column.property].value || row[column.property].value === '') {
+        return;
+      }
+      let date = row[column.property].date
+      if (DateUtil.unix(date) > DateUtil.unix()) {
+        return;
+      }
+      getFeedbackList(row.id, date).then(res => {
+        this.feedbacks = res.data.map(item => {
+          item['executor'] = row.executor
+          return item
+        })
+      })
+    },
+    cellDbClick(row, column, cell, event) {
+      let feedbackDate = DateUtil.getFeedBackDate(this.queryParams.rangeDate, column.property)
+      this.form = {
+        id: undefined,
+        taskName: row.taskName,
+        taskId: row.id,
+        projectName: row.projectName,
+        feedbackDate: feedbackDate,
+        feedbackType: '4',
+        fileUrl: undefined,
+        description: undefined
+      };
+      this.open = true
+    },
+    rowClick(row, column, event) {
+      if (column.property != "taskName") {
+        return
+      }
+      getTask(row.id).then(res => {
+        this.detailForm = res.data
+        this.openDetail = true
+      })
+    },
+    feedbackTypeChange(val) {
+      if (val === '2') {
+        this.form.value = 100
+      } else {
+        this.form.value = undefined
+      }
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: undefined,
+        taskName: undefined,
+        taskId: undefined,
+        projectName: undefined,
+        feedbackDate: undefined,
+        feedbackType: undefined,
+        value: undefined,
+        hours: undefined,
+        fileUrl: undefined,
+        description: undefined
+      };
+      this.resetForm("form");
+    },
+    getFileUrl(val) {
+      this.form['files'] = this.form.files || []
+      this.form.files.push(val)
+    },
+    removeFile(val) {
+      this.form.files.splice(val)
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          this.form.fileUrl = JSON.stringify(this.form.files)
+          addTaskFeedback(this.form).then(res => {
+            this.$message.success("反馈成功");
+            this.open = false;
+            this.getList();
+          });
         }
+      })
+    },
+
+    getFeedbackTypeName(type) {
+      if (type === '1') {
+        return '进度反馈'
+      } else if (type === '2') {
+        return '完成'
+      } else if (type === '3') {
+        return '终止'
+      } else if (type === '4') {
+        return '评论'
       }
+      return '审批'
+    },
+    /** 确认收到评论 */
+    confirmComment(row) {
+      confirmComment({feedbackId: row.id}).then(res => {
+        if (res.code === '2000') {
+          this.getList()
+        }
+      })
     }
   }
 }
 </script>
 
-<style scoped>
+<style scoped lang="scss">
+::v-deep.el-table .cell {
+  padding-left: 5px;
+  padding-right: 5px;
+}
+
+::v-deep.el-table th.el-table__cell > .cell {
+  padding-left: 5px;
+  padding-right: 5px;
+}
+
+.el-form-item--mini.el-form-item, .el-form-item--small.el-form-item {
+  margin-bottom: 10px;
+}
+
+.comment-badge {
+  height: 10px;
+  width: 10px;
+  border-radius: 5px;
+  background-color: #409eff;
+  position: absolute;
+  top: 1px;
+  right: 1px;
+  font-size: 8px;
+  color: white;
+  vertical-align: text-top;
+}
 
 </style>

+ 12 - 3
src/views/task/task.vue

@@ -56,8 +56,12 @@
         </el-row>
 
         <el-table :data="taskList"
+                  size="mini"
                   @row-click="rowClick"
-                  size="mini">
+                  row-key="id"
+                  lazy
+                  :load="load"
+                  :tree-props="{children: 'children', hasChildren: 'hasChildren'}">
           <el-table-column label="任务编号" prop="id" width="80"/>
           <el-table-column label="任务名称" prop="taskName" min-width="150" :show-overflow-tooltip="true"/>
           <el-table-column label="执行(负责)人" prop="executorName"/>
@@ -263,7 +267,7 @@ import {
   addTask,
   updateTask,
   auditTask,
-  splitTask
+  splitTask, childrenTask
 } from "@/api/task/task";
 import {getProjectList, listProject} from "@/api/task/project";
 import {getDeptUserTree} from "@/api/system/user";
@@ -287,7 +291,7 @@ const statusMap = {
 export default {
   name: "Task",
   components: {Project, TaskDetail, DeptUserTree, FileUpload, Treeselect},
-  dicts:['task_status'],
+  dicts: ['task_status'],
   data() {
     return {
       projectOptions: [],
@@ -425,6 +429,11 @@ export default {
         this.detailTitle = "任务详情";
       })
     },
+    load(row, treeNode, resolve) {
+      childrenTask({taskId: row.id}).then(res => {
+        resolve(res.data)
+      })
+    },
     /** 转换项目数据结构 */
     normalizer(node) {
       if (node.children && !node.children.length) {

+ 12 - 11
src/views/task/view.vue

@@ -1,15 +1,15 @@
 <template>
   <div class="app-container">
     <el-form :model="queryParams" ref="queryForm" size="mini" :inline="true">
-      <el-form-item label="所属项目" prop="projectId">
-        <el-cascader
-          v-model="queryParams.projectId"
-          :options="projectTree"
-          @change="selectProjectChange"
-          :props="{ expandTrigger: 'hover',value:'id',label:'name', checkStrictly: true }"
-          collapse-tags
-          :show-all-levels="false"></el-cascader>
-      </el-form-item>
+      <!--      <el-form-item label="所属项目" prop="projectId">-->
+      <!--        <el-cascader-->
+      <!--          v-model="queryParams.projectId"-->
+      <!--          :options="projectTree"-->
+      <!--          @change="selectProjectChange"-->
+      <!--          :props="{ expandTrigger: 'hover',value:'id',label:'name', checkStrictly: true }"-->
+      <!--          collapse-tags-->
+      <!--          :show-all-levels="false"></el-cascader>-->
+      <!--      </el-form-item>-->
       <el-form-item label="日期" prop="month">
         <el-date-picker
           v-model="queryParams.month"
@@ -207,7 +207,8 @@ export default {
       queryParams: {
         projectId: undefined,
         month: undefined,
-        status: '0'
+        status: '0',
+        userId: undefined
       },
       projectTree: [],
       tableHeaders: [],
@@ -265,6 +266,7 @@ export default {
         this.$message.warning("时间跨度不可超过一年")
         return
       }
+      this.queryParams.userId = this.userId
       listView(this.queryParams).then(res => {
         this.tableHeaders = res.data.headers
         this.tableData = res.data.data
@@ -329,7 +331,6 @@ export default {
         fileUrl: undefined,
         description: undefined
       };
-      console.log(this.form);
       this.open = true
     },
     rowClick(row, column, event) {