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 = '';
|
||||
const { nameSpaces, deployName } = form.value.testResourceDTO;
|
||||
// 镜像内的版本号需要去掉尾部的 -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,
|
||||
appStore.version.lastIndexOf('-')
|
||||
)}`;
|
||||
|
|
|
@ -10,23 +10,16 @@
|
|||
<a-alert v-if="batchModalMode === 'project'" class="mb-[16px]">
|
||||
{{ t('system.user.batchModalTip') }}
|
||||
</a-alert>
|
||||
<a-transfer
|
||||
<MsTransfer
|
||||
v-model="target"
|
||||
:title="[t('system.user.batchOptional'), t('system.user.batchChosen')]"
|
||||
:data="transferData"
|
||||
show-search
|
||||
>
|
||||
<template #source="{ data, selectedKeys, onSelect }">
|
||||
<a-tree
|
||||
:checkable="true"
|
||||
checked-strategy="child"
|
||||
:checked-keys="selectedKeys"
|
||||
:data="getTreeData(data)"
|
||||
block-node
|
||||
@check="onSelect"
|
||||
:data="treeData"
|
||||
:tree-filed="{
|
||||
key: 'id',
|
||||
title: 'name',
|
||||
children: 'children',
|
||||
disabled: 'disabled',
|
||||
}"
|
||||
/>
|
||||
</template>
|
||||
</a-transfer>
|
||||
</a-spin>
|
||||
<template #footer>
|
||||
<a-button type="secondary" :disabled="batchLoading" @click="cancelBatch">{{
|
||||
|
@ -51,23 +44,12 @@
|
|||
getSystemProjects,
|
||||
getSystemRoles,
|
||||
} from '@/api/modules/setting/user';
|
||||
import MsTransfer from '@/components/pure/ms-transfer/index.vue';
|
||||
|
||||
import type { OrgsItem } from '@/models/setting/user';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
interface TreeDataItem {
|
||||
key: string;
|
||||
title: string;
|
||||
children?: TreeDataItem[];
|
||||
}
|
||||
|
||||
interface TransferDataItem {
|
||||
value: string;
|
||||
label: string;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
tableSelected: (string | number)[];
|
||||
|
@ -87,46 +69,6 @@
|
|||
const batchModalMode = ref<'project' | 'userGroup' | 'organization'>('project');
|
||||
const treeData = ref<OrgsItem[]>([]);
|
||||
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) {
|
||||
showBatchModal.value = true;
|
||||
|
@ -153,7 +95,6 @@
|
|||
break;
|
||||
}
|
||||
treeData.value = resTree;
|
||||
transferData.value = getTransferData(treeData.value, []);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
|
|
Loading…
Reference in New Issue