Procházet zdrojové kódy

Merge branch 'dev_drawer' into dev

Cyt928 před 5 roky
rodič
revize
fe7a9d3afb

+ 1 - 0
src/assets/icon/edit.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1616380247069" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1139" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M864 479.91a32 32 0 0 0-32 32V800a32 32 0 0 1-32 32H224a32 32 0 0 1-32-32V224a32 32 0 0 1 32-32h286.4a32 32 0 0 0 0-64H224a96.11 96.11 0 0 0-96 96v576a96.1 96.1 0 0 0 96 96h576a96.11 96.11 0 0 0 96-96V511.91a32 32 0 0 0-32-32z" p-id="1140" fill="#8a8a8a"></path><path d="M356.43 660.33a6.41 6.41 0 0 0 6.32 7.31 7.26 7.26 0 0 0 0.92-0.07l150-21.43 362-362a64 64 0 0 0 0-90.51l-45.27-45.29a64 64 0 0 0-90.51 0l-362 362z m81.76-119.78l346.89-346.94h0.06l45.26 45.26-346.95 346.94-52.8 7.54z" p-id="1141" fill="#8a8a8a"></path></svg>

+ 1 - 0
src/assets/icon/edit_hover.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1616380247069" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1139" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M864 479.91a32 32 0 0 0-32 32V800a32 32 0 0 1-32 32H224a32 32 0 0 1-32-32V224a32 32 0 0 1 32-32h286.4a32 32 0 0 0 0-64H224a96.11 96.11 0 0 0-96 96v576a96.1 96.1 0 0 0 96 96h576a96.11 96.11 0 0 0 96-96V511.91a32 32 0 0 0-32-32z" p-id="1140" fill="#707070"></path><path d="M356.43 660.33a6.41 6.41 0 0 0 6.32 7.31 7.26 7.26 0 0 0 0.92-0.07l150-21.43 362-362a64 64 0 0 0 0-90.51l-45.27-45.29a64 64 0 0 0-90.51 0l-362 362z m81.76-119.78l346.89-346.94h0.06l45.26 45.26-346.95 346.94-52.8 7.54z" p-id="1141" fill="#707070"></path></svg>

+ 0 - 156
src/ccccyt/Tree.vue

@@ -1,156 +0,0 @@
-<template>
-  <div class="tree-container">
-    <el-row :gutter="20" >
-        <TreeNode v-for="item in colList[0]" :title="item.title" :position-y="item.positionY" position-x="0" :key="item.id"></TreeNode>
-        <TreeNode v-for="item in colList[1]" :title="item.title" :position-y="item.positionY" :position-x="1" :key="item.id"></TreeNode>
-        <TreeNode v-for="item in colList[2]" :title="item.title" :position-y="item.positionY" :position-x="2" :key="item.id"></TreeNode>
-        <TreeNode v-for="item in colList[3]" :title="item.title" :position-y="item.positionY" :position-x="3" :key="item.id"></TreeNode>
-    </el-row>
-  </div>
-</template>
-
-<script>
-import TreeNode from "@/components/TreeNode";
-export default {
-name: "Tree",
-  components: {TreeNode},
-  data() {
-  return {
-    treeHeight: 4,
-    requirements: [{
-      deep: 1,
-      id: 1,
-      parent: null,
-      title: '这是标题',
-    },
-      {
-        deep: 2,
-        id: 2,
-        parent: 1,
-        title: '这是标题2',
-      },
-      {
-        deep: 2,
-        id: 3,
-        parent: 1,
-        priority: 0,
-        title: '这是标题3',
-      },
-      {
-        deep: 3,
-        id: 4,
-        parent: 3,
-        title: '这是标题4',
-      },
-      {
-        deep:4,
-        id:9,
-        parent: 4,
-        title: '这是标题9'
-      },
-      {
-        deep: 3,
-        id: 5,
-        parent: 3,
-        title: '这是标题5',
-      },
-      {
-        deep: 4,
-        id: 6,
-        parent: 5,
-        title: '这是标题6',
-      },
-      {
-        deep: 4,
-        id: 7,
-        parent: 5,
-        title: '这是标题7',
-      },
-      {
-        deep: 4,
-        id: 8,
-        parent: 5,
-        title: '这是标题8',
-      },
-    ],
-    tree: {},
-    nodeMap: {},
-    colList: [[],[],[],[]]
-  }
-  },
-  mounted() {
-    this.array2tree()
-    this.getList()
-    this.tree.positionY = 0
-    this.calculateContentHeight(this.tree)
-    this.calculateNodePositionY()
-    this.offsetPositionY()
-    console.warn(this.tree)
-  },
-  methods: {
-  array2tree(){
-    let treeMap = new Map()
-    this.requirements.forEach((item)=>{
-      treeMap.set(item.id,item)
-    })
-    this.nodeMap = treeMap
-    this.requirements.forEach((item)=>{
-      if(item.parent){
-        let tempParent = treeMap.get(item.parent)
-        if(!tempParent.childs){
-          tempParent.childs = []
-        }
-        tempParent.childs.push(item)
-      }else{
-        this.tree = item
-      }
-    })
-  },
-  getList(){
-    this.requirements.forEach((item)=>{
-      this.colList[item.deep-1].push(item)
-    })
-  },
-  calculateContentHeight(node){
-    if(!node.childs){
-      node.contentHeight = 1
-      return 1
-    }
-    node.contentHeight = node.childs.reduce((total,current)=>{
-      return total + this.calculateContentHeight(current)
-    },0)
-    return node.contentHeight
-  },
-  calculateNodePositionY() {
-    for(let i=1;i<4;i++){
-      let sum = 0
-      this.colList[i].forEach((item)=>{
-        item.positionY = this.nodeMap.get(item.parent).positionY + sum
-        sum = sum + item.contentHeight
-      })
-    }
-  },
-  offsetPositionY(){
-    for(let i=2;i>=0;i--){
-      this.colList[i].forEach((item)=>{
-        if(item.childs){
-          let length = item.childs.length
-          item.positionY = item.childs.reduce((total,current)=>{
-            return total+current.positionY/length
-          },0)
-        }
-      })
-
-    }
-  }
-  }
-}
-</script>
-
-<style scoped>
-.tree-container{
-  min-height: 300px;
-  width: 100%;
-  background-color: #ffffff;
-}
-</style>

+ 0 - 44
src/ccccyt/TreeNode.vue

@@ -1,44 +0,0 @@
-<template>
-  <div class="node-container" :style="nodeStyle">
-  <el-card class="node-card">
-    {{title}}
-  </el-card>
-  </div>
-</template>
-
-<script>
-export default {
-name: "TreeNode",
-props:{
-  title: String,
-  positionY: Number,
-  positionX: Number
-},
-computed:{
-  nodeStyle(){
-    return {
-      top: (this.positionY*120)+'px',
-      left: (300*this.positionX)+'px'
-    }
-  }
-}
-}
-</script>
-
-<style scoped>
-.node-container{
-  width: 300px;
-  height: 120px;
-  box-sizing: border-box;
-  position: absolute;
-  display: flex;
-  justify-content: center;
-  align-items: center;
-}
-.node-card{
-  width: 260px;
-  height: 100px;
-  background-color: #42b983;
-}
-
-</style>

+ 103 - 0
src/components/AddDrawerContent.vue

@@ -0,0 +1,103 @@
+<template>
+  <div class="drawer-content">
+    <el-form
+      ref="form"
+      :rules="rules"
+      :model="param"
+      label-width="80px"
+      style="width: 80%"
+      size="small">
+      <el-form-item label="标题" prop="title">
+        <el-input v-model="param.title"></el-input>
+      </el-form-item>
+      <el-form-item label="描述">
+        <el-input type="textarea"
+                  maxlength="120"
+                  show-word-limit v-model="param.description"></el-input>
+      </el-form-item>
+      <el-form-item label="类型" required>
+        <el-radio-group v-model="param.type">
+          <el-radio label="需求"></el-radio>
+          <el-radio label="任务"></el-radio>
+        </el-radio-group>
+      </el-form-item>
+      <el-form-item label="工作量">
+        <el-input-number  v-model="param.timeToTake" :min="0" :max="200"/> h
+      </el-form-item>
+      <el-form-item label="优先级">
+        <el-select placeholder="选择优先级" v-model="param.priority">
+          <el-option
+            v-for="item in  priorityOptions"
+            :key="item.value"
+            :value="item.value"
+            :label="item.label" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="经办人">
+        <el-select placeholder="选择经办人" v-model="param.userId">
+          <el-option
+            v-for="item in  userOptions"
+            :key="item.value"
+            :value="item.value"
+            :label="item.label" />
+        </el-select>
+      </el-form-item>
+      <el-form-item style="display: flex">
+        <el-button type="primary" @click="submitAdd">创建</el-button>
+        <el-button @click="cancelAdd">取消</el-button>
+      </el-form-item>
+    </el-form>
+
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'AddDrawerContent',
+  props: {
+    parent: Number,
+    priorityOptions: Array,
+    userOptions: Array,
+  },
+  data() {
+    return {
+      param: {
+        title: '',
+        description: '',
+        type: '',
+        timeToTake: null,
+        priority: null,
+        userId: null,
+      },
+      rules: {
+        title: [{
+          required: true, massage: '请输入标题', trigger: 'blur',
+        }],
+        type: [{
+          required: true, massage: '请选择类型', trigger: 'change',
+        }],
+      },
+    };
+  },
+  methods: {
+    submitAdd() {
+      // 调新增接口
+      this.$emit('add-success');
+    },
+    cancelAdd() {
+      this.$emit('add-cancel');
+    },
+  },
+};
+</script>
+
+<style scoped>
+.drawer-content{
+  width: 100%;
+  padding: 10px;
+  display: flex;
+  flex-direction: column;
+  justify-content: flex-start;
+  align-items: flex-start;
+}
+</style>

+ 381 - 0
src/components/TaskDrawerContent.vue

@@ -0,0 +1,381 @@
+<template>
+  <div class="drawer-content">
+   <el-row class="entry-row">
+     <div class="node-type">{{type}}</div>
+     <h3>{{title}}</h3>
+     <div class="edit-icon" @click="editEntry('title')"></div>
+     <i class="el-icon-close"
+        style="margin-left: auto;margin-right: 30px; font-size: 20px;cursor: pointer"
+        @click="closeDrawer"></i>
+   </el-row>
+    <el-row class="entry-row" v-if="isEditing.title">
+      <el-input placeholder="请输入标题" size="mini" style="width: 300px;display: inline;" v-model="editedRequirement.title"/>
+      <el-button
+        style="margin-left: 5px"
+        type="primary"
+        size="mini"
+        icon="el-icon-edit"
+        circle
+        @click="submitEdit('title')" />
+      <el-button
+        style="margin-left: 5px"
+        type="danger"
+        size="mini"
+        icon="el-icon-close"
+        circle
+        @click="cancelEdit('title')" />
+    </el-row>
+    <!-- 描述 -->
+    <el-row class="entry-row" style="margin-top: 15px">
+      <h4>需求描述</h4>
+      <div class="edit-icon" @click="editEntry('description')"></div>
+    </el-row>
+    <el-row class="entry-row">
+      <div v-if="isEditing.description">
+        <el-input placeholder="请输入描述"
+                  type="textarea"
+                  maxlength="120"
+                  show-word-limit
+                  :rows="3" style="width: 300px;" v-model="editedRequirement.description" />
+        <br/>
+        <el-button
+          style="margin-left: 5px; margin-top: 5px"
+          type="primary"
+          size="mini"
+          icon="el-icon-edit"
+          circle
+          @click="submitEdit('description')" />
+        <el-button
+          style="margin-left: 5px; margin-top: 5px"
+          type="danger"
+          size="mini"
+          icon="el-icon-close"
+          circle
+          @click="cancelEdit('description')" />
+      </div>
+      <p v-else>{{description}}</p>
+    </el-row>
+    <!-- 状态、优先级等 -->
+    <el-row class="entry-row" style="margin-top: 15px">
+      <h4>详情</h4>
+    </el-row>
+    <el-row style="margin: 3px 20px;width: 100%">
+      <el-col :span="12">
+        <div class="detail-row">
+          <span>状态:</span>
+          <div  v-if="!isEditing.state"   @click="editEntry('state')" class="detail-value">
+            <span class="state-entry">{{state}}</span>
+            <div class="edit-icon" />
+          </div>
+          <div v-else>
+          <el-select style="width: 160px" size="mini" v-model="editedRequirement.state">
+            <el-option
+              v-for="item in stateOptions"
+              :key="item.value"
+              :value="item.value"
+              :label="item.value" />
+          </el-select>
+            <el-button
+              style="margin-left: 5px"
+              type="primary"
+              size="mini"
+              icon="el-icon-edit"
+              circle
+              @click="submitEdit('state')" />
+            <el-button
+              style="margin-left: 5px"
+              type="danger"
+              size="mini"
+              icon="el-icon-close"
+              circle
+              @click="cancelEdit('state')" />
+          </div>
+        </div>
+        <div class="detail-row">
+          <span>优先级:</span>
+          <div class="detail-value"  v-if="!isEditing.priority"   @click="editEntry('priority')">
+          <span class="state-entry">{{priority}}</span>
+            <div class="edit-icon" />
+          </div>
+          <div v-else>
+          <el-select style="width: 160px" size="mini" v-model="editedRequirement.priority">
+            <el-option
+              v-for="item in  priorityOptions"
+              :key="item.value"
+              :value="item.value"
+              :label="item.label" />
+          </el-select>
+            <el-button
+              style="margin-left: 5px"
+              type="primary"
+              size="mini"
+              icon="el-icon-edit"
+              circle
+              @click="submitEdit('priority')" />
+            <el-button
+              style="margin-left: 5px"
+              type="danger"
+              size="mini"
+              icon="el-icon-close"
+              circle
+              @click="cancelEdit('priority')" />
+          </div>
+        </div>
+        <div class="detail-row">
+          <span>经办人:</span>
+          <div class="detail-value"   v-if="!isEditing.userId"  @click="editEntry('userId')">
+          <span class="state-entry">{{userId}}</span>
+            <div class="edit-icon" />
+          </div>
+          <div v-else>
+          <el-select style="width: 160px" size="mini" v-model="editedRequirement.userId" >
+            <el-option
+              v-for="item in  userOptions"
+              :key="item.value"
+              :value="item.value"
+              :label="item.label" />
+          </el-select>
+            <el-button
+              style="margin-left: 5px"
+              type="primary"
+              size="mini"
+              icon="el-icon-edit"
+              circle
+              @click="submitEdit('userId')" />
+            <el-button
+              style="margin-left: 5px"
+              type="danger"
+              size="mini"
+              icon="el-icon-close"
+              circle
+              @click="cancelEdit('userId')" />
+          </div>
+        </div>
+      </el-col>
+      <el-col :span="12">
+        <div class="detail-row">
+          <span>工作量:</span>
+          <div class="detail-value" v-if="!isEditing.timeToTake" @click="editEntry('timeToTake')">
+          <span>{{timeToTake}}h</span>
+            <div class="edit-icon" />
+          </div>
+          <div v-else >
+           <el-input-number size="mini" v-model="editedRequirement.timeToTake" :min="1" :max="200"/>
+            <el-button
+              style="margin-left: 5px"
+              type="primary"
+              size="mini"
+              icon="el-icon-edit"
+              circle
+              @click="submitEdit('timeToTake')" />
+            <el-button
+              style="margin-left: 5px"
+              type="danger"
+              size="mini"
+              icon="el-icon-close"
+              circle
+              @click="cancelEdit('timeToTake')" />
+          </div>
+        </div>
+        <div class="detail-row">
+          <span>创建时间:</span>
+          <span>{{startTime}}</span>
+        </div>
+        <div class="detail-row"><span>完成时间:</span>
+          <span>{{endTime}}</span></div>
+      </el-col>
+    </el-row>
+    <!-- 相关需求 -->
+    <el-row class="entry-row" style="margin-top: 15px">
+      <h4>相关需求</h4>
+    </el-row>
+    <el-row style="margin: 3px 25px;width: 100%">
+      <h5>父项</h5>
+    </el-row>
+    <el-row style="margin: 0px 25px;">
+      <el-link type="primary">{{parent}}</el-link>
+    </el-row>
+    <el-row style="margin: 3px 25px;width: 100%">
+      <h5>子项</h5>
+    </el-row>
+    <el-row style="margin: 0px 25px;" v-for="item in childs" :key="item.id">
+        <el-link @click.prevent="changeContent(item.id)" type="primary">{{item.title}}</el-link>
+    </el-row>
+    <!-- 相关缺陷 -->
+    <el-row class="entry-row" style="margin-top: 15px">
+      <h4>相关缺陷(0)</h4>
+    </el-row>
+    <!-- 相关提交 -->
+    <el-row class="entry-row" style="margin-top: 15px">
+      <h4>相关提交(0)</h4>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import { RequirementAPI } from '../api';
+
+export default {
+  name: 'TaskDrawerContent',
+  props: {
+    id: Number,
+    type: String,
+    title: String,
+    description: String,
+    state: String,
+    priority: String,
+    userId: Number,
+    timeToTake: Number,
+    startTime: String,
+    endTime: String,
+    childs: Array,
+    parent: Number,
+    stateOptions: Array,
+    userOptions: Array,
+    priorityOptions: Array,
+  },
+  data() {
+    return {
+      isEditing: {
+        title: false,
+        description: false,
+        state: false,
+        timeToTake: false,
+        priority: false,
+        userId: false,
+      },
+      editedRequirement: {
+        title: null,
+        description: null,
+        state: null,
+        timeToTake: null,
+        priority: null,
+        userId: null,
+      },
+    };
+  },
+  methods: {
+    getUserName(value) {
+      let res;
+      this.userOptions.some((item) => {
+        if (item.value === value) {
+          res = item.label;
+          return true;
+        }
+        return false;
+      });
+      return res;
+    },
+    editEntry(key) {
+      Object.keys(this.isEditing).forEach((item) => {
+        if (key === item) this.isEditing[item] = true;
+        else this.isEditing[item] = false;
+      });
+    },
+    submitEdit(key) {
+      console.log('submit edit node');
+      const param = { id: this.id, ...this.editedRequirement };
+      RequirementAPI.updateTreeNode(param).then((res) => {
+        this.isEditing[key] = false;
+        this.$emit('requirement-update');
+      });
+    },
+    cancelEdit(key) {
+      this.isEditing[key] = false;
+      this.editedRequirement[key] = null;
+    },
+    changeContent(id) {
+      this.$emit('change', id);
+    },
+    closeDrawer() {
+      this.$emit('close-drawer');
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.drawer-content{
+  width: 100%;
+  padding: 10px;
+  display: flex;
+  flex-direction: column;
+  justify-content: flex-start;
+  align-items: flex-start;
+}
+.entry-row{
+  width: 100%;
+  margin: 3px 20px;
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  justify-content: flex-start;
+  .edit-icon{
+    margin-left: 5px;
+    display: none;
+    cursor: pointer;
+    width: 20px;
+    height: 20px;
+    background-position: center;
+    background-repeat: no-repeat;
+    background-size: 100% 100%;
+    transition: all .2s linear;
+    background-image: url("../assets/icon/edit.svg");
+    &:hover{
+      background-image: url("../assets/icon/edit_hover.svg");
+    }
+  }
+  &:hover .edit-icon{
+    display: block;
+  }
+  h3, h4{
+    color: #172b4d
+  }
+}
+.detail-row{
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  line-height: 24px;
+  margin: 3px 0;
+  .edit-icon{
+    margin-left: 5px;
+    display: none;
+    width: 20px;
+    height: 20px;
+    background-position: center;
+    background-repeat: no-repeat;
+    background-size: 100% 100%;
+    transition: all .2s linear;
+    background-image: url("../assets/icon/edit.svg");
+    cursor: pointer;
+    &:hover{
+      background-image: url("../assets/icon/edit_hover.svg");
+    }
+  }
+  &:hover .edit-icon{
+    display: block;
+  }
+}
+.detail-value{
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+}
+.node-type{
+  color: #FFFFFF;
+  border-radius: 3px;
+  font-size: 14px;
+  background-color: #42b983;
+  padding: 1px 4px;
+  margin-right: 4px;
+}
+.state-entry{
+  border-radius: 5px;
+  background-color: cornflowerblue;
+  font-size: 12px;
+  color: #FFFFFF;
+  padding: 0px 4px;
+  cursor: pointer
+}
+</style>

+ 19 - 12
src/components/Tree.vue

@@ -32,7 +32,7 @@
 import TreeNode from './TreeNode.vue';
 
 // 容器宽高
-const HEIGHT = 600;
+const HEIGHT = 800;
 const WIDTH = 1100;
 const MARGIN_TOP = 80;
 const MARGIN_LEFT = 100;
@@ -59,27 +59,31 @@ export default {
       this.data.forEach((item) => {
         nodeMap.set(item.id, item);
       });
-      // 将array转为树状结构;顺便拆成col
+      // 将array转为树状结构(父组件中已处理无需再处理);顺便拆成col
       let rootNode;
       const colList = [];
       for (let i = 0; i < TREE_MAX_HEIGHT; i += 1) {
         colList.push([]);
       }
+      if (this.data.length === 0) {
+        return colList;
+      }
       this.data.forEach((item) => {
         if (item.deep > 0 && item.deep <= TREE_MAX_HEIGHT) {
           colList[item.deep - 1].push(item);
         } else {
           console.warn('需求树数据有误,树高度错误');
         }
-        if (item.parent) {
-          const tempParent = nodeMap.get(item.parent);
-          if (!tempParent.childs) {
-            tempParent.childs = [];
-          }
-          tempParent.childs.push(item);
-        } else {
-          rootNode = item;
-        }
+        if (!item.parent) rootNode = item;
+        // if (item.parent) {
+        //   const tempParent = nodeMap.get(item.parent);
+        //   if (!tempParent.childs) {
+        //     tempParent.childs = [];
+        //   }
+        //   tempParent.childs.push(item);
+        // } else {
+        //   rootNode = item;
+        // }
       });
       // 计算一个节点包含他的后代所需要的空间
       const calculateNodeContentHeight = (node) => {
@@ -142,7 +146,6 @@ export default {
         this.data.forEach((item) => {
           if (!item.childs) return;
           const parentCard = this.$refs[`treenode-${item.id}`].getTreeNodeSizeAndPosition();
-          console.warn(parentCard);
           const start = {
             x: parentCard.left + parentCard.width,
             y: parentCard.top + parentCard.height / 2,
@@ -182,9 +185,13 @@ export default {
 
 <style lang="scss">
 #tree-container {
+  border-radius: 4px;
+  box-shadow: 0 0 3px 0px #BDC3C7;
   position: relative;
   display: flex;
+  overflow-y: auto;
   justify-content:center;
+  background: url("https://devcloud.cn-north-4.huaweicloud.com/projectman/plan_back.05189e7a664e6edc2f8e.png") repeat;
 }
 #node-container {
   position:absolute;

+ 12 - 0
src/element-plus.ts

@@ -27,6 +27,12 @@ import {
   ElOption,
   ElTabs,
   ElTabPane,
+  ElButtonGroup,
+  ElCol,
+  ElInputNumber,
+  ElRadio,
+  ElRadioGroup,
+  ElTooltip,
 } from 'element-plus';
 
 import ZH_CN from 'element-plus/lib/locale/lang/zh-cn';
@@ -63,6 +69,12 @@ export const Components = [
   ElSubmenu,
   ElTabs,
   ElTabPane,
+  ElButtonGroup,
+  ElCol,
+  ElInputNumber,
+  ElRadio,
+  ElRadioGroup,
+  ElTooltip,
 ];
 
 export const Plugins = [];

+ 14 - 14
src/typings/project.d.ts

@@ -9,21 +9,21 @@ declare type TreeNodeType = '需求' | '任务';
 declare type TreeNodeState = '已创建' | '进行中' | '已结束';
 
 declare interface TreeNode {
-  deep: number;
-  description: string;
-  endTime: string;
-  groupId: number;
+  deep?: number;
+  description?: string;
+  endTime?: string;
+  groupId?: number;
   id: number;
-  leaf: boolean;
-  parentId: number;
-  priority: number;
-  projectId: number;
-  startTime: string;
-  state: TreeNodeState;
-  timeToTake: number;
-  title: string;
-  type: TreeNodeType;
-  userId: number;
+  leaf?: boolean;
+  parentId?: number;
+  priority?: number;
+  projectId?: number;
+  startTime?: string;
+  state?: TreeNodeState;
+  timeToTake?: number;
+  title?: string;
+  type?: TreeNodeType;
+  userId?: number;
 }
 
 declare type Role = 'TEACHER' | 'STUDENT' | 'ADMIN' | 'GUEST';

+ 52 - 142
src/views/Project/Schedule.vue

@@ -1,7 +1,23 @@
 <template>
+  <el-row style="display: flex;flex-direction: row;align-items: center;margin-bottom: 10px;color: #333333">
+    <h4>项目规划</h4>
+    <el-tooltip class="item" effect="dark" :content="scheduleGuide" placement="right-end">
+      <i class="el-icon-question"></i>
+    </el-tooltip>
+  </el-row>
   <Tree :data="requirements" @change="handleTreeChange" @nodeClick="handleNodeClick" ref="tree"></Tree>
-  <ElDrawer v-model="showRequirementDrawer">
+  <ElDrawer v-model="showRequirementDrawer"  :with-header="false" size="650px">
     <!-- TODO: 供展示和编辑节点详情,可在taskList中复用  -->
+    <template #default>
+      <TaskDrawerContent
+        @change="handleDrawerContentChange"
+        @close-drawer="showRequirementDrawer = false"
+        @requirement-update="getTreeData"
+        v-bind="requirementInDrawer"
+        :state-options="stateOptions"
+        :user-options="userOptions"
+        :priority-options="priorityOptions">
+      </TaskDrawerContent></template>
   </ElDrawer>
 </template>
 
@@ -9,167 +25,61 @@
 import Tree from '@/components/Tree.vue';
 
 import { RequirementAPI } from '@/api';
+import TaskDrawerContent from '@/components/TaskDrawerContent.vue';
 
+const scheduleGuide = '项目规划用于asdsadsadda';
 export default {
   name: 'Schedule',
-  components: { Tree },
+  components: { TaskDrawerContent, Tree },
   data() {
     return {
-      requirements: [{
-        deep: 1,
-        description: '这里是节点描述啊啊啊。',
-        endTime: '2021/3/17 12:00:01',
-        groupId: 101,
-        id: 1,
-        leaf: false,
-        parent: null,
-        priority: 0,
-        projectId: 101,
-        startTime: '2021/3/17 12:00:01',
-        state: '已创建',
-        timeToTake: 10,
-        title: '这是标题1',
-        type: '需求',
-        userId: 101,
-      },
-      {
-        deep: 2,
-        description: '这里是节点描述啊啊啊。',
-        endTime: '2021/3/17 12:00:01',
-        groupId: 101,
-        id: 2,
-        leaf: false,
-        parent: 1,
-        priority: 0,
-        projectId: 101,
-        startTime: '2021/3/17 12:00:01',
-        state: '已创建',
-        timeToTake: 10,
-        title: '这是标题2',
-        type: '需求',
-        userId: 101,
-      },
-      {
-        deep: 2,
-        description: '这里是节点描述啊啊啊。',
-        endTime: '2021/3/17 12:00:01',
-        groupId: 101,
-        id: 3,
-        leaf: false,
-        parent: 1,
-        priority: 0,
-        projectId: 101,
-        startTime: '2021/3/17 12:00:01',
-        state: '进行中',
-        timeToTake: 10,
-        title: '这是标题3',
-        type: '需求',
-        userId: 101,
-      },
-      {
-        deep: 3,
-        description: '这里是节点描述啊啊啊。',
-        endTime: '2021/3/17 12:00:01',
-        groupId: 101,
-        id: 4,
-        leaf: false,
-        parent: 3,
-        priority: 1,
-        projectId: 101,
-        startTime: '2021/3/17 12:00:01',
-        state: '已完成',
-        timeToTake: 10,
-        title: '这是标题4',
-        type: '需求',
-        userId: 101,
-      },
-      {
-        deep: 3,
-        description: '这里是节点描述啊啊啊。',
-        endTime: '2021/3/17 12:00:01',
-        groupId: 101,
-        id: 5,
-        leaf: false,
-        parent: 3,
-        priority: 2,
-        projectId: 101,
-        startTime: '2021/3/17 12:00:01',
-        state: '已完成',
-        timeToTake: 10,
-        title: '这是标题5',
-        type: '需求',
-        userId: 101,
-      },
-      {
-        deep: 4,
-        description: '这里是节点描述啊啊啊。',
-        endTime: '2021/3/17 12:00:01',
-        groupId: 101,
-        id: 6,
-        leaf: false,
-        parent: 5,
-        priority: 0,
-        projectId: 101,
-        startTime: '2021/3/17 12:00:01',
-        state: '进行中',
-        timeToTake: 10,
-        title: '这是标题6',
-        type: '需求',
-        userId: 101,
-      },
-      {
-        deep: 4,
-        description: '这里是节点描述啊啊啊。',
-        endTime: '2021/3/17 12:00:01',
-        groupId: 101,
-        id: 7,
-        leaf: false,
-        parent: 5,
-        priority: 0,
-        projectId: 101,
-        startTime: '2021/3/17 12:00:01',
-        state: '已完成',
-        timeToTake: 10,
-        title: '这是标题7',
-        type: '需求',
-        userId: 101,
-      },
-      {
-        deep: 4,
-        description: '这里是节点描述啊啊啊。',
-        endTime: '2021/3/17 12:00:01',
-        groupId: 101,
-        id: 8,
-        leaf: false,
-        parent: 5,
-        priority: 0,
-        projectId: 101,
-        startTime: '2021/3/17 12:00:01',
-        state: '已完成',
-        timeToTake: 10,
-        title: '这是标题8',
-        type: '需求',
-        userId: 101,
-      },
-      ],
+      scheduleGuide,
+      requirements: [],
       showRequirementDrawer: false,
+      addRequirementDrawer: false,
+      parentOfAddingRequirement: null,
+      requirementInDrawer: {},
+      stateOptions: [{ value: '已完成' }, { value: '进行中' }, { value: '已创建' }],
+      priorityOptions: [{ value: 0, label: '高' }, { value: 1, label: '中' }, { value: 2, label: '低' }],
+      userOptions: [{ value: 100, label: 'abc' }, { value: 101, label: 'def' }, { value: 102, label: 'gg' }],
     };
   },
   async mounted() {
-    // this.requirements = await RequirementAPI.getTreeNodesByProjectId(this.$store.state.workspace.currentProjectId);
+    await this.getTreeData();
+    console.warn('Schedule.vue_mounted');
     // this.$refs.tree.regenerateTree();
   },
   methods: {
+    async getTreeData() {
+      this.requirements = this.addChild2Node(await RequirementAPI.getTreeNodesByProjectId(this.$store.state.workspace.currentProjectId));
+    },
+    addChild2Node(array) {
+      const nodeMap = new Map();
+      array.forEach((item) => nodeMap.set(item.id, item));
+      array.forEach((item) => {
+        if (item.parent) {
+          const tempParent = nodeMap.get(item.parent);
+          if (!tempParent.childs) {
+            tempParent.childs = [];
+          }
+          tempParent.childs.push(item);
+        }
+      });
+      return array;
+    },
     async handleTreeChange(msg) {
       const { type, id } = msg;
       await RequirementAPI[type](id);
-      // this.requirements = await RequirementAPI.getTreeNodesByProjectId(this.$store.state.workspace.currentProjectId);
-      this.$refs.tree.regenerateTree();
+      console.warn('treeNode_event');
+      await this.getTreeData();
     },
     handleNodeClick(id) {
       this.showRequirementDrawer = true;
       this.requirementInDrawer = Array.prototype.find.call(this.requirements, (requirement) => requirement.id === id);
     },
+    handleDrawerContentChange(id) {
+      this.requirementInDrawer = Array.prototype.find.call(this.requirements, (requirement) => requirement.id === id);
+    },
   },
 };
 </script>