You are viewing a plain text version of this content. The canonical link for it is here.
Posted to submarine-dev@hadoop.apache.org by li...@apache.org on 2019/10/12 02:17:00 UTC

[hadoop-submarine] branch master updated: SUBMARINE-216. Integrated workbench add project web & server

This is an automated email from the ASF dual-hosted git repository.

liuxun pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hadoop-submarine.git


The following commit(s) were added to refs/heads/master by this push:
     new 36d8c78  SUBMARINE-216. Integrated workbench add project web & server
36d8c78 is described below

commit 36d8c78383bf953849e4dd0062085dd3de420253
Author: Xun Liu <li...@apache.org>
AuthorDate: Fri Oct 4 17:09:33 2019 +0800

    SUBMARINE-216. Integrated workbench add project web & server
    
    ### What is this PR for?
    The submarine project manager is independently developed.
    Integration of front-end pages and back-end services is required.
    
    ### What type of PR is it?
    [Improvement]
    
    ### Todos
    * [ ] - Task
    
    ### What is the Jira issue?
    * https://issues.apache.org/jira/browse/SUBMARINE-216
    
    ### How should this be tested?
    * [CI pass](https://travis-ci.org/liuxunorg/hadoop-submarine/builds/596424390)
    
    ### Screenshots (if appropriate)
    
    ![project-add](https://user-images.githubusercontent.com/3677382/66627864-8aee4580-ec2f-11e9-82b1-9fe1cf1ff602.gif)
    
    ### Questions:
    * Does the licenses files need update? Yes/No
    * Is there breaking changes for older versions? Yes/No
    * Does this needs documentation? Yes/No
    
    Author: Xun Liu <li...@apache.org>
    
    Closes #42 from liuxunorg/SUBMARINE-216 and squashes the following commits:
    
    022f86e [Xun Liu] [SUBMARINE-216] Integrated workbench add project web & server
---
 docs/database/submarine-data.sql                   |  15 ++
 docs/database/submarine.sql                        |  14 +-
 .../apache/submarine/database/entity/Project.java  |  99 +++++++++---
 .../org/apache/submarine/rest/ProjectRestApi.java  |  60 +++++++-
 .../submarine/database/mappers/ProjectMapper.xml   |  70 ++++++---
 .../database/service/ProjectServiceTest.java       |  26 ++--
 .../workbench-web/public/assets/notebook_logo.png  | Bin 0 -> 8354 bytes
 .../workbench-web/public/assets/python_logo.png    | Bin 0 -> 10836 bytes
 .../workbench-web/public/assets/r_logo.png         | Bin 0 -> 8793 bytes
 .../workbench-web/public/assets/scala_logo.png     | Bin 0 -> 10055 bytes
 .../workbench-web/src/api/system.js                |  14 +-
 .../src/components/Dict/DictSelectTag.vue          |  47 +++++-
 .../workbench/workspace/project/NewProject.vue     | 117 +++++++-------
 .../workspace/project/NewProjectStep1.vue          | 105 +++++++++++--
 .../workspace/project/NewProjectStep2.vue          | 139 +++++++++++++----
 .../workspace/project/NewProjectStep3.vue          |  36 +++--
 .../workbench/workspace/project/ProjectList.vue    | 168 +++++++--------------
 17 files changed, 623 insertions(+), 287 deletions(-)

diff --git a/docs/database/submarine-data.sql b/docs/database/submarine-data.sql
index 4f37838..644a15e 100644
--- a/docs/database/submarine-data.sql
+++ b/docs/database/submarine-data.sql
@@ -16,6 +16,9 @@
 -- ----------------------------
 INSERT INTO `sys_dict` VALUES ('ca2dd544ca4c11e9a71e0242ac110002','SYS_USER_SEX','Sys user sex','submarine system dict, Do not modify.',0,0,NULL,'2019-08-29 11:04:36',NULL,'2019-09-01 01:08:12');
 INSERT INTO `sys_dict` VALUES ('f405a7b1cc5411e9af810242ac110002','SYS_USER_STATUS','Sys user status','submarine system dict, Do not modify.',0,0,NULL,'2019-09-01 01:08:05',NULL,'2019-09-01 01:08:05');
+INSERT INTO `sys_dict` VALUES ('3a1ed33ae83611e9ab840242ac110002','PROJECT_TYPE','Project machine learning type','submarine system dict, Do not modify.',0,0,NULL,'2019-09-01 01:08:05',NULL,'2019-09-01 01:08:05');
+INSERT INTO `sys_dict` VALUES ('8a101495e84011e9ab840242ac110002','PROJECT_VISIBILITY','Project visibility type','submarine system dict, Do not modify.',0,0,NULL,'2019-09-01 01:08:05',NULL,'2019-09-01 01:08:05');
+INSERT INTO `sys_dict` VALUES ('8f0439c9e84011e9ab840242ac110002','PROJECT_PERMISSION','Project permission type','submarine system dict, Do not modify.',0,0,NULL,'2019-09-01 01:08:05',NULL,'2019-09-01 01:08:05');
 
 -- ----------------------------
 -- Records of sys_dict_item
@@ -25,6 +28,18 @@ INSERT INTO `sys_dict_item` VALUES ('4c5d736acc5511e9af810242ac110002','SYS_USER
 INSERT INTO `sys_dict_item` VALUES ('6d5ae3b2cc5511e9af810242ac110002','SYS_USER_STATUS_REGISTERED','New Registered','SYS_USER_STATUS','submarine system dict, Do not modify.',3,0,NULL,'2019-09-01 01:11:29',NULL,'2019-09-01 01:12:47');
 INSERT INTO `sys_dict_item` VALUES ('d018e2b0ca4c11e9a71e0242ac110002','SYS_USER_SEX_MALE','Male','SYS_USER_SEX','submarine system dict, Do not modify.',1,0,NULL,'2019-08-29 11:04:46',NULL,'2019-09-01 00:53:54');
 INSERT INTO `sys_dict_item` VALUES ('d94410adca4c11e9a71e0242ac110002','SYS_USER_SEX_FEMALE','Female','SYS_USER_SEX','submarine system dict, Do not modify.',2,0,NULL,'2019-08-29 11:05:02',NULL,'2019-09-01 00:54:00');
+INSERT INTO `sys_dict_item` VALUES ('7b9aafa0e83611e9ab840242ac110002','PROJECT_TYPE_NOTEBOOK','notebook','PROJECT_TYPE','submarine system dict, Do not modify.',1,0,NULL,'2019-08-29 11:05:02',NULL,'2019-09-01 00:54:00');
+INSERT INTO `sys_dict_item` VALUES ('8229a76be83611e9ab840242ac110002','PROJECT_TYPE_PYTHON','python','PROJECT_TYPE','submarine system dict, Do not modify.',2,0,NULL,'2019-08-29 11:05:02',NULL,'2019-09-01 00:54:00');
+INSERT INTO `sys_dict_item` VALUES ('ac80ab12e83611e9ab840242ac110002','PROJECT_TYPE_R','R','PROJECT_TYPE','submarine system dict, Do not modify.',3,0,NULL,'2019-08-29 11:05:02',NULL,'2019-09-01 00:54:00');
+INSERT INTO `sys_dict_item` VALUES ('b1070158e83611e9ab840242ac110002','PROJECT_TYPE_SCALA','scala','PROJECT_TYPE','submarine system dict, Do not modify.',4,0,NULL,'2019-08-29 11:05:02',NULL,'2019-09-01 00:54:00');
+INSERT INTO `sys_dict_item` VALUES ('8c53870be83611e9ab840242ac110002','PROJECT_TYPE_TENSORFLOW','tensorflow','PROJECT_TYPE','submarine system dict, Do not modify.',5,0,NULL,'2019-08-29 11:05:02',NULL,'2019-09-01 00:54:00');
+INSERT INTO `sys_dict_item` VALUES ('90ca63dfe83611e9ab840242ac110002','PROJECT_TYPE_PYTORCH','pytorch','PROJECT_TYPE','submarine system dict, Do not modify.',6,0,NULL,'2019-08-29 11:05:02',NULL,'2019-09-01 00:54:00');
+INSERT INTO `sys_dict_item` VALUES ('2ed844fae84111e9ab840242ac110002','PROJECT_VISIBILITY_PRIVATE','private','PROJECT_VISIBILITY','submarine system dict, Do not modify.',1,0,NULL,'2019-09-01 01:09:32',NULL,'2019-09-01 01:13:19');
+INSERT INTO `sys_dict_item` VALUES ('341d5a35e84111e9ab840242ac110002','PROJECT_VISIBILITY_TEAM','team','PROJECT_VISIBILITY','submarine system dict, Do not modify.',2,0,NULL,'2019-09-01 01:10:33',NULL,'2019-09-01 01:12:53');
+INSERT INTO `sys_dict_item` VALUES ('3866b369e84111e9ab840242ac110002','PROJECT_VISIBILITY_PUBLIC','public','PROJECT_VISIBILITY','submarine system dict, Do not modify.',3,0,NULL,'2019-09-01 01:11:29',NULL,'2019-09-01 01:12:47');
+INSERT INTO `sys_dict_item` VALUES ('3cc1a373e84111e9ab840242ac110002','PROJECT_PERMISSION_VIEW','can view','PROJECT_PERMISSION','submarine system dict, Do not modify.',1,0,NULL,'2019-09-01 01:09:32',NULL,'2019-09-01 01:13:19');
+INSERT INTO `sys_dict_item` VALUES ('44e90f6ce84111e9ab840242ac110002','PROJECT_PERMISSION_EDIT','can edit','PROJECT_PERMISSION','submarine system dict, Do not modify.',2,0,NULL,'2019-09-01 01:11:29',NULL,'2019-09-01 01:12:47');
+INSERT INTO `sys_dict_item` VALUES ('40dbb5ece84111e9ab840242ac110002','PROJECT_PERMISSION_EXECUTE','can execute','PROJECT_PERMISSION','submarine system dict, Do not modify.',3,0,NULL,'2019-09-01 01:10:33',NULL,'2019-09-01 01:12:53');
 
 -- ----------------------------
 -- Records of sys_department
diff --git a/docs/database/submarine.sql b/docs/database/submarine.sql
index a8b2482..38ebe7c 100644
--- a/docs/database/submarine.sql
+++ b/docs/database/submarine.sql
@@ -162,16 +162,24 @@ CREATE TABLE `team_member` (
 DROP TABLE IF EXISTS `project`;
 CREATE TABLE `project` (
   `id` varchar(32) NOT NULL,
-  `project_name` varchar(100) NOT NULL COMMENT 'project name',
-  `visibility` int(1) default 0 COMMENT '0:Private, 1:Team, 2:Public',
-  `type` int(1) default 0 COMMENT 'machine learning type (0:notebook, 1:python, 2:spark, 3:R, 4:tensorflow, 5:pytorch)',
+  `name` varchar(100) NOT NULL COMMENT 'project name',
+  `visibility` varchar(32) default NULL COMMENT 'dict_code:PROJECT_VISIBILITY',
+  `permission` varchar(32) default NULL COMMENT 'dict_code:PROJECT_PERMISSION',
+  `type` varchar(32) default NULL COMMENT 'dict_code:PROJECT_TYPE',
   `description` varchar(255) COMMENT 'description',
   `user_name` varchar(32) NOT NULL COMMENT 'owner user id',
+  `team_name` varchar(32) default NULL COMMENT 'team name',
+  `tags` varchar(128) default NULL COMMENT 'Comma separated tag',
+  `star_num` int(8) default 0 COMMENT 'star number',
+  `like_num` int(8) default 0 COMMENT 'like number',
+  `message_num` int(8) default 0 COMMENT 'message number',
   `create_by` varchar(32) default NULL COMMENT 'create user',
   `create_time` datetime default NULL COMMENT 'create time',
   `update_by` varchar(32) default NULL COMMENT 'last update user',
   `update_time` datetime default NULL COMMENT 'last update time',
   PRIMARY KEY  (`id`)/*,
+  CONSTRAINT `FK_PROJECT_TYPE` FOREIGN KEY (`type`) REFERENCES `sys_dict_item` (`item_code`),
+  CONSTRAINT `FK_PROJECT_TEAM_NAME` FOREIGN KEY (`team_name`) REFERENCES `team` (`team_name`),
   CONSTRAINT `FK_PROJECT_USER_NAME` FOREIGN KEY (`user_name`) REFERENCES `sys_user` (`user_name`)*/
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
diff --git a/submarine-workbench/workbench-server/src/main/java/org/apache/submarine/database/entity/Project.java b/submarine-workbench/workbench-server/src/main/java/org/apache/submarine/database/entity/Project.java
index 98066f1..848ab22 100644
--- a/submarine-workbench/workbench-server/src/main/java/org/apache/submarine/database/entity/Project.java
+++ b/submarine-workbench/workbench-server/src/main/java/org/apache/submarine/database/entity/Project.java
@@ -18,45 +18,65 @@
  */
 package org.apache.submarine.database.entity;
 
+import org.apache.submarine.annotation.Dict;
+
 import java.util.ArrayList;
 import java.util.List;
 
 public class Project extends BaseEntity {
-  private String projectName;
+  private String name;
+
+  @Dict(Code = "PROJECT_VISIBILITY")
+  private String visibility;
+
+  @Dict(Code = "PROJECT_TYPE")
+  private String type;
+
+  @Dict(Code = "PROJECT_PERMISSION")
+  private String permission;
 
-  // 0:Private, 1:Team, 2:Public
-  private Integer visibility;
+  // Comma separated tag
+  private String tags;
 
-  // 0:notebook, 1:python, 2:spark, 3:R, 4:tensorflow, 5:pytorch
-  private Integer type;
+  // number of star
+  private Integer starNum = 0;
+
+  // number of like
+  private Integer likeNum = 0;
+
+  // number of message
+  private Integer messageNum = 0;
+
+  // Team.teamName
+  private String teamName;
 
   private String description;
 
   private String userName;
 
-  private List<ProjectFiles> projectFilesList;
+  private List<ProjectFiles> projectFilesList = new ArrayList<>();
 
-  public String getProjectName() {
-    return projectName;
+  public String getName() {
+    return name;
   }
 
-  public void setProjectName(String projectName) {
-    this.projectName = projectName == null ? null : projectName.trim();
+  public void setName(String name) {
+    this.name = name == null ? null : name.trim();
   }
 
-  public Integer getVisibility() {
+  public String getVisibility() {
     return visibility;
   }
 
-  public void setVisibility(Integer visibility) {
+  public void setVisibility(String visibility) {
     this.visibility = visibility;
   }
 
-  public Integer getType() {
+  public String getType() {
     return type;
   }
 
-  public void setType(Integer type) {
+  public void setType(String type) {
     this.type = type;
   }
 
@@ -85,9 +105,54 @@ public class Project extends BaseEntity {
   }
 
   public void addProjectFilesList(ProjectFiles projectFiles) {
-    if (projectFilesList == null) {
-      projectFilesList = new ArrayList<>();
-    }
     this.projectFilesList.add(projectFiles);
   }
+
+  public String getTags() {
+    return tags;
+  }
+
+  public void setTags(String tags) {
+    this.tags = tags;
+  }
+
+  public Integer getStarNum() {
+    return starNum;
+  }
+
+  public void setStarNum(Integer starNum) {
+    this.starNum = starNum;
+  }
+
+  public Integer getLikeNum() {
+    return likeNum;
+  }
+
+  public void setLikeNum(Integer likeNum) {
+    this.likeNum = likeNum;
+  }
+
+  public Integer getMessageNum() {
+    return messageNum;
+  }
+
+  public void setMessageNum(Integer messageNum) {
+    this.messageNum = messageNum;
+  }
+
+  public String getTeamName() {
+    return teamName;
+  }
+
+  public void setTeamName(String teamName) {
+    this.teamName = teamName;
+  }
+
+  public String getPermission() {
+    return permission;
+  }
+
+  public void setPermission(String permission) {
+    this.permission = permission;
+  }
 }
diff --git a/submarine-workbench/workbench-server/src/main/java/org/apache/submarine/rest/ProjectRestApi.java b/submarine-workbench/workbench-server/src/main/java/org/apache/submarine/rest/ProjectRestApi.java
index 275fe15..8d98b08 100644
--- a/submarine-workbench/workbench-server/src/main/java/org/apache/submarine/rest/ProjectRestApi.java
+++ b/submarine-workbench/workbench-server/src/main/java/org/apache/submarine/rest/ProjectRestApi.java
@@ -29,7 +29,10 @@ import org.slf4j.LoggerFactory;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
+import javax.ws.rs.DELETE;
 import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
@@ -61,7 +64,7 @@ public class ProjectRestApi {
 
     List<Project> projectList = new ArrayList<>();
     try {
-      projectList = projectService.queryPageList("liuxun", column, order, pageNo, pageSize);
+      projectList = projectService.queryPageList(userName, column, order, pageNo, pageSize);
     } catch (Exception e) {
       LOG.error(e.getMessage(), e);
       return new JsonResponse.Builder<>(Response.Status.OK).success(false).build();
@@ -72,4 +75,59 @@ public class ProjectRestApi {
         .success(true).result(listResult).build();
   }
 
+  @POST
+  @Path("/add")
+  @SubmarineApi
+  public Response add(Project project) {
+    LOG.info("add project:{}", project.toString());
+
+    // insert into database, return id
+    try {
+      projectService.add(project);
+    } catch (Exception e) {
+      LOG.error(e.getMessage(), e);
+      return new JsonResponse.Builder<>(Response.Status.OK).success(false)
+          .message("Save project failed!").build();
+    }
+
+    return new JsonResponse.Builder<Project>(Response.Status.OK)
+        .message("Save project successfully!").result(project).success(true).build();
+  }
+
+  @PUT
+  @Path("/edit")
+  @SubmarineApi
+  public Response edit(Project project) {
+    LOG.info("edit project:{}", project.toString());
+
+    try {
+      // update project
+      projectService.updateByPrimaryKeySelective(project);
+    } catch (Exception e) {
+      return new JsonResponse.Builder<>(Response.Status.OK).success(false)
+          .message("Update project failed!").build();
+    }
+
+    return new JsonResponse.Builder<>(Response.Status.OK)
+        .message("Update project successfully!").success(true).build();
+  }
+
+  @DELETE
+  @Path("/delete")
+  @SubmarineApi
+  public Response delete(@QueryParam("id") String id) {
+    // TODO(zhulinhao): At the front desk need to id
+    LOG.info("delete project:{}", id);
+
+    try {
+      projectService.delete(id);
+    } catch (Exception e) {
+      LOG.error(e.getMessage(), e);
+      return new JsonResponse.Builder<>(Response.Status.OK).success(false)
+          .message("Delete project failed!").build();
+    }
+
+    return new JsonResponse.Builder<>(Response.Status.OK)
+        .message("Delete project successfully!").success(true).build();
+  }
 }
diff --git a/submarine-workbench/workbench-server/src/main/resources/org/apache/submarine/database/mappers/ProjectMapper.xml b/submarine-workbench/workbench-server/src/main/resources/org/apache/submarine/database/mappers/ProjectMapper.xml
index 3911db4..b70f04e 100644
--- a/submarine-workbench/workbench-server/src/main/resources/org/apache/submarine/database/mappers/ProjectMapper.xml
+++ b/submarine-workbench/workbench-server/src/main/resources/org/apache/submarine/database/mappers/ProjectMapper.xml
@@ -28,16 +28,18 @@
   </resultMap>
 
   <resultMap id="resultMap" type="org.apache.submarine.database.entity.Project" extends="BaseEntityResultMap">
-    <result column="project_name" jdbcType="VARCHAR" property="projectName" />
-    <result column="visibility" jdbcType="INTEGER" property="visibility" />
-    <result column="type" jdbcType="INTEGER" property="type" />
+    <result column="name" jdbcType="VARCHAR" property="name" />
+    <result column="visibility" jdbcType="VARCHAR" property="visibility" />
+    <result column="permission" jdbcType="VARCHAR" property="permission" />
+    <result column="type" jdbcType="VARCHAR" property="type" />
     <result column="description" jdbcType="VARCHAR" property="description" />
     <result column="user_name" jdbcType="VARCHAR" property="userName" />
+    <result column="team_name" jdbcType="VARCHAR" property="teamName" />
   </resultMap>
 
   <sql id="Base_Column_List">
-    id, project_name, visibility, type, description, user_name, create_by, create_time, 
-    update_by, update_time
+    id, name, visibility, permission, type, description, user_name, team_name,
+    create_by, create_time, update_by, update_time
   </sql>
 
   <select id="selectAll" parameterType="java.util.Map" resultMap="resultMap">
@@ -50,7 +52,7 @@
   </select>
 
   <select id="selectByPrimaryKey" parameterType="java.lang.String" resultMap="resultMap">
-    select 
+    select
     <include refid="Base_Column_List" />
     from project
     where id = #{id,jdbcType=VARCHAR}
@@ -65,11 +67,13 @@
     <selectKey keyProperty="id" resultType="java.lang.String" order="BEFORE">
       SELECT REPLACE(UUID(),"-","")
     </selectKey>
-    insert into project (id, project_name, visibility, 
-      type, description, user_name, 
+    insert into project (id, name, visibility,
+      permission, type, description,
+      user_name, team_name,
       create_by, create_time, update_by, update_time)
-    values (#{id,jdbcType=VARCHAR}, #{projectName,jdbcType=VARCHAR}, #{visibility,jdbcType=INTEGER}, 
-      #{type,jdbcType=INTEGER}, #{description,jdbcType=VARCHAR}, #{userName,jdbcType=VARCHAR}, 
+    values (#{id,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR}, #{visibility,jdbcType=VARCHAR},
+      #{permission,jdbcType=VARCHAR}, #{type,jdbcType=VARCHAR}, #{description,jdbcType=VARCHAR},
+      #{userName,jdbcType=VARCHAR}, #{teamName,jdbcType=VARCHAR},
       #{createBy,jdbcType=VARCHAR}, now(), #{updateBy,jdbcType=VARCHAR}, now())
   </insert>
 
@@ -79,12 +83,15 @@
       <if test="id != null">
         id,
       </if>
-      <if test="projectName != null">
-        project_name,
+      <if test="name != null">
+        name,
       </if>
       <if test="visibility != null">
         visibility,
       </if>
+      <if test="permission != null">
+        permission,
+      </if>
       <if test="type != null">
         type,
       </if>
@@ -94,6 +101,9 @@
       <if test="userName != null">
         user_name,
       </if>
+      <if test="teamName != null">
+        team_name,
+      </if>
       <if test="createBy != null">
         create_by,
       </if>
@@ -111,14 +121,17 @@
       <if test="id != null">
         #{id,jdbcType=VARCHAR},
       </if>
-      <if test="projectName != null">
-        #{projectName,jdbcType=VARCHAR},
+      <if test="name != null">
+        #{name,jdbcType=VARCHAR},
       </if>
       <if test="visibility != null">
-        #{visibility,jdbcType=INTEGER},
+        #{visibility,jdbcType=VARCHAR},
+      </if>
+      <if test="permission != null">
+        #{permission,jdbcType=VARCHAR},
       </if>
       <if test="type != null">
-        #{type,jdbcType=INTEGER},
+        #{type,jdbcType=VARCHAR},
       </if>
       <if test="description != null">
         #{description,jdbcType=VARCHAR},
@@ -126,6 +139,9 @@
       <if test="userName != null">
         #{userName,jdbcType=VARCHAR},
       </if>
+      <if test="teamName != null">
+        #{teamName,jdbcType=VARCHAR},
+      </if>
       <if test="createBy != null">
         #{createBy,jdbcType=VARCHAR},
       </if>
@@ -144,14 +160,17 @@
   <update id="updateByPrimaryKeySelective" parameterType="org.apache.submarine.database.entity.Project">
     update project
     <set>
-      <if test="projectName != null">
-        project_name = #{projectName,jdbcType=VARCHAR},
+      <if test="name != null">
+        name = #{name,jdbcType=VARCHAR},
       </if>
       <if test="visibility != null">
-        visibility = #{visibility,jdbcType=INTEGER},
+        visibility = #{visibility,jdbcType=VARCHAR},
+      </if>
+      <if test="permission != null">
+        permission = #{permission,jdbcType=VARCHAR},
       </if>
       <if test="type != null">
-        type = #{type,jdbcType=INTEGER},
+        type = #{type,jdbcType=VARCHAR},
       </if>
       <if test="description != null">
         description = #{description,jdbcType=VARCHAR},
@@ -159,6 +178,9 @@
       <if test="userName != null">
         user_name = #{userName,jdbcType=VARCHAR},
       </if>
+      <if test="teamName != null">
+        team_name = #{teamName,jdbcType=VARCHAR},
+      </if>
       <if test="updateBy != null">
         update_by = #{updateBy,jdbcType=VARCHAR},
       </if>
@@ -169,11 +191,13 @@
 
   <update id="updateByPrimaryKey" parameterType="org.apache.submarine.database.entity.Project">
     update project
-    set project_name = #{projectName,jdbcType=VARCHAR},
-      visibility = #{visibility,jdbcType=INTEGER},
-      type = #{type,jdbcType=INTEGER},
+    set name = #{name,jdbcType=VARCHAR},
+      visibility = #{visibility,jdbcType=VARCHAR},
+      permission = #{permission,jdbcType=VARCHAR},
+      type = #{type,jdbcType=VARCHAR},
       description = #{description,jdbcType=VARCHAR},
       user_name = #{userName,jdbcType=VARCHAR},
+      team_name = #{teamName,jdbcType=VARCHAR},
       update_by = #{updateBy,jdbcType=VARCHAR},
       update_time = #{updateTime,jdbcType=TIMESTAMP}
     where id = #{id,jdbcType=VARCHAR}
diff --git a/submarine-workbench/workbench-server/src/test/java/org/apache/submarine/database/service/ProjectServiceTest.java b/submarine-workbench/workbench-server/src/test/java/org/apache/submarine/database/service/ProjectServiceTest.java
index 180d891..c2536be 100644
--- a/submarine-workbench/workbench-server/src/test/java/org/apache/submarine/database/service/ProjectServiceTest.java
+++ b/submarine-workbench/workbench-server/src/test/java/org/apache/submarine/database/service/ProjectServiceTest.java
@@ -53,10 +53,10 @@ public class ProjectServiceTest {
 
     Project project = new Project();
     project.setDescription("ProjectServiceTest-Description");
-    project.setProjectName("ProjectServiceTest-ProjectName");
-    project.setType(1);
+    project.setName("ProjectServiceTest-ProjectName");
+    project.setType("PROJECT_TYPE_NOTEBOOK");
     project.setUserName("ProjectServiceTest-UserName");
-    project.setVisibility(1);
+    project.setVisibility("PROJECT_VISIBILITY_PRIVATE");
     project.setCreateBy("ProjectServiceTest-UserName");
     List list = new ArrayList<ProjectFiles>();
     list.add(projectFiles);
@@ -71,7 +71,7 @@ public class ProjectServiceTest {
 
     Project projectDb = projectList.get(0);
     assertEquals(project.getDescription(), projectDb.getDescription());
-    assertEquals(project.getProjectName(), projectDb.getProjectName());
+    assertEquals(project.getName(), projectDb.getName());
     assertEquals(project.getType(), projectDb.getType());
     assertEquals(project.getUserName(), projectDb.getUserName());
     assertEquals(project.getVisibility(), projectDb.getVisibility());
@@ -95,10 +95,10 @@ public class ProjectServiceTest {
 
     Project project = new Project();
     project.setDescription("ProjectServiceTest-Description");
-    project.setProjectName("ProjectServiceTest-ProjectName");
-    project.setType(1);
+    project.setName("ProjectServiceTest-ProjectName");
+    project.setType("PROJECT_TYPE_NOTEBOOK");
     project.setUserName("ProjectServiceTest-UserName");
-    project.setVisibility(1);
+    project.setVisibility("PROJECT_VISIBILITY_PRIVATE");
     project.setCreateBy("ProjectServiceTest-UserName");
     List list = new ArrayList<ProjectFiles>();
     list.add(projectFiles);
@@ -107,9 +107,9 @@ public class ProjectServiceTest {
     Boolean ret = projectService.add(project);
     assertTrue(ret);
 
-    project.setProjectName("update_projectName");
+    project.setName("update_projectName");
     project.setDescription("update_description");
-    project.setVisibility(2);
+    project.setVisibility("PROJECT_VISIBILITY_PUBLIC");
     project.setUpdateBy("project_updateBy");
     ProjectFiles projectFilesUpdate = new ProjectFiles();
     projectFilesUpdate.setFileContent("ProjectServiceTest-FileContent2");
@@ -126,7 +126,7 @@ public class ProjectServiceTest {
     assertEquals(projectList.size(), 1);
 
     Project projectDb = projectList.get(0);
-    assertEquals(project.getProjectName(), projectDb.getProjectName());
+    assertEquals(project.getName(), projectDb.getName());
     assertEquals(project.getDescription(), projectDb.getDescription());
     assertEquals(project.getVisibility(), projectDb.getVisibility());
     assertEquals(project.getUpdateBy(), projectDb.getUpdateBy());
@@ -152,10 +152,10 @@ public class ProjectServiceTest {
 
     Project project = new Project();
     project.setDescription("ProjectServiceTest-Description");
-    project.setProjectName("ProjectServiceTest-ProjectName");
-    project.setType(1);
+    project.setName("ProjectServiceTest-ProjectName");
+    project.setType("PROJECT_TYPE_NOTEBOOK");
     project.setUserName("ProjectServiceTest-UserName");
-    project.setVisibility(1);
+    project.setVisibility("PROJECT_VISIBILITY_PRIVATE");
     project.setCreateBy("ProjectServiceTest-UserName");
     List list = new ArrayList<ProjectFiles>();
     list.add(projectFiles);
diff --git a/submarine-workbench/workbench-web/public/assets/notebook_logo.png b/submarine-workbench/workbench-web/public/assets/notebook_logo.png
new file mode 100644
index 0000000..430455b
Binary files /dev/null and b/submarine-workbench/workbench-web/public/assets/notebook_logo.png differ
diff --git a/submarine-workbench/workbench-web/public/assets/python_logo.png b/submarine-workbench/workbench-web/public/assets/python_logo.png
new file mode 100644
index 0000000..24d3c09
Binary files /dev/null and b/submarine-workbench/workbench-web/public/assets/python_logo.png differ
diff --git a/submarine-workbench/workbench-web/public/assets/r_logo.png b/submarine-workbench/workbench-web/public/assets/r_logo.png
new file mode 100644
index 0000000..bcbbcfb
Binary files /dev/null and b/submarine-workbench/workbench-web/public/assets/r_logo.png differ
diff --git a/submarine-workbench/workbench-web/public/assets/scala_logo.png b/submarine-workbench/workbench-web/public/assets/scala_logo.png
new file mode 100644
index 0000000..3117e79
Binary files /dev/null and b/submarine-workbench/workbench-web/public/assets/scala_logo.png differ
diff --git a/submarine-workbench/workbench-web/src/api/system.js b/submarine-workbench/workbench-web/src/api/system.js
index 93b3111..b2b6ddb 100644
--- a/submarine-workbench/workbench-web/src/api/system.js
+++ b/submarine-workbench/workbench-web/src/api/system.js
@@ -34,10 +34,17 @@ const resetParentDept = (params) => putAction('/sys/dept/resetParentDept', param
 const ajaxGetDictItems = (code, params) => getAction(`/sys/dictItem/getDictItems/${code}`, params)
 
 // team
+const queryTeam = (params) => getAction('/team/list', params)
 const addTeam = (params) => postAction('/team/add', params)
 const editTeam = (params) => putAction('/team/edit', params)
 const deleteTeam = (params) => deleteAction('/team/delete', params)
 
+// project
+const queryProject = (params) => getAction('/project/list', params)
+const addProject = (params) => postAction('/project/add', params)
+const editProject = (params) => putAction('/project/edit', params)
+const deleteProject = (params) => deleteAction('/project/delete', params)
+
 export {
   ajaxGetDictItems,
   addUser,
@@ -61,7 +68,12 @@ export {
   deleteByDepartId,
   queryIdTree,
   resetParentDept,
+  queryTeam,
   addTeam,
   editTeam,
-  deleteTeam
+  deleteTeam,
+  queryProject,
+  addProject,
+  editProject,
+  deleteProject
 }
diff --git a/submarine-workbench/workbench-web/src/components/Dict/DictSelectTag.vue b/submarine-workbench/workbench-web/src/components/Dict/DictSelectTag.vue
index a9f768d..61f8172 100644
--- a/submarine-workbench/workbench-web/src/components/Dict/DictSelectTag.vue
+++ b/submarine-workbench/workbench-web/src/components/Dict/DictSelectTag.vue
@@ -3,7 +3,12 @@
     <a-radio v-for="(item, key) in dictOptions" :key="key" :value="item.itemName">{{ item.itemCode }}</a-radio>
   </a-radio-group>
 
-  <a-select v-else-if="tagType=='select'" :placeholder="placeholder" :disabled="disabled" :value="value" @change="handleInput">
+  <a-select
+    v-else-if="tagType=='select'"
+    :placeholder="placeholder"
+    :disabled="disabled"
+    :value="value"
+    @change="handleInput">
     <a-select-option value="">Please Select</a-select-option>
     <a-select-option v-for="(item, key) in dictOptions" :key="key" :value="item.itemCode">
       <span style="display: inline-block;width: 100%" :title=" item.itemName ">
@@ -14,11 +19,16 @@
 </template>
 
 <script>
-import { ajaxGetDictItems } from '@/api/system'
+import { ajaxGetDictItems, queryTeam } from '@/api/system'
 
 export default {
   name: 'DictSelectTag',
   props: {
+    tableName: {
+      type: String,
+      default: '',
+      required: true
+    },
     dictCode: {
       type: String,
       default: '',
@@ -57,14 +67,18 @@ export default {
     }
   },
   created () {
-    console.log(this.dictCode)
+    // console.log(this.dictCode)
     if (!this.type || this.type === 'list') {
       this.tagType = 'select'
     } else {
       this.tagType = this.type
     }
     // Get dictionary data
-    this.initDictData()
+    if (this.tableName === 'team') {
+      this.initTeamData()
+    } else {
+      this.initDictData()
+    }
   },
   methods: {
     initDictData () {
@@ -76,6 +90,31 @@ export default {
         }
       })
     },
+    initTeamData () {
+      // Initialize the dictionary array according to the dictionary Code
+      var params = {}
+      params.owner = this.$store.getters.userInfo.name
+      params.column = 'createTime'
+      params.order = 'desc'
+      params.pageNo = 0
+      params.pageSize = 100
+      queryTeam(params).then((res) => {
+        if (res.success) {
+          console.log(res.result.records)
+          if (res.result.records) {
+            var dictItems = []
+            res.result.records.forEach(function (team) {
+              console.log('team=', team)
+              var item = {}
+              item.itemCode = team.teamName
+              item.itemName = team.teamName
+              dictItems = [...dictItems, item]
+            })
+            this.dictOptions = dictItems
+          }
+        }
+      })
+    },
     handleInput (e) {
       let val
       if (this.tagType === 'radio') {
diff --git a/submarine-workbench/workbench-web/src/views/workbench/workspace/project/NewProject.vue b/submarine-workbench/workbench-web/src/views/workbench/workspace/project/NewProject.vue
index ea76779..d432f48 100644
--- a/submarine-workbench/workbench-web/src/views/workbench/workspace/project/NewProject.vue
+++ b/submarine-workbench/workbench-web/src/views/workbench/workspace/project/NewProject.vue
@@ -11,19 +11,19 @@ limitations under the License.
 -->
 <template>
   <a-card title="Create New Project">
-    <a-button shape="circle" icon="close" slot="extra" @click="showProjectList"/>
+    <a-button type="primary" shape="circle" icon="close" slot="extra" @click="showProjectList"/>
 
     <a-spin :spinning="confirmLoading">
       <a-steps :current="currentStep" class="steps">
         <a-step title="Basic Information" />
-        <a-step title="Initial Setup" />
+        <a-step title="Initial Project" />
         <a-step title="Preview Project" />
       </a-steps>
       <a-divider type="horizontal" style="width: 100%"/>
       <div class="content">
-        <step1 v-if="currentStep === 0" @nextStep="nextStep" />
-        <step2 v-if="currentStep === 1" @nextStep="nextStep" @prevStep="prevStep"/>
-        <step3 v-if="currentStep === 2" @prevStep="prevStep" @finish="finish"/>
+        <step1 v-if="currentStep === 0" v-model="project" @nextStep="nextStep" @updateProject="updateProject"/>
+        <step2 v-if="currentStep === 1" v-model="project" @nextStep="nextStep" @prevStep="prevStep" @updateProject="updateProject"/>
+        <step3 v-if="currentStep === 2" v-model="project" @prevStep="prevStep" @finish="finish" @updateProject="updateProject"/>
       </div>
     </a-spin>
   </a-card>
@@ -34,12 +34,7 @@ import pick from 'lodash.pick'
 import Step1 from './NewProjectStep1'
 import Step2 from './NewProjectStep2'
 import Step3 from './NewProjectStep3'
-
-const stepForms = [
-  ['name', 'desc'],
-  ['target', 'template', 'type'],
-  ['time', 'frequency']
-]
+import { addProject } from '@/api/system'
 
 export default {
   name: 'StepByStepModal',
@@ -61,32 +56,74 @@ export default {
       visible: false,
       confirmLoading: false,
       currentStep: 0,
-      mdl: {},
+      project: {
+        name: '',
+        userName: '',
+        description: '',
+        type: 'PROJECT_TYPE_NOTEBOOK',
+        teamName: '',
+        visibility: 'PROJECT_VISIBILITY_PRIVATE',
+        permission: 'PROJECT_PERMISSION_VIEW',
+        starNum: 0,
+        likeNum: 0,
+        messageNum: 0
+      },
       radioStyle: {
         display: 'block',
         height: '30px',
         lineHeight: '30px'
       },
+      login_user: {},
       form: this.$form.createForm(this)
     }
   },
+  computed: {
+    userInfo () {
+      return this.$store.getters.userInfo
+    }
+  },
+  created () {
+    this.login_user = this.userInfo
+  },
   methods: {
     showProjectList () {
       this.$emit('showProjectList')
     },
-    nextStep () {
+    updateProject: function (childProject) {
+      console.log('updateProject=', childProject)
+      this.project = Object.assign(this.project, childProject)
+    },
+    nextStep: function (childModel) {
+      console.log('childModel=', childModel)
+      this.project = Object.assign(this.project, childModel)
+      console.log('project = ', this.project)
       if (this.currentStep < 2) {
         this.currentStep += 1
       }
     },
-    prevStep () {
+    prevStep: function (childModel) {
+      console.log('childModel=', childModel)
+      this.project = Object.assign(this.project, childModel)
+      console.log('project = ', this.project)
       if (this.currentStep > 0) {
         this.currentStep -= 1
       }
     },
-    finish () {
-      this.$emit('showProjectList')
-      this.currentStep = 0
+    finish: function (childModel) {
+      this.project = Object.assign(this.project, childModel)
+      this.project.userName = this.login_user.name
+      this.project.createBy = this.login_user.name
+      this.project.updateBy = this.login_user.name
+
+      addProject(this.project).then((res) => {
+        if (res.success) {
+          this.$message.info(res.message)
+          this.$emit('showProjectList')
+          this.currentStep = 0
+        } else {
+          this.$message.warning(res.message)
+        }
+      })
     },
     edit (record) {
       this.visible = true
@@ -94,52 +131,6 @@ export default {
       this.$nextTick(() => {
         setFieldsValue(pick(record, []))
       })
-    },
-    handleNext (step) {
-      const { form: { validateFields } } = this
-      const currentStep = step + 1
-      if (currentStep <= 2) {
-        // stepForms
-        validateFields(stepForms[ this.currentStep ], (errors, values) => {
-          if (!errors) {
-            this.currentStep = currentStep
-          }
-        })
-        return
-      }
-      // last step
-      this.confirmLoading = true
-      validateFields((errors, values) => {
-        console.log('errors:', errors, 'val:', values)
-        if (!errors) {
-          console.log('values:', values)
-          setTimeout(() => {
-            this.confirmLoading = false
-            this.$emit('ok', values)
-          }, 1500)
-        } else {
-          this.confirmLoading = false
-        }
-      })
-    },
-    backward () {
-      this.currentStep--
-    },
-    handleCancel () {
-      // clear form & currentStep
-      this.visible = false
-      this.currentStep = 0
-    },
-    handleChange (info) {
-      const status = info.file.status
-      if (status !== 'uploading') {
-        console.log(info.file, info.fileList)
-      }
-      if (status === 'done') {
-        this.$message.success(`${info.file.name} file uploaded successfully.`)
-      } else if (status === 'error') {
-        this.$message.error(`${info.file.name} file upload failed.`)
-      }
     }
   }
 }
diff --git a/submarine-workbench/workbench-web/src/views/workbench/workspace/project/NewProjectStep1.vue b/submarine-workbench/workbench-web/src/views/workbench/workspace/project/NewProjectStep1.vue
index 10379a1..1a1be3b 100644
--- a/submarine-workbench/workbench-web/src/views/workbench/workspace/project/NewProjectStep1.vue
+++ b/submarine-workbench/workbench-web/src/views/workbench/workspace/project/NewProjectStep1.vue
@@ -14,18 +14,33 @@ limitations under the License.
     <a-form :form="form" style="max-width: 650px; margin: 10px auto 0;">
 
       <a-form-item label="Project Name" :labelCol="labelCol" :wrapperCol="wrapperCol">
-        <a-input v-decorator="['name', {rules: [{required: true}]}]" />
+        <a-input v-model="project.name" v-decorator="[ 'name', validatorRules.name]" />
       </a-form-item>
 
       <a-form-item label="Description" :labelCol="labelCol" :wrapperCol="wrapperCol">
-        <a-textarea :rows="4" v-decorator="['desc', {rules: [{required: true}]}]"></a-textarea>
+        <a-textarea :rows="4" v-model="project.description" v-decorator="['desc', validatorRules.description]" />
       </a-form-item>
 
       <a-form-item label="Visibility" :labelCol="labelCol" :wrapperCol="wrapperCol">
-        <a-radio-group v-decorator="['type', {initialValue: 0, rules: [{required: true}]}]" style="width: 100%">
-          <a-radio class="radioStyle" :value="0"><a class="a-radio_a">Private</a> - Only project collaborators can view or edit the project. </a-radio>
-          <a-radio class="radioStyle" :value="1"><a class="a-radio_a">Team</a> - All members of the team can view the project.</a-radio>
-          <a-radio class="radioStyle" :value="2"><a class="a-radio_a">Public</a> - All authenticated users can view the project.</a-radio>
+        <a-radio-group v-model="project.visibility" @change="visibilityOnChange" style="width: 100%">
+          <a-radio class="radioStyle" :value="'PROJECT_VISIBILITY_PRIVATE'"><a class="a-radio_a">Private</a> - Only project collaborators can view or edit the project. </a-radio>
+          <a-radio class="radioStyle" :value="'PROJECT_VISIBILITY_TEAM'"><a class="a-radio_a">Team</a> - All members of the team can view the project.</a-radio>
+          <dict-select-tag
+            v-if="project.visibility==='PROJECT_VISIBILITY_TEAM'"
+            tableName="team"
+            triggerChange="true"
+            @change="teamNameOnChange"
+            v-model="project.teamName"
+            v-decorator="['teamName', validatorRules.teamName]"/>
+          <a-radio class="radioStyle" :value="'PROJECT_VISIBILITY_PUBLIC'"><a class="a-radio_a">Public</a> - All authenticated users can view the project.</a-radio>
+        </a-radio-group>
+      </a-form-item>
+
+      <a-form-item label="Permission" :labelCol="labelCol" :wrapperCol="wrapperCol" v-if="project.visibility!=='PROJECT_VISIBILITY_PRIVATE'">
+        <a-radio-group v-model="project.permission" @change="permissionOnChange" style="width: 100%">
+          <a-radio class="radioStyle" :value="'PROJECT_PERMISSION_VIEW'"><a class="a-radio_a">Can View</a>{{ permissionCanView }}</a-radio>
+          <a-radio class="radioStyle" :value="'PROJECT_PERMISSION_EDIT'"><a class="a-radio_a">Can Edit</a>{{ permissionCanEdit }}</a-radio>
+          <a-radio class="radioStyle" :value="'PROJECT_PERMISSION_EXECUTE'"><a class="a-radio_a">Can Execute</a>{{ permissionCanExecute }}</a-radio>
         </a-radio-group>
       </a-form-item>
 
@@ -38,28 +53,92 @@ limitations under the License.
 </template>
 
 <script>
+import pick from 'lodash.pick'
+import DictSelectTag from '@/components/Dict/DictSelectTag.vue'
 
 export default {
-  name: 'Step1',
+  name: 'NewProjectStep1',
+  components: { DictSelectTag },
+
+  props: {
+    project: {
+      type: Object,
+      // Object or array defaults must be obtained from a factory function
+      default: function () {
+        return { }
+      },
+      required: true
+    }
+  },
+
+  model: {
+    // Pass the variable value to the child component when the parent component sets the v-model
+    prop: 'project'
+  },
+  mounted () {
+    console.log('projectaaa', this.project)
+    const that = this
+    that.form.resetFields()
+    that.$nextTick(() => {
+      that.form.setFieldsValue(pick(this.project, 'name', 'description', 'visibility', 'teamName', 'permission'))
+    })
+    that.setPermissionDesc()
+  },
+
   data () {
     return {
       labelCol: { lg: { span: 5 }, sm: { span: 5 } },
       wrapperCol: { lg: { span: 19 }, sm: { span: 19 } },
-      form: this.$form.createForm(this),
-      fileList: [],
-      uploading: false,
-      dataSourceType: 'file'
+      validatorRules: {
+        name: { rules: [{ required: true, message: 'Please enter project name!' }] },
+        description: { rules: [{ required: true, message: 'Please enter project description!' }] },
+        teamName: { rules: [{ required: true, message: 'Please select team name!' }] }
+      },
+      permissionCanView: '',
+      permissionCanEdit: '',
+      permissionCanExecute: '',
+      form: this.$form.createForm(this)
     }
   },
+
   methods: {
     nextStep () {
       const { form: { validateFields } } = this
-      // 先校验,通过表单校验后,才进入下一步
+      // Check the form, then go to the next step.
       validateFields((err, values) => {
         if (!err) {
-          this.$emit('nextStep')
+          console.log('project=', this.project)
+          this.$emit('nextStep', this.project)
         }
       })
+    },
+    visibilityOnChange (e) {
+      this.project.visibility = e.target.value
+      console.log('project.visibility=', this.project.visibility)
+      if (this.project.visibility !== 'PROJECT_VISIBILITY_TEAM') {
+        this.project.teamName = ''
+      }
+      this.setPermissionDesc()
+      this.$emit('updateProject', this.project)
+    },
+    setPermissionDesc () {
+      if (this.project.visibility !== 1) {
+        this.permissionCanView = ' - All members can view the project.'
+        this.permissionCanEdit = ' - All members can edit the project.'
+        this.permissionCanExecute = ' - All members can execute the project.'
+      } else {
+        this.permissionCanView = ' - All members of the team can view the project.'
+        this.permissionCanEdit = ' - All members of the team can edit the project.'
+        this.permissionCanExecute = ' - All members of the team can execute the project.'
+      }
+    },
+    permissionOnChange (e) {
+      this.project.permission = e.target.value
+      this.$emit('updateProject', this.project)
+    },
+    teamNameOnChange (val) {
+      this.project.teamName = val
+      this.$emit('updateProject', this.project)
     }
   }
 }
diff --git a/submarine-workbench/workbench-web/src/views/workbench/workspace/project/NewProjectStep2.vue b/submarine-workbench/workbench-web/src/views/workbench/workspace/project/NewProjectStep2.vue
index 8180a06..ea7771f 100644
--- a/submarine-workbench/workbench-web/src/views/workbench/workspace/project/NewProjectStep2.vue
+++ b/submarine-workbench/workbench-web/src/views/workbench/workspace/project/NewProjectStep2.vue
@@ -11,33 +11,49 @@ limitations under the License.
 -->
 <template>
   <div style="margin: 20px auto 0;">
-    <a-tabs defaultActiveKey="1" tab-position="top" type="card">
-      <a-tab-pane key="1">
-        <span slot="tab">
-          <a-icon type="file" />
-          Blank
-        </span>
-        <div class="tab-content"><p>You can either create a blank project.</p></div>
-      </a-tab-pane>
+    <a-tabs defaultActiveKey="2" tab-position="top" type="card">
 
-      <a-tab-pane key="2">
+      <a-tab-pane key="1" disabled>
         <span slot="tab">
-          <a-icon type="database" />
-          Template
+          <a-tooltip placement="top" >
+            <template slot="title">
+              <span>Future is under development</span>
+            </template>
+            <a-icon type="database" />
+            Template
+          </a-tooltip>
         </span>
         <a-table
+          bordered
+          rowKey="id"
           :columns="templateColumns"
           :dataSource="templateDataSource"
           :pagination="false"
+          :locale="{emptyText: 'No data record'}"
           :scroll="{ y: 300 }"
-          bordered
+          :customRow="templateCustomRow"
+          :rowSelection="{type:'radio', onChange:onTemplateSelectChange, selectedRowKeys:templateSelectedRowKeys}"
         >
         </a-table>
       </a-tab-pane>
-      <a-tab-pane key="3">
+
+      <a-tab-pane key="2">
+        <span slot="tab">
+          <a-icon type="file" />
+          Blank
+        </span>
+        <div class="tab-content"><p>You can either create a blank project.</p></div>
+      </a-tab-pane>
+
+      <a-tab-pane key="3" disabled>
         <span slot="tab">
-          <a-icon type="upload" />
-          Upload
+          <a-tooltip placement="top" >
+            <template slot="title">
+              <span>Future is under development</span>
+            </template>
+            <a-icon type="upload" />
+            Upload
+          </a-tooltip>
         </span>
         <a-upload-dragger name="file" :multiple="true" action="https://www.mocky.io/v2/5cc8019d300000980a055e76">
           <p class="ant-upload-drag-icon">
@@ -47,17 +63,28 @@ limitations under the License.
           <p class="ant-upload-hint">Support for a single or bulk upload. Strictly prohibit from uploading company data or other band files</p>
         </a-upload-dragger>
       </a-tab-pane>
-      <a-tab-pane key="4">
+
+      <a-tab-pane key="4" disabled>
         <span slot="tab">
-          <a-icon type="github" />
-          Git Repo
+          <a-tooltip placement="top" >
+            <template slot="title">
+              <span>Future is under development</span>
+            </template>
+            <a-icon type="github" />
+            Git Repo
+          </a-tooltip>
         </span>
         <a-table
+          style="height: 325px"
+          bordered
+          rowKey="id"
           :columns="gitColumns"
           :dataSource="gitDataSource"
           :pagination="false"
+          :locale="{emptyText: 'No data record'}"
           :scroll="{ y: 300 }"
-          bordered
+          :customRow="gitCustomRow"
+          :rowSelection="{type:'radio', onChange:onGitSelectChange, selectedRowKeys:gitSelectedRowKeys}"
         >
         </a-table>
       </a-tab-pane>
@@ -73,7 +100,22 @@ limitations under the License.
 <script>
 
 export default {
-  name: 'Step1',
+  name: 'NewProjectStep2',
+  props: {
+    project: {
+      type: Object,
+      // Object or array defaults must be obtained from a factory function
+      default: function () {
+        return { }
+      },
+      required: true
+    }
+  },
+
+  model: {
+    // Pass the variable value to the child component when the parent component sets the v-model
+    prop: 'project'
+  },
   data () {
     return {
       labelCol: { lg: { span: 5 }, sm: { span: 5 } },
@@ -184,21 +226,66 @@ export default {
           name: 'PyTorch test',
           description: 'Apache {Submarine} is the latest machine learning framework'
         }
-      ]
+      ],
+      templateSelectedRowKeys: [],
+      templateSelectedRows: [],
+      gitSelectedRowKeys: [],
+      gitSelectedRows: []
     }
   },
+
   methods: {
     nextStep () {
       const { form: { validateFields } } = this
       // 先校验,通过表单校验后,才进入下一步
       validateFields((err, values) => {
         if (!err) {
-          this.$emit('nextStep')
+          console.log('project21=', this.project)
+          this.$emit('nextStep', this.project)
         }
       })
     },
     prevStep () {
-      this.$emit('prevStep')
+      console.log('project22=', this.project)
+      this.$emit('prevStep', this.project)
+    },
+    cleanSelect () {
+      this.templateSelectedRowKeys = []
+      this.templateSelectedRows = []
+      this.gitSelectedRowKeys = []
+      this.gitSelectedRows = []
+    },
+    onTemplateSelectChange (selectedRowKeys, selectedRows) {
+      this.cleanSelect()
+      this.templateSelectedRowKeys = selectedRowKeys
+      this.templateSelectedRows = selectedRows
+    },
+    onGitSelectChange (selectedRowKeys, selectedRows) {
+      this.cleanSelect()
+      this.gitSelectedRowKeys = selectedRowKeys
+      this.gitSelectedRows = selectedRows
+    },
+    templateCustomRow (record, index) {
+      return {
+        on: {
+          click: () => {
+            this.cleanSelect()
+            this.templateSelectedRowKeys.push(index)
+            this.templateSelectedRows.push(record)
+          }
+        }
+      }
+    },
+    gitCustomRow (record, index) {
+      return {
+        on: {
+          click: () => {
+            this.cleanSelect()
+            this.gitSelectedRowKeys.push(index)
+            this.gitSelectedRows.push(record)
+          }
+        }
+      }
     }
   }
 }
@@ -210,13 +297,13 @@ export default {
     border: 1px dashed #e9e9e9;
     border-radius: 6px;
     background-color: #fafafa;
-    min-height: 200px;
+    min-height: 325px;
     text-align: center;
     // padding-top: 30px;
 
     p {
-      padding-top: 80px;
-      font-size: 20px;
+      padding-top: 145px;
+      font-size: 24px;
     }
   }
 
diff --git a/submarine-workbench/workbench-web/src/views/workbench/workspace/project/NewProjectStep3.vue b/submarine-workbench/workbench-web/src/views/workbench/workspace/project/NewProjectStep3.vue
index ac33c74..f0f3814 100644
--- a/submarine-workbench/workbench-web/src/views/workbench/workspace/project/NewProjectStep3.vue
+++ b/submarine-workbench/workbench-web/src/views/workbench/workspace/project/NewProjectStep3.vue
@@ -12,29 +12,30 @@ limitations under the License.
 <template>
   <div>
     <a-row style="margin-bottom: 8px; margin-left: 8px; margin-right: 8px;">
-      <a-col :span="6"><span style="font-size: 18px; font-weight: bold">Files</span></a-col>
+      <a-col :span="6"><span style="font-size: 18px; font-weight: bold">Project Files</span></a-col>
       <a-col :span="12"></a-col>
       <a-col :span="6" style="text-align: right">
       </a-col>
     </a-row>
 
     <a-table
+      style="height: 300px"
       :columns="projectColumns"
-      :dataSource="projectDataSource"
       :pagination="false"
       :scroll="{ y: 300 }"
+      :locale="{emptyText: 'No data record'}"
       bordered
     >
 
       <template slot="footer">
-        Total: 35 files
+        Total: 0 files
       </template>
     </a-table>
 
-    <div style="text-align: center; margin-top: 40px;">
+    <div style="text-align: center; margin-top: 100px;">
       <a-button @click="prevStep"><a-icon type="left" />Previous Step</a-button>
-      <a-button style="margin-left: 8px" type="primary" @click="finish" icon="check">Submit</a-button>
-      <a-button style="margin-left: 8px" type="primary" icon="form">Open Workbench</a-button>
+      <a-button style="margin-left: 8px" type="primary" @click="finish" icon="save">Save</a-button>
+      <a-button style="margin-left: 8px" type="primary" icon="form">Open In Notebook</a-button>
     </div>
   </div>
 </template>
@@ -43,10 +44,25 @@ limitations under the License.
 import { STable } from '@/components'
 
 export default {
-  name: 'DataPage',
+  name: 'NewProjectStep3',
   components: {
     STable
   },
+  props: {
+    project: {
+      type: Object,
+      // Object or array defaults must be obtained from a factory function
+      default: function () {
+        return { }
+      },
+      required: true
+    }
+  },
+
+  model: {
+    // Object or array defaults must be obtained from a factory function
+    prop: 'project'
+  },
   data () {
     return {
       labelCol: { lg: { span: 5 }, sm: { span: 5 } },
@@ -139,10 +155,12 @@ export default {
   },
   methods: {
     prevStep () {
-      this.$emit('prevStep')
+      console.log('project31=', this.project)
+      this.$emit('prevStep', this.project)
     },
     finish () {
-      this.$emit('finish')
+      console.log('project32=', this.project)
+      this.$emit('finish', this.project)
     }
   }
 }
diff --git a/submarine-workbench/workbench-web/src/views/workbench/workspace/project/ProjectList.vue b/submarine-workbench/workbench-web/src/views/workbench/workspace/project/ProjectList.vue
index dce670d..7ad6e11 100644
--- a/submarine-workbench/workbench-web/src/views/workbench/workspace/project/ProjectList.vue
+++ b/submarine-workbench/workbench-web/src/views/workbench/workspace/project/ProjectList.vue
@@ -20,16 +20,21 @@ limitations under the License.
       <a-list
         :grid="{gutter: 24, lg: 3, md: 2, sm: 1, xs: 1}"
         :dataSource="data"
+        :pagination="pagination"
       >
         <a-list-item slot="renderItem" slot-scope="item">
           <template>
             <a-card :hoverable="true">
               <a-card-meta style="min-height: 148px">
-                <div style="margin-bottom: 3px;" slot="title">{{ item.title }}
+                <div style="margin-bottom: 3px;" slot="title">{{ item.name }}
 
                 </div>
-                <a-avatar class="card-avatar" slot="avatar" size="large" v-if="item.mlType === 'tensorflow'" :src="'assets/tensorflow_logo.png'"/>
-                <a-avatar class="card-avatar" slot="avatar" size="large" v-if="item.mlType === 'pytorch'" :src="'assets/pytorch_logo.png'"/>
+                <a-avatar class="card-avatar" slot="avatar" size="large" v-if="item.type === 'PROJECT_TYPE_NOTEBOOK'" :src="'assets/notebook_logo.png'"/>
+                <a-avatar class="card-avatar" slot="avatar" size="large" v-if="item.type === 'PROJECT_TYPE_PYTHON'" :src="'assets/python_logo.png'"/>
+                <a-avatar class="card-avatar" slot="avatar" size="large" v-if="item.type === 'PROJECT_TYPE_R'" :src="'assets/r_logo.png'"/>
+                <a-avatar class="card-avatar" slot="avatar" size="large" v-if="item.type === 'PROJECT_TYPE_SCALA'" :src="'assets/scala_logo.png'"/>
+                <a-avatar class="card-avatar" slot="avatar" size="large" v-if="item.type === 'PROJECT_TYPE_TENSORFLOW'" :src="'assets/tensorflow_logo.png'"/>
+                <a-avatar class="card-avatar" slot="avatar" size="large" v-if="item.type === 'PROJECT_TYPE_PYTORCH'" :src="'assets/pytorch_logo.png'"/>
 
                 <div slot="title">
                   <template v-for="(tag, index) in tags">
@@ -64,35 +69,18 @@ limitations under the License.
                 </div>
 
                 <div class="meta-content" slot="description"><ellipsis :length="140">{{ item.description }}</ellipsis><br/>
-                  <a slot="description" class="list-content-item_icon"><a-icon type="star-o"/>{{ item.star }}</a>
+                  <a slot="description" class="list-content-item_icon"><a-icon type="star-o"/>{{ item.starNum }}</a>
                   <a-divider slot="title" type="vertical" class="list-content-item_divider"/>
-                  <a slot="description" class="list-content-item_icon"><a-icon type="like-o"/>{{ item.like }}</a>
+                  <a slot="description" class="list-content-item_icon"><a-icon type="like-o"/>{{ item.likeNum }}</a>
                   <a-divider slot="title" type="vertical" class="list-content-item_divider"/>
-                  <a slot="description" class="list-content-item_icon"><a-icon type="message"/>{{ item.message }}</a>
+                  <a slot="description" class="list-content-item_icon"><a-icon type="message"/>{{ item.messageNum }}</a>
                 </div>
               </a-card-meta>
               <template class="ant-card-actions" slot="actions">
                 <a><a-icon type="edit"/> Edit</a>
                 <a><a-icon type="download"/> Download</a>
                 <a><a-icon type="setting"/> Setting</a>
-                <a>
-                  <a-dropdown>
-                    <a class="ant-dropdown-link" href="javascript:;">
-                      <a-icon type="ellipsis"/>
-                    </a>
-                    <a-menu slot="overlay">
-                      <a-menu-item>
-                        <a href="javascript:;">1st menu item</a>
-                      </a-menu-item>
-                      <a-menu-item>
-                        <a href="javascript:;">2nd menu item</a>
-                      </a-menu-item>
-                      <a-menu-item>
-                        <a href="javascript:;">3rd menu item</a>
-                      </a-menu-item>
-                    </a-menu>
-                  </a-dropdown>
-                </a>
+                <a><a-icon type="delete"/> Delete</a>
               </template>
             </a-card>
           </template>
@@ -106,94 +94,9 @@ limitations under the License.
 import HeadInfo from '@/components/tools/HeadInfo'
 import Ellipsis from '@/components/Ellipsis'
 import NewProject from './NewProject'
-
-const dataSource = []
-dataSource.push(null)
-for (let i = 0; i < 11; i++) {
-  dataSource.push({
-    title: 'Alipay',
-    avatar: 'https://gw.alipayobjects.com/zos/rmsportal/WdGqmHpayyMjiEhcKoVE.png',
-    description: ''
-  })
-}
+import { queryProject } from '@/api/system'
 
 const data = []
-data.push({
-  mlType: 'tensorflow',
-  title: 'Tensorflow-test1',
-  description: 'Basic Operations on multi-GPU (notebook) (code). A simple example to introduce multi-GPU in TensorFlow. Basic Operations on multi-GPU (notebook) (code). A simple example to introduce multi-GPU in TensorFlow. A simple example to introduce multi-GPU in TensorFlow. Train a Neural Network on multi-GPU (notebook) (code)',
-  owner: 'Frank',
-  star: 10,
-  like: 24,
-  message: 2,
-  visibility: 'Private',
-  runTimes: 2,
-  lastRunTime: '2018-07-26 22:44',
-  progress: {
-    value: 90
-  }
-})
-data.push({
-  mlType: 'pytorch',
-  title: 'Tensorflow-test1',
-  description: 'Basic Operations on multi-GPU (notebook) (code). ',
-  owner: 'Frank',
-  star: 10,
-  like: 24,
-  message: 2,
-  visibility: 'Private',
-  runTimes: 2,
-  lastRunTime: '2018-07-26 22:44',
-  progress: {
-    value: 54
-  }
-})
-data.push({
-  mlType: 'tensorflow',
-  title: 'Tensorflow-test1',
-  description: 'Basic Operations on multi-GPU (notebook) (code). A simple example to introduce multi-GPU in TensorFlow. Train a Neural Network on multi-GPU (notebook) (code)',
-  owner: 'Frank',
-  star: 10,
-  like: 24,
-  message: 2,
-  visibility: 'Public',
-  runTimes: 2,
-  lastRunTime: '2018-07-26 22:44',
-  progress: {
-    value: 66
-  }
-})
-data.push({
-  mlType: 'pytorch',
-  title: 'Tensorflow-test1',
-  description: 'Basic Operations on multi-GPU (notebook) (code). A simple example to introduce multi-GPU in TensorFlow. Train a Neural Network on multi-GPU (notebook) (code)',
-  owner: 'Frank',
-  star: 10,
-  like: 24,
-  message: 2,
-  visibility: 'Public',
-  runTimes: 2,
-  lastRunTime: '2018-07-26 22:44',
-  progress: {
-    value: 30
-  }
-})
-data.push({
-  mlType: 'tensorflow',
-  title: 'Tensorflow-test1',
-  description: 'Basic Operations on multi-GPU (notebook) (code). A simple example to introduce multi-GPU in TensorFlow. Train a Neural Network on multi-GPU (notebook) (code)',
-  owner: 'Frank',
-  star: 10,
-  like: 24,
-  message: 2,
-  visibility: 'Public',
-  runTimes: 2,
-  lastRunTime: '2018-07-26 22:44',
-  progress: {
-    status: 'exception',
-    value: 100
-  }
-})
 
 export default {
   name: 'StandardList',
@@ -207,14 +110,51 @@ export default {
       tags: ['python', 'test', 'mnist'],
       tagInputVisible: false,
       tagInputValue: '',
-
-      dataSource,
       data,
-      activeTabKey: '1'
+      activeTabKey: '1',
+      login_user: {},
+      pagination: {
+        pageSize: 9,
+        onChange: (page) => {
+          this.pagination.current = page
+          this.loadProjectData()
+        }
+      }
     }
   },
-
+  computed: {
+    userInfo () {
+      return this.$store.getters.userInfo
+    }
+  },
+  created () {
+    this.login_user = this.userInfo
+    this.loadProjectData(1)
+  },
   methods: {
+    loadProjectData () {
+      var params = {
+        userName: this.login_user.name,
+        column: 'update_time',
+        order: 'desc',
+        pageNo: this.pagination.current,
+        pageSize: this.pagination.pageSize
+      }
+
+      queryProject(params).then((res) => {
+        console.log('res=', res)
+        if (res.success) {
+          this.data = res.result.records
+          this.pagination.total = res.result.total
+        } else {
+          this.$message.error(res.message)
+        }
+      })
+    },
+    changePage (page, pageSize) {
+      console.log('page=', page)
+      console.log('pageSize=', pageSize)
+    },
     onShowNewProject () {
       this.$emit('showNewProject')
     },