refactor(权限管理): 权限设置

This commit is contained in:
shiziyuan9527 2021-05-24 16:07:22 +08:00 committed by 刘瑞斌
parent a10099da54
commit db96d93cf4
14 changed files with 1060 additions and 56 deletions

View File

@ -66,6 +66,9 @@
<if test="proRequest.workspaceId != null"> <if test="proRequest.workspaceId != null">
and w.id = #{proRequest.workspaceId} and w.id = #{proRequest.workspaceId}
</if> </if>
<if test="proRequest.projectId != null">
and p.id = #{proRequest.projectId}
</if>
</where> </where>
<if test="proRequest.orders != null and proRequest.orders.size() > 0"> <if test="proRequest.orders != null and proRequest.orders.size() > 0">
order by order by

View File

@ -68,8 +68,12 @@ public class PermissionConstants {
public static final String WORKSPACE_TEMPLATE_READ_ISSUE_TEMPLATE = "WORKSPACE_TEMPLATE:READ+ISSUE_TEMPLATE"; public static final String WORKSPACE_TEMPLATE_READ_ISSUE_TEMPLATE = "WORKSPACE_TEMPLATE:READ+ISSUE_TEMPLATE";
public static final String WORKSPACE_TEMPLATE_READ_CUSTOM = "WORKSPACE_TEMPLATE:READ+CUSTOM"; public static final String WORKSPACE_TEMPLATE_READ_CUSTOM = "WORKSPACE_TEMPLATE:READ+CUSTOM";
public static final String WORKSPACE_TEMPLATE_READ_REPORT_TEMPLATE = "WORKSPACE_TEMPLATE:READ+REPORT_TEMPLATE"; public static final String WORKSPACE_TEMPLATE_READ_REPORT_TEMPLATE = "WORKSPACE_TEMPLATE:READ+REPORT_TEMPLATE";
public static final String WORKSPACE_PROJECT_MANAGER_READ = "WORKSPACE_PROJECT_MANAGER:READ";
public static final String WORKSPACE_PROJECT_MANAGER_READ_CREATE = "WORKSPACE_PROJECT_MANAGER:READ+CREATE";
public static final String WORKSPACE_PROJECT_MANAGER_READ_EDIT = "WORKSPACE_PROJECT_MANAGER:READ+EDIT";
public static final String WORKSPACE_PROJECT_MANAGER_READ_DELETE = "WORKSPACE_PROJECT_MANAGER:READ+DELETE";
public static final String PROJECT_USER_READ = "PROJECT_USER:READ"; public static final String PROJECT_USER_READ = "PROJECT_USER:READ";
public static final String PROJECT_USER_READ_CREATE = "PROJECT_USER:READ+CREATE"; public static final String PROJECT_USER_READ_CREATE = "PROJECT_USER:READ+CREATE";
public static final String PROJECT_USER_READ_EDIT = "PROJECT_USER:READ+EDIT"; public static final String PROJECT_USER_READ_EDIT = "PROJECT_USER:READ+EDIT";

View File

@ -10,6 +10,7 @@ import java.util.Map;
@Setter @Setter
public class ProjectRequest { public class ProjectRequest {
private String workspaceId; private String workspaceId;
private String projectId;
private String name; private String name;
private List<OrderRequest> orders; private List<OrderRequest> orders;
private Map<String, List<String>> filters; private Map<String, List<String>> filters;

View File

@ -470,9 +470,7 @@ public class UserService {
List<Workspace> workspaces = workspaceService.getWorkspaceListByOrgIdAndUserId(sourceId); List<Workspace> workspaces = workspaceService.getWorkspaceListByOrgIdAndUserId(sourceId);
if (workspaces.size() > 0) { if (workspaces.size() > 0) {
user.setLastWorkspaceId(workspaces.get(0).getId()); user.setLastWorkspaceId(workspaces.get(0).getId());
ProjectExample projectExample = new ProjectExample(); List<Project> projects = getProjectListByWsAndUserId(workspaces.get(0).getId());
projectExample.createCriteria().andWorkspaceIdEqualTo(workspaces.get(0).getId());
List<Project> projects = projectMapper.selectByExample(projectExample);
if (projects.size() > 0) { if (projects.size() > 0) {
user.setLastProjectId(projects.get(0).getId()); user.setLastProjectId(projects.get(0).getId());
} else { } else {
@ -484,12 +482,10 @@ public class UserService {
} }
} }
if (StringUtils.equals("workspace", sign)) { if (StringUtils.equals("workspace", sign)) {
ProjectExample projectExample = new ProjectExample();
projectExample.createCriteria().andWorkspaceIdEqualTo(sourceId);
List<Project> projects = projectMapper.selectByExample(projectExample);
Workspace workspace = workspaceMapper.selectByPrimaryKey(sourceId); Workspace workspace = workspaceMapper.selectByPrimaryKey(sourceId);
user.setLastOrganizationId(workspace.getOrganizationId()); user.setLastOrganizationId(workspace.getOrganizationId());
user.setLastWorkspaceId(sourceId); user.setLastWorkspaceId(sourceId);
getProjectListByWsAndUserId(sourceId)
if (projects.size() > 0) { if (projects.size() > 0) {
user.setLastProjectId(projects.get(0).getId()); user.setLastProjectId(projects.get(0).getId());
} else { } else {
@ -502,6 +498,28 @@ public class UserService {
userMapper.updateByPrimaryKeySelective(newUser); userMapper.updateByPrimaryKeySelective(newUser);
} }
private List<Project> getProjectListByWsAndUserId(String workspaceId) {
String useId = SessionUtils.getUser().getId();
ProjectExample projectExample = new ProjectExample();
projectExample.createCriteria().andWorkspaceIdEqualTo(workspaceId);
List<Project> projects = projectMapper.selectByExample(projectExample);
UserGroupExample userGroupExample = new UserGroupExample();
userGroupExample.createCriteria().andUserIdEqualTo(useId);
List<UserGroup> userGroups = userGroupMapper.selectByExample(userGroupExample);
List<Project> projectList = new ArrayList<>();
userGroups.forEach(userGroup -> {
projects.forEach(project -> {
if (StringUtils.equals(userGroup.getSourceId(), project.getId())) {
if (!projectList.contains(project)) {
projectList.add(project);
}
}
});
});
return projectList;
}
public UserDTO getUserInfo(String userId) { public UserDTO getUserInfo(String userId) {
return getUserDTO(userId); return getUserDTO(userId);
} }

View File

@ -232,6 +232,14 @@ INSERT INTO user_group_permission (id, group_id, permission_id, module_id)
VALUES (uuid(), 'ws_admin', 'WORKSPACE_TEMPLATE:READ+ISSUE_TEMPLATE', 'WORKSPACE_TEMPLATE'); VALUES (uuid(), 'ws_admin', 'WORKSPACE_TEMPLATE:READ+ISSUE_TEMPLATE', 'WORKSPACE_TEMPLATE');
INSERT INTO user_group_permission (id, group_id, permission_id, module_id) INSERT INTO user_group_permission (id, group_id, permission_id, module_id)
VALUES (uuid(), 'ws_admin', 'WORKSPACE_TEMPLATE:READ+CASE_TEMPLATE', 'WORKSPACE_TEMPLATE'); VALUES (uuid(), 'ws_admin', 'WORKSPACE_TEMPLATE:READ+CASE_TEMPLATE', 'WORKSPACE_TEMPLATE');
insert into metersphere_dev.user_group_permission (id, group_id, permission_id, module_id)
values (uuid(), 'ws_admin', 'WORKSPACE_PROJECT_MANAGER:READ', 'WORKSPACE_PROJECT_MANAGER');
insert into metersphere_dev.user_group_permission (id, group_id, permission_id, module_id)
values (uuid(), 'ws_admin', 'WORKSPACE_PROJECT_MANAGER:READ+CREATE','WORKSPACE_PROJECT_MANAGER');
insert into metersphere_dev.user_group_permission (id, group_id, permission_id, module_id)
values (uuid(), 'ws_admin', 'WORKSPACE_PROJECT_MANAGER:READ+DELETE', 'WORKSPACE_PROJECT_MANAGER');
insert into metersphere_dev.user_group_permission (id, group_id, permission_id, module_id)
values (uuid(), 'ws_admin', 'WORKSPACE_PROJECT_MANAGER:READ+EDIT', 'WORKSPACE_PROJECT_MANAGER');
-- 工作空间成员 -- 工作空间成员
INSERT INTO user_group_permission (id, group_id, permission_id, module_id) INSERT INTO user_group_permission (id, group_id, permission_id, module_id)

View File

@ -254,6 +254,31 @@
"name": "删除成员", "name": "删除成员",
"resourceId": "WORKSPACE_USER" "resourceId": "WORKSPACE_USER"
}, },
{
"id": "WORKSPACE_PROJECT_MANAGER:READ",
"name": "查询项目",
"resourceId": "WORKSPACE_PROJECT_MANAGER"
},
{
"id": "WORKSPACE_PROJECT_MANAGER:READ+CREATE",
"name": "创建项目",
"resourceId": "WORKSPACE_PROJECT_MANAGER"
},
{
"id": "WORKSPACE_PROJECT_MANAGER:READ+EDIT",
"name": "创建项目",
"resourceId": "WORKSPACE_PROJECT_MANAGER"
},
{
"id": "WORKSPACE_PROJECT_MANAGER:READ+DELETE",
"name": "删除项目",
"resourceId": "WORKSPACE_PROJECT_MANAGER"
},
{
"id": "WORKSPACE_PROJECT_ENVIRONMENT:READ",
"name": "查询环境",
"resourceId": "WORKSPACE_PROJECT_MANAGER"
},
{ {
"id": "WORKSPACE_TEMPLATE:READ", "id": "WORKSPACE_TEMPLATE:READ",
"name": "查询", "name": "查询",
@ -701,6 +726,14 @@
"id": "WORKSPACE_TEMPLATE", "id": "WORKSPACE_TEMPLATE",
"name": "模版设置" "name": "模版设置"
}, },
{
"id": "WORKSPACE_PROJECT_MANAGER",
"name": "项目管理"
},
{
"id": "WORKSPACE_PROJECT_ENVIRONMENT",
"name": "环境设置"
},
{ {
"id": "PROJECT_USER", "id": "PROJECT_USER",
"name": "成员" "name": "成员"

View File

@ -1,4 +1,4 @@
import MsProject from "@/business/components/settings/project/MsProject"; import MsProject from "@/business/components/settings/workspace/MsProject";
export default { export default {
path: "/api", path: "/api",

View File

@ -113,36 +113,32 @@ export default {
}, },
methods: { methods: {
initMenuData() { initMenuData() {
if (hasRoles(ROLE_ORG_ADMIN, ROLE_TEST_VIEWER, ROLE_TEST_USER, ROLE_TEST_MANAGER)) { this.$get("/organization/list/userorg/" + encodeURIComponent(this.currentUserId), response => {
this.$get("/organization/list/userorg/" + encodeURIComponent(this.currentUserId), response => { let data = response.data;
let data = response.data; this.organizationList = data;
this.organizationList = data; this.orgListCopy = data;
this.orgListCopy = data; let org = data.filter(r => r.id === this.currentUser.lastOrganizationId);
let org = data.filter(r => r.id === this.currentUser.lastOrganizationId); if (org.length > 0) {
if (org.length > 0) { this.currentOrganizationName = org[0].name;
this.currentOrganizationName = org[0].name;
}
});
}
if (hasRoles(ROLE_TEST_VIEWER, ROLE_TEST_USER, ROLE_TEST_MANAGER)) {
if (!this.currentUser.lastOrganizationId) {
return false;
} }
this.$get("/workspace/list/orgworkspace/", response => { });
let data = response.data; if (!this.currentUser.lastOrganizationId) {
if (data.length === 0) { return false;
this.workspaceList = [{name: this.$t('workspace.none')}];
} else {
this.workspaceList = data;
this.wsListCopy = data;
let workspace = data.filter(r => r.id === this.currentUser.lastWorkspaceId);
if (workspace.length > 0) {
this.currentWorkspaceName = workspace[0].name;
localStorage.setItem(WORKSPACE_ID, workspace[0].id);
}
}
});
} }
this.$get("/workspace/list/orgworkspace/", response => {
let data = response.data;
if (data.length === 0) {
this.workspaceList = [{name: this.$t('workspace.none')}];
} else {
this.workspaceList = data;
this.wsListCopy = data;
let workspace = data.filter(r => r.id === this.currentUser.lastWorkspaceId);
if (workspace.length > 0) {
this.currentWorkspaceName = workspace[0].name;
localStorage.setItem(WORKSPACE_ID, workspace[0].id);
}
}
});
}, },
getCurrentUserInfo() { getCurrentUserInfo() {
this.$get("/user/info/" + encodeURIComponent(this.currentUserId), response => { this.$get("/user/info/" + encodeURIComponent(this.currentUserId), response => {
@ -159,11 +155,11 @@ export default {
if (response.data.workspaceId) { if (response.data.workspaceId) {
localStorage.setItem("workspace_id", response.data.workspaceId); localStorage.setItem("workspace_id", response.data.workspaceId);
} }
if (response.data.lastProjectId) { // if (response.data.lastProjectId) {
localStorage.setItem(PROJECT_ID, response.data.lastProjectId); // localStorage.setItem(PROJECT_ID, response.data.lastProjectId);
} else { // } else {
localStorage.removeItem(PROJECT_ID); // localStorage.removeItem(PROJECT_ID);
} // }
this.$router.push('/').then(() => { this.$router.push('/').then(() => {
window.location.reload(); window.location.reload();
}).catch(err => err); }).catch(err => err);
@ -177,11 +173,11 @@ export default {
this.$post("/user/switch/source/ws/" + workspaceId, {}, response => { this.$post("/user/switch/source/ws/" + workspaceId, {}, response => {
saveLocalStorage(response); saveLocalStorage(response);
localStorage.setItem("workspace_id", workspaceId); localStorage.setItem("workspace_id", workspaceId);
if (response.data.lastProjectId) { // if (response.data.lastProjectId) {
localStorage.setItem(PROJECT_ID, response.data.lastProjectId); // localStorage.setItem(PROJECT_ID, response.data.lastProjectId);
} else { // } else {
localStorage.removeItem(PROJECT_ID); // localStorage.removeItem(PROJECT_ID);
} // }
this.$router.push('/').then(() => { this.$router.push('/').then(() => {
window.location.reload(); window.location.reload();
}).catch(err => err); }).catch(err => err);

View File

@ -120,7 +120,7 @@ export default {
initTableData() { initTableData() {
let param = {}; let param = {};
param.projectId = this.projectId; param.projectId = this.projectId;
this.$post('/user/project/member/list/' + this.currentPage + "/" + this.pageSize, param, response => { this.result = this.$post('/user/project/member/list/' + this.currentPage + "/" + this.pageSize, param, response => {
let data = response.data; let data = response.data;
this.tableData = data.listObject; this.tableData = data.listObject;
let url = "/user/group/list/project/" + this.projectId; let url = "/user/group/list/project/" + this.projectId;

View File

@ -0,0 +1,379 @@
<template>
<div v-loading="result.loading">
<el-card class="table-card">
<template v-slot:header>
<ms-table-header :show-create="false" :condition.sync="condition"
@search="search" @create="create"
:create-tip="btnTips" :title="$t('commons.project')">
<template v-slot:button>
<ms-table-button icon="el-icon-box"
:content="$t('api_test.jar_config.title')" @click="openJarConfig"/>
</template>
</ms-table-header>
</template>
<el-table border class="adjust-table" :data="items" style="width: 100%"
@sort-change="sort"
@filter-change="filter"
:height="screenHeight"
>
<el-table-column prop="name" :label="$t('commons.name')" min-width="100" show-overflow-tooltip/>
<el-table-column prop="description" :label="$t('commons.description')" show-overflow-tooltip>
<template v-slot:default="scope">
<pre>{{ scope.row.description }}</pre>
</template>
</el-table-column>
<!--<el-table-column prop="workspaceName" :label="$t('project.owning_workspace')"/>-->
<el-table-column
prop="createUser"
:label="$t('commons.create_user')"
:filters="userFilters"
column-key="create_user"
show-overflow-tooltip>
<template v-slot:default="scope">
<span>{{ scope.row.createUserName }}</span>
</template>
</el-table-column>
<el-table-column min-width="100"
sortable
prop="createTime"
:label="$t('commons.create_time')"
show-overflow-tooltip>
<template v-slot:default="scope">
<span>{{ scope.row.createTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<el-table-column min-width="100"
sortable
prop="updateTime"
:label="$t('commons.update_time')"
show-overflow-tooltip>
<template v-slot:default="scope">
<span>{{ scope.row.updateTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<el-table-column :label="$t('commons.operating')" width="180">
<template v-slot:default="scope">
<ms-table-operator
:edit-permission="['PROJECT_MANAGER:READ+EDIT']"
:delete-permission="['PROJECT_MANAGER:READ+DELETE']"
@editClick="edit(scope.row)"
@deleteClick="handleDelete(scope.row)">
<template v-slot:behind>
<ms-table-operator-button
v-permission="['PROJECT_MANAGER:READ+EDIT']"
:tip="$t('api_test.environment.environment_config')" icon="el-icon-setting"
type="info" @exec="openEnvironmentConfig(scope.row)"/>
<ms-table-operator-button
v-permission="['PROJECT_MANAGER:READ+EDIT']"
:tip="$t('load_test.other_resource')"
icon="el-icon-files"
type="success" @exec="openFiles(scope.row)"/>
</template>
</ms-table-operator>
</template>
</el-table-column>
</el-table>
<ms-table-pagination :change="list" :current-page.sync="currentPage" :page-size.sync="pageSize"
:total="total"/>
</el-card>
<el-dialog :close-on-click-modal="false" :title="title" :visible.sync="createVisible" destroy-on-close
@close="handleClose">
<el-form :model="form" :rules="rules" ref="form" label-position="right" label-width="140px" size="small">
<el-form-item :label="$t('commons.name')" prop="name">
<el-input v-model="form.name" autocomplete="off"></el-input>
</el-form-item>
<el-form-item :label="$t('用例模板')" prop="caseTemplateId">
<template-select :data="form" scene="API_CASE" prop="caseTemplateId" ref="caseTemplate"/>
</el-form-item>
<el-form-item :label="$t('缺陷模板')" prop="issueTemplateId">
<template-select :data="form" scene="ISSUE" prop="issueTemplateId" ref="issueTemplate"/>
</el-form-item>
<el-form-item :label="$t('commons.description')" prop="description">
<el-input :autosize="{ minRows: 2, maxRows: 4}" type="textarea" v-model="form.description"></el-input>
</el-form-item>
<el-form-item :label="$t('project.tapd_id')" v-if="tapd">
<el-input v-model="form.tapdId" autocomplete="off"></el-input>
</el-form-item>
<el-form-item :label="$t('project.jira_key')" v-if="jira">
<el-input v-model="form.jiraKey" autocomplete="off"></el-input>
</el-form-item>
<el-form-item :label="$t('project.zentao_id')" v-if="zentao">
<el-input v-model="form.zentaoId" autocomplete="off"></el-input>
</el-form-item>
<el-form-item :label="$t('project.repeatable')" prop="repeatable">
<el-switch v-model="form.repeatable"></el-switch>
</el-form-item>
<el-form-item label="测试用例自定义ID" prop="customNum">
<el-switch v-model="form.customNum"></el-switch>
</el-form-item>
<el-form-item label="场景自定义ID" prop="scenarioCustomNum">
<el-switch v-model="form.scenarioCustomNum"></el-switch>
</el-form-item>
</el-form>
<template v-slot:footer>
<div class="dialog-footer">
<ms-dialog-footer
@cancel="createVisible = false"
@confirm="submit('form')"/>
</div>
</template>
</el-dialog>
<ms-delete-confirm :title="$t('project.delete')" @delete="_handleDelete" ref="deleteConfirm"/>
<api-environment-config ref="environmentConfig"/>
<ms-jar-config ref="jarConfig"/>
<ms-resource-files ref="resourceFiles"/>
</div>
</template>
<script>
import MsCreateBox from "../CreateBox";
import {Message} from "element-ui";
import MsTablePagination from "../../common/pagination/TablePagination";
import MsTableHeader from "../../common/components/MsTableHeader";
import MsTableOperator from "../../common/components/MsTableOperator";
import MsDialogFooter from "../../common/components/MsDialogFooter";
import {
getCurrentProjectID,
getCurrentUser,
getCurrentWorkspaceId,
listenGoBack,
removeGoBackListener
} from "@/common/js/utils";
import MsContainer from "../../common/components/MsContainer";
import MsMainContainer from "../../common/components/MsMainContainer";
import MsDeleteConfirm from "../../common/components/MsDeleteConfirm";
import MsTableOperatorButton from "../../common/components/MsTableOperatorButton";
import ApiEnvironmentConfig from "../../api/test/components/ApiEnvironmentConfig";
import TemplateComponent from "../../track/plan/view/comonents/report/TemplateComponent/TemplateComponent";
import {PROJECT_ID} from "@/common/js/constants";
import MsJarConfig from "../../api/test/components/jar/JarConfig";
import MsTableButton from "../../common/components/MsTableButton";
import {_filter, _sort} from "@/common/js/tableUtils";
import MsResourceFiles from "@/business/components/performance/test/components/ResourceFiles";
import TemplateSelect from "@/business/components/settings/workspace/template/TemplateSelect";
import {PROJECT_CONFIGS} from "@/business/components/common/components/search/search-components";
export default {
name: "MsProject",
components: {
TemplateSelect,
MsResourceFiles,
MsTableButton,
MsJarConfig,
TemplateComponent,
ApiEnvironmentConfig,
MsTableOperatorButton,
MsDeleteConfirm,
MsMainContainer,
MsContainer, MsTableOperator, MsCreateBox, MsTablePagination, MsTableHeader, MsDialogFooter
},
data() {
return {
createVisible: false,
result: {},
btnTips: this.$t('project.create'),
title: this.$t('project.create'),
condition: {components: PROJECT_CONFIGS},
items: [],
tapd: false,
jira: false,
zentao: false,
form: {},
currentPage: 1,
pageSize: 10,
total: 0,
userFilters: [],
rules: {
name: [
{required: true, message: this.$t('project.input_name'), trigger: 'blur'},
{min: 2, max: 50, message: this.$t('commons.input_limit', [2, 50]), trigger: 'blur'}
],
description: [
{max: 250, message: this.$t('commons.input_limit', [0, 250]), trigger: 'blur'}
],
// caseTemplateId: [{required: true}],
// issueTemplateId: [{required: true}],
},
screenHeight: 'calc(100vh - 255px)',
};
},
props: {
baseUrl: {
type: String
}
},
mounted() {
if (this.$route.path.split('/')[2] === 'project' &&
this.$route.path.split('/')[3] === 'create') {
this.create();
this.$router.replace('/setting/project/all');
}
this.list();
this.getMaintainerOptions();
},
activated() {
this.list();
},
computed: {
currentUser: () => {
return getCurrentUser();
}
},
destroyed() {
this.createVisible = false;
},
methods: {
getMaintainerOptions() {
let workspaceId = getCurrentWorkspaceId();
this.$post('/user/ws/member/tester/list', {workspaceId: workspaceId}, response => {
this.userFilters = response.data.map(u => {
return {text: u.name, value: u.id};
});
});
},
create() {
let workspaceId = this.currentUser.lastWorkspaceId;
this.getOptions();
if (!workspaceId) {
this.$warning(this.$t('project.please_choose_workspace'));
return false;
}
this.title = this.$t('project.create');
// listenGoBack(this.handleClose);
this.createVisible = true;
this.form = {};
},
getOptions() {
if (this.$refs.issueTemplate) {
this.$refs.issueTemplate.getTemplateOptions();
}
if (this.$refs.caseTemplate) {
this.$refs.caseTemplate.getTemplateOptions();
}
},
edit(row) {
this.title = this.$t('project.edit');
this.getOptions();
this.createVisible = true;
listenGoBack(this.handleClose);
this.form = Object.assign({}, row);
this.$get("/service/integration/all/" + getCurrentUser().lastOrganizationId, response => {
let data = response.data;
let platforms = data.map(d => d.platform);
if (platforms.indexOf("Tapd") !== -1) {
this.tapd = true;
}
if (platforms.indexOf("Jira") !== -1) {
this.jira = true;
}
if (platforms.indexOf("Zentao") !== -1) {
this.zentao = true;
}
});
},
submit(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
let saveType = "add";
if (this.form.id) {
saveType = "update";
}
var protocol = document.location.protocol;
protocol = protocol.substring(0, protocol.indexOf(":"));
this.form.protocal = protocol;
this.result = this.$post("/project/" + saveType, this.form, () => {
this.createVisible = false;
this.list();
Message.success(this.$t('commons.save_success'));
});
} else {
return false;
}
});
},
openJarConfig() {
this.$refs.jarConfig.open();
},
openFiles(project) {
this.$refs.resourceFiles.open(project);
},
handleDelete(project) {
this.$refs.deleteConfirm.open(project);
},
_handleDelete(project) {
this.$confirm(this.$t('project.delete_tip'), '', {
confirmButtonText: this.$t('commons.confirm'),
cancelButtonText: this.$t('commons.cancel'),
type: 'warning'
}).then(() => {
this.$get('/project/delete/' + project.id, () => {
if (project.id === getCurrentProjectID()) {
localStorage.removeItem(PROJECT_ID);
this.$post("/user/update/current", {id: getCurrentUser().id, lastProjectId: ''});
}
Message.success(this.$t('commons.delete_success'));
this.list();
});
}).catch(() => {
this.$message({
type: 'info',
message: this.$t('commons.delete_cancelled')
});
});
},
handleClose() {
removeGoBackListener(this.handleClose);
this.createVisible = false;
this.tapd = false;
this.jira = false;
this.zentao = false;
},
search() {
this.list();
},
list() {
this.condition.projectId = getCurrentProjectID();
let url = "/project/list/" + this.currentPage + '/' + this.pageSize;
this.result = this.$post(url, this.condition, (response) => {
let data = response.data;
this.items = data.listObject;
this.total = data.itemCount;
});
},
sort(column) {
_sort(column, this.condition);
this.list();
},
filter(filters) {
_filter(filters, this.condition);
this.list();
},
openEnvironmentConfig(project) {
this.$refs.environmentConfig.open(project.id);
},
},
created() {
document.addEventListener('keydown', this.handleEvent);
},
beforeDestroy() {
document.removeEventListener('keydown', this.handleEvent);
}
};
</script>
<style scoped>
pre {
margin: 0 0;
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", Arial, sans-serif;
}
</style>

View File

@ -109,10 +109,20 @@ export default {
} }
}, },
{ {
path: 'project/:type', path: 'project/list/:type',
component: () => import('@/business/components/settings/project/MsProject'), component: () => import('@/business/components/settings/project/ProjectList'),
meta: {project: true, title: 'project.manager', permissions: ['PROJECT_MANAGER:READ']} meta: {project: true, title: 'project.manager', permissions: ['PROJECT_MANAGER:READ']}
}, },
{
path: 'project/:type',
component: () => import('@/business/components/settings/workspace/MsProject'),
meta: {workspace: true, title: 'project.manager', permissions: ['WORKSPACE_PROJECT_MANAGER:READ']}
},
{
path: 'wsenvlist',
component: () => import('@/business/components/settings/workspace/WsEnvironmentList'),
meta: {workspace: true, title: 'api_test.environment.environment_config', permissions: ['PROJECT_ENVIRONMENT:READ']}
},
{ {
path: 'envlist', path: 'envlist',
component: () => import('@/business/components/settings/project/EnvironmentList'), component: () => import('@/business/components/settings/project/EnvironmentList'),

View File

@ -2,7 +2,7 @@
<div v-loading="result.loading"> <div v-loading="result.loading">
<el-card class="table-card"> <el-card class="table-card">
<template v-slot:header> <template v-slot:header>
<ms-table-header :create-permission="['PROJECT_MANAGER:READ+CREATE']" :condition.sync="condition" <ms-table-header :create-permission="['WORKSPACE_PROJECT_MANAGER:READ+CREATE']" :condition.sync="condition"
@search="search" @create="create" @search="search" @create="create"
:create-tip="btnTips" :title="$t('commons.project')"> :create-tip="btnTips" :title="$t('commons.project')">
<template v-slot:button> <template v-slot:button>
@ -54,17 +54,20 @@
<el-table-column :label="$t('commons.operating')" width="180"> <el-table-column :label="$t('commons.operating')" width="180">
<template v-slot:default="scope"> <template v-slot:default="scope">
<ms-table-operator <ms-table-operator
:edit-permission="['PROJECT_MANAGER:READ+EDIT']" :edit-permission="['WORKSPACE_PROJECT_MANAGER:READ+EDIT']"
:delete-permission="['PROJECT_MANAGER:READ+DELETE']" :delete-permission="['WORKSPACE_PROJECT_MANAGER:READ+DELETE']"
@editClick="edit(scope.row)" @editClick="edit(scope.row)"
@deleteClick="handleDelete(scope.row)"> @deleteClick="handleDelete(scope.row)">
<template v-slot:behind> <template v-slot:behind>
<ms-table-operator-button <ms-table-operator-button
v-permission="['WORKSPACE_PROJECT_MANAGER:READ+EDIT']"
:tip="$t('api_test.environment.environment_config')" icon="el-icon-setting" :tip="$t('api_test.environment.environment_config')" icon="el-icon-setting"
type="info" @exec="openEnvironmentConfig(scope.row)"/> type="info" @exec="openEnvironmentConfig(scope.row)"/>
<ms-table-operator-button :tip="$t('load_test.other_resource')" <ms-table-operator-button
icon="el-icon-files" v-permission="['WORKSPACE_PROJECT_MANAGER:READ+EDIT']"
type="success" @exec="openFiles(scope.row)"/> :tip="$t('load_test.other_resource')"
icon="el-icon-files"
type="success" @exec="openFiles(scope.row)"/>
</template> </template>
</ms-table-operator> </ms-table-operator>
</template> </template>

View File

@ -0,0 +1,140 @@
<template>
<el-dialog :visible="dialogVisible" :title="dialogTitle"
@close="close" :close-on-click-modal="false"
width="50%">
<el-form>
<el-row>
<el-col :span="6">
<div class="project-item">
<div style="margin-bottom: 10px">
{{$t('project.select')}}
</div>
<el-select v-model="currentProjectId" filterable clearable >
<el-option v-for="item in projectList" :key="item.id"
:label="item.name" :value="item.id">
</el-option>
</el-select>
</div>
</el-col>
<el-col :span="16" :offset="1">
<el-upload
class="api-upload" drag action="alert"
:on-change="handleFileChange"
:limit="1" :file-list="uploadFiles"
:on-remove="handleRemove"
:on-exceed="handleExceed"
:auto-upload="false" accept=".json">
<i class="el-icon-upload"></i>
<div class="el-upload__text" v-html="$t('load_test.upload_tips')"></div>
<div class="el-upload__tip" slot="tip">
{{ $t('api_test.api_import.file_size_limit') }} {{'' + $t('api_test.api_import.ms_env_import_file_limit') }}
</div>
</el-upload>
</el-col>
</el-row>
</el-form>
<template v-slot:footer>
<el-button type="primary" @click="save">
{{ $t('commons.confirm') }}
</el-button>
</template>
</el-dialog>
</template>
<script>
export default {
name: "EnvironmentImport",
props: {
projectList: {
type: Array,
default() {
return [];
}
},
},
data() {
return {
currentProjectId: '', //id
uploadFiles: [],
dialogTitle: this.$t('api_test.environment.import'),
dialogVisible: false
}
},
watch: {
//
dialogVisible(val, oldVal) {
if (oldVal === false) {
this.currentProjectId = '';
this.uploadFiles = [];
}
}
},
methods: {
handleFileChange(file, uploadFiles) {
this.uploadFiles = uploadFiles;
},
save() {
if (this.uploadFiles.length > 0) {
for (let i = 0; i < this.uploadFiles.length; i++) {
this.uploadValidate(this.uploadFiles[i]);
let file = this.uploadFiles[i];
if (!file) {
continue;
}
let reader = new FileReader();
reader.readAsText(file.raw)
reader.onload = (e) => {
let fileString = e.target.result;
try {
JSON.parse(fileString).map(env => {
//projectIdnullprojectId
env.projectId = this.currentProjectId === '' ? null : this.currentProjectId;
this.$fileUpload('/api/environment/add', null,[], env, response => {
this.$emit('refresh');
this.$success(this.$t('commons.save_success'));
})
})
} catch (exception) {
this.$warning(this.$t('api_test.api_import.ms_env_import_file_limit'));
}
}
}
}
},
handleExceed() {
this.$warning(this.$t('api_test.api_import.file_exceed_limit'));
},
handleRemove() {
},
uploadValidate(file) { //.json20M
const extension = file.name.substring(file.name.lastIndexOf('.') + 1);
if (!(extension === 'json')) {
this.$warning(this.$t('api_test.api_import.ms_env_import_file_limit'));
}
if (file.size / 1024 / 1024 > 20) {
this.$warning(this.$t('api_test.api_import.file_size_limit'));
}
},
open() {
this.dialogVisible = true;
},
close() {
this.dialogVisible = false;
}
},
}
</script>
<style scoped>
.project-item {
padding-left: 20px;
padding-right: 20px;
}
</style>

View File

@ -0,0 +1,409 @@
<template>
<div>
<el-card class="table-card" v-loading="result.loading">
<!-- 表头 -->
<template v-slot:header>
<ms-table-header :create-permission="['PROJECT_ENVIRONMENT:READ+CREATE']" :title="$t('api_test.environment.environment_list')" :create-tip="btnTips"
:condition.sync="condition" :is-tester-permission="isTesterPermission" @search="search" @create="createEnv">
<template v-slot:button>
<ms-table-button v-permission="['PROJECT_ENVIRONMENT:READ+IMPORT']" :is-tester-permission="isTesterPermission" icon="el-icon-box"
:content="$t('commons.import')" @click="importJSON"/>
<ms-table-button v-permission="['PROJECT_ENVIRONMENT:READ+EXPORT']" :is-tester-permission="isTesterPermission" icon="el-icon-box"
:content="$t('commons.export')" @click="exportJSON"/>
</template>
</ms-table-header>
</template>
<!-- 环境列表内容 -->
<el-table border :data="environments" @filter-change="filter"
@selection-change="handleSelectionChange" class="adjust-table" style="width: 100%" ref="table"
:height="screenHeight"
>
<el-table-column type="selection"></el-table-column>
<el-table-column :label="$t('commons.project')" width="250" :filters="projectFilters" column-key="projectId"
show-overflow-tooltip>
<template v-slot="scope">
<span>{{ idNameMap.get(scope.row.projectId) }}</span>
</template>
</el-table-column>
<el-table-column :label="$t('api_test.environment.name')" prop="name" show-overflow-tooltip>
</el-table-column>
<el-table-column :label="$t('api_test.environment.socket')" show-overflow-tooltip>
<template v-slot="scope">
<span v-if="parseDomainName(scope.row)!='SHOW_INFO'">{{ parseDomainName(scope.row) }}</span>
<el-button size="mini" icon="el-icon-s-data" @click="showInfo(scope.row)" v-else>查看域名详情</el-button>
</template>
</el-table-column>
<el-table-column :label="$t('commons.operating')">
<template v-slot:default="scope">
<ms-table-operator :edit-permission="['PROJECT_ENVIRONMENT:READ+EDIT']"
:delete-permission="['PROJECT_ENVIRONMENT:READ+DELETE']"
@editClick="editEnv(scope.row)" @deleteClick="deleteEnv(scope.row)">
<template v-slot:middle>
<ms-table-operator-button v-permission="['PROJECT_ENVIRONMENT:READ+COPY']" :tip="$t('commons.copy')" @exec="copyEnv(scope.row)" :is-tester-permission="isTesterPermission"
icon="el-icon-document-copy" type="info"/>
</template>
</ms-table-operator>
</template>
</el-table-column>
</el-table>
<ms-table-pagination :change="list" :current-page.sync="currentPage" :page-size.sync="pageSize"
:total="total"/>
</el-card>
<!-- 创建编辑复制环境时的对话框 -->
<el-dialog :visible.sync="dialogVisible" :close-on-click-modal="false" :title="dialogTitle" width="66%">
<div class="project-item">
<div>
{{$t('project.select')}}
</div>
<el-select @change="handleProjectChange" v-model="currentProjectId" filterable clearable>
<el-option v-for="item in projectList" :key="item.id" :label="item.name" :value="item.id"></el-option>
</el-select>
</div>
<environment-edit :environment="currentEnvironment" ref="environmentEdit" @close="close"
:project-id="currentProjectId" @refreshAfterSave="refresh">
</environment-edit>
</el-dialog>
<environment-import :project-list="projectList" @refresh="refresh" ref="envImport"></environment-import>
<el-dialog title="域名列表" :visible.sync="domainVisible">
<el-table :data="conditions">
<el-table-column prop="socket" :label="$t('load_test.domain')" show-overflow-tooltip width="180">
<template v-slot:default="{row}">
{{getUrl(row)}}
</template>
</el-table-column>
<el-table-column prop="type" :label="$t('api_test.environment.condition_enable')" show-overflow-tooltip min-width="100px">
<template v-slot:default="{row}">
{{getName(row)}}
</template>
</el-table-column>
<el-table-column prop="details" show-overflow-tooltip min-width="120px" :label="$t('api_test.value')">
<template v-slot:default="{row}">
{{getDetails(row)}}
</template>
</el-table-column>
<el-table-column prop="createTime" show-overflow-tooltip min-width="120px" :label="$t('commons.create_time')">
<template v-slot:default="{row}">
<span>{{ row.time | timestampFormatDate }}</span>
</template>
</el-table-column>
</el-table>
<span slot="footer" class="dialog-footer">
<el-button @click="domainVisible = false" size="mini">{{$t('commons.cancel')}}</el-button>
<el-button type="primary" @click="domainVisible = false" size="mini">{{$t('commons.confirm')}}</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import MsTableHeader from "@/business/components/common/components/MsTableHeader";
import MsTableButton from "@/business/components/common/components/MsTableButton";
import MsTableOperator from "@/business/components/common/components/MsTableOperator";
import MsTableOperatorButton from "@/business/components/common/components/MsTableOperatorButton";
import MsTablePagination from "@/business/components/common/pagination/TablePagination";
import ApiEnvironmentConfig from "@/business/components/api/test/components/ApiEnvironmentConfig";
import {Environment, parseEnvironment} from "@/business/components/api/test/model/EnvironmentModel";
import EnvironmentEdit from "@/business/components/api/test/components/environment/EnvironmentEdit";
import MsAsideItem from "@/business/components/common/components/MsAsideItem";
import MsAsideContainer from "@/business/components/common/components/MsAsideContainer";
import ProjectSwitch from "@/business/components/common/head/ProjectSwitch";
import SearchList from "@/business/components/common/head/SearchList";
import {checkoutTestManagerOrTestUser, downloadFile} from "@/common/js/utils";
import EnvironmentImport from "@/business/components/settings/project/EnvironmentImport";
export default {
name: "EnvironmentList",
components: {
EnvironmentImport,
SearchList,
ProjectSwitch,
MsAsideContainer,
MsAsideItem,
EnvironmentEdit,
ApiEnvironmentConfig,
MsTablePagination, MsTableOperatorButton, MsTableOperator, MsTableButton, MsTableHeader
},
data() {
return {
btnTips: this.$t('api_test.environment.create'),
projectList: [],
condition: {}, //
environments: [],
currentEnvironment: new Environment(),
result: {},
dialogVisible: false,
idNameMap: new Map(),
dialogTitle: '',
currentProjectId: '', //id
selectRows: [],
isTesterPermission: false,
domainVisible: false,
conditions: [],
currentPage: 1,
pageSize: 10,
total: 0,
projectIds: [], //id
projectFilters: [],
screenHeight: 'calc(100vh - 255px)',
}
},
created() {
this.isTesterPermission = checkoutTestManagerOrTestUser();
},
activated() {
this.list();
},
watch: {
//projectId
currentProjectId() {
// el-select''''projectId使
if (this.currentProjectId === '') {
this.currentEnvironment.projectId = null;
} else {
this.currentEnvironment.projectId = this.currentProjectId;
}
}
},
methods: {
showInfo(row) {
const config = JSON.parse(row.config);
this.conditions = config.httpConfig.conditions;
this.domainVisible = true;
},
getName(row) {
switch (row.type) {
case "NONE":
return this.$t("api_test.definition.document.data_set.none");
case "MODULE":
return this.$t("test_track.module.module");
case "PATH":
return this.$t("api_test.definition.api_path");
}
},
getUrl(row) {
return row.protocol + "://" + row.socket;
},
getDetails(row) {
if (row && row.type === "MODULE") {
if (row.details && row.details instanceof Array) {
let value = "";
row.details.forEach((item) => {
value += item.name + ",";
});
if (value.endsWith(",")) {
value = value.substr(0, value.length - 1);
}
return value;
}
} else if (row && row.type === "PATH" && row.details.length > 0 && row.details[0].name) {
return row.details[0].value === "equals" ? this.$t("commons.adv_search.operators.equals") + row.details[0].name : this.$t("api_test.request.assertions.contains") + row.details[0].name;
} else {
return "";
}
},
list() {
if (!this.projectList || this.projectList.length === 0) { //
this.$get("/project/listAll", (response) => {
this.projectList = response.data; //,
this.projectList.forEach(project => {
this.idNameMap.set(project.id, project.name);
this.projectIds.push(project.id);
this.projectFilters.push({
text: project.name,
value: project.id,
})
});
this.getEnvironments();
})
} else {
this.getEnvironments()
}
},
getEnvironments(projectIds){
this.environments = [];
if (projectIds && projectIds.length > 0) {
this.condition.projectIds = projectIds;
} else {
this.condition.projectIds = this.projectIds;
}
let url = '/api/environment/list/' + this.currentPage + '/' + this.pageSize;
this.result = this.$post(url, this.condition, response => {
this.environments = response.data.listObject;
this.total = response.data.itemCount;
})
},
createEnv() {
this.currentProjectId = '';
this.dialogTitle = this.$t('api_test.environment.create');
this.dialogVisible = true;
this.currentEnvironment = new Environment();
},
search() {
this.list()
},
editEnv(environment) {
this.dialogTitle = this.$t('api_test.environment.config_environment');
this.currentProjectId = environment.projectId;
const temEnv = {};
Object.assign(temEnv, environment);
parseEnvironment(temEnv); //parseEnvironment
this.currentEnvironment = temEnv;
this.dialogVisible = true;
},
copyEnv(environment) {
this.currentProjectId = environment.projectId; //
this.dialogTitle = this.$t('api_test.environment.copy_environment');
const temEnv = {};
Object.assign(temEnv, environment);
parseEnvironment(temEnv); //parseEnvironment
let newEnvironment = new Environment(temEnv);
newEnvironment.id = null;
newEnvironment.name = this.getNoRepeatName(newEnvironment.name);
this.dialogVisible = true;
this.currentEnvironment = newEnvironment;
},
deleteEnv(environment) {
if (environment.id) {
this.$confirm(this.$t('commons.confirm_delete') + environment.name, {
confirmButtonText: this.$t('commons.confirm'),
cancelButtonText: this.$t('commons.cancel'),
type: "warning"
}).then(() => {
this.result = this.$get('/api/environment/delete/' + environment.id, () => {
this.$success(this.$t('commons.delete_success'));
this.list();
});
}).catch(() => {
this.$info(this.$t('commons.delete_cancelled'));
})
}
},
getNoRepeatName(name) {
for (let i in this.environments) {
if (this.environments[i].name === name) {
return this.getNoRepeatName(name + ' copy');
}
}
return name;
},
//
filter(filters) {
this.getEnvironments(filters.projectId)
},
//
close() {
this.dialogVisible = false;
this.$refs.environmentEdit.clearValidate();
},
refresh() {
this.list();
},
handleSelectionChange(value) {
this.selectRows = value;
},
importJSON() {
this.$refs.envImport.open();
},
exportJSON() {
if (this.selectRows.length < 1) {
this.$warning(this.$t('api_test.environment.select_environment'));
return;
}
//idprojectId
const envs = JSON.parse(JSON.stringify(this.selectRows));
envs.map(env => { //idprojectId
if (env.config){ //config
let tempConfig = JSON.parse(env.config);
if (tempConfig.httpConfig.conditions && tempConfig.httpConfig.conditions.length > 0) {
tempConfig.httpConfig.conditions = tempConfig.httpConfig.conditions.filter(condition => { //
return condition.type !== 'MODULE';
});
env.config = JSON.stringify(tempConfig);
}
}
delete env.id;
delete env.projectId;
})
downloadFile('MS_' + envs.length + '_Environments.json', JSON.stringify(envs));
},
handleProjectChange() { //,
this.currentEnvironment.config.httpConfig.conditions = [];
},
parseDomainName(environment) { //
if (environment.config) {
const config = JSON.parse(environment.config);
if (config.httpConfig && !config.httpConfig.conditions) {
if (config.httpConfig.protocol && config.httpConfig.domain) {
return config.httpConfig.protocol + "://" + config.httpConfig.domain;
}
return "";
} else {
if (config.httpConfig.conditions.length === 1) {
let obj = config.httpConfig.conditions[0];
if (obj.protocol && obj.domain) {
return obj.protocol + "://" + obj.domain;
}
} else if (config.httpConfig.conditions.length > 1) {
return "SHOW_INFO";
}
else {
return "";
}
}
} else { //config,protocoldomain
return environment.protocol + '://' + environment.domain;
}
}
},
}
</script>
<style scoped>
.item-bar {
width: 100%;
background: #F9F9F9;
padding: 5px 10px;
box-sizing: border-box;
border: solid 1px #e6e6e6;
}
.item-selected {
background: #ECF5FF;
border-left: solid #409EFF 5px;
}
.item-selected .item-right {
visibility: visible;
}
.environment-edit {
margin-left: 0;
width: 100%;
border: 0;
}
.project-item {
padding-left: 20px;
padding-right: 20px;
}
.project-item .el-select {
margin-top: 15px;
}
</style>