feat(系统设置): 穿梭框组件&资源池配置调整
This commit is contained in:
parent
e2b713d0f1
commit
14d401342b
|
@ -0,0 +1,137 @@
|
||||||
|
<template>
|
||||||
|
<a-transfer
|
||||||
|
v-model="innerTarget"
|
||||||
|
:title="props.title || [t('system.user.batchOptional'), t('system.user.batchChosen')]"
|
||||||
|
:data="transferData"
|
||||||
|
show-search
|
||||||
|
>
|
||||||
|
<template #source="{ data: tData, selectedKeys, onSelect }">
|
||||||
|
<a-tree
|
||||||
|
:checkable="true"
|
||||||
|
checked-strategy="child"
|
||||||
|
:checked-keys="selectedKeys"
|
||||||
|
:data="getTreeData(tData)"
|
||||||
|
block-node
|
||||||
|
@check="onSelect"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</a-transfer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, watch } from 'vue';
|
||||||
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
|
||||||
|
export interface TreeDataItem {
|
||||||
|
key?: string;
|
||||||
|
title?: string;
|
||||||
|
children?: TreeDataItem[];
|
||||||
|
disabled?: boolean;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TransferDataItem {
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
disabled: boolean;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
modelValue: string[];
|
||||||
|
title?: string[]; // [left, right]左右标题
|
||||||
|
data: TreeDataItem[]; // 树结构数据
|
||||||
|
treeFiled?: Record<keyof TreeDataItem, string>; // 自定义树结构字段
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
treeFiled: () => ({
|
||||||
|
key: 'key',
|
||||||
|
title: 'title',
|
||||||
|
children: 'children',
|
||||||
|
disabled: 'disabled',
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const emit = defineEmits(['update:modelValue']);
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const innerTarget = ref<string[]>([]);
|
||||||
|
const transferData = ref<TransferDataItem[]>([]);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
(val) => {
|
||||||
|
innerTarget.value = val;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => innerTarget.value,
|
||||||
|
(val) => {
|
||||||
|
emit('update:modelValue', val);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取穿梭框数据,根据树结构获取
|
||||||
|
* @param _treeData 树结构
|
||||||
|
* @param transferDataSource 穿梭框数组
|
||||||
|
*/
|
||||||
|
const getTransferData = (_treeData: TreeDataItem[], transferDataSource: TransferDataItem[]) => {
|
||||||
|
_treeData.forEach((item) => {
|
||||||
|
const itemChildren = item[props.treeFiled.children];
|
||||||
|
if (Array.isArray(itemChildren) && itemChildren.length > 0) getTransferData(itemChildren, transferDataSource);
|
||||||
|
else
|
||||||
|
transferDataSource.push({
|
||||||
|
label: item[props.treeFiled.title],
|
||||||
|
value: item[props.treeFiled.key],
|
||||||
|
disabled: item[props.treeFiled.disabled],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return transferDataSource;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取树结构数据,根据穿梭框过滤的数据获取
|
||||||
|
*/
|
||||||
|
const getTreeData = (data: TransferDataItem[]) => {
|
||||||
|
const values = data.map((item) => item[props.treeFiled.key]);
|
||||||
|
|
||||||
|
const travel = (_treeData: TreeDataItem[]) => {
|
||||||
|
const treeDataSource: TreeDataItem[] = [];
|
||||||
|
_treeData.forEach((item) => {
|
||||||
|
const itemChildren = item[props.treeFiled.children];
|
||||||
|
const itemKey = item[props.treeFiled.key];
|
||||||
|
const itemTitle = item[props.treeFiled.title];
|
||||||
|
// 需要判断当前父节点下的子节点是否全部选中,若选中则不会 push 进穿梭框数组内,否则会出现空的节点无法选中
|
||||||
|
const allSelected =
|
||||||
|
innerTarget.value.length > 0 &&
|
||||||
|
Array.isArray(itemChildren) &&
|
||||||
|
itemChildren.length > 0 &&
|
||||||
|
itemChildren?.every((child: TreeDataItem) => innerTarget.value.includes(child[props.treeFiled.key]));
|
||||||
|
if (!allSelected && !innerTarget.value.includes(itemKey) && (itemChildren || values.includes(itemKey))) {
|
||||||
|
// 非选中父节点时,需要判断每个子节点是否已经在右侧的选中的数组内,不在才渲染到左侧
|
||||||
|
treeDataSource.push({
|
||||||
|
title: itemTitle,
|
||||||
|
key: itemKey,
|
||||||
|
children: itemChildren ? travel(itemChildren) : [],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return treeDataSource;
|
||||||
|
};
|
||||||
|
|
||||||
|
return travel(props.data);
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.data,
|
||||||
|
(arr) => {
|
||||||
|
transferData.value = getTransferData(arr, []);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped></style>
|
|
@ -632,7 +632,7 @@
|
||||||
let yamlStr = '';
|
let yamlStr = '';
|
||||||
const { nameSpaces, deployName } = form.value.testResourceDTO;
|
const { nameSpaces, deployName } = form.value.testResourceDTO;
|
||||||
// 镜像内的版本号需要去掉尾部的 -xxx
|
// 镜像内的版本号需要去掉尾部的 -xxx
|
||||||
const apiImage = `registry.cn-qingdao.aliyuncs.com/metersphere/node-controller:${appStore.version.substring(
|
const apiImage = `registry.cn-qingdao.aliyuncs.com/metersphere/task-runner:${appStore.version.substring(
|
||||||
0,
|
0,
|
||||||
appStore.version.lastIndexOf('-')
|
appStore.version.lastIndexOf('-')
|
||||||
)}`;
|
)}`;
|
||||||
|
|
|
@ -10,23 +10,16 @@
|
||||||
<a-alert v-if="batchModalMode === 'project'" class="mb-[16px]">
|
<a-alert v-if="batchModalMode === 'project'" class="mb-[16px]">
|
||||||
{{ t('system.user.batchModalTip') }}
|
{{ t('system.user.batchModalTip') }}
|
||||||
</a-alert>
|
</a-alert>
|
||||||
<a-transfer
|
<MsTransfer
|
||||||
v-model="target"
|
v-model="target"
|
||||||
:title="[t('system.user.batchOptional'), t('system.user.batchChosen')]"
|
:data="treeData"
|
||||||
:data="transferData"
|
:tree-filed="{
|
||||||
show-search
|
key: 'id',
|
||||||
>
|
title: 'name',
|
||||||
<template #source="{ data, selectedKeys, onSelect }">
|
children: 'children',
|
||||||
<a-tree
|
disabled: 'disabled',
|
||||||
:checkable="true"
|
}"
|
||||||
checked-strategy="child"
|
/>
|
||||||
:checked-keys="selectedKeys"
|
|
||||||
:data="getTreeData(data)"
|
|
||||||
block-node
|
|
||||||
@check="onSelect"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</a-transfer>
|
|
||||||
</a-spin>
|
</a-spin>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<a-button type="secondary" :disabled="batchLoading" @click="cancelBatch">{{
|
<a-button type="secondary" :disabled="batchLoading" @click="cancelBatch">{{
|
||||||
|
@ -51,23 +44,12 @@
|
||||||
getSystemProjects,
|
getSystemProjects,
|
||||||
getSystemRoles,
|
getSystemRoles,
|
||||||
} from '@/api/modules/setting/user';
|
} from '@/api/modules/setting/user';
|
||||||
|
import MsTransfer from '@/components/pure/ms-transfer/index.vue';
|
||||||
|
|
||||||
import type { OrgsItem } from '@/models/setting/user';
|
import type { OrgsItem } from '@/models/setting/user';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
interface TreeDataItem {
|
|
||||||
key: string;
|
|
||||||
title: string;
|
|
||||||
children?: TreeDataItem[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface TransferDataItem {
|
|
||||||
value: string;
|
|
||||||
label: string;
|
|
||||||
disabled: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
tableSelected: (string | number)[];
|
tableSelected: (string | number)[];
|
||||||
|
@ -87,46 +69,6 @@
|
||||||
const batchModalMode = ref<'project' | 'userGroup' | 'organization'>('project');
|
const batchModalMode = ref<'project' | 'userGroup' | 'organization'>('project');
|
||||||
const treeData = ref<OrgsItem[]>([]);
|
const treeData = ref<OrgsItem[]>([]);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const transferData = ref<TransferDataItem[]>([]);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取穿梭框数据,根据树结构获取
|
|
||||||
* @param _treeData 树结构
|
|
||||||
* @param transferDataSource 穿梭框数组
|
|
||||||
*/
|
|
||||||
const getTransferData = (_treeData: OrgsItem[], transferDataSource: TransferDataItem[]) => {
|
|
||||||
_treeData.forEach((item) => {
|
|
||||||
if (!item.leafNode && item.children) getTransferData(item.children, transferDataSource);
|
|
||||||
else transferDataSource.push({ label: item.name, value: item.id, disabled: false });
|
|
||||||
});
|
|
||||||
return transferDataSource;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取树结构数据,根据穿梭框过滤的数据获取
|
|
||||||
*/
|
|
||||||
const getTreeData = (data: TransferDataItem[]) => {
|
|
||||||
const values = data.map((item) => item.value);
|
|
||||||
|
|
||||||
const travel = (_treeData: OrgsItem[]) => {
|
|
||||||
const treeDataSource: TreeDataItem[] = [];
|
|
||||||
_treeData.forEach((item) => {
|
|
||||||
// 需要判断当前父节点下的子节点是否全部选中,若选中则不会 push 进穿梭框数组内,否则会出现空的节点无法选中
|
|
||||||
const allSelected =
|
|
||||||
target.value.length > 0 && !item.leafNode && item.children?.every((child) => target.value.includes(child.id));
|
|
||||||
if (!allSelected && !target.value.includes(item.id) && (item.children || values.includes(item.id))) {
|
|
||||||
treeDataSource.push({
|
|
||||||
title: item.name,
|
|
||||||
key: item.id,
|
|
||||||
children: item.children ? travel(item.children) : [],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return treeDataSource;
|
|
||||||
};
|
|
||||||
|
|
||||||
return travel(treeData.value);
|
|
||||||
};
|
|
||||||
|
|
||||||
async function handleTableBatch(action: string) {
|
async function handleTableBatch(action: string) {
|
||||||
showBatchModal.value = true;
|
showBatchModal.value = true;
|
||||||
|
@ -153,7 +95,6 @@
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
treeData.value = resTree;
|
treeData.value = resTree;
|
||||||
transferData.value = getTransferData(treeData.value, []);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
} finally {
|
} finally {
|
||||||
|
|
Loading…
Reference in New Issue