소스 검색

feat: 完成需求节点抽屉的编辑,样式待优化

Jinyao 5 년 전
부모
커밋
413838185b

+ 322 - 0
src/components/RequirementDrawer.vue

@@ -0,0 +1,322 @@
+<template>
+  <el-drawer
+    v-model="showDrawer"
+    :size="650"
+    @before-close="(done) => {
+      isEditing = false;
+      done();
+    }">
+    <template #title>
+      <el-row class="entry-row">
+        <el-col :span="2">
+          <div class="node-type">{{type}}</div>
+        </el-col>
+        <el-col :span="12" :offset="2">
+          <template class="entry-row" v-if="isEditing">
+            <el-input placeholder="请输入标题" size="mini" style="width: 200px;display: inline;" v-model="editedRequirement.title"/>
+          </template>
+          <template v-else><h3>{{title}}</h3></template>
+        </el-col>
+        <el-col :span="6" :offset="2">
+          <el-switch
+            v-if="activeTab ==='basic_info'"
+            style="float: right;"
+            v-model="isEditing"
+            active-text="编辑"
+            @change="(isEditing) => {
+              isEditing && fillDefaultValue();
+            }">
+          </el-switch>
+        </el-col>
+      </el-row>
+    </template>
+    <template #default>
+      <el-row type="flex" justify="center">
+        <el-col :span="20">
+          <el-tabs
+            class="tabs"
+            v-model="activeTab"
+            @tab-click="(tab) => {
+              if(tab.panename !== 'basic_info')
+                isEditing = false;
+            }"
+          >
+            <el-tab-pane label="基本信息" name="basic_info">
+              <el-row class="entry-row" style="margin-top: 15px">
+                <h4>需求描述</h4>
+              </el-row>
+              <el-row class="entry-row">
+                <div v-if="isEditing">
+                  <el-input placeholder="请输入描述"
+                            type="textarea"
+                            maxlength="120"
+                            show-word-limit
+                            :rows="3" style="width: 300px;" v-model="editedRequirement.description" />
+                  <br/>
+                </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" class="detail-value">
+                      <StateTag :state="state"></StateTag>
+                    </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>
+                    </div>
+                  </div>
+                  <div class="detail-row">
+                    <span>优先级:</span>
+                    <div class="detail-value"  v-if="!isEditing">
+                      <PriorityTag :priority="priority"></PriorityTag>
+                    </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>
+                    </div>
+                  </div>
+                  <div class="detail-row">
+                    <span>经办人:</span>
+                    <div class="detail-value"   v-if="!isEditing">
+                      <el-tag>{{processorName}}</el-tag>
+                    </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>
+                    </div>
+                  </div>
+                </el-col>
+                <el-col :span="12">
+                  <div class="detail-row">
+                    <span>工作量:</span>
+                    <div class="detail-value" v-if="!isEditing">
+                      <span>{{timeToTake}}人日</span>
+                    </div>
+                    <div v-else>
+                      <el-input-number size="mini" v-model="editedRequirement.timeToTake" :min="1" :max="200">
+                      </el-input-number>
+                    </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>
+              <!-- 相关需求 -->
+              <template v-if="showSons">
+              <el-row class="entry-row" style="margin-top: 15px">
+                <h4>子需求</h4>
+              </el-row>
+              <template v-if="(childs && childs.length) || false">
+                <el-row style="margin: 10px 25px;" v-for="item in childs" :key="item.id">
+                  <el-link @click.prevent="changeContent(item.id)" type="primary">{{item.title}}</el-link>
+                </el-row>
+              </template>
+              <template v-else>
+                <el-row style="margin: 0px 25px;">
+                  没有子需求
+                </el-row>
+              </template>
+              </template>
+              <el-row v-if="isEditing" class="entry-row" style="margin-top: 15px">
+                <el-button @click="submitEdit" size="mini" type="primary">提交</el-button>
+                <el-button @click="cancelEdit" size="mini">取消</el-button>
+              </el-row>
+            </el-tab-pane>
+            <el-tab-pane label="历史提交" name="commit_history">
+              <!-- 相关提交 -->
+              <el-row class="entry-row" style="margin-top: 15px">
+                <el-table :data="commits">
+                  <el-table-column type="expand">
+                    <template #default="scope">
+                      <pre>{{scope.row}}</pre>
+                    </template>
+                  </el-table-column>
+                  <el-table-column
+                    prop="id"
+                    label="ID"
+                    align="center"
+                  >
+                  </el-table-column>
+                  <el-table-column
+                    prop="title"
+                    label="标题"
+                    align="center"
+                  >
+                  </el-table-column>
+                  <el-table-column
+                    prop="gitlabUsername"
+                    label="用户"
+                    align="center"
+                  >
+                  </el-table-column>
+                </el-table>
+              </el-row>
+            </el-tab-pane>
+          </el-tabs>
+        </el-col>
+      </el-row>
+    </template>
+  </el-drawer>
+</template>
+
+<script>
+import { RequirementAPI } from '@/api';
+import PriorityTag from '@/components/PriorityTag.vue';
+import StateTag from '@/components/StateTag.vue';
+import { priorityOptions, stateOptions } from '@/utils/constants';
+
+export default {
+  name: 'RequirementDrawers',
+  components: { PriorityTag, StateTag },
+  emits: ['change', 'requirement-update'],
+  props: {
+    id: Number,
+    type: String,
+    title: String,
+    description: String,
+    state: String,
+    priority: String,
+    processorId: Number,
+    processorName: String,
+    timeToTake: Number,
+    startTime: String,
+    endTime: String,
+    childs: Array,
+    parent: Number,
+    commits: Array,
+    showSons: {
+      type: Boolean,
+      default: true,
+    },
+  },
+  data() {
+    return {
+      activeTab: 'basic_info',
+      showDrawer: false,
+      isEditing: false,
+      editedRequirement: {
+        title: null,
+        description: null,
+        state: null,
+        timeToTake: null,
+        priority: null,
+        processorId: null,
+        processorName: null,
+      },
+      stateOptions,
+      priorityOptions,
+    };
+  },
+  computed: {
+    userOptions() {
+      return this.$store.getters.userOptions;
+    },
+  },
+  methods: {
+    show() {
+      this.showDrawer = true;
+    },
+    fillDefaultValue() {
+      this.editedRequirement.title = this.title;
+      this.editedRequirement.description = this.description;
+      this.editedRequirement.state = this.state;
+      this.editedRequirement.timeToTake = this.timeToTake;
+      this.editedRequirement.priority = this.priority;
+      this.editedRequirement.processorId = this.processorId;
+      this.editedRequirement.processorName = this.processorName;
+    },
+    getUserName(value) {
+      let res;
+      this.userOptions.some((item) => {
+        if (item.value === value) {
+          res = item.label;
+          return true;
+        }
+        return false;
+      });
+      return res;
+    },
+    submitEdit() {
+      const param = { id: this.id, ...this.editedRequirement };
+      RequirementAPI.updateTreeNode(param).then((res) => {
+        this.isEditing = false;
+        this.$emit('requirement-update');
+      });
+    },
+    cancelEdit() {
+      this.isEditing = false;
+      this.fillDefaultValue();
+    },
+    changeContent(id) {
+      this.$emit('change', id);
+    },
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.entry-row{
+  width: 100%;
+  margin: 3px 20px;
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  justify-content: flex-start;
+  h3, h4{
+    color: #172b4d
+  }
+}
+.detail-row{
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  line-height: 24px;
+  margin: 3px 0;
+}
+.detail-value{
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+}
+.node-type{
+  color: #FFFFFF;
+  border-radius: 3px;
+  font-size: 14px;
+  background-color: #42b983;
+  text-align: center;
+}
+.state-entry{
+  border-radius: 5px;
+  background-color: cornflowerblue;
+  font-size: 12px;
+  color: #FFFFFF;
+  padding: 0px 4px;
+  cursor: pointer
+}
+</style>

+ 0 - 384
src/components/TaskDrawerContent.vue

@@ -1,384 +0,0 @@
-<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">
-            <StateTag :state="state"></StateTag>
-            <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')">
-            <PriorityTag :priority="priority"></PriorityTag>
-            <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')">
-          <ElTag>{{userId}}</ElTag>
-            <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';
-import PriorityTag from '@/components/PriorityTag.vue';
-import StateTag from '@/components/StateTag.vue';
-
-export default {
-  name: 'TaskDrawerContent',
-  components: { PriorityTag, StateTag },
-  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>

+ 2 - 0
src/element-plus.ts

@@ -14,6 +14,7 @@ import {
   ElIcon,
   ElInput,
   ElSubmenu,
+  ElSwitch,
   ElMenuItem,
   ElForm,
   ElFormItem,
@@ -67,6 +68,7 @@ export const Components = [
   ElPagination,
   ElButton,
   ElSelect,
+  ElSwitch,
   ElOption,
   ElSubmenu,
   ElTabs,

+ 6 - 0
src/store/modules/workspace.ts

@@ -47,6 +47,12 @@ const getters: GetterTree<WorkspaceState, RootState> = {
         name: '未选择项目',
       };
   },
+  userOptions(state) {
+    return state.currentProjectMembers.map((user) => ({
+      label: user.username,
+      value: user.id,
+    }));
+  },
 };
 
 export const workspace: Module<WorkspaceState, RootState> = {

+ 2 - 0
src/utils/constants.ts

@@ -0,0 +1,2 @@
+export const stateOptions = [{ value: '已完成' }, { value: '进行中' }, { value: '已创建' }];
+export const priorityOptions = [{ value: 0, label: '紧急' }, { value: 1, label: '宽松' }, { value: 2, label: '一般' }];

+ 9 - 22
src/views/Project/Schedule.vue

@@ -6,31 +6,24 @@
     </el-tooltip>
   </el-row>
   <Tree :data="requirements" @change="handleTreeChange" @nodeClick="handleNodeClick" ref="tree"></Tree>
-  <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>
+  <RequirementDrawer
+    ref="drawer"
+    v-bind="requirementInDrawer"
+    @change="handleDrawerContentChange"
+    @requirement-update="getTreeData"
+  ></RequirementDrawer>
 </template>
 
 <script>
 import Tree from '@/components/Tree.vue';
 
 import { RequirementAPI } from '@/api';
-import TaskDrawerContent from '@/components/TaskDrawerContent.vue';
+import RequirementDrawer from '@/components/RequirementDrawer.vue';
 
 const scheduleGuide = '项目规划用于asdsadsadda';
 export default {
   name: 'Schedule',
-  components: { TaskDrawerContent, Tree },
+  components: { RequirementDrawer, Tree },
   data() {
     return {
       scheduleGuide,
@@ -39,15 +32,10 @@ export default {
       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() {
     await this.getTreeData();
-    console.warn('Schedule.vue_mounted');
-    // this.$refs.tree.regenerateTree();
   },
   methods: {
     async getTreeData() {
@@ -70,11 +58,10 @@ export default {
     async handleTreeChange(msg) {
       const { type, id } = msg;
       await RequirementAPI[type](id);
-      console.warn('treeNode_event');
       await this.getTreeData();
     },
     handleNodeClick(id) {
-      this.showRequirementDrawer = true;
+      this.$refs.drawer.show();
       this.requirementInDrawer = Array.prototype.find.call(this.requirements, (requirement) => requirement.id === id);
     },
     handleDrawerContentChange(id) {

+ 20 - 9
src/views/Project/TaskList.vue

@@ -1,22 +1,33 @@
 <template>
-<div class="table-container">
-  <GeneralTable
-    :data="taskList"
-    @row-click="openDrawer"
-  />
-</div>
+  <div class="table-container">
+    <GeneralTable
+      :data="taskList"
+      @row-click="openDrawer"
+    />
+  </div>
+  <RequriementDrawer
+    ref="drawer"
+    v-bind="requirementInDrawer"
+    @change="handleDrawerContentChange"
+    @requirement-update="fetch"
+    :showSons="false"
+  ></RequriementDrawer>
 </template>
 
 <script>
 import { RequirementAPI } from '@/api';
 
 import GeneralTable from '@/components/GeneralTable.vue';
+import RequriementDrawer from '@/components/RequirementDrawer.vue';
 
 export default {
-  components: { GeneralTable },
+  components: { GeneralTable, RequriementDrawer },
   data() {
     return {
       taskList: [],
+      requirementInDrawer: {},
+      stateOptions: [{ value: '已完成' }, { value: '进行中' }, { value: '已创建' }],
+      priorityOptions: [{ value: 0, label: '紧急' }, { value: 1, label: '宽松' }, { value: 2, label: '一般' }],
     };
   },
   mounted() {
@@ -27,8 +38,8 @@ export default {
       this.taskList = await RequirementAPI.getTasksByProjectId(this.$store.state.workspace.currentProjectId);
     },
     openDrawer(row) {
-      // TODO: 打开详情抽屉
-      console.log(row);
+      this.requirementInDrawer = row;
+      this.$refs.drawer.show();
     },
   },
 };