feat(项目管理): 环境管理显示设置

This commit is contained in:
RubyLiu 2024-02-06 18:06:22 +08:00 committed by Craftsman
parent cd1e8d3ca4
commit 58d2fa1b9e
8 changed files with 271 additions and 22 deletions

View File

@ -59,3 +59,15 @@ export interface GlobalParams {
projectId: string; projectId: string;
globalParams: GlobalParamsItem; globalParams: GlobalParamsItem;
} }
export interface ContentTabItem {
value: string;
label: string;
canHide: boolean;
isShow: boolean;
}
export interface ContentTabsMap {
tabList: ContentTabItem[];
backupTabList: ContentTabItem[];
}

View File

@ -1,9 +1,11 @@
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import localforage from 'localforage';
import { getDetailEnv, getGlobalParamDetail } from '@/api/modules/project-management/envManagement'; import { getDetailEnv, getGlobalParamDetail } from '@/api/modules/project-management/envManagement';
import { useAppStore } from '@/store'; import { useAppStore } from '@/store';
import { isArraysEqualWithOrder } from '@/utils/equal';
import { EnvDetailItem, GlobalParams } from '@/models/projectManagement/environmental'; import { ContentTabItem, ContentTabsMap, EnvDetailItem, GlobalParams } from '@/models/projectManagement/environmental';
export const ALL_PARAM = 'allParam'; export const ALL_PARAM = 'allParam';
export const NEW_ENV_PARAM = 'newEnvParam'; export const NEW_ENV_PARAM = 'newEnvParam';
@ -11,13 +13,18 @@ export const NEW_ENV_PARAM = 'newEnvParam';
const useProjectEnvStore = defineStore( const useProjectEnvStore = defineStore(
'projectEnv', 'projectEnv',
() => { () => {
const currentId = ref<string>(ALL_PARAM); // 当前选中的key值 // 项目中的key值
const currentId = ref<string>(ALL_PARAM);
// 项目组选中的key值
const currentGroupId = ref<string>('');
const currentEnvDetailInfo = ref<EnvDetailItem>({ projectId: '', name: '', config: {} }); // 当前选中的环境详情 const currentEnvDetailInfo = ref<EnvDetailItem>({ projectId: '', name: '', config: {} }); // 当前选中的环境详情
const backupEnvDetailInfo = ref<EnvDetailItem>({ projectId: '', name: '', config: {} }); // 当前选中的环境详情-备份 const backupEnvDetailInfo = ref<EnvDetailItem>({ projectId: '', name: '', config: {} }); // 当前选中的环境详情-备份
const allParamDetailInfo = ref<GlobalParams>(); // 全局参数详情 const allParamDetailInfo = ref<GlobalParams>(); // 全局参数详情
// 当前选中的项目组详情
const groupDetailInfo = ref<GlobalParams>();
const httpNoWarning = ref(true); const httpNoWarning = ref(true);
const getHttpNoWarning = computed(() => httpNoWarning.value); const getHttpNoWarning = computed(() => httpNoWarning.value);
const groupLength = ref(0); // 环境分组数据
// 设置选中项 // 设置选中项
function setCurrentId(id: string) { function setCurrentId(id: string) {
currentId.value = id; currentId.value = id;
@ -51,18 +58,68 @@ const useProjectEnvStore = defineStore(
} }
} }
// 初始化内容tab列表
async function initContentTabList(arr: ContentTabItem[]) {
try {
const tabsMap = await localforage.getItem<ContentTabsMap>('bugTabsMap');
if (tabsMap) {
// 初始化过了
const { backupTabList } = tabsMap;
const isEqual = isArraysEqualWithOrder<ContentTabItem>(backupTabList, arr);
if (!isEqual) {
tabsMap.tabList = arr;
tabsMap.backupTabList = arr;
await localforage.setItem('bugTabsMap', tabsMap);
}
} else {
// 没初始化过
await localforage.setItem('bugTabsMap', { tabList: arr, backupTabList: arr });
}
} catch (e) {
// eslint-disable-next-line no-console
console.log(e);
}
}
// 获取Tab列表
async function getContentTabList() {
try {
const tabsMap = await localforage.getItem<ContentTabsMap>('bugTabsMap');
if (tabsMap) {
return tabsMap.tabList;
}
return [];
} catch (e) {
// eslint-disable-next-line no-console
console.log(e);
}
}
// 设置Tab列表
async function setContentTabList(arr: ContentTabItem[]) {
const tabsMap = await localforage.getItem<ContentTabsMap>('bugTabsMap');
if (tabsMap) {
const tmpArrList = JSON.parse(JSON.stringify(arr));
tabsMap.tabList = tmpArrList;
await localforage.setItem('bugTabsMap', tabsMap);
}
}
return { return {
currentId, currentId,
currentGroupId,
getHttpNoWarning, getHttpNoWarning,
httpNoWarning, httpNoWarning,
allParamDetailInfo, allParamDetailInfo,
currentEnvDetailInfo, currentEnvDetailInfo,
backupEnvDetailInfo, backupEnvDetailInfo,
groupLength, groupDetailInfo,
setCurrentId, setCurrentId,
setHttpNoWarning, setHttpNoWarning,
setAllParamDetailInfo, setAllParamDetailInfo,
initEnvDetail, initEnvDetail,
initContentTabList,
getContentTabList,
setContentTabList,
}; };
}, },
{ {

View File

@ -549,7 +549,6 @@
}); });
return; return;
} }
debugger;
router.push({ router.push({
name: BugManagementRouteEnum.BUG_MANAGEMENT_CREATE_SUCCESS, name: BugManagementRouteEnum.BUG_MANAGEMENT_CREATE_SUCCESS,
query: { query: {

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="page"> <div class="page">
<template v-if="store.groupLength"> <template v-if="store.currentGroupId">
<div class="header"> <div class="header">
<a-form ref="envGroupForm" layout="vertical" :model="form"> <a-form ref="envGroupForm" layout="vertical" :model="form">
<a-form-item <a-form-item

View File

@ -18,8 +18,10 @@
/> />
</a-form-item> </a-form-item>
</a-form> </a-form>
<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 v-if="contentTabList.length" v-model:active-key="activeKey" class="no-content" @change="handleTabChange">
<a-tab-pane v-for="item of contentTabList" :key="item.value" :title="t(item.label)" />
<a-tab-pane key="displaySetting" :title="t('project.environmental.displaySetting')" />
</a-tabs> </a-tabs>
</div> </div>
<a-divider :margin="0" class="!mb-[16px]" /> <a-divider :margin="0" class="!mb-[16px]" />
@ -33,6 +35,7 @@
<PostTab v-else-if="activeKey === 'post'" /> <PostTab v-else-if="activeKey === 'post'" />
<AssertTab v-else-if="activeKey === 'assert'" /> <AssertTab v-else-if="activeKey === 'assert'" />
</div> </div>
<TabSettingDrawer v-model:visible="tabSettingVisible" @init-data="initTab" />
<div class="footer" :style="{ width: '100%' }"> <div class="footer" :style="{ width: '100%' }">
<a-button @click="handleReset">{{ t('common.cancel') }}</a-button> <a-button @click="handleReset">{{ t('common.cancel') }}</a-button>
@ -45,6 +48,7 @@
import { Message } from '@arco-design/web-vue'; import { Message } from '@arco-design/web-vue';
import { isEqual } from 'lodash-es'; import { isEqual } from 'lodash-es';
import TabSettingDrawer from './common/TabSettingDrawer.vue';
import AssertTab from './envParams/AssertTab.vue'; import AssertTab from './envParams/AssertTab.vue';
import DataBaseTab from './envParams/DatabaseTab.vue'; import DataBaseTab from './envParams/DatabaseTab.vue';
import EnvParamsTab from './envParams/EnvParamsTab.vue'; import EnvParamsTab from './envParams/EnvParamsTab.vue';
@ -58,11 +62,14 @@
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import useProjectEnvStore from '@/store/modules/setting/useProjectEnvStore'; import useProjectEnvStore from '@/store/modules/setting/useProjectEnvStore';
const activeKey = ref('assert'); import { ContentTabItem } from '@/models/projectManagement/environmental';
const activeKey = ref('envParams');
const envForm = ref(); const envForm = ref();
const canSave = ref(false); const canSave = ref(false);
const { t } = useI18n(); const { t } = useI18n();
const loading = ref(false); const loading = ref(false);
const tabSettingVisible = ref(false);
const store = useProjectEnvStore(); const store = useProjectEnvStore();
@ -70,44 +77,61 @@
name: '', name: '',
}); });
const contentTabList = [ const contentTabList = ref<ContentTabItem[]>([]);
const sourceTabList = [
{ {
value: 'envParams', value: 'envParams',
label: t('project.environmental.envParams'), label: 'project.environmental.envParams',
canHide: false,
isShow: true,
}, },
{ {
value: 'http', value: 'http',
label: 'HTTP', label: 'project.environmental.HTTP',
canHide: true,
isShow: true,
}, },
{ {
value: 'database', value: 'database',
label: t('project.environmental.database'), label: 'project.environmental.database',
canHide: true,
isShow: true,
}, },
{ {
value: 'host', value: 'host',
label: 'Host', label: 'project.environmental.HOST',
canHide: true,
isShow: true,
}, },
{ {
value: 'tcp', value: 'tcp',
label: 'TCP', label: 'project.environmental.TCP',
canHide: true,
isShow: true,
}, },
{ {
value: 'pre', value: 'pre',
label: t('project.environmental.pre'), label: 'project.environmental.pre',
canHide: true,
isShow: true,
}, },
{ {
value: 'post', value: 'post',
label: t('project.environmental.post'), label: 'project.environmental.post',
canHide: true,
isShow: true,
}, },
{ {
value: 'assert', value: 'assert',
label: t('project.environmental.assert'), label: 'project.environmental.assert',
}, canHide: true,
{ isShow: true,
value: 'display',
label: t('project.environmental.displaySetting'),
}, },
]; ];
await store.initContentTabList(sourceTabList);
contentTabList.value = ((await store.getContentTabList()) || []).filter((item) => item.isShow);
const handleReset = () => { const handleReset = () => {
envForm.value?.resetFields(); envForm.value?.resetFields();
store.initEnvDetail(); store.initEnvDetail();
@ -148,6 +172,21 @@
canSave.value = !isEqual(currentEnvDetailInfo, backupEnvDetailInfo); canSave.value = !isEqual(currentEnvDetailInfo, backupEnvDetailInfo);
} }
}); });
const initTab = async () => {
tabSettingVisible.value = false;
const tmpArr = (await store.getContentTabList()) || [];
contentTabList.value = tmpArr.filter((item) => item.isShow);
if (contentTabList.value.length) {
activeKey.value = contentTabList.value[0].value;
}
};
const handleTabChange = (key: string | number) => {
if (key === 'displaySetting') {
tabSettingVisible.value = true;
}
};
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>

View File

@ -0,0 +1,134 @@
<template>
<MsDrawer
:visible="innerVisible"
:width="480"
unmount-on-close
:footer="false"
:title="t('project.environmental.displaySetting')"
@cancel="handleCancel"
>
<div class="ms-table-column-seletor">
<div class="mb-2 flex items-center justify-between">
<div class="text-[var(--color-text-4)]">{{ t('project.environmental.displaySetting') }}</div>
<MsButton v-if="hasChange" @click="handleReset">{{ t('msTable.columnSetting.resetDefault') }}</MsButton>
</div>
<div class="flex-col">
<div v-for="item in nonCloseColumn" :key="item.value" class="column-item">
<div>{{ t(item.label) }}</div>
<a-switch v-model="item.isShow" disabled size="small" type="line" @change="handleSwitchChange" />
</div>
</div>
<a-divider orientation="center" class="non-sort"
><span class="one-line-text text-xs text-[var(--color-text-4)]">{{
t('project.environmental.nonClose')
}}</span></a-divider
>
<div v-for="element in couldCloseColumn" :key="element.value" class="column-drag-item">
<div class="flex w-[90%] items-center">
<span class="ml-[8px]">{{ t(element.label) }}</span>
</div>
<a-switch v-model="element.isShow" size="small" type="line" @change="handleSwitchChange" />
</div>
</div>
</MsDrawer>
</template>
<script lang="ts" setup>
import { onBeforeMount, ref } from 'vue';
import MsButton from '@/components/pure/ms-button/index.vue';
import MsDrawer from '@/components/pure/ms-drawer/index.vue';
import { useI18n } from '@/hooks/useI18n';
import useProjectEnvStore from '@/store/modules/setting/useProjectEnvStore';
import { ContentTabItem } from '@/models/projectManagement/environmental';
const store = useProjectEnvStore();
const { t } = useI18n();
const innerVisible = defineModel<boolean>('visible', { default: false });
const nonCloseColumn = ref<ContentTabItem[]>([]);
//
const couldCloseColumn = ref<ContentTabItem[]>([]);
//
const hasChange = ref(false);
const emit = defineEmits<{
(e: 'initData'): void;
}>();
const handleCancel = async () => {
await store.setContentTabList([...nonCloseColumn.value, ...couldCloseColumn.value]);
emit('initData');
};
const loadColumn = async () => {
const res = (await store.getContentTabList()) || [];
nonCloseColumn.value = res.filter((item) => !item.canHide);
couldCloseColumn.value = res.filter((item) => item.canHide);
};
const handleReset = () => {
loadColumn();
hasChange.value = false;
};
const handleSwitchChange = () => {
hasChange.value = true;
};
onBeforeMount(() => {
loadColumn();
});
</script>
<style lang="less" scoped>
:deep(.arco-divider-horizontal) {
margin: 16px 0;
border-bottom-color: var(--color-text-n8);
}
:deep(.arco-divider-text) {
padding: 0 8px;
}
.mode-button {
display: flex;
flex-flow: row nowrap;
align-items: center;
.active-color {
color: rgba(var(--primary-5));
}
.mode-button-title {
margin-left: 4px;
}
}
.column-item {
display: flex;
flex-flow: row nowrap;
justify-content: space-between;
align-items: center;
padding: 8px 12px 8px 36px;
&:hover {
border-radius: 6px;
background: var(--color-text-n9);
}
}
.column-drag-item {
display: flex;
flex-flow: row nowrap;
justify-content: space-between;
align-items: center;
padding: 8px 12px;
&:hover {
border-radius: 6px;
background-color: var(--color-text-n9);
}
}
.ghost {
border: 1px dashed rgba(var(--primary-5));
background-color: rgba(var(--primary-1));
}
.non-sort {
font-size: 12px;
line-height: 16px;
}
</style>

View File

@ -20,11 +20,15 @@ export default {
'project.environmental.envNamePlaceholder': 'Please enter the environment name', 'project.environmental.envNamePlaceholder': 'Please enter the environment name',
'project.environmental.envNameRequired': 'Environment name cannot be empty', 'project.environmental.envNameRequired': 'Environment name cannot be empty',
'project.environmental.database': 'Database', 'project.environmental.database': 'Database',
'project.environmental.HTTP': 'HTTP',
'project.environmental.HOST': 'HOST',
'project.environmental.TCP': 'TCP',
'project.environmental.pre': 'Pre', 'project.environmental.pre': 'Pre',
'project.environmental.post': 'Post', 'project.environmental.post': 'Post',
'project.environmental.host': 'Host', 'project.environmental.host': 'Host',
'project.environmental.assert': 'Assertion', 'project.environmental.assert': 'Assertion',
'project.environmental.displaySetting': 'Display Setting', 'project.environmental.displaySetting': 'Display Setting',
'project.environmental.nonClose': 'Do not close the above properties',
'project.environmental.httpTitle': 'When multiple enable conditions are met, match in order from top to bottom', 'project.environmental.httpTitle': 'When multiple enable conditions are met, match in order from top to bottom',
'project.environmental.httpNoWarning': 'No warning', 'project.environmental.httpNoWarning': 'No warning',
'project.environmental.addHttp': 'Add HTTP', 'project.environmental.addHttp': 'Add HTTP',

View File

@ -28,12 +28,16 @@ export default {
'project.environmental.newEnv': '未命名环境', 'project.environmental.newEnv': '未命名环境',
'project.environmental.envNamePlaceholder': '请输入环境名称', 'project.environmental.envNamePlaceholder': '请输入环境名称',
'project.environmental.envNameRequired': '环境名称不能为空', 'project.environmental.envNameRequired': '环境名称不能为空',
'project.environmental.HTTP': 'HTTP',
'project.environmental.HOST': 'HOST',
'project.environmental.TCP': 'TCP',
'project.environmental.database': '数据库', 'project.environmental.database': '数据库',
'project.environmental.pre': '前置', 'project.environmental.pre': '前置',
'project.environmental.post': '后置', 'project.environmental.post': '后置',
'project.environmental.host': '域名', 'project.environmental.host': '域名',
'project.environmental.assert': '断言', 'project.environmental.assert': '断言',
'project.environmental.displaySetting': '显示设置', 'project.environmental.displaySetting': '显示设置',
'project.environmental.nonClose': '以上属性不关闭',
'project.environmental.httpTitle': '当满足多个启用条件时,按从上到下的顺序匹配', 'project.environmental.httpTitle': '当满足多个启用条件时,按从上到下的顺序匹配',
'project.environmental.httpNoWarning': '不在提醒', 'project.environmental.httpNoWarning': '不在提醒',
'project.environmental.addHttp': '添加 HTTP', 'project.environmental.addHttp': '添加 HTTP',