Просмотр исходного кода

Merge branch 'dev_schedule_tree' into dev_drawer

chenyitao.0928 5 лет назад
Родитель
Сommit
5516063d6e

+ 1 - 1
.env

@@ -1,2 +1,2 @@
-VUE_APP_PORTAL_HOST='http://p.seecoder.cn'
+VUE_APP_PORTAL_HOST='p.seecoder.cn'
 VUE_APP_API_URL = '/api'

+ 8 - 5
mock/index.js

@@ -11,11 +11,13 @@ app.use(bodyParser.json());
 // 添加跨域
 app.use((req, res, next) => {
   res.header('Access-Control-Allow-Origin', '*');
-  res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS');
-  res.header('Access-Control-Allow-Headers', 'X-Requested-With');
-  res.header('Access-Control-Allow-Headers', 'Content-Type');
-  res.header('Access-Control-Allow-Headers', 'Authorization');
-  next();
+  res.header('Access-Control-Allow-Methods', '*');
+  res.header('Access-Control-Allow-Headers', '*');
+  if (req.method === 'OPTIONS') {
+    res.sendStatus(200);
+  } else {
+    next();
+  }
 });
 
 const prefix = '/api';
@@ -25,6 +27,7 @@ app.use(`${prefix}/project`, require('./routers/project'));
 app.use(`${prefix}/pipelines`, require('./routers/pipeline'));
 app.use(`${prefix}/deployments`, require('./routers/deployment'));
 app.use(`${prefix}/tree`, require('./routers/tree'));
+app.use(`${prefix}/bug_list`, require('./routers/buglist'));
 
 app.listen(4000);
 

+ 70 - 0
mock/routers/buglist.js

@@ -0,0 +1,70 @@
+/* eslint-disable max-len */
+/* eslint-disable import/no-extraneous-dependencies */
+/* eslint-disable @typescript-eslint/no-var-requires */
+const router = require('express').Router();
+const Mock = require('mockjs');
+const wrapper = require('../wrapper');
+
+const { Random } = Mock;
+
+router.post('', (req, res) => {
+  res.json(wrapper('success'));
+});
+
+router.put('', (req, res) => {
+  res.json(wrapper('success'));
+});
+
+router.post('/finish', (req, res) => {
+  res.json(wrapper('success'));
+});
+
+router.get('/list', (req, res) => {
+  res.json(wrapper(Mock.mock(
+    {
+      'data|5-10': {
+        'commits|0-10': [
+          {
+            'gitlabUsername|1': ['hks', 'wph', 'zxr', 'cyt'],
+            'id|+1': 101,
+            message: 'git commit message',
+            timestamp: 'time',
+            title: 'titleeee',
+          },
+        ],
+        description: 'bug description',
+        endTime: {
+          date: 0,
+          day: 0,
+          hours: 0,
+          minutes: 0,
+          month: 0,
+          nanos: 0,
+          seconds: 0,
+          time: 0,
+          timezoneOffset: 0,
+          year: 0,
+        },
+        expectedTimeCost: 140,
+        'id|+5': 1001,
+        priority: ['紧急', '一般', '宽松'],
+        startTime: {
+          date: 0,
+          day: 0,
+          hours: 0,
+          minutes: 0,
+          month: 0,
+          nanos: 0,
+          seconds: 0,
+          time: 0,
+          timezoneOffset: 0,
+          year: 0,
+        },
+        'state|1': ['CREATED', 'PROCESSING', 'FINISHED'],
+        title: 'bug titleeee',
+      },
+    },
+  ).data));
+});
+
+module.exports = router;

+ 2 - 2
mock/routers/deployment.js

@@ -6,11 +6,11 @@ const wrapper = require('../wrapper');
 
 const { Random } = Mock;
 
-router.delete((_, res) => {
+router.delete('', (_, res) => {
   res.json(wrapper('success'));
 });
 
-router.post((_, res) => {
+router.post('', (_, res) => {
   res.json(wrapper('success'));
 });
 

+ 7 - 3
mock/routers/pipeline.js

@@ -7,11 +7,11 @@ const wrapper = require('../wrapper');
 
 const { Random } = Mock;
 
-router.delete((req, res) => {
+router.delete('', (req, res) => {
   res.json(wrapper('success'));
 });
 
-router.get((req, res) => {
+router.get('', (req, res) => {
   res.json(wrapper({
     configJson: {
       code: 0,
@@ -25,10 +25,14 @@ router.get((req, res) => {
   }));
 });
 
-router.post((req, res) => {
+router.post('', (req, res) => {
   res.json(wrapper('success'));
 });
 
+router.get('/templates', (req, res) => {
+  res.json(wrapper(['Java', 'MySQL', 'Node', 'Go', 'Android']));
+});
+
 router.put('/config', (req, res) => {
   res.json(wrapper('success'));
 });

+ 8 - 16
mock/routers/project.js

@@ -46,25 +46,17 @@ router.get('/listByUser/:userId',
     ));
   });
 
-router.get('/listByGroup/:groupId',
-  (req, res) => {
-    const { groupId } = req.params;
-    res.json(wrapper(
-      Mock.mock(Mock.mock({
-        'data|3-5': [{
-          'id|+1': 100,
-          description: Random.csentence(5, 40),
-          gitRemoteUrl: Random.url('http', 'gitlab.seecoder.cn'),
-          groupId,
-          name: Random.cname(),
-        }],
-      }).data),
-    ));
-  });
-
 router.get('/delete/:projectId',
   (req, res) => {
     res.json(wrapper({}));
   });
 
+router.get('/members', (req, res) => {
+  res.json(wrapper([101, 102, 103, 104]));
+});
+
+router.get('/refresh', (req, res) => {
+  res.json(wrapper('success'));
+});
+
 module.exports = router;

+ 5 - 0
mock/routers/user.js

@@ -12,3 +12,8 @@ router.get('/self',
     res.json(wrapper(userinfo));
   });
 module.exports = router;
+
+router.post('/gitlab',
+  (req, res) => {
+    res.json(wrapper('success'));
+  });

+ 34 - 0
src/api/devcloud/buglist.ts

@@ -0,0 +1,34 @@
+import axios from '@/utils/axios';
+
+import { BUGLIST_PREFIX } from './prefix';
+
+interface BugRecordPayload {
+  description: string;
+  expectedTimeCost: number;
+  priority: '紧急'|'一般'|'宽松';
+  title: string;
+}
+
+type CreateBugRecordPayload = BugRecordPayload & {
+  projectId: number;
+}
+
+type UpdateBugRecordPayload = BugRecordPayload & {
+  id: number;
+}
+
+export const createBugRecord = (createBugRecordPayload: CreateBugRecordPayload) => axios.post(`${BUGLIST_PREFIX}`, createBugRecordPayload);
+
+export const updateBugRecord = (updateBugRecordPayload: UpdateBugRecordPayload) => axios.post(`${BUGLIST_PREFIX}`, updateBugRecordPayload);
+
+export const finishBugRecord = (id: number) => axios.post(`${BUGLIST_PREFIX}/finish`, null, {
+  params: {
+    id,
+  },
+});
+
+export const getBugListByProjectId = (projectId: number) => axios.get(`${BUGLIST_PREFIX}/list`, {
+  params: {
+    projectId,
+  },
+});

+ 4 - 0
src/api/pipeline.ts → src/api/devcloud/pipeline.ts

@@ -16,6 +16,7 @@ type UpdatePipelinePayload = PipelinePayload & {
   configJson: string;
 }
 
+// PIPELINE API
 export const deletePipeline = (pipeline: PipelinePayload) => axios.delete(`${PIPELINE_PREFIX}`, {
   params: pipeline,
 });
@@ -29,6 +30,8 @@ export const createPipeline = (createPipelinePayload: CreatePipelinePayload) =>
 export const updatePipeline = (updatePipelinePayload: UpdatePipelinePayload) => axios.put(`${PIPELINE_PREFIX}/config`,
   updatePipelinePayload);
 
+export const getPipelineTemplates = () => axios.get(`${PIPELINE_PREFIX}/templates`);
+
 export const getPipelineHistory = (pipeline: PipelinePayload) => axios.get(`${PIPELINE_PREFIX}/history/list`, {
   params: pipeline,
 });
@@ -39,6 +42,7 @@ export const getPipelinesByProjectId = (projectId: number) => axios.get(`${PIPEL
   },
 });
 
+// DEPLOYMENT API
 export const deleteInstance = (deploymentId: number) => axios.delete(`${DEPLOYMENT_PREFIX}`, {
   params: {
     deploymentId,

+ 2 - 0
src/api/prefix.ts → src/api/devcloud/prefix.ts

@@ -3,3 +3,5 @@ export const DEPLOYMENT_PREFIX = '/deployments';
 export const USER_PREFIX = '/users';
 export const TREE_PREFIX = '/tree';
 export const PROJECT_PREFIX = '/project';
+export const GROUP_PREFIX = '/groups';
+export const BUGLIST_PREFIX = '/bug_list';

+ 12 - 3
src/api/project.ts → src/api/devcloud/project.ts

@@ -2,12 +2,21 @@ import axios from '@/utils/axios';
 
 import { PROJECT_PREFIX } from './prefix';
 
+interface AddProjectMemberPayload {
+  invitedUserId: number;
+  projectId: number;
+}
+
 export const getProjectById = (projectId: number) => axios.get(`${PROJECT_PREFIX}/${projectId}`);
 
-export const createProject = (project: Project) => axios.post(`${PROJECT_PREFIX}/create`, project);
+export const createProject = (name: string, description: string) => axios.post(`${PROJECT_PREFIX}/create`, {
+  name,
+  description,
+});
 
 export const getProjectListByUserId = (userId: number) => axios.get(`${PROJECT_PREFIX}/listByUser/${userId}`);
 
-export const getProjectListByGroupId = (groupId: number) => axios.get(`${PROJECT_PREFIX}/listByGroup/${groupId}`);
-
 export const deleteProject = (projectId: string) => axios.get(`${PROJECT_PREFIX}/delete/${projectId}`);
+
+export const addProjectMember = (addProjectMemberPayload: AddProjectMemberPayload) => axios.post(`${PROJECT_PREFIX}/members`,
+  addProjectMemberPayload);

+ 0 - 0
src/api/tree.ts → src/api/devcloud/tree.ts


+ 15 - 0
src/api/devcloud/user.ts

@@ -0,0 +1,15 @@
+import axios from '@/utils/axios';
+
+import { USER_PREFIX } from './prefix';
+
+interface CreateGitlabAccountPayload {
+  email: string;
+  password: string;
+  username: string;
+}
+
+// USER API
+export const getUserInfo = () => axios.get(`${USER_PREFIX}/self`);
+
+export const createGitlabAccount = (createGitlabAccountPayload: CreateGitlabAccountPayload) => axios.post(`${USER_PREFIX}/gitlab`,
+  createGitlabAccountPayload);

+ 6 - 4
src/api/index.ts

@@ -1,11 +1,13 @@
-import * as UserAPI from './user';
-import * as ProjectAPI from './project';
-import * as RequirementAPI from './tree';
-import * as PipelineAPI from './pipeline';
+import * as UserAPI from './devcloud/user';
+import * as ProjectAPI from './devcloud/project';
+import * as RequirementAPI from './devcloud/tree';
+import * as PipelineAPI from './devcloud/pipeline';
+import * as PortalAPI from './portal';
 
 export {
   UserAPI,
   ProjectAPI,
   RequirementAPI,
   PipelineAPI,
+  PortalAPI,
 };

+ 5 - 0
src/api/portal.ts

@@ -0,0 +1,5 @@
+import axios from '@/utils/axios';
+
+export const getUserIdByPhoneNumber = (phone: string) => axios.get(
+  `http://${process.env.VUE_APP_PORTAL_HOST}/api/user/findIdByPhone/${phone}`,
+);

+ 0 - 5
src/api/user.ts

@@ -1,5 +0,0 @@
-import axios from '@/utils/axios';
-
-import { USER_PREFIX } from './prefix';
-
-export const getUserInfo = () => axios.get(`${USER_PREFIX}/self`);

+ 128 - 30
src/components/Menu.vue

@@ -3,32 +3,20 @@
     <ElMenuItem class="menu-logo" index="logo" disabled>
       LOGO
     </ElMenuItem>
-    <ElSubmenu index="group">
-      <template #title>{{selectedGroup.name}}</template>
-      <template  v-if="groupList.length > 0">
-      <ElMenuItem v-for="group in groupList" :key="group.id" :index="`group-${group.id}`">{{group.name}}</ElMenuItem>
-      </template>
-      <template v-else>
-        <ElMenuItem disabled>暂未加入任何小组</ElMenuItem>
-      </template>
-    </ElSubmenu>
     <ElSubmenu index="project">
       <template #title>{{selectedProject.name}}</template>
       <template v-if="projectList.length > 0">
         <ElMenuItem v-for="project in projectList" :key="project.id" :index="`project-${project.id}`">{{project.name}}</ElMenuItem>
       </template>
-      <template v-else-if="selectedGroup.id === -1">
-        <ElMenuItem disabled>请先选择小组</ElMenuItem>
-      </template>
       <template v-else>
-        <ElMenuItem disabled>该小组暂无项目</ElMenuItem>
+        <ElMenuItem disabled>暂无项目</ElMenuItem>
       </template>
     </ElSubmenu>
       <template v-if="userLoggedIn">
         <ElSubmenu index="user" class="user">
           <template #title>{{userInfo.username}}</template>
-          <ElMenuItem index="user-create">创建小组</ElMenuItem>
-          <ElMenuItem index="user-join">加入小组</ElMenuItem>
+          <ElMenuItem index="user-project">创建项目</ElMenuItem>
+          <ElMenuItem index="user-invite">邀请成员</ElMenuItem>
           <ElMenuItem index="user-logout">登出</ElMenuItem>
         </ElSubmenu>
       </template>
@@ -36,21 +24,120 @@
         <ElLink type="primary" href="/login" class="user">登录</ElLink>
       </template>
   </ElMenu>
+  <ElDrawer v-model="showProjectDrawer">
+    <template #title><span>创建项目</span></template>
+    <div class="drawer-content">
+      <div class="form-container">
+        <ElForm :model="createProjectForm">
+          <ElFormItem label="项目名" prop="name">
+            <ElInput v-model="createProjectForm.name" placeholder="请输入要创建的项目名"></ElInput>
+          </ElFormItem>
+          <ElFormItem label="项目描述" prop="description">
+            <ElInput
+              v-model="createProjectForm.description"
+              placeholder="请输入项目描述"
+              type="textarea"
+              :autosize="{ minRows: 3, maxRows: 3}"
+            ></ElInput>
+          </ElFormItem>
+          <ElFormItem>
+            <ElButton @click="createProject" type="primary">创建</ElButton>
+          </ElFormItem>
+        </ElForm>
+      </div>
+    </div>
+  </ElDrawer>
+  <ElDrawer v-model="showInviteDrawer">
+    <template #title><span>邀请成员</span></template>
+    <div class="drawer-content">
+      <div class="form-container">
+      <ElForm :model="inviteProjectForm">
+        <ElFormItem label="项目" prop="projectId">
+          <ElSelect v-model="inviteProjectForm.projectId" placeholder="请选择成员将要加入的项目">
+            <ElOption v-for="project in projectList" :key="project.id" :value="project.id" :label="project.name"></ElOption>
+          </ElSelect>
+        </ElFormItem>
+        <ElFormItem label="手机号" prop="phone">
+          <ElInput v-model="inviteProjectForm.phone" placeholder="请输入成员注册所用手机号"></ElInput>
+        </ElFormItem>
+        <ElFormItem>
+          <ElButton @click="inviteMember" type="primary">邀请</ElButton>
+        </ElFormItem>
+      </ElForm>
+    </div>
+  </div>
+  </ElDrawer>
 </template>
 
 <script lang="ts">
+import { ElMessage } from 'element-plus';
+
 import { useProjectList } from '@/composable/useProject';
-import { useGroupList } from '@/composable/useGroup';
 import { useUser } from '@/composable/useUser';
-import { defineComponent } from 'vue';
+import { defineComponent, ref, reactive } from 'vue';
+import { PortalAPI, ProjectAPI } from '@/api';
 
 export default defineComponent({
   name: 'Menu',
-  setup(_, { emit }) {
-    const { projectListOfCurrentGroup: projectList, selectedProject, handleProjectChange } = useProjectList();
-    const { groupList, selectedGroup, handleGroupChange } = useGroupList();
+  setup() {
+    const { projectList, selectedProject, handleProjectChange } = useProjectList();
     const { userInfo, userLoggedIn, userLogOut } = useUser();
 
+    const showProjectDrawer = ref(false);
+    const showInviteDrawer = ref(false);
+
+    const openProjectDrawer = () => {
+      showProjectDrawer.value = true;
+    };
+    const openInviteDrawer = () => {
+      showInviteDrawer.value = true;
+    };
+    const createProjectForm = reactive({
+      name: '',
+      description: '',
+    });
+    const createProject = () => {
+      ProjectAPI.createProject(createProjectForm.name, createProjectForm.description);
+    };
+
+    interface InviteProjectForm {
+      projectId: undefined | number;
+      phone: string;
+    }
+
+    const inviteProjectForm = reactive<InviteProjectForm>({
+      projectId: undefined,
+      phone: '',
+    });
+    const inviteMember = async () => {
+      let invitedUserId: number;
+
+      const res = await PortalAPI.getUserIdByPhoneNumber(inviteProjectForm.phone) as unknown as {id: number};
+
+      if (res) {
+        invitedUserId = res.id;
+      } else {
+        ElMessage({
+          type: 'error',
+          message: '成员不存在,请再次确认手机号',
+        });
+        return;
+      }
+
+      if (!inviteProjectForm.projectId) {
+        ElMessage({
+          type: 'error',
+          message: '请选择有效的项目',
+        });
+        return;
+      }
+
+      ProjectAPI.addProjectMember({
+        projectId: inviteProjectForm.projectId,
+        invitedUserId,
+      });
+    };
+
     const handleSelect = (index: string) => {
       const [type, id] = index.split('-');
       switch (type) {
@@ -61,15 +148,11 @@ export default defineComponent({
           handleProjectChange(parseInt(id, 10));
           break;
         }
-        case 'group': {
-          handleGroupChange(parseInt(id, 10));
-          break;
-        }
         case 'user': {
-          if (id === 'create') {
-            emit('show-create-group-drawer');
-          } else if (id === 'join') {
-            emit('show-join-group-drawer');
+          if (id === 'project') {
+            openProjectDrawer();
+          } else if (id === 'invite') {
+            openInviteDrawer();
           } else if (id === 'logout') {
             userLogOut();
           }
@@ -84,12 +167,20 @@ export default defineComponent({
     return {
       projectList,
       selectedProject,
-      groupList,
-      selectedGroup,
+
       handleSelect,
+
       userInfo,
       userLoggedIn,
       userLogOut,
+
+      showProjectDrawer,
+      showInviteDrawer,
+
+      createProjectForm,
+      createProject,
+      inviteProjectForm,
+      inviteMember,
     };
   },
 });
@@ -116,4 +207,11 @@ export default defineComponent({
   height: 60px;
   margin: 0 20px;
 }
+.drawer-content {
+  display: flex;
+  justify-content: center;
+}
+.form-container {
+  width: 80%;
+}
 </style>

+ 0 - 34
src/components/SideBar.vue

@@ -24,40 +24,6 @@
 <script>
 export default {
   name: 'SideBar',
-  data() {
-    return {
-      selectedIndex: 0,
-    };
-  },
-  methods: {
-    handleClick(e) {
-      const value = e.target.attributes.target.nodeValue ? e.target.attributes.target.nodeValue : '';
-      switch (value) {
-        case 'schedule':
-          this.selectedIndex = 0;
-          break;
-        case 'taskList':
-          this.selectedIndex = 1;
-          break;
-        case 'bugList':
-          this.selectedIndex = 2;
-          break;
-        case 'statistics':
-          this.selectedIndex = 3;
-          break;
-        case 'pipeline':
-          this.selectedIndex = 4;
-          break;
-        case 'apitest':
-          this.selectedIndex = 5;
-          break;
-        default:
-          throw new TypeError('Unknown Tab Value!');
-      }
-
-      this.$emit('change', value);
-    },
-  },
 };
 </script>
 

+ 0 - 19
src/composable/useGroup.ts

@@ -1,19 +0,0 @@
-import { computed } from 'vue';
-import { useStore } from '@/store';
-import { WorkspaceDispatcher } from '@/store/dispatchers';
-
-export const useGroupList = () => {
-  const store = useStore();
-
-  const groupList = computed(() => store.state.workspace.groupList);
-  const selectedGroup = computed(() => store.getters.currentGroupInfo);
-
-  const handleGroupChange = (groupId: number) => {
-    WorkspaceDispatcher.changeGroup(groupId);
-  };
-  return {
-    groupList,
-    selectedGroup,
-    handleGroupChange,
-  };
-};

+ 0 - 2
src/composable/useProject.ts

@@ -8,7 +8,6 @@ export const useProjectList = () => {
   const router = useRouter();
 
   const projectList = computed(() => store.state.workspace.projectList);
-  const projectListOfCurrentGroup = computed(() => store.getters.projectListOfCurrentGroup);
   const selectedProject = computed(() => store.getters.currentProjectInfo);
 
   const handleProjectChange = (projectId: number) => {
@@ -17,7 +16,6 @@ export const useProjectList = () => {
   };
   return {
     projectList,
-    projectListOfCurrentGroup,
     selectedProject,
     handleProjectChange,
   };

+ 1 - 3
src/composable/useUser.ts

@@ -1,11 +1,9 @@
 import { computed } from 'vue';
 import { useStore } from '@/store';
 import { UserDispatcher, TokenDispatcher } from '@/store/dispatchers';
-import { useRouter } from 'vue-router';
 
 export const useUser = () => {
   const store = useStore();
-  const router = useRouter();
 
   const userInfo = computed(() => store.state.user);
   const userLoggedIn = computed(() => store.state.user.role !== 'GUEST');
@@ -13,7 +11,7 @@ export const useUser = () => {
   const userLogOut = () => {
     UserDispatcher.userLogOut();
     TokenDispatcher.destroyToken();
-    router.push('/');
+    window.location.href = '/';
   };
   return {
     userInfo,

+ 5 - 0
src/element-plus.ts

@@ -34,6 +34,11 @@ import {
   ElRadioGroup,
 } from 'element-plus';
 
+import ZH_CN from 'element-plus/lib/locale/lang/zh-cn';
+import locale from 'element-plus/lib/locale';
+
+locale.use(ZH_CN);
+
 export const Components = [
   ElAside,
   ElRow,

+ 16 - 16
src/router/index.ts

@@ -67,22 +67,22 @@ const router = createRouter({
   routes,
 });
 
-// router.beforeEach((to, from, next) => {
-//   if (to.matched.some((record) => record.meta.requiresAuth)) {
-//     // this route requires auth, check if logged in
-//     // if not, redirect to login page.
-//     if (!isUserLoggedIn()) {
-//       next({
-//         name: LOGIN,
-//         query: { redirect: to.fullPath },
-//       });
-//     } else {
-//       next();
-//     }
-//   } else {
-//     next();
-//   }
-// });
+router.beforeEach((to, from, next) => {
+  if (to.matched.some((record) => record.meta.requiresAuth)) {
+    // this route requires auth, check if logged in
+    // if not, redirect to login page.
+    if (!isUserLoggedIn()) {
+      next({
+        name: LOGIN,
+        query: { redirect: to.fullPath },
+      });
+    } else {
+      next();
+    }
+  } else {
+    next();
+  }
+});
 
 router.beforeEach((to, from, next) => {
   if (to.name !== LOGIN || !isUserLoggedIn()) {

+ 0 - 5
src/store/dispatchers/workspace.ts

@@ -7,8 +7,3 @@ export const refreshProjectList = () => {
 export const changeProject = (projectId: number) => {
   store.dispatch('changeProject', { projectId });
 };
-
-export const changeGroup = (groupId: number) => {
-  store.dispatch('changeGroup', { groupId });
-  store.dispatch('resetCurrentProject');
-};

+ 0 - 2
src/store/modules/token.ts

@@ -1,5 +1,4 @@
 import { saveToken, destoryToken } from '@/utils/token';
-import { useRouter } from 'vue-router';
 import { ActionTree, Module, MutationTree } from 'vuex';
 import { RootState, TokenPayload, TokenState } from '../typings';
 
@@ -17,7 +16,6 @@ const actions: ActionTree<TokenState, RootState> = {
       token: '',
     } as TokenPayload);
     destoryToken();
-    useRouter().push('/home');
   },
 };
 

+ 1 - 0
src/store/modules/user.ts

@@ -8,6 +8,7 @@ const DEFAULT: UserState = {
   id: -1,
   phone: '',
   role: 'GUEST',
+  withGitlab: false,
 };
 
 const actions: ActionTree<UserState, RootState> = {

+ 1 - 40
src/store/modules/workspace.ts

@@ -3,34 +3,11 @@ import {
   ActionTree, GetterTree, Module, MutationTree,
 } from 'vuex';
 import {
-  WorkspaceState, RootState, ChangeGroupPayload, ChangeProjectPayload,
+  WorkspaceState, RootState, ChangeProjectPayload,
 } from '../typings';
 
 const DEFAULT: WorkspaceState = {
   currentProjectId: -1,
-  currentGroupId: -1,
-  groupList: [
-    {
-      id: 101,
-      name: 'Group-1',
-    },
-    {
-      id: 102,
-      name: 'Group-2',
-    },
-    {
-      id: 13,
-      name: 'Group-3',
-    },
-    {
-      id: 104,
-      name: 'Group-4',
-    },
-    {
-      id: 105,
-      name: 'Group-5',
-    },
-  ],
   projectList: [],
 };
 
@@ -39,9 +16,6 @@ const actions: ActionTree<WorkspaceState, RootState> = {
     const projectList = await ProjectAPI.getProjectListByUserId(userId);
     commit('updateProjectList', projectList);
   },
-  changeGroup({ commit }, payload: ChangeGroupPayload) {
-    commit('changeGroup', payload);
-  },
   changeProject({ commit }, payload: ChangeProjectPayload) {
     commit('changeProject', payload);
   },
@@ -54,18 +28,12 @@ const mutations: MutationTree<WorkspaceState> = {
   updateProjectList(state, payload: Array<Project>) {
     state.projectList = payload;
   },
-  changeGroup(state, { groupId }: ChangeGroupPayload) {
-    state.currentGroupId = groupId;
-  },
   changeProject(state, { projectId }: ChangeProjectPayload) {
     state.currentProjectId = projectId;
   },
 };
 
 const getters: GetterTree<WorkspaceState, RootState> = {
-  projectListOfCurrentGroup(state) {
-    return state.projectList.filter((project) => project.groupId === state.currentGroupId);
-  },
   currentProjectInfo(state) {
     return Array.prototype.find.call(state.projectList, (project) => project.id === state.currentProjectId)
       || {
@@ -73,13 +41,6 @@ const getters: GetterTree<WorkspaceState, RootState> = {
         name: '未选择项目',
       };
   },
-  currentGroupInfo(state) {
-    return Array.prototype.find.call(state.groupList, (group) => group.id === state.currentGroupId)
-      || {
-        id: -1,
-        name: '未选择小组',
-      };
-  },
 };
 
 export const workspace: Module<WorkspaceState, RootState> = {

+ 1 - 8
src/store/typings.ts

@@ -1,7 +1,5 @@
-type GroupId = number;
 type ProjectId = number;
 
-// eslint-disable-next-line @typescript-eslint/no-empty-interface
 export interface RootState {
   token: TokenState;
   user: UserState;
@@ -18,19 +16,14 @@ export interface UserState {
   phone: string;
   role: Role;
   username: string;
+  withGitlab: boolean;
 }
 
 export interface WorkspaceState {
-  currentGroupId: GroupId;
   currentProjectId: ProjectId;
-  groupList: Array<Group>;
   projectList: Array<Project>;
 }
 
-export interface ChangeGroupPayload {
-  groupId: GroupId;
-}
-
 export interface ChangeProjectPayload {
   projectId: ProjectId;
 }

+ 2 - 8
src/typings/project.d.ts

@@ -2,17 +2,11 @@ declare interface Project {
   id: number;
   name: string;
   description: string;
-  groupId: number;
   gitRemoteUrl: string;
 }
 
-declare interface Group {
-  id: number;
-  name: string;
-}
-
-declare type TreeNodeType = '0:需求' | '1:任务';
-declare type TreeNodeState = '0:已创建' | '1:进行中' | '2:已结束';
+declare type TreeNodeType = '需求' | '任务';
+declare type TreeNodeState = '已创建' | '进行中' | '已结束';
 
 declare interface TreeNode {
   deep: number;

+ 1 - 2
src/typings/vuex.d.ts

@@ -13,11 +13,10 @@ declare module '@vue/runtime-core' {
       phone: string;
       role: Role;
       username: string;
+      withGitlab: boolean;
     };
     workspace: {
-      currentGroupId: GroupId;
       currentProjectId: ProjectId;
-      groupList: Array<Group>;
       projectList: Array<Project>;
     };
   }

+ 2 - 2
src/utils/debounce.ts

@@ -1,6 +1,6 @@
 /* eslint-disable */
 const debounce = (fn: Function, delay: number) => {
-  let timer: NodeJS.Timeout | null = null;
+  let timer: NodeJS.Timeout;
   return function () {
     // @ts-ignore
     const context = this;
@@ -8,7 +8,7 @@ const debounce = (fn: Function, delay: number) => {
     if (timer) {
       clearTimeout(timer);
     }
-    timer = setTimeout(fn.apply(context, args), delay);
+    timer = setTimeout(() => fn.apply(context, args), delay);
   };
 };
 

+ 3 - 3
src/views/Login.vue

@@ -11,9 +11,9 @@ import { defineComponent } from 'vue';
 export default defineComponent({
   methods: {
     async login() {
-      window.location = `${process.env.VUE_APP_PORTAL_HOST}/api/interaction?scope=openid&client_id=seec-seec&redirect_uri=${encodeURI(
-        `http://${window.location.host}/`,
-      )}` as unknown as Location;
+      window.location = `http://${process.env.VUE_APP_PORTAL_HOST}/api/interaction?scope=openid&client_id=seec-seec&redirect_uri=${
+        encodeURI(`http://${window.location.host}/`)
+      }` as unknown as Location;
     },
   },
 });

+ 11 - 7
src/views/Project/Pipeline.vue

@@ -134,7 +134,6 @@
           <span>编辑流水线配置</span>
           <span class="button-group">
             <ElButton type="text">脚本教程</ElButton>
-            <ElButton type="text">模板详解</ElButton>
           </span>
         </template>
         <ElInput
@@ -159,7 +158,12 @@
         }"
         destroy-on-close
       >
-        <template #title>创建流水线</template>
+        <template #title>
+          <span>创建流水线</span>
+          <span class="button-group">
+            <ElButton type="text">模板详解</ElButton>
+          </span>
+        </template>
         <ElForm
           :model="createPipelineForm"
           ref="createPipelineForm"
@@ -237,7 +241,7 @@
         <ElTableColumn>
           <template #default="scope">
             <ElButton type="text" @click="deleteProduction(scope.row.id)">
-              <i class="el-icon-delete-solid"></i>
+              <i class="el-icon-delete"></i>
             </ElButton>
           </template>
         </ElTableColumn>
@@ -267,7 +271,7 @@ export default {
       pipelineConfig: '',
       jsonValid: true,
       editingPipelineId: -1,
-      templates: ['Java项目', 'MySQL'],
+      templates: [],
       createPipelineForm: {
         name: '',
         template: '',
@@ -277,6 +281,7 @@ export default {
   async mounted() {
     this.pipelines = await PipelineAPI.getPipelinesByProjectId(this.$store.state.workspace.currentProjectId);
     this.productions = await PipelineAPI.getDeploymentsByProjectId(this.$store.state.workspace.currentProjectId);
+    this.templates = await PipelineAPI.getPipelineTemplates();
   },
   methods: {
     timeFormatter: (row, property) => `${row[property].year}/${row[property].month}/${row[property].day} `
@@ -330,15 +335,14 @@ export default {
       }
     },
     // eslint-disable-next-line func-names
-    // FIXME: debounce效果有点问题,待排查
     lintJson: debounce(function (value) {
+      this.jsonValid = true;
       try {
         const result = jsonlint.parse(value);
         this.pipelineConfig = JSON.stringify(result, null, '  ');
-        this.jsonValid = true;
       } catch (e) {
         this.jsonValid = false;
-        this.hint = e;
+        this.hint = `${value}\n${e}`;
       }
     }, 500),
     async handlePipelineUpdateConfirm() {

+ 5 - 5
src/views/ProjectContainer.vue

@@ -1,6 +1,6 @@
 <template>
   <el-aside width="65">
-    <SideBar @change="handleTabChange"></SideBar>
+    <SideBar></SideBar>
   </el-aside>
   <el-main class="container">
     <router-view></router-view>
@@ -9,14 +9,14 @@
 
 <script>
 import SideBar from '@/components/SideBar.vue';
+import { WorkspaceDispatcher } from '@/store/dispatchers';
 
 export default {
   name: 'ProjectContainer',
   components: { SideBar },
-  methods: {
-    handleTabChange(value) {
-      this.$router.push(value);
-    },
+  mounted() {
+    const { projectId } = this.$route.params;
+    WorkspaceDispatcher.changeProject(parseInt(projectId, 10));
   },
 };
 </script>