fix(项目管理): 环境管理相关bug和对接字段

This commit is contained in:
xinxin.wu 2024-03-05 18:26:42 +08:00 committed by Craftsman
parent a4e9f8c2a5
commit e810e8c8c7
14 changed files with 848 additions and 208 deletions

View File

@ -69,7 +69,7 @@
>
<div ref="refBtn" class="ref-btn"></div>
<template #content>
<message-box />
<MessageBox />
</template>
</a-popover>
</li>
@ -145,6 +145,7 @@
import { useClipboard } from '@vueuse/core';
import { Message } from '@arco-design/web-vue';
import MessageBox from '@/components/pure/message-box/index.vue';
import MessageCenterDrawer from '@/components/business/ms-message/MessageCenterDrawer.vue';
import TopMenu from '@/components/business/ms-top-menu/index.vue';
import TaskCenterModal from './taskCenterModal.vue';

View File

@ -17,15 +17,14 @@ export interface EnvGroupListItem {
projectList: EnvGroupProjectListItem[];
}
export interface DataSourceItem {
id?: string;
name: string;
driverId?: string;
dbUrl: string;
username: string;
password?: string;
poolMax?: number;
timeout?: number;
enable?: boolean;
id: string;
dataSource: string; // 数据源名称
driverId: string; // 驱动id
dbUrl: string; // 数据库连接url
username: string; // 用户名
password: string; // 密码
poolMax?: number; // 最大连接数
timeout?: number; // 超时时间
}
export interface EnvConfigItem {
@ -101,6 +100,7 @@ export interface EnvPluginScript {
script: any[];
scriptType: string;
tabName: string;
fields: string[];
}
export interface EnvPluginListItem {
pluginId: string;
@ -130,10 +130,13 @@ export interface DragParam {
export interface HttpForm {
id?: string;
protocol: string;
description?: string;
hostname: string;
enableCondition: string;
type: string;
headers: Record<string, any>[];
// pathMatchRule: {
path: string;
operator: string;
headerParams: any[];
condition: string;
// };
}

View File

@ -706,7 +706,7 @@
};
const handleEnvironment = (obj: Record<string, any>, record: Record<string, any>) => {
record.host = obj.host;
record.domain = {};
emit('change', propsRes.value.data);
};

View File

@ -1,16 +1,16 @@
<template>
<div class="page">
<a-tabs v-model:active-key="activeKey" class="no-content">
<!-- <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]" />
<a-divider :margin="0" class="!mb-[16px]" /> -->
<RequestHeader v-if="activeKey === 'requestHeader'" v-model:params="headerParams" @change="canSave = true" />
<AllPrams
<!-- <AllPrams
v-else-if="activeKey === 'globalVariable'"
v-model:params="GlobalVariable"
:table-key="TableKeyEnum.PROJECT_MANAGEMENT_ENV_ALL_PARAM_VARIABLE"
@change="canSave = true"
/>
/> -->
<div class="footer" :style="{ width: '100%' }">
<a-button :disabled="!canSave" type="primary" @click="handleSave">{{ t('common.save') }}</a-button>
</div>
@ -34,7 +34,7 @@
const projectEnvStore = useProjectEnvStore();
const appStore = useAppStore();
const activeKey = ref('globalVariable');
const activeKey = ref('requestHeader');
const headerParams = ref<EnvConfigItem[]>([]);
const GlobalVariable = ref<EnvConfigItem[]>([]);
const { t } = useI18n();

View File

@ -42,7 +42,9 @@
</div>
<div class="footer" :style="{ width: '100%' }">
<a-button :disabled="!canSave" @click="handleReset">{{ t('common.cancel') }}</a-button>
<a-button :disabled="!canSave" type="primary" @click="handleSave">{{ t('common.save') }}</a-button>
<a-button :disabled="!canSave" :loading="loading" type="primary" @click="handleSave">{{
t('common.save')
}}</a-button>
</div>
</template>
<template v-else>
@ -54,7 +56,7 @@
</template>
<script lang="ts" setup async>
import { ValidatedError } from '@arco-design/web-vue';
import { Message, ValidatedError } from '@arco-design/web-vue';
import paramsTable, { type ParamTableColumn } from '@/views/api-test/components/paramTable.vue';
@ -133,17 +135,17 @@
innerParams.value = detail.environmentGroupInfo || [];
}
};
const loading = ref<boolean>(false);
const handleSave = () => {
envGroupForm.value?.validate(async (errors: undefined | Record<string, ValidatedError>) => {
if (errors) {
return;
}
loading.value = true;
try {
const id = store.currentGroupId === NEW_ENV_GROUP ? undefined : store.currentGroupId;
const envGroupProject = innerParams.value.filter((item) => item.projectId && item.environmentId);
if (!envGroupProject.length) {
return;
}
const params = {
id,
name: form.name,
@ -154,6 +156,7 @@
let res: EnvListItem;
if (id) {
res = await groupUpdateEnv(params);
Message.success(t('common.saveSuccess'));
initDetail(res.id);
} else {
res = await groupAddEnv(params);
@ -162,6 +165,8 @@
} catch (e) {
// eslint-disable-next-line no-console
console.error(e);
} finally {
loading.value = false;
}
});
};

View File

@ -52,7 +52,9 @@
<PluginTab
v-if="activeKey === item.pluginId"
:model-value="store.currentEnvDetailInfo.config.pluginConfigMap[item.pluginId]"
:plugin-id="item.pluginId"
:script="item.script"
:fields="item.script.fields"
@update:model-value="store.currentEnvDetailInfo.config.pluginConfigMap[item.pluginId] = $event"
/>
</template>
@ -160,6 +162,7 @@
isShow: true,
},
];
//
const initPlugin = async () => {
try {
@ -200,6 +203,7 @@
watchEffect(() => {
if (store.currentId) {
store.initEnvDetail();
// initPlugin();
}
});
@ -245,7 +249,7 @@
.content {
overflow-y: auto;
padding: 0 24px;
max-height: calc(100% - 260px);
max-height: calc(100% - 320px);
background-color: #ffffff;
}
.no-content {

View File

@ -10,22 +10,24 @@
></a-input-search>
</div>
<MsBaseTable class="mt-[16px]" v-bind="propsRes" v-on="propsEvent">
<template #driverId="{ record }">
{{ getDriver(record.driverId) }}
</template>
<template #operation="{ record }">
<div class="flex flex-row flex-nowrap">
<div class="flex flex-row flex-nowrap items-center">
<MsButton class="!mr-0" @click="handleCopy(record)">{{ t('common.copy') }}</MsButton>
<a-divider direction="vertical" />
<a-divider class="h-[16px]" direction="vertical" />
<MsButton class="!mr-0" @click="handleEdit(record)">{{ t('common.edit') }}</MsButton>
<a-divider direction="vertical" />
<a-divider class="h-[16px]" direction="vertical" />
<MsTableMoreAction :list="moreActionList" trigger="click" @select="handleMoreActionSelect($event, record)" />
</div>
</template>
</MsBaseTable>
<AddDatabaseModal
v-model:visible="addVisible"
:current-database="currentDatabase"
:current-id="currentId"
:is-copy="isCopy"
@close="addVisible = false"
@add-or-update="handleAddOrUpdate"
/>
</template>
@ -34,7 +36,7 @@
import MsButton from '@/components/pure/ms-button/index.vue';
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
import { MsTableColumn, MsTableDataItem } from '@/components/pure/ms-table/type';
import { MsTableColumn } from '@/components/pure/ms-table/type';
import useTable from '@/components/pure/ms-table/useTable';
import MsTableMoreAction from '@/components/pure/ms-table-more-action/index.vue';
import { ActionsItem } from '@/components/pure/ms-table-more-action/types';
@ -45,8 +47,6 @@
import useProjectEnvStore from '@/store/modules/setting/useProjectEnvStore';
import { BugListItem } from '@/models/bug-management';
import { CommonList } from '@/models/common';
import { DataSourceItem } from '@/models/projectManagement/environmental';
import { TableKeyEnum } from '@/enums/tableEnum';
const { t } = useI18n();
@ -58,36 +58,24 @@
const tableStore = useTableStore();
const addVisible = ref(false);
const currentId = ref('');
const currentDatabase = ref<DataSourceItem>({
id: '',
name: '',
driverId: '',
dbUrl: '',
username: '',
password: '',
poolMax: 1,
timeout: 1000,
enable: true,
});
const columns: MsTableColumn = [
{
title: 'project.environmental.database.name',
dataIndex: 'name',
dataIndex: 'dataSource',
showTooltip: true,
showDrag: true,
showInTable: true,
},
{
title: 'project.environmental.database.driver',
dataIndex: 'desc',
dataIndex: 'driverId',
slotName: 'driverId',
showDrag: true,
showInTable: true,
},
{
title: 'URL',
dataIndex: 'url',
dataIndex: 'dbUrl',
showDrag: true,
showInTable: true,
},
@ -118,21 +106,16 @@
},
];
await tableStore.initColumn(TableKeyEnum.PROJECT_MANAGEMENT_ENV_ENV_HTTP, columns);
const { propsRes, propsEvent } = useTable(
() =>
Promise.resolve([]) as unknown as Promise<
MsTableDataItem<DataSourceItem> | CommonList<MsTableDataItem<DataSourceItem>>
>,
{
tableKey: TableKeyEnum.PROJECT_MANAGEMENT_ENV_ENV_HTTP,
scroll: { x: '100%' },
selectable: false,
showSetting: true,
showPagination: false,
showMode: false,
isSimpleSetting: true,
}
);
const { propsRes, propsEvent } = useTable(undefined, {
tableKey: TableKeyEnum.PROJECT_MANAGEMENT_ENV_ENV_HTTP,
scroll: { x: '100%' },
selectable: false,
showSetting: true,
showPagination: false,
heightUsed: 590,
showMode: false,
isSimpleSetting: true,
});
const moreActionList: ActionsItem[] = [
{
@ -155,39 +138,64 @@
}
}
function getDriver(driverId: string) {
return driverId === 'oracle.jdbc.OracleDriver&oracle.jdbc.OracleDriver'
? 'oracle.jdbc.OracleDriver'
: 'com.mysql.cj.jdbc.Driver';
}
const isCopy = ref<boolean>(false);
/**
* 复制
*/
const handleCopy = (record: any) => {
addVisible.value = true;
currentId.value = '';
currentDatabase.value = { ...record, id: '' };
};
const handleEdit = (record: any) => {
addVisible.value = true;
isCopy.value = true;
currentId.value = record.id;
};
const handleAdd = () => {
currentDatabase.value = { name: '', dbUrl: '', username: '' };
addVisible.value = true;
};
/**
* 编辑
*/
const handleEdit = (record: any) => {
isCopy.value = false;
currentId.value = record.id;
addVisible.value = true;
};
/**
* 添加
*/
const handleAdd = () => {
currentId.value = '';
isCopy.value = false;
addVisible.value = true;
};
/**
* 查询
*/
const fetchData = () => {
if (keyword.value) {
propsRes.value.data = innerParam.value.filter((item) => item.name.includes(keyword.value));
propsRes.value.data = innerParam.value.filter((item) => item.dataSource.includes(keyword.value));
} else {
propsRes.value.data = innerParam.value;
}
};
const handleAddOrUpdate = (data: DataSourceItem, cb: (v: boolean) => void) => {
if (data.id) {
const index = innerParam.value.findIndex((item) => item.id === data.id);
store.currentEnvDetailInfo.config.dataSources[index] = data;
} else {
data.id = new Date().getTime().toString();
store.currentEnvDetailInfo.config.dataSources.push(data);
watch(
() => innerParam.value,
(val) => {
if (val) {
propsRes.value.data = val;
}
},
{
deep: true,
immediate: true,
}
cb(true);
};
watch(innerParam.value, () => {
fetchData();
});
);
</script>
<style lang="less" scoped>

View File

@ -28,22 +28,25 @@
</a-select>
</div>
</div>
<MsBaseTable class="mt-[16px]" v-bind="propsRes" v-on="propsEvent">
<MsBaseTable class="mt-[16px]" v-bind="propsRes" v-on="propsEvent" @change="changeHandler">
<template #type="{ record }">
<span>{{ getEnableScope(record.type) }}</span>
</template>
<template #operation="{ record }">
<div class="flex flex-row flex-nowrap">
<div class="flex flex-row flex-nowrap items-center">
<MsButton class="!mr-0" @click="handleCopy(record)">{{ t('common.copy') }}</MsButton>
<a-divider direction="vertical" />
<a-divider class="h-[16px]" direction="vertical" />
<MsButton class="!mr-0" @click="handleEdit(record)">{{ t('common.edit') }}</MsButton>
<a-divider direction="vertical" />
<a-divider class="h-[16px]" direction="vertical" />
<MsTableMoreAction :list="moreActionList" trigger="click" @select="handleMoreActionSelect($event, record)" />
</div>
</template>
</MsBaseTable>
<AddHttpDrawer v-model:visible="addVisible" :current-obj="currentObj" @close="addVisible = false" />
<AddHttpDrawer v-model:visible="addVisible" :is-copy="isCopy" :current-id="httpId" @close="addVisible = false" />
</template>
<script lang="ts" async setup>
import { TableData } from '@arco-design/web-vue';
import { TableChangeExtra, TableData } from '@arco-design/web-vue';
import MsButton from '@/components/pure/ms-button/index.vue';
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
@ -69,31 +72,25 @@
const showTitle = computed(() => store.httpNoWarning);
const tableStore = useTableStore();
const addVisible = ref(false);
const currentObj = ref<HttpForm>({
id: '',
hostname: '',
enableCondition: 'none',
path: '',
operator: '',
headerParams: [],
});
const columns: MsTableColumn = [
{
title: 'project.environmental.http.host',
dataIndex: 'host',
dataIndex: 'hostname',
slotName: 'hostname',
showTooltip: true,
showDrag: true,
showInTable: true,
},
{
title: 'project.environmental.http.desc',
dataIndex: 'desc',
dataIndex: 'description',
showDrag: true,
showInTable: true,
},
{
title: 'project.environmental.http.enableScope',
dataIndex: 'enableScope',
dataIndex: 'type',
slotName: 'type',
showDrag: true,
showInTable: true,
},
@ -120,7 +117,9 @@
noDisable: true,
showSetting: true,
showPagination: false,
enableDrag: true,
showMode: false,
heightUsed: 644,
debug: true,
});
@ -146,34 +145,37 @@
handleSingleDelete(record);
}
}
const httpId = ref<string>('');
const isCopy = ref<boolean>(false);
const handleCopy = (record: any) => {
currentObj.value = record;
currentObj.value.id = '';
httpId.value = record.id;
isCopy.value = true;
addVisible.value = true;
};
const handleEdit = (record: any) => {
currentObj.value = record;
httpId.value = record.id;
isCopy.value = false;
addVisible.value = true;
};
const handleAddHttp = () => {
currentObj.value = {
id: '',
hostname: '',
enableCondition: 'none',
path: '',
operator: '',
headerParams: [],
};
httpId.value = '';
isCopy.value = false;
addVisible.value = true;
};
const handleNoWarning = () => {
store.setHttpNoWarning(false);
};
watch(store.currentEnvDetailInfo.config.httpConfig, () => {
propsRes.value.data = store.currentEnvDetailInfo.config.httpConfig;
});
watch(
store.currentEnvDetailInfo.config.httpConfig,
() => {
propsRes.value.data = store.currentEnvDetailInfo.config.httpConfig;
},
{ deep: true, immediate: true }
);
const form = computed({
set: (value: any) => {
@ -181,6 +183,46 @@
},
get: () => store.currentEnvDetailInfo.config.commonParams as CommonParams,
});
const data = computed({
set: (value: any) => {
store.currentEnvDetailInfo.config.httpConfig = value;
},
get: () => {
return store.currentEnvDetailInfo.config.httpConfig;
},
});
watch(
() => data.value,
(val) => {
if (val) {
propsRes.value.data = data.value;
}
}
);
//
function changeHandler(_data: TableData[], extra: TableChangeExtra, currentData: TableData[]) {
if (!currentData || currentData.length === 1) {
return false;
}
propsRes.value.data = _data;
data.value = _data;
}
function getEnableScope(type: string) {
switch (type) {
case 'NONE':
return t('project.environmental.http.none');
case 'MODULE':
return t('project.environmental.http.module');
case 'PATH':
return t('project.environmental.http.path');
default:
break;
}
}
</script>
<style lang="less" scoped>

View File

@ -12,16 +12,22 @@
import MsFormCreate from '@/components/pure/ms-form-create/formCreate.vue';
import useProjectEnvStore from '@/store/modules/setting/useProjectEnvStore';
import { EnvPluginScript } from '@/models/projectManagement/environmental';
import type { Api } from '@form-create/arco-design';
const props = defineProps<{
script: EnvPluginScript;
pluginId: string;
fields: string[];
}>();
const fApi = ref<Api>();
const currentPluginScript = computed(() => props.script?.script || {});
const store = useProjectEnvStore();
const currentPluginScript = computed(() => props.script?.script || []);
const currentPluginOptions = computed(() => props.script?.options || {});
const innerParams = defineModel<Record<string, any>>({ default: () => ({}) });
@ -30,6 +36,51 @@
const handlePluginFormChange = debounce(() => {
innerParams.value = fApi.value?.formData() || {};
}, 300);
/**
* 设置插件表单数据
*/
function setPluginFormData() {
const tempForm = { ...store.currentEnvDetailInfo.config.pluginConfigMap[props.pluginId] };
if (store.currentEnvDetailInfo.config.pluginConfigMap[props.pluginId]) {
fApi.value?.reload(currentPluginScript.value);
if (fApi.value) {
const form = {};
props.fields.forEach((key) => {
form[key] = tempForm[key];
});
fApi.value?.setValue(tempForm);
}
fApi.value?.refresh();
} else {
nextTick(() => {
fApi.value?.resetFields();
});
}
}
watch(
() => store.currentId,
(val) => {
if (val) {
setPluginFormData();
}
}
);
watch(
() => innerParams.value,
(val) => {
setPluginFormData();
},
{
deep: true,
}
);
onMounted(() => {
setPluginFormData();
});
</script>
<style lang="less" scoped></style>

View File

@ -18,7 +18,7 @@
:label="t('project.environmental.http.hostName')"
:rules="[{ required: true, message: t('project.environmental.http.hostNameRequired') }]"
>
<a-input
<!-- <a-input
v-model="form.hostname"
class="w-[100%]"
:max-length="255"
@ -26,14 +26,32 @@
>
<template #prefix>
<div class="input-prefix"> http:// </div>
<a-select :options="['http://', 'https://']" />
</template>
</a-input>
</a-input> -->
<a-input-group class="w-full">
<a-select v-model="form.protocol" :style="{ width: '160px' }" default-value="http">
<a-option value="http">http://</a-option>
<a-option value="https">https://</a-option>
</a-select>
<a-input
v-model="form.hostname"
class="w-full"
:max-length="255"
:placeholder="
hostType === 'http://'
? t('project.environmental.http.httpHostNamePlaceholder')
: t('project.environmental.http.httpsHostNamePlaceholder')
"
/>
</a-input-group>
</a-form-item>
<a-form-item class="mb-[16px]" field="enableCondition" :label="t('project.environmental.http.enableCondition')">
<a-select v-model:model-value="form.enableCondition">
<a-option value="none">{{ t('project.environmental.http.none') }}</a-option>
<a-option value="module">{{ t('project.environmental.http.module') }}</a-option>
<a-option value="path">{{ t('project.environmental.http.path') }}</a-option>
<a-form-item class="mb-[16px]" field="type" :label="t('project.environmental.http.enableCondition')">
<a-select v-model:model-value="form.type">
<a-option value="NONE">{{ t('project.environmental.http.none') }}</a-option>
<a-option value="MODULE">{{ t('project.environmental.http.module') }}</a-option>
<a-option value="PATH">{{ t('project.environmental.http.path') }}</a-option>
</a-select>
</a-form-item>
<!-- 接口模块选择 -->
@ -50,7 +68,7 @@
</a-select>
</a-form-item> -->
<a-form-item class="mb-[16px]" field="description" :label="t('project.environmental.http.description')">
<a-input />
<a-input v-model="form.description" />
</a-form-item>
<!-- 选择UI测试模块 -->
<!-- <a-form-item
@ -65,51 +83,78 @@
<a-option value="none">{{ t('project.environmental.http.none') }}</a-option>
</a-select>
</a-form-item> -->
<!-- 展示模块 -->
<!-- TODO 模块还没有加 -->
<!-- <a-form-item class="mb-[16px]" field="description" :label="t('project.environmental.http.selectApiModule')">
<ApiTree
v-model:focus-node-key="focusNodeKey"
:placeholder="t('project.environmental.http.selectApiModule')"
:selected-keys="selectedKeys"
:data="moduleTree"
:field-names="{
title: 'name',
key: 'id',
children: 'children',
count: 'count',
}"
:tree-checkable="true"
:hide-more-action="true"
>
<template #tree-slot-title="nodeData">
<div class="inline-flex w-full">
<div class="one-line-text w-[calc(100%-32px)] text-[var(--color-text-1)]">{{ nodeData.name }}</div>
</div>
</template>
<template #tree-slot-extra="nodeData">
<span><MsTableMoreAction :list="moreActions" @select="handleMoreActionSelect($event, nodeData)" /></span>
</template>
</ApiTree>
</a-form-item> -->
<!-- 路径 -->
<a-form-item
v-if="showPathInput"
class="path-input mb-[16px]"
class="mb-[16px]"
asterisk-position="end"
field="hostname"
field="path"
:label="t('project.environmental.http.path')"
:rules="[{ required: true, message: t('project.environmental.http.pathRequired') }]"
>
<a-input
v-model="form.hostname"
class="w-[100%]"
:max-length="255"
:placeholder="t('project.environmental.http.pathPlaceholder')"
>
<template #prefix>
<div class="input-prefix">
<a-select default-value="like">
<a-option v-for="item in OPERATOR_MAP.string" :key="item.value" :value="item.value">{{
t(item.label)
}}</a-option>
</a-select>
</div>
</template>
</a-input>
<a-input-group class="w-full">
<a-select v-model="form.condition" :style="{ width: '160px' }" default-value="CONTAINS">
<a-option v-for="item in OPERATOR_MAP" :key="item.value" :value="item.value">{{ t(item.label) }}</a-option>
</a-select>
<a-input
v-model="form.path"
class="w-full"
:max-length="255"
:placeholder="t('project.environmental.http.pathPlaceholder')"
/>
</a-input-group>
</a-form-item>
</a-form>
<RequestHeader :params="form.headerParams" />
<RequestHeader v-model:params="form.headers" />
</MsDrawer>
</template>
<script lang="ts" setup>
import { defineModel } from 'vue';
import { OPERATOR_MAP } from '@/components/pure/ms-advance-filter/index';
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
import MsTableMoreAction from '@/components/pure/ms-table-more-action/index.vue';
import type { ActionsItem } from '@/components/pure/ms-table-more-action/types';
import type { MsTreeFieldNames, MsTreeNodeData, MsTreeSelectedData } from '@/components/business/ms-tree/types';
import RequestHeader from '../../requestHeader/index.vue';
// import ApiTree from './apiTree.vue';
import { useI18n } from '@/hooks/useI18n';
import useProjectEnvStore from '@/store/modules/setting/useProjectEnvStore';
import { getGenerateId } from '@/utils';
import { HttpForm } from '@/models/projectManagement/environmental';
const props = defineProps<{
currentObj: HttpForm;
currentId: string;
isCopy: boolean;
}>();
const store = useProjectEnvStore();
@ -118,37 +163,505 @@
(e: 'close'): void;
}>();
const form = ref<HttpForm>({
const OPERATOR_MAP = [
{
value: 'CONTAINS',
label: '包含',
},
{
value: 'EQUALS',
label: '等于',
},
];
const initForm = {
id: '',
hostname: '',
enableCondition: 'none',
type: 'NONE',
headers: [],
path: '',
operator: '',
headerParams: [],
});
condition: 'CONTAINS',
description: '',
protocol: 'http',
};
const form = ref<HttpForm>({ ...initForm });
const hostType = ref<string>('http://');
const httpRef = ref();
const showPathInput = computed(() => form.value.enableCondition === 'path');
const showPathInput = computed(() => form.value.type === 'PATH');
const visible = defineModel('visible', { required: true, type: Boolean, default: false });
const { t } = useI18n();
const isEdit = computed(() => !!props.currentObj.id);
function resetForm() {
form.value = { ...initForm };
}
const title = computed(() => {
return isEdit.value ? t('project.environmental.http.edit') : t('project.environmental.http.add');
});
const handleAddOrUpdate = () => {
if (form.value.id) {
const index = store.currentEnvDetailInfo.config.httpConfig.findIndex((item) => item.id === form.value.id);
store.currentEnvDetailInfo.config.httpConfig.splice(index, 1, form.value);
const index = store.currentEnvDetailInfo.config.httpConfig.findIndex((item) => item.id === form.value.id);
//
if (index > -1 && !props.isCopy) {
store.currentEnvDetailInfo.config.httpConfig.splice(index + 1, 1, form.value);
//
} else if (index > -1 && props.isCopy) {
const insertItem = {
...form.value,
id: getGenerateId(),
hostname: `copy_${form.value.hostname}`,
order: store.currentEnvDetailInfo.config.httpConfig.length + 1,
};
store.currentEnvDetailInfo.config.httpConfig.splice(index, 0, insertItem);
//
} else {
store.currentEnvDetailInfo.config.httpConfig.push(form);
const { protocol, hostname, condition, path } = form.value;
const httpItem = {
...form.value,
hostname: `${protocol}://${hostname}`,
pathMatchRule: {
path,
condition,
},
id: getGenerateId(),
order: store.currentEnvDetailInfo.config.httpConfig.length + 1,
};
store.currentEnvDetailInfo.config.httpConfig.push(httpItem);
}
emit('close');
};
const moduleTree = ref([
{
id: 'root',
name: '未规划请求',
type: 'MODULE',
parentId: 'NONE',
children: [
{
id: '4112912223068160',
name: '随便写的',
type: 'API',
parentId: 'root',
children: [],
attachInfo: {
protocol: 'HTTP',
method: 'OPTIONS',
},
count: 0,
path: '/',
},
{
id: '1150192243335168',
name: '文件儿',
type: 'API',
parentId: 'root',
children: [],
attachInfo: {
protocol: 'HTTP',
method: 'GET',
},
count: 0,
path: '/',
},
{
id: '1165379247169536',
name: '901',
type: 'API',
parentId: 'root',
children: [],
attachInfo: {
protocol: 'TCP',
},
count: 0,
path: '/',
},
{
id: '1165705664684032',
name: '888',
type: 'API',
parentId: 'root',
children: [],
attachInfo: {
protocol: 'TCP',
},
count: 0,
path: '/',
},
{
id: '2125544956010496',
name: '0129-1',
type: 'API',
parentId: 'root',
children: [],
attachInfo: {
protocol: 'SPX',
},
count: 0,
path: '/',
},
{
id: '2126988065021952',
name: '0129-2',
type: 'API',
parentId: 'root',
children: [],
attachInfo: {
protocol: 'SPX',
},
count: 0,
path: '/',
},
{
id: '2171827523477504',
name: 'fffggg',
type: 'API',
parentId: 'root',
children: [],
attachInfo: {
protocol: 'HTTP',
method: 'GET',
},
count: 0,
path: '/',
},
{
id: '2297223388766208',
name: '0129-3',
type: 'API',
parentId: 'root',
children: [],
attachInfo: {
protocol: 'SPX',
},
count: 0,
path: '/',
},
{
id: '4034709458542592',
name: '测试一下百度',
type: 'API',
parentId: 'root',
children: [],
attachInfo: {
protocol: 'HTTP',
method: 'PATCH',
},
count: 0,
path: '/',
},
{
id: '1017890070175744',
name: 'TTTTTCCCCCPPPPP',
type: 'API',
parentId: 'root',
children: [],
attachInfo: {
protocol: 'TCP',
},
count: 0,
path: '/',
},
{
id: '864679996792832',
name: '委托',
type: 'API',
parentId: 'root',
children: [],
attachInfo: {
protocol: 'SPX',
},
count: 0,
path: '/',
},
{
id: '867463135600640',
name: '登入',
type: 'API',
parentId: 'root',
children: [],
attachInfo: {
protocol: 'SPX',
},
count: 0,
path: '/',
},
{
id: '868236229713920',
name: '买入',
type: 'API',
parentId: 'root',
children: [],
attachInfo: {
protocol: 'SPX',
},
count: 0,
path: '/',
},
{
id: '927008562290689',
name: '账号校验1',
type: 'API',
parentId: 'root',
children: [],
attachInfo: {
protocol: 'SPX',
},
count: 0,
path: '/',
},
{
id: '943707395039232',
name: 'ddd',
type: 'API',
parentId: 'root',
children: [],
attachInfo: {
protocol: 'TCP',
},
count: 0,
path: '/',
},
{
id: '1068845561815040',
name: 'TCP测试2',
type: 'API',
parentId: 'root',
children: [],
attachInfo: {
protocol: 'TCP',
},
count: 0,
path: '/',
},
{
id: '1131706704060416',
name: 'aaa',
type: 'API',
parentId: 'root',
children: [],
attachInfo: {
protocol: 'HTTP',
method: 'GET',
},
count: 0,
path: '/',
},
{
id: '921184586637312',
name: '读取系统日期22',
type: 'API',
parentId: 'root',
children: [],
attachInfo: {
protocol: 'SPX',
},
count: 0,
path: '/',
},
{
id: '642853526609920',
name: 'Test',
type: 'API',
parentId: 'root',
children: [],
attachInfo: {
protocol: 'HTTP',
method: 'GET',
},
count: 0,
path: '/',
},
{
id: '851760736174080',
name: 'dd',
type: 'API',
parentId: 'root',
children: [],
attachInfo: {
protocol: 'HTTP',
method: 'GET',
},
count: 0,
path: '/',
},
{
id: '853891039952896',
name: 'fasdfd',
type: 'API',
parentId: 'root',
children: [],
attachInfo: {
protocol: 'HTTP',
method: 'GET',
},
count: 0,
path: '/',
},
{
id: '1118186147643392',
name: 'eeee',
type: 'API',
parentId: 'root',
children: [],
attachInfo: {
protocol: 'HTTP',
method: 'GET',
},
count: 0,
path: '/',
},
{
id: '1120161832599552',
name: 'eeeeqqq',
type: 'API',
parentId: 'root',
children: [],
attachInfo: {
protocol: 'HTTP',
method: 'GET',
},
count: 0,
path: '/',
},
{
id: '1162149432885248',
name: 'a',
type: 'API',
parentId: 'root',
children: [],
attachInfo: {
protocol: 'HTTP',
method: 'GET',
},
count: 0,
path: '/',
},
{
id: '1177147458682880',
name: '这是Curl导入的请求',
type: 'API',
parentId: 'root',
children: [],
attachInfo: {
protocol: 'HTTP',
method: 'GET',
},
count: 0,
path: '/',
},
{
id: '2226957725106176',
name: 'test12',
type: 'API',
parentId: 'root',
children: [],
attachInfo: {
protocol: 'HTTP',
method: 'GET',
},
count: 0,
path: '/',
},
{
id: '2601169635672064',
name: 'testvvvv',
type: 'API',
parentId: 'root',
children: [],
attachInfo: {
protocol: 'HTTP',
method: 'GET',
},
count: 0,
path: '/',
},
{
id: '885467640184832',
name: 'okko',
type: 'API',
parentId: 'root',
children: [],
attachInfo: {
protocol: 'HTTP',
method: 'GET',
},
count: 0,
path: '/',
},
{
id: '891635213221888',
name: 'gs',
type: 'API',
parentId: 'root',
children: [],
attachInfo: {
protocol: 'HTTP',
method: 'GET',
},
count: 0,
path: '/',
},
{
id: '640018849742848',
name: '0228',
type: 'API',
parentId: 'root',
children: [],
attachInfo: {
protocol: 'SPX',
},
count: 0,
path: '/',
},
{
id: '2178166898065408',
name: '0304',
type: 'API',
parentId: 'root',
children: [],
attachInfo: {
protocol: 'SPX',
},
count: 0,
path: '/',
},
],
attachInfo: {},
count: 0,
path: '/未规划请求',
},
]);
const moreActions: ActionsItem[] = [
{
label: 'caseManagement.featureCase.copyStep',
eventTag: 'copyStep',
},
];
const selectedKeys = ref<string[]>([]);
const focusNodeKey = ref<string>('');
function handleMoreActionSelect(item: ActionsItem, node: MsTreeNodeData) {}
const title = ref<string>('');
watchEffect(() => {
title.value = props.currentId ? t('project.environmental.http.edit') : t('project.environmental.http.add');
if (props.currentId) {
const currentItem = store.currentEnvDetailInfo.config.httpConfig.find(
(item) => item.id === props.currentId
) as HttpForm;
if (currentItem) {
form.value = {
...currentItem,
};
}
} else {
resetForm();
}
});
</script>
<style lang="less" scoped>
@ -158,7 +671,6 @@
display: flex;
justify-content: center;
align-items: center;
padding: 5px 8px;
border-top: 1px solid var(--color-text-7);
border-bottom: 1px solid var(--color-text-7);
border-left: 1px solid var(--color-text-7);

View File

@ -4,12 +4,15 @@
title-align="start"
class="ms-modal-form ms-modal-medium"
unmount-on-close
:modal-style="{
top: '0 !important',
}"
@cancel="handleCancel(false)"
>
<template #title>
<span v-if="isEdit">
<span v-if="props.currentId && !props.isCopy">
{{ t('project.environmental.database.updateDatabase') }}
<span class="text-[var(--color-text-4)]">({{ form.name }})</span>
<span class="text-[var(--color-text-4)]">({{ updateName }})</span>
</span>
<span v-else>
{{ t('project.environmental.database.addDatabase') }}
@ -18,20 +21,20 @@
<div class="form">
<a-form ref="formRef" class="rounded-[4px]" :model="form" layout="vertical">
<a-form-item
field="name"
field="dataSource"
required
:label="t('project.environmental.database.name')"
asterisk-position="end"
:rules="[{ required: true, message: t('project.environmental.database.nameIsRequire') }]"
>
<a-input
v-model="form.name"
v-model="form.dataSource"
:max-length="255"
allow-clear
:placeholder="t('project.environmental.database.namePlaceholder')"
/>
</a-form-item>
<a-form-item field="driverId" asterisk-position="end" :label="t('project.environmental.database.driver')">
<a-form-item field="driver" asterisk-position="end" :label="t('project.environmental.database.driver')">
<a-select v-model="form.driverId" :options="driverOption" />
</a-form-item>
<a-form-item
@ -39,7 +42,9 @@
required
:label="t('project.environmental.database.url')"
asterisk-position="end"
:extra="t('project.environmental.database.urlExtra')"
:extra="
form.driverId === 'system&com.mysql.cj.jdbc.Driver' ? t('project.environmental.database.urlExtra') : ''
"
:rules="[{ required: true, message: t('project.environmental.database.urlIsRequire') }]"
>
<a-input v-model="form.dbUrl" :max-length="255" allow-clear :placeholder="t('common.pleaseInput')" />
@ -92,7 +97,7 @@
{{ t('common.cancel') }}
</a-button>
<a-button type="primary" :loading="loading" @click="handleBeforeOk">
{{ isEdit ? t('common.confirm') : t('common.add') }}
{{ props.currentId && !props.isCopy ? t('common.confirm') : t('common.add') }}
</a-button>
</div>
</div>
@ -108,16 +113,19 @@
import { useI18n } from '@/hooks/useI18n';
import { useAppStore } from '@/store';
import useLicenseStore from '@/store/modules/setting/license';
import useProjectEnvStore from '@/store/modules/setting/useProjectEnvStore';
import { getGenerateId } from '@/utils';
import { DataSourceItem } from '@/models/projectManagement/environmental';
import type { FormInstance, ValidatedError } from '@arco-design/web-vue';
const { t } = useI18n();
const store = useProjectEnvStore();
const props = defineProps<{
currentDatabase: DataSourceItem;
currentId: string;
visible: boolean;
isCopy: boolean;
}>();
const formRef = ref<FormInstance>();
@ -139,18 +147,15 @@
const form = ref<DataSourceItem>({
id: '',
name: '',
dataSource: '',
driverId: '',
dbUrl: '',
username: '',
password: '',
poolMax: 1,
timeout: 1000,
enable: true,
});
const isEdit = computed(() => !!props.currentDatabase.id);
const getDriverOption = async () => {
try {
const res = (await driverOptionFun(appStore.currentOrgId)) || [];
@ -158,7 +163,7 @@
label: item.name,
value: item.id,
}));
if (res.length && !isEdit.value) {
if (res.length && !props.currentId) {
//
form.value.driverId = res[0].id;
}
@ -178,13 +183,7 @@
try {
loading.value = true;
await validateDatabaseEnv({
name: form.value.name,
driverId: form.value.driverId,
dbUrl: form.value.dbUrl,
username: form.value.username,
password: form.value.password,
poolMax: form.value.poolMax,
timeout: form.value.timeout,
...form.value,
});
Message.success(t('project.environmental.database.testConnectionSuccess'));
} catch (error) {
@ -204,14 +203,13 @@
const formReset = () => {
form.value = {
id: '',
name: '',
dataSource: '',
driverId: '',
dbUrl: '',
username: '',
password: '',
poolMax: 1,
timeout: 1000,
enable: true,
};
};
const handleCancel = (shouldSearch: boolean) => {
@ -226,15 +224,24 @@
return;
}
try {
loading.value = true;
emit('addOrUpdate', form.value, (v: boolean) => {
Message.success(
isEdit.value
? t('project.environmental.database.updateDataSourceSuccess')
: t('project.environmental.database.createDataSourceSuccess')
);
handleCancel(v);
});
const index = store.currentEnvDetailInfo.config.dataSources.findIndex((item: any) => item.id === form.value.id);
if (index > -1 && !props.isCopy) {
store.currentEnvDetailInfo.config.dataSources.splice(index, 1, form.value);
} else if (index > -1 && props.isCopy) {
const insertItem = {
...form.value,
dataSource: `copy_${form.value.dataSource}`,
id: getGenerateId(),
};
store.currentEnvDetailInfo.config.dataSources.splice(index + 1, 0, insertItem);
} else {
const dataSourceItem = {
...form.value,
id: getGenerateId(),
};
store.currentEnvDetailInfo.config.dataSources.push(dataSourceItem);
}
currentVisible.value = false;
} catch (error) {
// eslint-disable-next-line no-console
console.error(error);
@ -243,19 +250,26 @@
}
});
};
const initData = () => {
onMounted(() => {
getDriverOption();
};
});
const updateName = ref<string>('');
watchEffect(() => {
initData();
if (props.currentDatabase?.id) {
//
if (props.currentDatabase) {
form.value = { ...props.currentDatabase };
if (props.currentId) {
const currentItem = store.currentEnvDetailInfo.config.dataSources.find(
(item) => item.id === props.currentId
) as DataSourceItem;
if (currentItem) {
form.value = {
...currentItem,
};
}
updateName.value = form.value.dataSource;
} else {
//
formReset();
}
});
</script>
<style lang="less" scoped></style>

View File

@ -424,8 +424,7 @@
if (envList.value[_newIndex + 1].id) {
params.moveMode = 'BEFORE';
params.moveId = envList.value[_newIndex + 1].id;
}
if (envList.value[_newIndex - 1].id) {
} else if (envList.value[_newIndex - 1].id && !envList.value[_newIndex + 1].id) {
params.moveMode = 'AFTER';
params.moveId = envList.value[_newIndex - 1].id;
}
@ -434,8 +433,7 @@
if (evnGroupList.value[_newIndex + 1].id) {
params.moveMode = 'AFTER';
params.moveId = evnGroupList.value[_newIndex + 1].id;
}
if (evnGroupList.value[_newIndex - 1].id) {
} else if (evnGroupList.value[_newIndex - 1].id && !evnGroupList.value[_newIndex + 1].id) {
params.moveMode = 'BEFORE';
params.moveId = evnGroupList.value[_newIndex - 1].id;
}
@ -494,8 +492,8 @@
function changeShowType(value: string | number | boolean) {
if (value === 'PROJECT_GROUP') {
initGroupList();
store.setCurrentGroupId('');
initGroupList(keyword.value, true);
// store.setCurrentGroupId('');
}
}

View File

@ -45,6 +45,7 @@ export default {
'project.environmental.http.enableScope': 'Enable Scope',
'project.environmental.http.value': 'Value',
'project.environmental.http.add': 'Add HTTP',
'project.environmental.http.edit': 'Update HTTP',
'project.environmental.http.hostName': 'Host Name',
'project.environmental.http.hostNameRequired': 'Host name is required',
'project.environmental.http.httpHostNamePlaceholder': 'For example: http://127.0.0.1',

View File

@ -52,6 +52,7 @@ export default {
'project.environmental.http.enableScope': '启用范围',
'project.environmental.http.value': '值',
'project.environmental.http.add': '添加HTTP',
'project.environmental.http.edit': '更新HTTP',
'project.environmental.http.hostName': '域名',
'project.environmental.http.hostNameRequired': '域名必填',
'project.environmental.http.httpHostNamePlaceholder': '例如http://127.0.0.1',