feat(项目管理): 环境管理全局变量静态页面
This commit is contained in:
parent
d14c30cdb2
commit
2bbee050aa
|
@ -1,7 +1,9 @@
|
|||
<template>
|
||||
<a-dropdown :trigger="props.trigger || 'hover'" @select="selectHandler" @popup-visible-change="visibleChange">
|
||||
<slot>
|
||||
<MsButton><icon-more /></MsButton>
|
||||
<MsButton type="text" size="mini" class="more-icon">
|
||||
<MsIcon type="icon-icon_more_outlined" size="16" class="text-[var(--color-text-4)]" />
|
||||
</MsButton>
|
||||
</slot>
|
||||
<template #content>
|
||||
<template v-for="item of props.list">
|
||||
|
@ -56,4 +58,14 @@
|
|||
color: rgb(var(--danger-6));
|
||||
}
|
||||
}
|
||||
.more-icon {
|
||||
padding: 4px;
|
||||
border-radius: var(--border-radius-mini);
|
||||
&:hover {
|
||||
background-color: rgb(var(--primary-9));
|
||||
.arco-icon {
|
||||
color: rgb(var(--primary-5));
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
</div>
|
||||
<div class="flex-col">
|
||||
<div v-for="item in nonSortColumn" :key="item.dataIndex" class="column-item">
|
||||
<div>{{ t(item.title as string) }}</div>
|
||||
<div>{{ t((item.title || item.columnTitle) as string) }}</div>
|
||||
<a-switch
|
||||
v-model="item.showInTable"
|
||||
size="small"
|
||||
|
@ -71,7 +71,7 @@
|
|||
<div v-for="element in couldSortColumn" :key="element.dataIndex" class="column-drag-item">
|
||||
<div class="flex w-[90%] items-center">
|
||||
<MsIcon type="icon-icon_drag" class="text-[16px] text-[var(--color-text-4)]" />
|
||||
<span class="ml-[8px]">{{ t(element.title as string) }}</span>
|
||||
<span class="ml-[8px]">{{ t((element.title || element.columnTitle) as string) }}</span>
|
||||
</div>
|
||||
<a-switch v-model="element.showInTable" size="small" @update="handleSwitchChange" />
|
||||
</div>
|
||||
|
@ -157,7 +157,9 @@
|
|||
onBeforeMount(() => {
|
||||
if (props.tableKey) {
|
||||
tableStore.getMode(props.tableKey).then((res) => {
|
||||
if (res) {
|
||||
currentMode.value = res;
|
||||
}
|
||||
});
|
||||
tableStore.getPageSize(props.tableKey).then((res) => {
|
||||
pageSize.value = res;
|
||||
|
|
|
@ -47,6 +47,8 @@ export interface MsTableColumnData extends TableColumnData {
|
|||
sortIndex?: number;
|
||||
// 筛选配置
|
||||
filterConfig?: MsTableColumnFilterConfig;
|
||||
// 列选择的title
|
||||
columnTitle?: string;
|
||||
}
|
||||
|
||||
export type MsTableErrorStatus = boolean | 'error' | 'empty';
|
||||
|
|
|
@ -451,6 +451,13 @@ export const pathMap: PathMapItem[] = [
|
|||
permission: [],
|
||||
level: MENU_LEVEL[2],
|
||||
},
|
||||
{
|
||||
key: 'PROJECT_MANAGEMENT_ENVIRONMENT', // 项目管理-环境管理
|
||||
locale: 'menu.projectManagement.environmentManagement',
|
||||
route: RouteEnum.PROJECT_MANAGEMENT_ENVIRONMENT_MANAGEMENT,
|
||||
permission: [],
|
||||
level: MENU_LEVEL[2],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
export enum EnvAuthScopeEnum {
|
||||
PROJECT = 'PROJECT',
|
||||
PROJECT_GROUP = 'PROJECT_GROUP',
|
||||
}
|
||||
|
||||
export enum EnvAuthTypeEnum {
|
||||
GLOBAL = 'GLOBAL', // 全局参数
|
||||
ENVIRONMENT = 'ENVIRONMENT', // 环境
|
||||
ENVIRONMENT_PARAM = 'ENVIRONMENT_PARAM', // 环境参数
|
||||
}
|
|
@ -6,7 +6,7 @@ export enum ApiTestRouteEnum {
|
|||
export enum BugManagementRouteEnum {
|
||||
BUG_MANAGEMENT = 'bugManagement',
|
||||
BUG_MANAGEMENT_INDEX = 'bugManagementIndex',
|
||||
BUG_MANAGEMENT_DETAIL = 'bugManagementDetail',
|
||||
BUG_MANAGEMENT_DETAIL = 'bbugManagementIndexDetail',
|
||||
BUG_MANAGEMENT_RECYCLE = 'bugManagementRecycle',
|
||||
}
|
||||
|
||||
|
@ -45,6 +45,7 @@ export enum ProjectManagementRouteEnum {
|
|||
PROJECT_MANAGEMENT_PERMISSION_USER_GROUP = 'projectManagementPermissionUserGroup',
|
||||
PROJECT_MANAGEMENT_PERMISSION_MEMBER = 'projectManagementPermissionMember',
|
||||
PROJECT_MANAGEMENT_MENU_MANAGEMENT_ERROR_REPORT_RULE = 'projectManagementMenuManagementErrorReportRule',
|
||||
PROJECT_MANAGEMENT_ENVIRONMENT_MANAGEMENT = 'projectManagementEnvironmentManagement',
|
||||
}
|
||||
|
||||
export enum TestPlanRouteEnum {
|
||||
|
|
|
@ -47,6 +47,7 @@ export enum TableKeyEnum {
|
|||
CASE_MANAGEMENT_TAB_CHANGE_HISTORY = 'caseManagementTabChangeHistory',
|
||||
CASE_MANAGEMENT_TAB_CASE_TABLE = 'caseManagementTabCaseTable',
|
||||
CASE_MANAGEMENT_TAB_DEMAND_PLATFORM = 'caseManagementTabDemandPlatformTable',
|
||||
PROJECT_MANAGEMENT_ENV_ALL_PARAM = 'projectManagementEnvAllParam',
|
||||
}
|
||||
|
||||
// 具有特殊功能的列
|
||||
|
|
|
@ -73,7 +73,7 @@ export default function useTableStore() {
|
|||
return columns;
|
||||
};
|
||||
|
||||
async function initColumn(tableKey: string, column: MsTableColumn, mode: TableOpenDetailMode) {
|
||||
async function initColumn(tableKey: string, column: MsTableColumn, mode?: TableOpenDetailMode) {
|
||||
try {
|
||||
const selectorColumnMap = await getSelectorColumnMap();
|
||||
if (!selectorColumnMap[tableKey]) {
|
||||
|
@ -112,7 +112,7 @@ export default function useTableStore() {
|
|||
console.log(e);
|
||||
}
|
||||
}
|
||||
async function setColumns(key: string, columns: MsTableColumn, mode: TableOpenDetailMode) {
|
||||
async function setColumns(key: string, columns: MsTableColumn, mode?: TableOpenDetailMode) {
|
||||
try {
|
||||
columns.forEach((item, idx) => {
|
||||
if (item.showDrag) {
|
||||
|
|
|
@ -80,4 +80,11 @@ export default {
|
|||
'common.newSuccess': 'Added successfully',
|
||||
'common.publish': 'Publish',
|
||||
'common.publishSuccessfully': 'Published successfully',
|
||||
'common.string': 'String',
|
||||
'common.number': 'Number',
|
||||
'common.boolean': 'Boolean',
|
||||
'common.array': 'Array',
|
||||
'common.json': 'Object',
|
||||
'common.integer': 'Integer',
|
||||
'common.file': 'File',
|
||||
};
|
||||
|
|
|
@ -46,6 +46,7 @@ export default {
|
|||
'menu.loadTest': 'Performance Test',
|
||||
'menu.projectManagement.projectPermission': 'Project Permission',
|
||||
'menu.projectManagement.log': 'Log',
|
||||
'menu.projectManagement.environmentManagement': 'EnvironmentManagement',
|
||||
'menu.settings': 'Settings',
|
||||
'menu.settings.system': 'System',
|
||||
'menu.settings.system.usergroup': 'User Group',
|
||||
|
|
|
@ -82,4 +82,11 @@ export default {
|
|||
'common.newSuccess': '新增成功',
|
||||
'common.publish': '发布',
|
||||
'common.publishSuccessfully': '发布成功',
|
||||
'common.string': '字符串',
|
||||
'common.number': '数字',
|
||||
'common.boolean': '布尔',
|
||||
'common.array': '数组',
|
||||
'common.json': '对象',
|
||||
'common.integer': '整数',
|
||||
'common.file': '文件',
|
||||
};
|
||||
|
|
|
@ -35,6 +35,7 @@ export default {
|
|||
'menu.projectManagement.messageManagement': '消息管理',
|
||||
'menu.projectManagement.commonScript': '公共脚本',
|
||||
'menu.projectManagement.messageManagementEdit': '更新模板',
|
||||
'menu.projectManagement.environmentManagement': '环境管理',
|
||||
'menu.caseManagement.featureCase': '功能用例',
|
||||
'menu.caseManagement.featureCaseRecycle': '回收站',
|
||||
'menu.caseManagement.featureCaseList': '用例列表',
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
export interface EnvListItem {
|
||||
name: string;
|
||||
id: string;
|
||||
}
|
|
@ -263,6 +263,17 @@ const ProjectManagement: AppRouteRecordRaw = {
|
|||
],
|
||||
},
|
||||
},
|
||||
// 环境管理
|
||||
{
|
||||
path: 'environmentManagement',
|
||||
name: ProjectManagementRouteEnum.PROJECT_MANAGEMENT_ENVIRONMENT_MANAGEMENT,
|
||||
component: () => import('@/views/project-management/environmental/index.vue'),
|
||||
meta: {
|
||||
locale: 'menu.projectManagement.environmentManagement',
|
||||
roles: ['*'],
|
||||
isTopMenu: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ export type TableOpenDetailMode = 'drawer' | 'new_window';
|
|||
|
||||
export interface MsTableSelectorItem {
|
||||
// 详情打开模式
|
||||
mode: TableOpenDetailMode;
|
||||
mode?: TableOpenDetailMode;
|
||||
// 列配置
|
||||
column: MsTableColumn;
|
||||
// 列配置的备份,用于比较当前定义的列配置是否和备份的列配置相同
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
import { defineStore } from 'pinia';
|
||||
|
||||
export const ALL_PARAM = 'allParam';
|
||||
|
||||
const useProjectEnvStore = defineStore('projectEnv', () => {
|
||||
const currentId = ref<string | number>(ALL_PARAM);
|
||||
const getCurrentId = computed(() => currentId.value);
|
||||
function setCurrentId(id: string | number) {
|
||||
currentId.value = id;
|
||||
}
|
||||
return { currentId, getCurrentId, setCurrentId };
|
||||
});
|
||||
|
||||
export default useProjectEnvStore;
|
|
@ -120,6 +120,9 @@
|
|||
@click="deleteParam(rowIndex)"
|
||||
/>
|
||||
</template>
|
||||
<template #mustContain="{ record }">
|
||||
<a-checkbox v-model:model-value="record.mustContain"></a-checkbox>
|
||||
</template>
|
||||
</MsBaseTable>
|
||||
<a-modal
|
||||
v-model:visible="showQuickInputParam"
|
||||
|
|
|
@ -94,13 +94,13 @@
|
|||
const loginConfig = useStorage('login-config', {
|
||||
rememberPassword: true,
|
||||
username: 'admin',
|
||||
password: 'metersphere',
|
||||
password: 'Calong@2015',
|
||||
});
|
||||
|
||||
const userInfo = reactive({
|
||||
authenticate: 'LOCAL',
|
||||
username: 'admin',
|
||||
password: 'metersphere',
|
||||
password: 'Calong@2015',
|
||||
});
|
||||
|
||||
const handleSubmit = async ({
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
<template>
|
||||
<div class="p-[24px]">
|
||||
<a-tabs v-model:active-key="activeKey" class="no-content">
|
||||
<a-tab-pane v-for="item of contentTabList" :key="item.value" :title="item.label" />
|
||||
</a-tabs>
|
||||
<a-divider :margin="0" class="!mb-[16px]" />
|
||||
<RequestHeader v-if="activeKey === 'requestHeader'" v-model:params="headerParams" />
|
||||
<AllPrams v-if="activeKey === 'allParams'" v-model:params="AllParams" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import AllPrams from './allParams/index.vue';
|
||||
import RequestHeader from './RequestHeader.vue';
|
||||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
const activeKey = ref('requestHeader');
|
||||
const headerParams = ref<[]>([]);
|
||||
const AllParams = ref<[]>([]);
|
||||
const { t } = useI18n();
|
||||
|
||||
const contentTabList = [
|
||||
{
|
||||
value: 'requestHeader',
|
||||
label: t('project.environmental.requestHeader'),
|
||||
},
|
||||
{
|
||||
value: 'allParams',
|
||||
label: t('project.environmental.allParams'),
|
||||
},
|
||||
];
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.no-content {
|
||||
:deep(.arco-tabs-content) {
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,13 @@
|
|||
<template>
|
||||
<div>
|
||||
<h1>环境变量</h1>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup></script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
h1 {
|
||||
color: blue;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,172 @@
|
|||
<template>
|
||||
<a-popover
|
||||
ref="popoverRef"
|
||||
:popup-visible="currentVisible"
|
||||
position="bl"
|
||||
trigger="click"
|
||||
class="w-[277px]"
|
||||
:content-class="props.id ? 'move-left' : ''"
|
||||
>
|
||||
<template #content>
|
||||
<div v-outer="handleOutsideClick">
|
||||
<div class="form">
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
size="large"
|
||||
layout="vertical"
|
||||
:label-col-props="{ span: 0 }"
|
||||
:wrapper-col-props="{ span: 24 }"
|
||||
>
|
||||
<div class="mb-[8px] text-[14px] font-medium text-[var(--color-text-1)]">{{
|
||||
props.id ? t('system.userGroup.rename') : t('system.userGroup.createUserGroup')
|
||||
}}</div>
|
||||
<a-form-item field="name" :rules="[{ validator: validateName }]">
|
||||
<a-input
|
||||
v-model="form.name"
|
||||
class="w-[243px]"
|
||||
:placeholder="t('system.userGroup.pleaseInputUserGroupName')"
|
||||
allow-clear
|
||||
@press-enter="handleBeforeOk"
|
||||
@keyup.esc="handleCancel"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
<div class="flex flex-row flex-nowrap justify-end gap-2">
|
||||
<a-button type="secondary" size="mini" :disabled="loading" @click="handleCancel">
|
||||
{{ t('common.cancel') }}
|
||||
</a-button>
|
||||
<a-button
|
||||
type="primary"
|
||||
size="mini"
|
||||
:loading="loading"
|
||||
:disabled="form.name.length === 0"
|
||||
@click="handleBeforeOk"
|
||||
>
|
||||
{{ props.id ? t('common.rename') : t('common.create') }}
|
||||
</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<slot></slot>
|
||||
</a-popover>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref, watchEffect } from 'vue';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
|
||||
import { updateOrAddProjectUserGroup } from '@/api/modules/project-management/usergroup';
|
||||
import { updateOrAddOrgUserGroup, updateOrAddUserGroup } from '@/api/modules/setting/usergroup';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import { useAppStore } from '@/store';
|
||||
|
||||
import { EnvListItem } from '@/models/projectManagement/environmental';
|
||||
import { EnvAuthScopeEnum, EnvAuthTypeEnum } from '@/enums/envEnum';
|
||||
|
||||
import type { FormInstance, ValidatedError } from '@arco-design/web-vue';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = defineProps<{
|
||||
id?: string;
|
||||
list: EnvListItem[];
|
||||
visible?: boolean;
|
||||
defaultName?: string;
|
||||
type: EnvAuthScopeEnum;
|
||||
}>();
|
||||
const systemType = ref(props.type);
|
||||
const emit = defineEmits<{
|
||||
(e: 'cancel', value: boolean): void;
|
||||
(e: 'submit', currentId: string): void;
|
||||
}>();
|
||||
|
||||
const formRef = ref<FormInstance>();
|
||||
const currentVisible = ref(props.visible);
|
||||
// trigger相关变量
|
||||
|
||||
const form = reactive({
|
||||
name: '',
|
||||
});
|
||||
|
||||
const appStore = useAppStore();
|
||||
|
||||
const loading = ref(false);
|
||||
|
||||
const validateName = (value: string | undefined, callback: (error?: string) => void) => {
|
||||
if (value === undefined || value === '') {
|
||||
callback(t('system.userGroup.userGroupNameIsNotNone'));
|
||||
} else {
|
||||
if (value === props.defaultName) {
|
||||
callback();
|
||||
} else {
|
||||
const isExist = props.list.some((item) => item.name === value);
|
||||
if (isExist) {
|
||||
callback(t('system.userGroup.userGroupNameIsExist', { name: value }));
|
||||
}
|
||||
}
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
form.name = '';
|
||||
loading.value = false;
|
||||
emit('cancel', false);
|
||||
};
|
||||
|
||||
const handleBeforeOk = () => {
|
||||
formRef.value?.validate(async (errors: undefined | Record<string, ValidatedError>) => {
|
||||
if (errors) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
loading.value = true;
|
||||
let res: EnvListItem | undefined;
|
||||
if (systemType.value === EnvAuthScopeEnum.PROJECT) {
|
||||
res = await updateOrAddUserGroup({ id: props.id, name: form.name });
|
||||
} else if (systemType.value === EnvAuthScopeEnum.PROJECT_GROUP) {
|
||||
// 组织用户组
|
||||
res = await updateOrAddOrgUserGroup({
|
||||
id: props.id,
|
||||
name: form.name,
|
||||
scopeId: appStore.currentOrgId,
|
||||
});
|
||||
} else {
|
||||
// 项目用户组 项目用户组只有创建
|
||||
res = await updateOrAddProjectUserGroup({ name: form.name });
|
||||
}
|
||||
if (res) {
|
||||
Message.success(
|
||||
props.id ? t('system.userGroup.updateUserGroupSuccess') : t('system.userGroup.addUserGroupSuccess')
|
||||
);
|
||||
emit('submit', res.id);
|
||||
handleCancel();
|
||||
}
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
});
|
||||
};
|
||||
watchEffect(() => {
|
||||
currentVisible.value = props.visible;
|
||||
form.name = props.defaultName || '';
|
||||
});
|
||||
|
||||
const handleOutsideClick = () => {
|
||||
if (currentVisible.value) {
|
||||
handleCancel();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.move-left {
|
||||
position: relative;
|
||||
right: 22px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,73 @@
|
|||
<template>
|
||||
<div class="mb-[8px] flex items-center justify-between">
|
||||
<div class="font-medium">{{ t('ms.apiTestDebug.header') }}</div>
|
||||
<batchAddKeyVal :params="innerParams" @apply="handleBatchParamApply" />
|
||||
</div>
|
||||
<paramTable v-model:params="innerParams" :columns="columns" @change="handleParamTableChange" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useVModel } from '@vueuse/core';
|
||||
|
||||
import type { MsTableColumn } from '@/components/pure/ms-table/type';
|
||||
import paramTable from '@/views/api-test/components/paramTable.vue';
|
||||
import batchAddKeyVal from '@/views/api-test/debug/components/debug/batchAddKeyVal.vue';
|
||||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
const props = defineProps<{
|
||||
params: any[];
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:params', value: any[]): void;
|
||||
(e: 'change'): void; // 数据发生变化
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const innerParams = useVModel(props, 'params', emit);
|
||||
|
||||
const columns: MsTableColumn = [
|
||||
{
|
||||
title: 'ms.apiTestDebug.paramName',
|
||||
dataIndex: 'name',
|
||||
slotName: 'name',
|
||||
},
|
||||
{
|
||||
title: 'ms.apiTestDebug.desc',
|
||||
dataIndex: 'desc',
|
||||
slotName: 'desc',
|
||||
},
|
||||
{
|
||||
title: 'project.environmental.mustContain',
|
||||
dataIndex: 'mustContain',
|
||||
slotName: 'mustContain',
|
||||
},
|
||||
{
|
||||
title: '',
|
||||
slotName: 'operation',
|
||||
width: 50,
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* 批量参数代码转换为参数表格数据
|
||||
*/
|
||||
function handleBatchParamApply(resultArr: any[]) {
|
||||
if (resultArr.length < innerParams.value.length) {
|
||||
innerParams.value.splice(0, innerParams.value.length - 1, ...resultArr);
|
||||
} else {
|
||||
innerParams.value = [...resultArr, innerParams.value[innerParams.value.length - 1]];
|
||||
}
|
||||
emit('change');
|
||||
}
|
||||
|
||||
function handleParamTableChange(resultArr: any[], isInit?: boolean) {
|
||||
innerParams.value = [...resultArr];
|
||||
if (!isInit) {
|
||||
emit('change');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
|
@ -0,0 +1,443 @@
|
|||
<template>
|
||||
<MsBaseTable v-bind="propsRes" :hoverable="false" v-on="propsEvent">
|
||||
<template #name="{ record }">
|
||||
<a-popover position="tl" :disabled="!record.name || record.name.trim() === ''" class="ms-params-input-popover">
|
||||
<template #content>
|
||||
<div class="param-popover-title">
|
||||
{{ t('ms.apiTestDebug.paramName') }}
|
||||
</div>
|
||||
<div class="param-popover-value">
|
||||
{{ record.name }}
|
||||
</div>
|
||||
</template>
|
||||
<a-input
|
||||
v-model:model-value="record.name"
|
||||
:placeholder="t('ms.apiTestDebug.paramNamePlaceholder')"
|
||||
class="param-input"
|
||||
@input="(val) => addTableLine(val)"
|
||||
/>
|
||||
</a-popover>
|
||||
</template>
|
||||
<template #type="{ record }">
|
||||
<a-select v-model:model-value="record.type" class="param-input" @change="(val) => handleTypeChange(val)">
|
||||
<a-option v-for="element in typeOptions" :key="element.value" :value="element.value">{{
|
||||
t(element.label)
|
||||
}}</a-option>
|
||||
</a-select>
|
||||
</template>
|
||||
<template #value="{ record }">
|
||||
<MsParamsInput
|
||||
v-model:value="record.value"
|
||||
@change="addTableLine"
|
||||
@dblclick="quickInputParams(record)"
|
||||
@apply="handleParamSettingApply"
|
||||
/>
|
||||
</template>
|
||||
<template #desc="{ record }">
|
||||
<ParamDescInput
|
||||
v-model:desc="record.desc"
|
||||
@input="addTableLine"
|
||||
@dblclick="quickInputDesc(record)"
|
||||
@change="handleDescChange"
|
||||
/>
|
||||
</template>
|
||||
<template #operation="{ record, rowIndex }">
|
||||
<a-switch v-if="rowIndex" v-model:model-value="record.enable" size="small" />
|
||||
<icon-minus-circle
|
||||
v-if="paramsLength > 1 && rowIndex !== paramsLength - 1"
|
||||
class="cursor-pointer text-[var(--color-text-4)]"
|
||||
size="20"
|
||||
@click="deleteParam(rowIndex)"
|
||||
/>
|
||||
</template>
|
||||
<template #tag="{ record }">
|
||||
<ParamTagInput
|
||||
v-model:model-value="record.tag"
|
||||
@input="addTableLine"
|
||||
@dblclick="quickInputDesc(record)"
|
||||
@change="handleDescChange"
|
||||
/>
|
||||
</template>
|
||||
</MsBaseTable>
|
||||
<a-modal
|
||||
v-model:visible="showQuickInputParam"
|
||||
:title="t('ms.paramsInput.value')"
|
||||
:ok-text="t('ms.apiTestDebug.apply')"
|
||||
class="ms-modal-form"
|
||||
body-class="!p-0"
|
||||
:width="680"
|
||||
title-align="start"
|
||||
@ok="applyQuickInputParam"
|
||||
@close="clearQuickInputParam"
|
||||
>
|
||||
<MsCodeEditor
|
||||
v-if="showQuickInputParam"
|
||||
v-model:model-value="quickInputParamValue"
|
||||
theme="MS-text"
|
||||
height="300px"
|
||||
:show-full-screen="false"
|
||||
>
|
||||
<template #title>
|
||||
<div class="flex justify-between">
|
||||
<div class="text-[var(--color-text-1)]">
|
||||
{{ t('ms.apiTestDebug.quickInputParamsTip') }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</MsCodeEditor>
|
||||
</a-modal>
|
||||
<a-modal
|
||||
v-model:visible="showQuickInputDesc"
|
||||
:title="t('ms.apiTestDebug.desc')"
|
||||
:ok-text="t('common.save')"
|
||||
:ok-button-props="{ disabled: !quickInputDescValue || quickInputDescValue.trim() === '' }"
|
||||
class="ms-modal-form"
|
||||
body-class="!p-0"
|
||||
:width="480"
|
||||
title-align="start"
|
||||
:auto-size="{ minRows: 2 }"
|
||||
@ok="applyQuickInputDesc"
|
||||
@close="clearQuickInputDesc"
|
||||
>
|
||||
<a-textarea
|
||||
v-model:model-value="quickInputDescValue"
|
||||
:placeholder="t('ms.apiTestDebug.descPlaceholder')"
|
||||
:max-length="255"
|
||||
show-word-limit
|
||||
></a-textarea>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script async setup lang="ts">
|
||||
import MsCodeEditor from '@/components/pure/ms-code-editor/index.vue';
|
||||
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
||||
import type { MsTableColumn } from '@/components/pure/ms-table/type';
|
||||
import useTable from '@/components/pure/ms-table/useTable';
|
||||
import MsParamsInput from '@/components/business/ms-params-input/index.vue';
|
||||
import ParamDescInput from './ParamDescInput.vue';
|
||||
import ParamTagInput from './ParamTagInput.vue';
|
||||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import { useTableStore } from '@/store';
|
||||
|
||||
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||
|
||||
interface Param {
|
||||
id: number;
|
||||
name: string;
|
||||
type: string;
|
||||
value: string;
|
||||
desc: string;
|
||||
tag: string[];
|
||||
enable: boolean;
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
params: Param[];
|
||||
scroll?: {
|
||||
x?: number | string;
|
||||
y?: number | string;
|
||||
maxHeight?: number | string;
|
||||
minWidth?: number | string;
|
||||
};
|
||||
heightUsed?: number;
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:params', value: Param[]): void;
|
||||
(e: 'change', data: Param[], isInit?: boolean): void;
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const columns: MsTableColumn = [
|
||||
{
|
||||
title: 'project.environmental.paramName',
|
||||
dataIndex: 'name',
|
||||
slotName: 'name',
|
||||
showInTable: true,
|
||||
showDrag: true,
|
||||
},
|
||||
{
|
||||
title: 'project.environmental.paramType',
|
||||
dataIndex: 'type',
|
||||
slotName: 'type',
|
||||
showInTable: true,
|
||||
showDrag: true,
|
||||
},
|
||||
{
|
||||
title: 'project.environmental.paramValue',
|
||||
dataIndex: 'value',
|
||||
slotName: 'value',
|
||||
showInTable: true,
|
||||
showDrag: true,
|
||||
},
|
||||
{
|
||||
title: 'project.environmental.tag',
|
||||
dataIndex: 'tag',
|
||||
slotName: 'tag',
|
||||
width: 200,
|
||||
showInTable: true,
|
||||
showDrag: true,
|
||||
},
|
||||
{
|
||||
title: 'project.environmental.desc',
|
||||
dataIndex: 'desc',
|
||||
slotName: 'desc',
|
||||
showInTable: true,
|
||||
showDrag: true,
|
||||
},
|
||||
{
|
||||
title: '',
|
||||
columnTitle: 'common.operation',
|
||||
slotName: 'operation',
|
||||
width: 50,
|
||||
showInTable: true,
|
||||
showDrag: true,
|
||||
},
|
||||
];
|
||||
|
||||
const defaultParams: Omit<Param, 'id'> = {
|
||||
name: '',
|
||||
type: 'string',
|
||||
value: '',
|
||||
desc: '',
|
||||
tag: [],
|
||||
enable: true,
|
||||
};
|
||||
const allType = [
|
||||
{
|
||||
label: 'common.string',
|
||||
value: 'string',
|
||||
},
|
||||
{
|
||||
label: 'common.integer',
|
||||
value: 'integer',
|
||||
},
|
||||
{
|
||||
label: 'common.number',
|
||||
value: 'number',
|
||||
},
|
||||
{
|
||||
label: 'common.array',
|
||||
value: 'array',
|
||||
},
|
||||
{
|
||||
label: 'common.json',
|
||||
value: 'json',
|
||||
},
|
||||
{
|
||||
label: 'common.file',
|
||||
value: 'file',
|
||||
},
|
||||
];
|
||||
|
||||
const tableStore = useTableStore();
|
||||
|
||||
const typeOptions = computed(() => {
|
||||
return allType;
|
||||
});
|
||||
|
||||
await tableStore.initColumn(TableKeyEnum.PROJECT_MANAGEMENT_ENV_ALL_PARAM, columns);
|
||||
|
||||
const { propsRes, propsEvent } = useTable<Param>(undefined, {
|
||||
tableKey: TableKeyEnum.PROJECT_MANAGEMENT_ENV_ALL_PARAM,
|
||||
scroll: props.scroll,
|
||||
heightUsed: props.heightUsed,
|
||||
selectable: true,
|
||||
draggable: { type: 'handle', width: 24 },
|
||||
showSetting: true,
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.params,
|
||||
(val) => {
|
||||
if (val.length > 0) {
|
||||
propsRes.value.data = val;
|
||||
} else {
|
||||
propsRes.value.data = props.params.concat({
|
||||
id: new Date().getTime(),
|
||||
name: '',
|
||||
type: 'string',
|
||||
value: '',
|
||||
desc: '',
|
||||
tag: [],
|
||||
enable: true,
|
||||
});
|
||||
emit('change', propsRes.value.data, true);
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.heightUsed,
|
||||
(val) => {
|
||||
propsRes.value.heightUsed = val;
|
||||
}
|
||||
);
|
||||
|
||||
const paramsLength = computed(() => propsRes.value.data.length);
|
||||
|
||||
function deleteParam(rowIndex: number) {
|
||||
propsRes.value.data.splice(rowIndex, 1);
|
||||
emit('change', propsRes.value.data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当表格输入框变化时,给参数表格添加一行数据行
|
||||
* @param val 输入值
|
||||
* @param isForce 是否强制添加
|
||||
*/
|
||||
function addTableLine(val?: string | number, isForce?: boolean) {
|
||||
const lastData = propsRes.value.data[propsRes.value.data.length - 1];
|
||||
const isNotChange = Object.keys(defaultParams).every((key) => {
|
||||
if (key === 'id') {
|
||||
return true;
|
||||
}
|
||||
if (key === 'tag') {
|
||||
return lastData[key].length === 0;
|
||||
}
|
||||
return lastData[key] === defaultParams[key as any];
|
||||
});
|
||||
if (isForce || (val !== '' && val !== undefined && !isNotChange)) {
|
||||
propsRes.value.data = [...propsRes.value.data, { id: new Date().getTime(), ...defaultParams }];
|
||||
emit('change', propsRes.value.data);
|
||||
}
|
||||
}
|
||||
|
||||
const showQuickInputParam = ref(false);
|
||||
const activeQuickInputRecord = ref<any>({});
|
||||
const quickInputParamValue = ref('');
|
||||
|
||||
function quickInputParams(record: any) {
|
||||
activeQuickInputRecord.value = record;
|
||||
showQuickInputParam.value = true;
|
||||
quickInputParamValue.value = record.value;
|
||||
}
|
||||
|
||||
function clearQuickInputParam() {
|
||||
activeQuickInputRecord.value = {};
|
||||
quickInputParamValue.value = '';
|
||||
}
|
||||
|
||||
function applyQuickInputParam() {
|
||||
activeQuickInputRecord.value.value = quickInputParamValue.value;
|
||||
showQuickInputParam.value = false;
|
||||
clearQuickInputParam();
|
||||
addTableLine(quickInputParamValue.value, true);
|
||||
emit('change', propsRes.value.data);
|
||||
}
|
||||
|
||||
function handleParamSettingApply(val: string | number) {
|
||||
addTableLine(val);
|
||||
}
|
||||
|
||||
const showQuickInputDesc = ref(false);
|
||||
const quickInputDescValue = ref('');
|
||||
|
||||
function quickInputDesc(record: any) {
|
||||
activeQuickInputRecord.value = record;
|
||||
showQuickInputDesc.value = true;
|
||||
quickInputDescValue.value = record.desc;
|
||||
}
|
||||
|
||||
function clearQuickInputDesc() {
|
||||
activeQuickInputRecord.value = {};
|
||||
quickInputDescValue.value = '';
|
||||
}
|
||||
|
||||
function applyQuickInputDesc() {
|
||||
activeQuickInputRecord.value.desc = quickInputDescValue.value;
|
||||
showQuickInputDesc.value = false;
|
||||
clearQuickInputDesc();
|
||||
addTableLine(quickInputDescValue.value, true);
|
||||
emit('change', propsRes.value.data);
|
||||
}
|
||||
|
||||
function handleDescChange() {
|
||||
emit('change', propsRes.value.data);
|
||||
}
|
||||
|
||||
function handleTypeChange(
|
||||
val: string | number | boolean | Record<string, any> | (string | number | boolean | Record<string, any>)[]
|
||||
) {
|
||||
addTableLine(val as string);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
:deep(.setting-icon) {
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
:deep(.arco-table-th) {
|
||||
background-color: var(--color-text-n9);
|
||||
}
|
||||
:deep(.arco-table-cell-align-left) {
|
||||
padding: 16px 4px;
|
||||
}
|
||||
:deep(.arco-table-cell) {
|
||||
padding: 11px 4px;
|
||||
}
|
||||
:deep(.param-input:not(.arco-input-focus, .arco-select-view-focus)) {
|
||||
&:not(:hover) {
|
||||
border-color: transparent !important;
|
||||
.arco-input::placeholder {
|
||||
@apply invisible;
|
||||
}
|
||||
.arco-select-view-icon {
|
||||
@apply invisible;
|
||||
}
|
||||
.arco-select-view-value {
|
||||
color: var(--color-text-brand);
|
||||
}
|
||||
}
|
||||
}
|
||||
.param-input-switch:not(:hover).arco-switch-checked {
|
||||
background-color: rgb(var(--primary-3)) !important;
|
||||
}
|
||||
.content-type-trigger-content {
|
||||
@apply bg-white;
|
||||
|
||||
padding: 8px;
|
||||
border-radius: var(--border-radius-small);
|
||||
box-shadow: 0 4px 10px -1px rgb(100 100 102 / 15%);
|
||||
}
|
||||
.param-input {
|
||||
.param-input-mock-icon {
|
||||
@apply invisible;
|
||||
}
|
||||
&:hover,
|
||||
&.arco-input-focus {
|
||||
.param-input-mock-icon {
|
||||
@apply visible cursor-pointer;
|
||||
&:hover {
|
||||
color: rgb(var(--primary-5));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.param-popover-title {
|
||||
@apply font-medium;
|
||||
|
||||
margin-bottom: 4px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
line-height: 16px;
|
||||
color: var(--color-text-1);
|
||||
}
|
||||
.param-popover-subtitle {
|
||||
margin-bottom: 2px;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
color: var(--color-text-4);
|
||||
}
|
||||
.param-popover-value {
|
||||
min-width: 100px;
|
||||
max-width: 280px;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
color: var(--color-text-1);
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,77 @@
|
|||
<template>
|
||||
<a-popover position="tl" :disabled="!props.desc || props.desc.trim() === ''" class="ms-params-input-popover">
|
||||
<template #content>
|
||||
<div class="param-popover-title">
|
||||
{{ t('ms.apiTestDebug.desc') }}
|
||||
</div>
|
||||
<div class="param-popover-value">
|
||||
{{ props.desc }}
|
||||
</div>
|
||||
</template>
|
||||
<a-input
|
||||
ref="inputRef"
|
||||
v-model:model-value="innerValue"
|
||||
class="param-input"
|
||||
@input="(val) => emit('input', val)"
|
||||
@change="(val) => emit('change', val)"
|
||||
/>
|
||||
</a-popover>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useEventListener, useVModel } from '@vueuse/core';
|
||||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
const props = defineProps<{
|
||||
desc: string;
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:desc', val: string): void;
|
||||
(e: 'input', val: string): void;
|
||||
(e: 'change', val: string): void;
|
||||
(e: 'dblclick'): void;
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const innerValue = useVModel(props, 'desc', emit);
|
||||
|
||||
const inputRef = ref<HTMLElement>();
|
||||
|
||||
onMounted(() => {
|
||||
useEventListener(inputRef.value, 'dblclick', () => {
|
||||
emit('dblclick');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.param-input:not(.arco-input-focus) {
|
||||
&:not(:hover) {
|
||||
border-color: transparent;
|
||||
}
|
||||
}
|
||||
.param-popover-title {
|
||||
@apply font-medium;
|
||||
|
||||
margin-bottom: 4px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
line-height: 16px;
|
||||
color: var(--color-text-1);
|
||||
}
|
||||
.param-popover-subtitle {
|
||||
margin-bottom: 2px;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
color: var(--color-text-4);
|
||||
}
|
||||
.param-popover-value {
|
||||
min-width: 100px;
|
||||
max-width: 280px;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
color: var(--color-text-1);
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,74 @@
|
|||
<template>
|
||||
<a-popover position="tl" class="ms-params-input-popover">
|
||||
<template #content>
|
||||
<div class="param-popover-title">
|
||||
{{ t('project.environmental.tag') }}
|
||||
</div>
|
||||
<div class="param-popover-value">
|
||||
<MsTagsGroup is-string-tag :tag-list="props.modelValue" :show-num="1" class="param-input" />
|
||||
</div>
|
||||
</template>
|
||||
<MsTagsInput ref="inputRef" v-model:model-value="innerValue" :max-tag-count="1" class="param-input" />
|
||||
</a-popover>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useEventListener, useVModel } from '@vueuse/core';
|
||||
|
||||
import MsTagsGroup from '@/components/pure/ms-tag/ms-tag-group.vue';
|
||||
import MsTagsInput from '@/components/pure/ms-tags-input/index.vue';
|
||||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: string[];
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', val: string[]): void;
|
||||
(e: 'input', val: string): void;
|
||||
(e: 'change', val: string): void;
|
||||
(e: 'dblclick'): void;
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const innerValue = useVModel(props, 'modelValue', emit);
|
||||
|
||||
const inputRef = ref<HTMLElement>();
|
||||
|
||||
onMounted(() => {
|
||||
useEventListener(inputRef.value, 'dblclick', () => {
|
||||
emit('dblclick');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.param-input:not(.arco-input-focus) {
|
||||
&:not(:hover) {
|
||||
border-color: transparent;
|
||||
}
|
||||
}
|
||||
.param-popover-title {
|
||||
@apply font-medium;
|
||||
|
||||
margin-bottom: 4px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
line-height: 16px;
|
||||
color: var(--color-text-1);
|
||||
}
|
||||
.param-popover-subtitle {
|
||||
margin-bottom: 2px;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
color: var(--color-text-4);
|
||||
}
|
||||
.param-popover-value {
|
||||
min-width: 100px;
|
||||
max-width: 280px;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
color: var(--color-text-1);
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,75 @@
|
|||
<template>
|
||||
<div class="mb-[8px] flex items-center justify-between">
|
||||
<a-input
|
||||
v-model:value="searchValue"
|
||||
:placeholder="t('project.environmental.searchParamsHolder')"
|
||||
allow-clear
|
||||
class="w-[240px]"
|
||||
@blur="handleSearch"
|
||||
@press-enter="handleSearch"
|
||||
>
|
||||
<template #prefix>
|
||||
<span class="arco-icon-hover">
|
||||
<icon-search class="cursor-pointer" @click="handleSearch" />
|
||||
</span>
|
||||
</template>
|
||||
</a-input>
|
||||
<batchAddKeyVal :params="innerParams" @apply="handleBatchParamApply" />
|
||||
</div>
|
||||
<AllParamsTable v-model:params="innerParams" @change="handleParamTableChange" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useVModel } from '@vueuse/core';
|
||||
|
||||
import AllParamsTable from './AllParamsTable.vue';
|
||||
import batchAddKeyVal from '@/views/api-test/debug/components/debug/batchAddKeyVal.vue';
|
||||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
|
||||
const props = defineProps<{
|
||||
params: any[];
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:params', value: any[]): void;
|
||||
(e: 'change'): void; // 数据发生变化
|
||||
}>();
|
||||
|
||||
const searchValue = ref('');
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const innerParams = useVModel(props, 'params', emit);
|
||||
|
||||
/**
|
||||
* 批量参数代码转换为参数表格数据
|
||||
*/
|
||||
function handleBatchParamApply(resultArr: any[]) {
|
||||
if (resultArr.length < innerParams.value.length) {
|
||||
innerParams.value.splice(0, innerParams.value.length - 1, ...resultArr);
|
||||
} else {
|
||||
innerParams.value = [...resultArr, innerParams.value[innerParams.value.length - 1]];
|
||||
}
|
||||
emit('change');
|
||||
}
|
||||
|
||||
function handleParamTableChange(resultArr: any[], isInit?: boolean) {
|
||||
innerParams.value = [...resultArr];
|
||||
if (!isInit) {
|
||||
emit('change');
|
||||
}
|
||||
}
|
||||
|
||||
function handleSearch() {
|
||||
if (searchValue.value.length === 0) {
|
||||
return;
|
||||
}
|
||||
const result = innerParams.value.filter((item) => item.name.includes(searchValue.value));
|
||||
if (result.length === 0) {
|
||||
return;
|
||||
}
|
||||
innerParams.value = [...result];
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
|
@ -0,0 +1,299 @@
|
|||
<template>
|
||||
<div class="page">
|
||||
<MsSplitBox>
|
||||
<template #first>
|
||||
<div class="p-[24px]">
|
||||
<a-radio-group v-model:model-value="showType" type="button" class="file-show-type" @change="changeShowType">
|
||||
<a-radio value="PROJECT">{{ t('project.environmental.project') }}</a-radio>
|
||||
<a-radio value="PROJECT_GROUP">{{ t('project.environmental.projectGroup') }}</a-radio>
|
||||
</a-radio-group>
|
||||
<a-input-search
|
||||
:placeholder="t('project.environmental.searchHolder')"
|
||||
allow-clear
|
||||
@press-enter="enterData"
|
||||
@search="searchData"
|
||||
/>
|
||||
<!-- 全局参数-->
|
||||
<div class="p-[8px] text-[var(--color-text-4)]">
|
||||
{{ t('project.environmental.allParam') }}
|
||||
</div>
|
||||
<div
|
||||
class="env-item justify-between font-medium text-[rgb(var(--primary-5))] hover:bg-[rgb(var(--primary-1))]"
|
||||
:class="{ 'bg-[rgb(var(--primary-1))]': activeKey === ALL_PARAM }"
|
||||
@click="handleListItemClick({ id: 'allParam', name: 'allParam' })"
|
||||
>
|
||||
{{ t('project.environmental.allParam') }}
|
||||
<div class="node-extra">
|
||||
<MsMoreAction
|
||||
:list="allMoreAction"
|
||||
@select="(value) => handleMoreAction(value, 'all', EnvAuthTypeEnum.GLOBAL)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<a-divider :margin="6" />
|
||||
<!-- 环境-->
|
||||
<div class="env-row p-[8px] hover:bg-[rgb(var(--primary-1))]">
|
||||
<div class="text-[var(--color-text-4)]">{{ t('project.environmental.env') }}</div>
|
||||
<div class="flex flex-row items-center">
|
||||
<div class="env-row-extra">
|
||||
<MsMoreAction
|
||||
:list="allMoreAction"
|
||||
@select="(value) => handleMoreAction(value, 'all', EnvAuthTypeEnum.ENVIRONMENT)"
|
||||
/>
|
||||
</div>
|
||||
<MsButton type="icon" class="!mr-0 p-[2px]">
|
||||
<MsIcon
|
||||
type="icon-icon_create_planarity"
|
||||
size="18"
|
||||
class="text-[rgb(var(--primary-5))] hover:text-[rgb(var(--primary-4))]"
|
||||
/>
|
||||
</MsButton>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<!-- 环境list-->
|
||||
<div v-if="envList.length">
|
||||
<VueDraggable v-model="envList" ghost-class="ghost">
|
||||
<div
|
||||
v-for="element in envList"
|
||||
:key="element.id"
|
||||
class="env-item hover:bg-[rgb(var(--primary-1))]"
|
||||
@click="handleListItemClick(element)"
|
||||
>
|
||||
<RenamePop
|
||||
:list="envList"
|
||||
:type="(showType as EnvAuthScopeEnum)"
|
||||
v-bind="popVisible[element.id]"
|
||||
@cancel="handleRenameCancel(element)"
|
||||
@submit="handleRenameCancel(element, element.id)"
|
||||
>
|
||||
<div class="flex max-w-[100%] grow flex-row items-center justify-between">
|
||||
<a-tooltip :content="element.name">
|
||||
<div
|
||||
class="one-line-text"
|
||||
:class="{ 'font-medium text-[rgb(var(--primary-5))]': element.id === activeKey }"
|
||||
>{{ element.name }}</div
|
||||
>
|
||||
</a-tooltip>
|
||||
<div class="node-extra">
|
||||
<div class="flex flex-row items-center gap-[8px]">
|
||||
<MsButton type="icon" class="!mr-0 p-[2px]">
|
||||
<MsIcon
|
||||
type="icon-icon_drag"
|
||||
size="16"
|
||||
class="text-[rgb(var(--primary-5))] hover:text-[rgb(var(--primary-4))]"
|
||||
/>
|
||||
</MsButton>
|
||||
<MsMoreAction
|
||||
:list="envMoreAction"
|
||||
@select="(value) => handleMoreAction(value, element.id, EnvAuthTypeEnum.ENVIRONMENT_PARAM)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</RenamePop>
|
||||
</div>
|
||||
</VueDraggable>
|
||||
</div>
|
||||
|
||||
<div v-else class="bg-[var(--color-text-n9)] p-[8px] text-[12px] text-[var(--color-text-4)]">
|
||||
{{ t('project.environmental.envListIsNull') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #second>
|
||||
<!-- 全局参数 -->
|
||||
<AllParamBox v-if="activeKey === ALL_PARAM" />
|
||||
<!-- 环境变量 -->
|
||||
<EnvParamBox v-else />
|
||||
</template>
|
||||
</MsSplitBox>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||
import MsIcon from '@/components/pure/ms-icon-font/index.vue';
|
||||
import MsSplitBox from '@/components/pure/ms-split-box/index.vue';
|
||||
import MsMoreAction from '@/components/pure/ms-table-more-action/index.vue';
|
||||
import { ActionsItem } from '@/components/pure/ms-table-more-action/types';
|
||||
import AllParamBox from './components/AllParamBox.vue';
|
||||
import EnvParamBox from './components/EnvParamBox.vue';
|
||||
import RenamePop from './components/RenamePop.vue';
|
||||
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useProjectEnvStore, { ALL_PARAM } from '@/store/modules/setting/useProjectEnvStore';
|
||||
|
||||
import { EnvListItem } from '@/models/projectManagement/environmental';
|
||||
import { PopVisible } from '@/models/setting/usergroup';
|
||||
import { EnvAuthScopeEnum, EnvAuthTypeEnum } from '@/enums/envEnum';
|
||||
|
||||
import { VueDraggable } from 'vue-draggable-plus';
|
||||
|
||||
const { t } = useI18n();
|
||||
const store = useProjectEnvStore();
|
||||
|
||||
const envList = ref<EnvListItem[]>([]); // 环境列表
|
||||
|
||||
const showType = ref<EnvAuthScopeEnum>(EnvAuthScopeEnum.PROJECT); // 展示类型
|
||||
|
||||
const activeKey = computed(() => store.currentId); // 当前选中的id
|
||||
|
||||
// 气泡弹窗
|
||||
const popVisible = ref<PopVisible>({});
|
||||
|
||||
// 默认环境MoreAction
|
||||
const envMoreAction: ActionsItem[] = [
|
||||
{
|
||||
label: t('common.rename'),
|
||||
eventTag: 'rename',
|
||||
},
|
||||
{
|
||||
label: t('common.export'),
|
||||
eventTag: 'export',
|
||||
},
|
||||
{
|
||||
isDivider: true,
|
||||
},
|
||||
{
|
||||
label: t('common.delete'),
|
||||
danger: true,
|
||||
eventTag: 'delete',
|
||||
},
|
||||
];
|
||||
// 全局参数/环境 MoreAction
|
||||
const allMoreAction: ActionsItem[] = [
|
||||
{
|
||||
label: t('common.import'),
|
||||
eventTag: 'import',
|
||||
},
|
||||
{
|
||||
label: t('common.export'),
|
||||
eventTag: 'export',
|
||||
},
|
||||
];
|
||||
|
||||
// 处理MoreAction
|
||||
const handleMoreAction = (item: ActionsItem, id: string, scopeType: EnvAuthTypeEnum) => {
|
||||
const { eventTag } = item;
|
||||
switch (eventTag) {
|
||||
case 'rename':
|
||||
break;
|
||||
case 'export':
|
||||
break;
|
||||
case 'delete':
|
||||
break;
|
||||
case 'import':
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
function changeShowType(value: string | number | boolean) {
|
||||
console.log(value);
|
||||
}
|
||||
|
||||
// 用户组数据初始化
|
||||
const initData = async (id?: string, isSelect = true) => {
|
||||
const tmpArr: EnvListItem[] = [];
|
||||
for (let i = 0; i < 100; i++) {
|
||||
tmpArr.push({
|
||||
id: `${i + 1}`,
|
||||
name: `环境${i + 1}`,
|
||||
});
|
||||
}
|
||||
envList.value = tmpArr;
|
||||
console.log(id, isSelect);
|
||||
};
|
||||
|
||||
const handleRenameCancel = (element: EnvListItem, id?: string) => {
|
||||
if (id) {
|
||||
initData(id, true);
|
||||
}
|
||||
popVisible.value[element.id].visible = false;
|
||||
};
|
||||
|
||||
const handleListItemClick = (element: EnvListItem) => {
|
||||
const { id } = element;
|
||||
store.setCurrentId(id);
|
||||
};
|
||||
|
||||
function enterData(eve: Event) {
|
||||
if (!(eve.target as HTMLInputElement).value) {
|
||||
return;
|
||||
}
|
||||
const keyword = (eve.target as HTMLInputElement).value;
|
||||
const tmpArr = envList.value.filter((ele) => ele.name.includes(keyword));
|
||||
envList.value = tmpArr;
|
||||
}
|
||||
function searchData(value: string) {
|
||||
if (!value) {
|
||||
// initData('', false);
|
||||
return;
|
||||
}
|
||||
const keyword = value;
|
||||
const tmpArr = envList.value.filter((ele) => ele.name.includes(keyword));
|
||||
envList.value = tmpArr;
|
||||
}
|
||||
onMounted(() => {
|
||||
initData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.page {
|
||||
@apply bg-white;
|
||||
|
||||
min-width: 1000px;
|
||||
height: calc(100vh - 88px);
|
||||
border-radius: var(--border-radius-large);
|
||||
}
|
||||
.env-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 7px 8px;
|
||||
height: 38px;
|
||||
box-sizing: border-box;
|
||||
border-radius: var(--border-radius-base);
|
||||
cursor: pointer;
|
||||
.node-extra {
|
||||
@apply relative hidden;
|
||||
&:hover {
|
||||
@apply block;
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
.node-extra {
|
||||
@apply block;
|
||||
}
|
||||
}
|
||||
:active {
|
||||
color: rgb(var(--primary-5));
|
||||
}
|
||||
}
|
||||
.env-row {
|
||||
@apply flex flex-row justify-between;
|
||||
&-extra {
|
||||
@apply relative hidden;
|
||||
}
|
||||
&:hover {
|
||||
.env-row-extra {
|
||||
@apply block;
|
||||
}
|
||||
}
|
||||
}
|
||||
.ghost {
|
||||
border: 1px dashed rgba(var(--primary-5));
|
||||
background-color: rgba(var(--primary-1));
|
||||
}
|
||||
.file-show-type {
|
||||
@apply grid grid-cols-2;
|
||||
|
||||
margin-bottom: 8px;
|
||||
:deep(.arco-radio-button-content) {
|
||||
@apply text-center;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,3 @@
|
|||
export default {
|
||||
'project.environmental.title': 'Environmental Management',
|
||||
};
|
|
@ -0,0 +1,19 @@
|
|||
export default {
|
||||
'project.environmental.title': '环境管理',
|
||||
'project.environmental.project': '项目',
|
||||
'project.environmental.projectGroup': '项目组',
|
||||
'project.environmental.searchHolder': '请输入环境名称',
|
||||
'project.environmental.allParam': '全局参数',
|
||||
'project.environmental.env': '环境',
|
||||
'project.environmental.envListIsNull': '暂无数据,请点击上方“+”创建环境',
|
||||
'project.environmental.requestHeader': '请求头',
|
||||
'project.environmental.allParams': '全局参数',
|
||||
'project.environmental.mustContain': '必含',
|
||||
'project.environmental.searchParamsHolder': '通过名称或标签搜索',
|
||||
'project.environmental.paramName': '参数名称',
|
||||
'project.environmental.paramType': '类型',
|
||||
'project.environmental.paramTypeTooltip': 'json:仅支持 UI 测试',
|
||||
'project.environmental.paramValue': '参数值',
|
||||
'project.environmental.tag': '标签',
|
||||
'project.environmental.desc': '描述',
|
||||
};
|
|
@ -145,7 +145,7 @@
|
|||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
<style lang="less" scoped>
|
||||
.card {
|
||||
@apply overflow-hidden bg-white;
|
||||
|
||||
|
|
|
@ -319,7 +319,7 @@
|
|||
await tableStore.initColumn(TableKeyEnum.SYSTEM_ORGANIZATION, organizationColumns, 'drawer');
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
<style lang="less" scoped>
|
||||
.primary-color {
|
||||
color: rgb(var(--primary-5));
|
||||
cursor: pointer;
|
||||
|
|
|
@ -314,7 +314,7 @@
|
|||
await tableStore.initColumn(TableKeyEnum.SYSTEM_PROJECT, organizationColumns, 'drawer');
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
<style lang="less" scoped>
|
||||
.primary-color {
|
||||
color: rgb(var(--primary-5));
|
||||
cursor: pointer;
|
||||
|
|
|
@ -147,7 +147,7 @@
|
|||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
<style lang="less" scoped>
|
||||
.card {
|
||||
@apply overflow-hidden bg-white;
|
||||
|
||||
|
|
Loading…
Reference in New Issue