2236 lines
66 KiB
Vue
2236 lines
66 KiB
Vue
<script lang="ts" setup>
|
||
import { useTabBarStore } from '@/store';
|
||
import { Modal, Message as $message } from '@arco-design/web-vue';
|
||
import { useI18n } from 'vue-i18n';
|
||
import { nextTick, onMounted, onActivated, ref, watch, computed, unref } from 'vue';
|
||
|
||
import {
|
||
postInterfaceInterfacePaginatedList,
|
||
postInterfaceBatchDeleteInterfaceByIds,
|
||
} from '@/api/InterfaceServices/interface';
|
||
|
||
import CascaderPro from '@/components/CascaderPro/Cascader.vue';
|
||
import { getServiceProviderGetServiceProviderInterfaceCascadedList } from '@/api/interface-serviceProvider/serviceProvider';
|
||
import { getDictionaryGetList } from '@/api/ApplicationServices/dictionary';
|
||
import { IconClose, IconEdit, IconPlus, IconSearch, IconMore, IconMinus, IconRefresh } from '@arco-design/web-vue/es/icon';
|
||
|
||
import dayjs from 'dayjs';
|
||
import { useRouter, useRoute } from 'vue-router';
|
||
import {
|
||
postCreate,
|
||
postSaveParameterMaps,
|
||
} from '@/api/InterfaceMappingServices/interfaceMap';
|
||
import useModal from '@/components/ModalPro/useModal';
|
||
import { $baseMessage, $baseConfirm } from '@/utils';
|
||
import EnteredMap from './components/EnteredMap.vue';
|
||
import MapServiceProvider from './components/map-service-provider.vue';
|
||
import ApiDebugger from './components/ApiDebugger.vue';
|
||
|
||
import { systemInterfaceColumns } from './models/tableColumns';
|
||
import UpstreamInterfaceModal from './components/UpstreamInterfaceModal.vue';
|
||
import CustomLeftMenu from '@/components/CustomLeftMenu/index.vue';
|
||
|
||
const { t } = useI18n();
|
||
defineOptions({
|
||
name: 'UpstreamInterface',
|
||
});
|
||
const active = ref(0);
|
||
const activeContentTab = ref('mapping'); // 映射明细、API调试、接口明细
|
||
const currentActiveTab = ref('mapping'); // 用于保存按钮判断的当前tab
|
||
const apiDebuggerRef = ref<any>();
|
||
const apiDebuggerRefreshKey = ref(0);
|
||
|
||
const drawerList = ref<any[]>([]);
|
||
|
||
// 左侧接口树相关数据
|
||
const searchKeyword = ref('');
|
||
// 中间映射列表搜索关键词
|
||
const mappingSearchKeyword = ref('');
|
||
const interfaceTreeData = ref<any[]>([]);
|
||
const selectedKeys = ref<string[]>([]);
|
||
const expandedKeys = ref<string[]>([]);
|
||
const selectedInterface = ref<any>(null);
|
||
const loading = ref(false);
|
||
async function ServiceProviderGetList() {
|
||
const data =
|
||
await getServiceProviderGetServiceProviderInterfaceCascadedList({
|
||
serviceProviderName: '',
|
||
interfaceId: upStreamIdRef.value || 0,
|
||
});
|
||
drawerList.value = data as any;
|
||
|
||
setTimeout(() => {
|
||
panelRef.value?.SetOptions(data);
|
||
}, 300);
|
||
}
|
||
|
||
const panelRef = ref<any>();
|
||
const drawerVisible = ref(false); // 参数弹窗
|
||
const cascaderValue = ref<number[]>([]);
|
||
const drawerFormRef = ref<any>();
|
||
const upStreamIdRef = ref<number>();
|
||
const enteredMapRef = ref<any>();
|
||
const drawerConfig = reactive({
|
||
itemList: [
|
||
{
|
||
comp: 'a-input',
|
||
field: 'serviceProviderName',
|
||
label: '',
|
||
compProps: {
|
||
placeholder: t('system.interface.search.placeholder.nameOrCode'),
|
||
allowClear: true,
|
||
size: 'small',
|
||
},
|
||
span: 12,
|
||
},
|
||
],
|
||
buttonConfig: {
|
||
showBtn: true,
|
||
span: 12,
|
||
showOk: true,
|
||
okText: t('system.interface.search.button.query'),
|
||
},
|
||
});
|
||
|
||
const tableRef = ref<any>();
|
||
const tableProps = reactive({
|
||
isToolsExport: false,
|
||
isToolsImport: false,
|
||
api: postInterfaceInterfacePaginatedList,
|
||
searchInfo: {
|
||
isUpstream: true,
|
||
},
|
||
tableConfig: {
|
||
columns: systemInterfaceColumns,
|
||
showIndex: true,
|
||
autoHeight: true,
|
||
showCheckbox: true,
|
||
isColumnsDrag: false,
|
||
toolbarConfig: {
|
||
enabled: true,
|
||
perfect: true,
|
||
slots: {
|
||
tools: 'toolbar_right',
|
||
buttons: 'toolbar_buttons',
|
||
},
|
||
},
|
||
},
|
||
formConfig: {
|
||
itemList: [
|
||
{
|
||
comp: 'a-input',
|
||
field: 'nameOrCode',
|
||
label: '',
|
||
compProps: {
|
||
placeholder: t('system.interface.search.placeholder.nameOrCode'),
|
||
allowClear: true,
|
||
size: 'small',
|
||
},
|
||
},
|
||
],
|
||
layout: 'inline',
|
||
labelColProps: { span: 0 },
|
||
wrapperColProps: { span: 24 },
|
||
buttonConfig: {
|
||
showBtn: true,
|
||
showOk: true,
|
||
okText: t('system.interface.search.button.query'),
|
||
showReset: false,
|
||
className: 'flex items-center',
|
||
span: 2,
|
||
},
|
||
},
|
||
});
|
||
const router = useRouter();
|
||
|
||
// 弹窗相关
|
||
const editVisible = ref(false);
|
||
const editId = ref<number | null>(null);
|
||
function interfaceClick() {
|
||
editId.value = null;
|
||
editVisible.value = true;
|
||
}
|
||
|
||
// 重置表单
|
||
const route = useRoute();
|
||
onActivated(() => {
|
||
if (route?.query?.openId) {
|
||
mapName.value = route?.query?.openName;
|
||
mapId.value = route?.query?.openId;
|
||
mapClick({
|
||
id: route.query.openId,
|
||
name: route.query.openName,
|
||
code: route.query.openCode,
|
||
});
|
||
}
|
||
});
|
||
|
||
// 组件挂载时加载数据
|
||
onMounted(() => {
|
||
loadInterfaceTree();
|
||
});
|
||
|
||
function compileClick(row: { id: any }) {
|
||
editId.value = row.id;
|
||
editVisible.value = true;
|
||
}
|
||
|
||
|
||
|
||
const [register, registerMethods] = useModal() as any;
|
||
const [registerEnter, registerEnterMethods] = useModal() as any;
|
||
const mapId = ref();
|
||
// 映射服务商
|
||
const mapName = ref();
|
||
async function mapClick(row: any) {
|
||
newmapClickROW.value = row;
|
||
mapName.value = row.name;
|
||
mapId.value = row.id;
|
||
|
||
await registerMethods.openModal();
|
||
await (unref(MapServiceProviderRef) as any)?.tableRef?.reload();
|
||
}
|
||
|
||
// 删除
|
||
async function deleteClick() {
|
||
const list = (unref(tableRef) as any)?.checkAllRows?.();
|
||
|
||
const isHasmapCount = list.some((i: any) => i.mapCount > 0) as boolean;
|
||
if (isHasmapCount) {
|
||
$baseMessage(t('system.interface.message.cannotDeleteWithMapping'), 'error');
|
||
return
|
||
}
|
||
const ids = list?.map((node: any) => node.id);
|
||
|
||
if (ids) {
|
||
$baseConfirm({
|
||
title: t('system.interface.modal.confirmTitle'),
|
||
content: t('system.interface.modal.deleteConfirm'),
|
||
okText: t('system.interface.modal.ok'),
|
||
cancelText: t('system.interface.modal.cancel'),
|
||
async onOk() {
|
||
await postInterfaceBatchDeleteInterfaceByIds({}, ids);
|
||
tableRef.value?.removeCheckboxRow();
|
||
$baseMessage(t('system.interface.message.deleteSuccess'), 'success');
|
||
},
|
||
});
|
||
}
|
||
}
|
||
|
||
// 删除单个接口
|
||
async function deleteInterfaceClick(nodeData: any) {
|
||
if (!nodeData || !nodeData.isLeaf) {
|
||
$baseMessage(t('system.interface.message.canOnlyDeleteInterface'), 'warning');
|
||
return;
|
||
}
|
||
|
||
if (nodeData.mapCount > 0) {
|
||
$baseMessage(t('system.interface.message.cannotDeleteWithMapping'), 'error');
|
||
return;
|
||
}
|
||
|
||
$baseConfirm({
|
||
title: t('system.interface.modal.warningTitle'),
|
||
content: t('system.interface.modal.deleteInterfaceConfirm', { name: nodeData.title }),
|
||
onOk: async () => {
|
||
try {
|
||
await postInterfaceBatchDeleteInterfaceByIds({}, [nodeData.id]);
|
||
$baseMessage(t('system.interface.message.deleteSuccess'), 'success');
|
||
loadInterfaceTree();
|
||
// 如果删除的是当前选中的接口,清空选中状态
|
||
if (selectedInterface.value?.id === nodeData.id) {
|
||
selectedInterface.value = null;
|
||
selectedKeys.value = [];
|
||
}
|
||
} catch (error) {
|
||
$baseMessage(t('system.interface.message.deleteFailed'), 'error');
|
||
}
|
||
},
|
||
});
|
||
}
|
||
|
||
// 新增映射
|
||
const [registerAddMap, registerAddMapMethods] = useModal() as any;
|
||
const newmapClickROW = ref();
|
||
|
||
// 级联选择器相关数据
|
||
const cascaderOptions = ref<any[]>([]);
|
||
const mappingList = ref<any[]>([{ value: [] }]);
|
||
|
||
async function newmapClick(row: any) {
|
||
drawerVisible.value = true;
|
||
mapId.value = row.id;
|
||
|
||
registerAddMapMethods.setModalProps({
|
||
title: t('system.interface.modal.addMapping'),
|
||
width: 1000,
|
||
});
|
||
registerAddMapMethods.openModal();
|
||
newmapClickROW.value = row;
|
||
upStreamIdRef.value = Number(row.id);
|
||
mapName.value = row.name;
|
||
|
||
// 重置映射列表,初始化为1个空映射
|
||
mappingList.value = [{ value: [] }];
|
||
|
||
// 初始化时加载所有服务商数据
|
||
await loadInitialServiceProviderData();
|
||
}
|
||
|
||
// 请求去重:记录正在请求的服务商接口ID
|
||
const loadingServiceProviderInterfaceId = ref<number | null>(null);
|
||
|
||
// 初始化加载服务商数据
|
||
async function loadInitialServiceProviderData() {
|
||
const interfaceId = upStreamIdRef.value || 0;
|
||
|
||
// 如果正在加载相同的接口,直接返回
|
||
if (loadingServiceProviderInterfaceId.value === interfaceId) {
|
||
return;
|
||
}
|
||
|
||
// 如果没有接口ID,直接返回
|
||
if (!interfaceId) {
|
||
return;
|
||
}
|
||
|
||
loadingServiceProviderInterfaceId.value = interfaceId;
|
||
|
||
try {
|
||
const response = await getServiceProviderGetServiceProviderInterfaceCascadedList({
|
||
serviceProviderName: '',
|
||
interfaceId: interfaceId,
|
||
});
|
||
|
||
// 处理数据,去掉服务商类型的一级菜单,直接显示服务商和服务商接口
|
||
// API返回的数据结构可能是 response 本身就是数组,或者 response.data 是数组
|
||
const rawData = Array.isArray(response) ? response : (response.data || []);
|
||
|
||
const processedData = processServiceProviderData(rawData);
|
||
|
||
drawerList.value = processedData;
|
||
|
||
// 构建级联选择器数据 - 直接使用处理后数据的label字段
|
||
buildCascaderOptions(processedData);
|
||
|
||
await nextTick();
|
||
panelRef.value?.SetOptions(processedData);
|
||
|
||
const arr = processedData?.filter((node: { children: any; }) => {
|
||
return node.children && node.children.length > 0;
|
||
});
|
||
cascaderValue.value = filterValue(arr);
|
||
} catch (error) {
|
||
cascaderOptions.value = [];
|
||
} finally {
|
||
loadingServiceProviderInterfaceId.value = null;
|
||
}
|
||
}
|
||
|
||
// 构建级联选择器数据
|
||
function buildCascaderOptions(data: any[]) {
|
||
if (!data || data.length === 0) {
|
||
cascaderOptions.value = [];
|
||
return;
|
||
}
|
||
|
||
// 直接使用处理后的数据,因为它已经包含了label和value字段
|
||
cascaderOptions.value = data.map((provider: any) => {
|
||
const providerData = {
|
||
value: provider.value,
|
||
label: provider.label,
|
||
isDisable: provider.isDisable || false,
|
||
children: provider.children?.map((interfaceItem: any) => ({
|
||
value: interfaceItem.value,
|
||
label: interfaceItem.label,
|
||
isDisable: interfaceItem.isDisable || false,
|
||
providerId: provider.value,
|
||
providerName: provider.label,
|
||
interfaceId: interfaceItem.value,
|
||
interfaceName: interfaceItem.label
|
||
})) || []
|
||
};
|
||
return providerData;
|
||
});
|
||
}
|
||
|
||
// 级联选择器过滤函数
|
||
function filterCascaderOption(inputValue: string, option: any) {
|
||
return option.label.toLowerCase().includes(inputValue.toLowerCase());
|
||
}
|
||
|
||
|
||
// 获取第一个映射的服务商ID和接口ID
|
||
const firstMapping = computed(() => {
|
||
if (mappingList.value.length > 0 && mappingList.value[0].value) {
|
||
// 如果 value 是数组且长度为2(完全选择)
|
||
if (Array.isArray(mappingList.value[0].value) && mappingList.value[0].value.length === 2) {
|
||
return {
|
||
providerId: mappingList.value[0].value[0],
|
||
interfaceId: mappingList.value[0].value[1]
|
||
};
|
||
}
|
||
// 如果 value 是数组但长度为1(可能只选择了服务商)
|
||
if (Array.isArray(mappingList.value[0].value) && mappingList.value[0].value.length === 1) {
|
||
return {
|
||
providerId: mappingList.value[0].value[0],
|
||
interfaceId: null
|
||
};
|
||
}
|
||
// 如果 value 是数字,需要通过接口ID找到对应的服务商ID
|
||
if (typeof mappingList.value[0].value === 'number') {
|
||
// 遍历级联选项找到包含该接口ID的服务商
|
||
for (const provider of cascaderOptions.value) {
|
||
if (provider.children) {
|
||
for (const interfaceItem of provider.children) {
|
||
if (interfaceItem.value === mappingList.value[0].value) {
|
||
return {
|
||
providerId: provider.value,
|
||
interfaceId: interfaceItem.value
|
||
};
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
}
|
||
return null;
|
||
});
|
||
|
||
// 获取第一个映射的服务商ID(向后兼容)
|
||
const firstProviderId = computed(() => {
|
||
return firstMapping.value?.providerId || null;
|
||
});
|
||
|
||
// 获取已选择的接口ID列表(包括第一个映射)
|
||
const selectedInterfaceIds = computed(() => {
|
||
const selectedIds: number[] = [];
|
||
|
||
// 遍历所有映射
|
||
for (let i = 0; i < mappingList.value.length; i++) {
|
||
const mappingValue = mappingList.value[i]?.value;
|
||
|
||
// 如果 value 是数组且长度为2
|
||
if (Array.isArray(mappingValue) && mappingValue.length === 2) {
|
||
selectedIds.push(mappingValue[1]);
|
||
}
|
||
// 如果 value 是数字,可能是接口ID
|
||
else if (typeof mappingValue === 'number') {
|
||
// 通过 firstMapping 确认这是接口ID
|
||
if (firstMapping.value && firstMapping.value.interfaceId === mappingValue) {
|
||
selectedIds.push(mappingValue);
|
||
}
|
||
}
|
||
}
|
||
|
||
return selectedIds;
|
||
});
|
||
|
||
// 获取每个映射项使用的级联选项
|
||
function getCascaderOptionsForIndex(index: number) {
|
||
// 如果是第一个映射,返回所有选项,但需要处理 isDisable
|
||
if (index === 0) {
|
||
return cascaderOptions.value.map((provider: any) => {
|
||
// 如果服务商的 isDisable 是 true,整个服务商及其接口都置灰
|
||
if (provider.isDisable) {
|
||
return {
|
||
...provider,
|
||
disabled: true,
|
||
children: provider.children?.map((interfaceItem: any) => ({
|
||
...interfaceItem,
|
||
disabled: true
|
||
})) || []
|
||
};
|
||
}
|
||
return provider;
|
||
});
|
||
}
|
||
|
||
// 第二个及之后的映射:只能选择第一个映射选择的服务商
|
||
if (!firstMapping.value || !firstMapping.value.providerId) {
|
||
return cascaderOptions.value;
|
||
}
|
||
|
||
return cascaderOptions.value.map((provider: any) => {
|
||
// 如果服务商的 isDisable 是 true,整个服务商及其接口都置灰
|
||
if (provider.isDisable) {
|
||
return {
|
||
...provider,
|
||
disabled: true,
|
||
children: provider.children?.map((interfaceItem: any) => ({
|
||
...interfaceItem,
|
||
disabled: true
|
||
})) || []
|
||
};
|
||
}
|
||
|
||
// 如果不是第一个映射选择的服务商,整个服务商都置灰
|
||
if (provider.value != firstMapping.value?.providerId) {
|
||
return {
|
||
...provider,
|
||
disabled: true,
|
||
children: provider.children?.map((interfaceItem: any) => ({
|
||
...interfaceItem,
|
||
disabled: true
|
||
})) || []
|
||
};
|
||
}
|
||
|
||
// 是第一个映射选择的服务商,需要禁用已选择的接口
|
||
return {
|
||
...provider,
|
||
children: provider.children?.map((interfaceItem: any) => ({
|
||
...interfaceItem,
|
||
disabled: selectedInterfaceIds.value.includes(interfaceItem.value)
|
||
})) || []
|
||
};
|
||
});
|
||
}
|
||
|
||
// 添加映射
|
||
function addMapping() {
|
||
mappingList.value.push({ value: [] });
|
||
}
|
||
|
||
// Tab 切换事件处理
|
||
function handleTabChange(tabName: string) {
|
||
// 更新当前激活的tab(用于保存按钮判断)
|
||
currentActiveTab.value = tabName;
|
||
|
||
// 如果切换到API调试tab,检查是否选择了映射服务商
|
||
if (tabName === 'debugger') {
|
||
if (!MapServiceProviderROW.value) {
|
||
$baseMessage(t('system.interface.message.selectProviderForDebug'), 'warning');
|
||
// 切换回映射明细tab
|
||
activeContentTab.value = 'mapping';
|
||
currentActiveTab.value = 'mapping';
|
||
return;
|
||
}
|
||
}
|
||
// 如果切换到接口明细tab,检查是否选择了接口
|
||
if (tabName === 'detail') {
|
||
if (!selectedInterface.value) {
|
||
$baseMessage(t('system.interface.message.selectLeftInterface'), 'warning');
|
||
activeContentTab.value = 'mapping';
|
||
currentActiveTab.value = 'mapping';
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 监听第一个映射的变化,如果改变了服务商,清空后续映射
|
||
watch(
|
||
() => mappingList.value[0]?.value,
|
||
(newValue, oldValue) => {
|
||
let newProviderId: any = null;
|
||
let oldProviderId: any = null;
|
||
|
||
// 获取新的服务商ID
|
||
if (Array.isArray(newValue) && newValue.length >= 1) {
|
||
newProviderId = newValue[0];
|
||
} else if (typeof newValue === 'number') {
|
||
newProviderId = newValue;
|
||
}
|
||
|
||
// 获取旧的服务商ID
|
||
if (Array.isArray(oldValue) && oldValue.length >= 1) {
|
||
oldProviderId = oldValue[0];
|
||
} else if (typeof oldValue === 'number') {
|
||
oldProviderId = oldValue;
|
||
}
|
||
|
||
// 如果服务商改变了,清空所有后续映射的选择
|
||
if (oldProviderId && newProviderId && newProviderId !== oldProviderId) {
|
||
for (let i = 1; i < mappingList.value.length; i++) {
|
||
mappingList.value[i].value = [];
|
||
}
|
||
}
|
||
},
|
||
{ deep: true }
|
||
);
|
||
|
||
// 处理映射提交
|
||
async function handleMappingSubmit() {
|
||
// 过滤掉没有选择值的映射
|
||
const validMappings = mappingList.value.filter(m => {
|
||
// 如果是数组且长度为2,或者是一个数字(接口ID)
|
||
return (Array.isArray(m.value) && m.value.length === 2) || typeof m.value === 'number';
|
||
});
|
||
|
||
if (validMappings.length === 0) {
|
||
$baseMessage('请至少选择一个接口', 'warning');
|
||
return;
|
||
}
|
||
|
||
// 提取接口ID数组(按顺序)
|
||
const interfaceIds: number[] = [];
|
||
for (const m of validMappings) {
|
||
if (Array.isArray(m.value) && m.value.length === 2) {
|
||
interfaceIds.push(m.value[1]); // 接口ID是数组的第二个元素
|
||
} else if (typeof m.value === 'number') {
|
||
interfaceIds.push(m.value); // 数字本身就是接口ID
|
||
}
|
||
}
|
||
|
||
try {
|
||
// 调用API创建映射
|
||
await postCreate({
|
||
upStreamId: upStreamIdRef.value || 0,
|
||
downStreamIds: interfaceIds
|
||
});
|
||
|
||
$baseMessage('映射创建成功', 'success');
|
||
registerAddMapMethods.closeModal();
|
||
|
||
// 刷新映射列表
|
||
(unref(MapServiceProviderRef) as any)?.tableRef?.reload();
|
||
} catch (error) {
|
||
$baseMessage('创建映射失败', 'error');
|
||
}
|
||
}
|
||
|
||
// 抽屉查询按钮
|
||
async function onDetaCasc(formValue: any) {
|
||
cascaderValue.value = [];
|
||
|
||
const response =
|
||
await getServiceProviderGetServiceProviderInterfaceCascadedList({
|
||
serviceProviderName: formValue?.serviceProviderName,
|
||
interfaceId: upStreamIdRef.value || 0,
|
||
});
|
||
|
||
// 处理数据,去掉服务商类型的一级菜单,直接显示服务商和服务商接口
|
||
const processedData = processServiceProviderData(response.data || []);
|
||
|
||
drawerList.value = processedData;
|
||
await nextTick();
|
||
panelRef.value?.SetOptions(processedData);
|
||
|
||
const arr = processedData?.filter((node: { children: any; }) => {
|
||
return node.children && node.children.length > 0;
|
||
});
|
||
cascaderValue.value = filterValue(arr);
|
||
}
|
||
|
||
// 处理服务商数据,去掉一级分类,直接显示服务商和服务商接口
|
||
function processServiceProviderData(data: any[]) {
|
||
if (!data || !Array.isArray(data)) return [];
|
||
|
||
const result: any[] = [];
|
||
|
||
data.forEach((category: any) => {
|
||
if (category && category.children && Array.isArray(category.children)) {
|
||
// 遍历每个分类下的服务商
|
||
category.children.forEach((provider: any) => {
|
||
if (provider && provider.children && Array.isArray(provider.children)) {
|
||
// 创建新的服务商节点,直接包含接口
|
||
const newProvider = {
|
||
value: provider.value || provider.id,
|
||
label: provider.label || provider.name,
|
||
key: provider.key || provider.value || provider.id,
|
||
isDisable: provider.isDisable || false,
|
||
// 其他属性
|
||
...provider
|
||
};
|
||
|
||
// 将接口作为服务商的直接子项
|
||
const interfaceItems = provider.children
|
||
.filter((interfaceItem: any) => interfaceItem) // 过滤掉空值
|
||
.map((interfaceItem: any) => ({
|
||
value: interfaceItem.value || interfaceItem.id,
|
||
label: interfaceItem.label || interfaceItem.name,
|
||
key: interfaceItem.key || interfaceItem.value || interfaceItem.id,
|
||
isDisable: interfaceItem.isDisable || false,
|
||
// 其他属性
|
||
...interfaceItem
|
||
}));
|
||
|
||
// 重新构建,让服务商直接包含接口
|
||
newProvider.children = interfaceItems;
|
||
|
||
result.push(newProvider);
|
||
}
|
||
});
|
||
}
|
||
});
|
||
|
||
return result;
|
||
}
|
||
|
||
function filterValue(arr: any[], arrValue: any[] = []): number[] {
|
||
if (!arr.length) return arrValue;
|
||
arrValue.push(arr[0]?.value);
|
||
|
||
if (arr[0].children && arr[0].children?.length > 0)
|
||
return filterValue(arr[0].children, arrValue);
|
||
return arrValue;
|
||
}
|
||
|
||
// 抽屉取消按钮
|
||
async function cancelCascader() {
|
||
drawerFormRef.value?.setFormValue('serviceProviderName', '');
|
||
const { data } =
|
||
await getServiceProviderGetServiceProviderInterfaceCascadedList({
|
||
serviceProviderName: '',
|
||
interfaceId: upStreamIdRef.value || 0,
|
||
});
|
||
|
||
// 处理数据,去掉一级分类
|
||
const processedData = processServiceProviderData(data);
|
||
|
||
cascaderValue.value = [];
|
||
drawerList.value = [];
|
||
setTimeout(() => {
|
||
drawerList.value = processedData;
|
||
}, 100);
|
||
}
|
||
|
||
// 处理树形选择变化
|
||
function handleTreeSelectionChange(selectedItems: any[]) {
|
||
// 这里可以处理选中的项目,比如更新UI状态等
|
||
}
|
||
|
||
// 处理树形搜索
|
||
function handleTreeSearch(keyword: string) {
|
||
// 调用原来的搜索逻辑
|
||
onDetaCasc({ serviceProviderName: keyword });
|
||
}
|
||
const InputParameterMappingList = ref<any[]>([
|
||
{
|
||
value: '创建袋子',
|
||
label: '入参映射',
|
||
data: 1,
|
||
},
|
||
{
|
||
value: '获取带牌标签',
|
||
label: '入参映射',
|
||
data: 2,
|
||
},
|
||
{
|
||
value: '袋子绑定包裹',
|
||
label: '入参映射',
|
||
data: 3,
|
||
},
|
||
{
|
||
value: t('system.interface.mapping.outputMapping'),
|
||
label: '',
|
||
data: 4,
|
||
},
|
||
]);
|
||
const confirmLoading = ref(false);
|
||
const serviceProviderId = ref(0);
|
||
// 抽屉确认按钮
|
||
async function detCascader() {
|
||
const arr: any[] = panelRef.value?.getActivaList();
|
||
serviceProviderId.value = panelRef.value?.getCheckedList().value;
|
||
serviceProviderName.value = panelRef.value?.getCheckedList().label;
|
||
|
||
if (!arr.length) return $message.warning(t('system.interface.message.selectProviderInterface'));
|
||
InputParameterMappingList.value = arr
|
||
.map((t: any, i: number) => (i = { ...t, data: i }))
|
||
.concat({
|
||
value: '',
|
||
label: t('system.interface.mapping.outputMapping'),
|
||
data: arr.length,
|
||
});
|
||
|
||
const id = arr.map((item: any) => item.value);
|
||
confirmLoading.value = true;
|
||
const data = await postCreate({
|
||
upStreamId: Number(upStreamIdRef.value),
|
||
downStreamIds: id,
|
||
}).finally(() => {
|
||
confirmLoading.value = false;
|
||
});
|
||
if (!data) return;
|
||
|
||
(unref(tableRef) as any)?.reload?.();
|
||
editMapIds.value = {
|
||
id: serviceProviderId.value,
|
||
addId: Number(upStreamIdRef.value),
|
||
intId: id,
|
||
};
|
||
|
||
registerEnterMethods.openModal();
|
||
|
||
await nextTick();
|
||
registerEnterMethods.setModalProps({
|
||
title: t('system.interface.modal.add'),
|
||
width: 1600,
|
||
height: 1600,
|
||
});
|
||
|
||
(unref(enteredMapRef) as any)?.initData?.({
|
||
serviceProviderId: serviceProviderId.value,
|
||
interfaceId: upStreamIdRef.value,
|
||
});
|
||
active.value = 1;
|
||
|
||
const gb = drawerList.value;
|
||
cascaderValue.value = [];
|
||
drawerList.value = [];
|
||
setTimeout(() => {
|
||
drawerList.value = gb;
|
||
}, 100);
|
||
confirmLoading.value = false;
|
||
registerAddMapMethods.closeModal();
|
||
}
|
||
const checkRowsLength = computed(() => {
|
||
return (unref(tableRef) as any)?.checkAllRows?.()?.length || 0;
|
||
});
|
||
|
||
// 映射服务商弹窗点击修改打开入参映射弹窗
|
||
const editMapIds = ref();
|
||
// 是否全屏弹框
|
||
const fullScreen = ref(false);
|
||
const serviceProviderName = ref('');
|
||
|
||
async function handlerEditMap(data: any) {
|
||
editMapIds.value = data;
|
||
serviceProviderId.value = data.serviceProviderId;
|
||
serviceProviderName.value = data.serviceProviderName;
|
||
upStreamIdRef.value = data.addId;
|
||
InputParameterMappingList.value = data.inputParameterMappingList;
|
||
registerEnterMethods.setModalProps({
|
||
title: t('system.interface.modal.edit'),
|
||
width: 1600,
|
||
height: 1600,
|
||
});
|
||
|
||
|
||
registerEnterMethods.openModal();
|
||
|
||
await nextTick();
|
||
|
||
await (unref(enteredMapRef) as any)?.initData?.({
|
||
serviceProviderId: data.serviceProviderId,
|
||
interfaceId: data.addId,
|
||
}
|
||
);
|
||
}
|
||
const MapServiceProviderROW = ref<any>();
|
||
const MapServiceProviderRef = ref<any>();
|
||
const loadingSubmit = ref(false);
|
||
const interfaceDetailModalRef = ref<any>();
|
||
const detailRefreshKey = ref(0);
|
||
const selectedProvider = ref<any>();
|
||
const tabsRef = ref<any>();
|
||
|
||
async function saveModelEnter() {
|
||
const data = (unref(enteredMapRef) as any)?.getdata?.();
|
||
// 兜底系统接口 code,避免 undefined 读属性报错
|
||
const sysCode =
|
||
(newmapClickROW?.value && newmapClickROW.value.code) ||
|
||
(MapServiceProviderROW?.value && MapServiceProviderROW.value.systemInterfaceCode) ||
|
||
(selectedInterface?.value && selectedInterface.value.code) ||
|
||
'';
|
||
|
||
MapServiceProviderROW.value = {
|
||
serviceProviderId: serviceProviderId.value,
|
||
serviceProviderName: serviceProviderName.value,
|
||
systemInterfaceCode: sysCode,
|
||
addId: upStreamIdRef.value,
|
||
inputParameterMappingList: InputParameterMappingList.value,
|
||
};
|
||
loadingSubmit.value = true;
|
||
|
||
try {
|
||
await postSaveParameterMaps(data);
|
||
$baseMessage(t('system.interface.message.saveSuccess'), 'success');
|
||
loadingSubmit.value = false;
|
||
(unref(enteredMapRef) as any)?.initData?.(
|
||
{
|
||
serviceProviderId: serviceProviderId.value,
|
||
interfaceId: upStreamIdRef.value,
|
||
});
|
||
|
||
$baseConfirm({
|
||
title: t('system.interface.modal.warningTitle'),
|
||
content: t('system.interface.modal.publishConfirm'),
|
||
onOk: async () => {
|
||
await registerEnterMethods.closeModal?.();
|
||
const currentRow = MapServiceProviderROW.value;
|
||
if (currentRow) {
|
||
(unref(MapServiceProviderRef) as any)?.onRelease?.(currentRow);
|
||
}
|
||
await (unref(MapServiceProviderRef) as any)?.tableRef?.reload?.();
|
||
await (unref(tableRef) as any)?.reload?.();
|
||
},
|
||
});
|
||
} catch (error) {
|
||
loadingSubmit.value = false;
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
function rowClick(row: any) {
|
||
MapServiceProviderROW.value = row;
|
||
// 联动右侧映射明细:根据左侧选中映射行初始化 EnteredMap
|
||
serviceProviderId.value = row.serviceProviderId;
|
||
serviceProviderName.value = row.serviceProviderName;
|
||
upStreamIdRef.value = row.systemInterfaceId || upStreamIdRef.value;
|
||
nextTick(() => {
|
||
(unref(enteredMapRef) as any)?.initData?.({
|
||
serviceProviderId: serviceProviderId.value,
|
||
interfaceId: upStreamIdRef.value,
|
||
});
|
||
});
|
||
}
|
||
|
||
// 中间映射列表(服务商映射行)切换时:若当前在 API 调试 tab,则强制刷新调试面板并重新导入 cURL
|
||
watch(
|
||
() => MapServiceProviderROW.value?.id || MapServiceProviderROW.value?.serviceProviderId || MapServiceProviderROW.value,
|
||
async (newVal, oldVal) => {
|
||
if (!newVal || newVal === oldVal) return;
|
||
apiDebuggerRefreshKey.value++;
|
||
if (activeContentTab.value === 'debugger') {
|
||
await nextTick();
|
||
importCurlToDebugger();
|
||
}
|
||
}
|
||
);
|
||
|
||
function handleInterfaceDetailCancel() {
|
||
// 取消编辑,重新加载接口详情
|
||
if (selectedInterface.value) {
|
||
detailRefreshKey.value++;
|
||
}
|
||
}
|
||
|
||
// 点击调试 / 切换 API 调试 tab:获取请求容器的 cURL 并导入到 API 调试
|
||
const curlToImport = ref('');
|
||
function importCurlToDebugger() {
|
||
const curl = interfaceDetailModalRef.value?.getRequestExample?.() || '';
|
||
if (curl && curl.trim()) {
|
||
curlToImport.value = curl;
|
||
nextTick(() => {
|
||
setTimeout(() => { curlToImport.value = ''; }, 500);
|
||
});
|
||
}
|
||
}
|
||
function handleDetailDebugClick() {
|
||
handleTabChange('debugger');
|
||
if (!MapServiceProviderROW.value) return;
|
||
importCurlToDebugger();
|
||
activeContentTab.value = 'debugger';
|
||
}
|
||
|
||
// 统一的保存函数,根据当前tab调用不同的接口
|
||
async function handleTabSave() {
|
||
// 使用独立的currentActiveTab变量,在handleTabChange中实时更新
|
||
const currentTab = currentActiveTab.value;
|
||
|
||
try {
|
||
if (currentTab === 'mapping') {
|
||
await saveModelEnter();
|
||
} else if (currentTab === 'detail') {
|
||
if (interfaceDetailModalRef.value && typeof interfaceDetailModalRef.value.saveInterface === 'function') {
|
||
await interfaceDetailModalRef.value.saveInterface();
|
||
// 保存成功后刷新接口明细
|
||
if (selectedInterface.value && interfaceDetailModalRef.value) {
|
||
await nextTick();
|
||
interfaceDetailModalRef.value.init({
|
||
id: selectedInterface.value.id,
|
||
interfaceId: selectedInterface.value.id,
|
||
forceRefresh: true // 强制刷新
|
||
});
|
||
}
|
||
} else {
|
||
$baseMessage('接口配置组件未初始化', 'warning');
|
||
}
|
||
} else {
|
||
$baseMessage('当前tab不支持保存操作', 'warning');
|
||
}
|
||
} catch (error) {
|
||
$baseMessage('保存失败', 'error');
|
||
throw error;
|
||
}
|
||
}
|
||
// 点击跳转
|
||
function goRouter(id: number | undefined, address: string, type?: string) {
|
||
router.replace({
|
||
name: address,
|
||
query: { id, type },
|
||
});
|
||
}
|
||
|
||
// 预加载接口明细所需的字典数据
|
||
async function preloadDictionaryData() {
|
||
const dictionaryTypes = [
|
||
'RequestProtocol', // 请求协议
|
||
'RequestMethod', // 请求方式
|
||
'ContentType', // Content-Type
|
||
'ResponseContentType', // Response-Content-Type
|
||
'ParameterType', // 参数类型(根节点)
|
||
];
|
||
|
||
// 并行请求所有字典数据
|
||
const promises = dictionaryTypes.map(type =>
|
||
getDictionaryGetList({ type }).catch(() => {
|
||
return null;
|
||
})
|
||
);
|
||
|
||
// 不等待结果,后台预加载即可
|
||
Promise.all(promises);
|
||
}
|
||
|
||
// 选中接口的辅助函数
|
||
function selectInterfaceByKey(key: string) {
|
||
const nodeData = getNodeData(key);
|
||
if (nodeData && nodeData.isLeaf) {
|
||
// 更新选中的keys,保持选中状态
|
||
selectedKeys.value = [key];
|
||
|
||
// 如果选择了不同的接口,更新 detailRefreshKey 以确保 InterfaceConfig 组件重新初始化
|
||
if (selectedInterface.value?.id !== nodeData.id) {
|
||
detailRefreshKey.value++;
|
||
}
|
||
|
||
selectedInterface.value = nodeData;
|
||
// 将上游接口ID同步给右侧明细区域
|
||
upStreamIdRef.value = Number(nodeData.id);
|
||
|
||
// 重置映射明细区域,确保EnteredMap组件重新渲染
|
||
serviceProviderId.value = 0;
|
||
serviceProviderName.value = '';
|
||
MapServiceProviderROW.value = null;
|
||
|
||
// 刷新右侧映射服务商列表
|
||
nextTick(() => {
|
||
if (MapServiceProviderRef.value && MapServiceProviderRef.value.tableRef) {
|
||
MapServiceProviderRef.value.tableRef.reload();
|
||
}
|
||
});
|
||
|
||
// 选择接口后,加载右侧嵌入的"新增映射"所需的服务商数据
|
||
loadInitialServiceProviderData();
|
||
|
||
// 预加载接口明细所需的字典数据
|
||
preloadDictionaryData();
|
||
|
||
// 如果当前在 detail tab,确保 InterfaceConfig 组件能正确初始化
|
||
if (activeContentTab.value === 'detail') {
|
||
initInterfaceConfig();
|
||
}
|
||
}
|
||
}
|
||
|
||
// 处理接口树选择
|
||
function handleInterfaceSelect(keys: string[], info: any) {
|
||
try {
|
||
if (info && info.selected && info.node && info.node.isLeaf) {
|
||
selectInterfaceByKey(info.node.key);
|
||
}
|
||
} catch (error) {
|
||
}
|
||
}
|
||
|
||
// 初始化接口明细组件的函数
|
||
async function initInterfaceConfig() {
|
||
if (!selectedInterface.value || activeContentTab.value !== 'detail') {
|
||
return;
|
||
}
|
||
|
||
// 等待组件完全挂载(v-if 条件渲染需要时间)
|
||
await nextTick();
|
||
await nextTick();
|
||
await nextTick();
|
||
|
||
// 使用轮询方式等待组件实例创建,最多等待 2 秒
|
||
let retryCount = 0;
|
||
const maxRetries = 20;
|
||
const checkInterval = 100;
|
||
|
||
const checkAndInit = () => {
|
||
// 检查组件是否还存在且处于 detail tab
|
||
if (activeContentTab.value !== 'detail' || !selectedInterface.value) {
|
||
return; // 如果已经切换了 tab,停止初始化
|
||
}
|
||
|
||
if (interfaceDetailModalRef.value && interfaceDetailModalRef.value.init) {
|
||
try {
|
||
interfaceDetailModalRef.value.init({
|
||
id: selectedInterface.value.id,
|
||
interfaceId: selectedInterface.value.id,
|
||
});
|
||
} catch {
|
||
// 初始化失败,静默处理
|
||
}
|
||
} else {
|
||
retryCount++;
|
||
if (retryCount < maxRetries) {
|
||
setTimeout(checkAndInit, checkInterval);
|
||
}
|
||
}
|
||
};
|
||
|
||
// 延迟一下再开始检查,给组件更多时间挂载
|
||
setTimeout(checkAndInit, 200);
|
||
}
|
||
|
||
// 监听 interfaceDetailModalRef 变化,当组件挂载后自动初始化(detail 用 v-show 保持挂载,需在有 selectedInterface 时初始化以便切换调试 tab 时能获取 cURL)
|
||
watch(interfaceDetailModalRef, async (newRef, oldRef) => {
|
||
if (newRef && !oldRef && selectedInterface.value && newRef.init) {
|
||
await nextTick();
|
||
await nextTick();
|
||
setTimeout(() => {
|
||
if (selectedInterface.value && newRef && newRef.init) {
|
||
try {
|
||
newRef.init({
|
||
id: selectedInterface.value.id,
|
||
interfaceId: selectedInterface.value.id,
|
||
});
|
||
} catch {
|
||
// 初始化失败,静默处理
|
||
}
|
||
}
|
||
}, 200);
|
||
}
|
||
});
|
||
|
||
// 监听 activeContentTab 变化,同步更新 currentActiveTab
|
||
watch(activeContentTab, async (newTab) => {
|
||
currentActiveTab.value = newTab;
|
||
if (newTab === 'debugger') {
|
||
importCurlToDebugger();
|
||
} else if (newTab === 'detail' && selectedInterface.value) {
|
||
// 等待 DOM 更新完成后再初始化
|
||
await nextTick();
|
||
await nextTick();
|
||
setTimeout(() => {
|
||
initInterfaceConfig();
|
||
}, 100);
|
||
} else if (newTab === 'mapping') {
|
||
// 切换回映射明细tab时,重新初始化EnteredMap组件
|
||
await nextTick();
|
||
await nextTick();
|
||
setTimeout(() => {
|
||
if (enteredMapRef.value && selectedInterface.value && upStreamIdRef.value) {
|
||
(unref(enteredMapRef) as any)?.initData?.({
|
||
serviceProviderId: serviceProviderId.value,
|
||
interfaceId: upStreamIdRef.value,
|
||
});
|
||
}
|
||
}, 100);
|
||
}
|
||
});
|
||
|
||
// 监听 selectedInterface 变化,初始化组件(detail 用 v-show 保持挂载,需在切换接口时初始化以便切换调试 tab 时能获取 cURL)
|
||
watch(selectedInterface, async (newInterface) => {
|
||
if (newInterface) {
|
||
await nextTick();
|
||
await nextTick();
|
||
setTimeout(() => {
|
||
if (interfaceDetailModalRef.value?.init && selectedInterface.value) {
|
||
try {
|
||
interfaceDetailModalRef.value.init({
|
||
id: selectedInterface.value.id,
|
||
interfaceId: selectedInterface.value.id,
|
||
});
|
||
} catch {
|
||
// 初始化失败,静默处理
|
||
}
|
||
}
|
||
}, activeContentTab.value === 'detail' ? 100 : 300);
|
||
}
|
||
}, { immediate: false });
|
||
|
||
// 监听模态框打开,初始化模态框中的组件
|
||
const modalForm = ref<any>();
|
||
watch([editVisible, editId], async ([visible, id]) => {
|
||
if (visible && modalForm.value) {
|
||
await nextTick();
|
||
await nextTick();
|
||
setTimeout(() => {
|
||
if (modalForm.value && modalForm.value.init) {
|
||
if (id) {
|
||
// 编辑模式
|
||
modalForm.value.init({ id, interfaceId: id });
|
||
} else {
|
||
// 新增模式
|
||
modalForm.value.init();
|
||
}
|
||
}
|
||
}, 200);
|
||
}
|
||
});
|
||
|
||
// 处理上游接口保存成功事件
|
||
function handleUpstreamInterfaceSaved() {
|
||
editVisible.value = false;
|
||
(unref(tableRef) as any)?.reload?.();
|
||
// 如果当前有选中的接口且处于 detail tab,刷新接口明细
|
||
if (selectedInterface.value && activeContentTab.value === 'detail' && interfaceDetailModalRef.value) {
|
||
nextTick(() => {
|
||
interfaceDetailModalRef.value.init({
|
||
id: selectedInterface.value.id,
|
||
interfaceId: selectedInterface.value.id,
|
||
forceRefresh: true // 强制刷新
|
||
});
|
||
});
|
||
}
|
||
}
|
||
|
||
// 处理模态框保存
|
||
async function handleModalSave() {
|
||
if (modalForm.value && modalForm.value.saveInterface) {
|
||
try {
|
||
await modalForm.value.saveInterface();
|
||
editVisible.value = false;
|
||
(unref(tableRef) as any)?.reload?.();
|
||
// 如果当前有选中的接口且处于 detail tab,刷新接口明细
|
||
if (selectedInterface.value && activeContentTab.value === 'detail' && interfaceDetailModalRef.value) {
|
||
await nextTick();
|
||
interfaceDetailModalRef.value.init({
|
||
id: selectedInterface.value.id,
|
||
interfaceId: selectedInterface.value.id,
|
||
forceRefresh: true // 强制刷新
|
||
});
|
||
}
|
||
} catch (error) {
|
||
// 错误已在组件内部处理
|
||
}
|
||
}
|
||
}
|
||
|
||
// 处理树展开
|
||
function handleTreeExpand(keys: string[]) {
|
||
try {
|
||
expandedKeys.value = keys;
|
||
} catch (error) {
|
||
}
|
||
}
|
||
|
||
// 加载接口树数据
|
||
async function loadInterfaceTree() {
|
||
loading.value = true;
|
||
try {
|
||
// 调用获取接口列表的API
|
||
const params: any = {
|
||
pageIndex: 1,
|
||
pageSize: 1000, // 获取所有接口
|
||
isUpstream: true, // 上游接口
|
||
};
|
||
|
||
// 如果有搜索关键词,添加到参数中
|
||
if (searchKeyword.value) {
|
||
params.nameOrCode = searchKeyword.value;
|
||
}
|
||
|
||
const response = await postInterfaceInterfacePaginatedList(params);
|
||
|
||
// 从API响应中获取数据
|
||
const data = (response as any)?.items || (response as any)?.data || (response as any)?.list || [];
|
||
|
||
if (Array.isArray(data) && data.length > 0) {
|
||
// 将接口数据转换为树形结构
|
||
const treeData = convertToTreeData(data);
|
||
interfaceTreeData.value = treeData;
|
||
|
||
// 默认展开所有分类节点
|
||
const categoryKeys = treeData.map(item => item.key);
|
||
expandedKeys.value = categoryKeys;
|
||
} else {
|
||
interfaceTreeData.value = [];
|
||
}
|
||
} catch (error) {
|
||
$baseMessage('加载接口列表失败', 'error');
|
||
} finally {
|
||
loading.value = false;
|
||
}
|
||
}
|
||
|
||
// 将接口数据转换为树形结构
|
||
function convertToTreeData(interfaces: any[]) {
|
||
// 按分类分组
|
||
const categoryMap = new Map();
|
||
|
||
interfaces.forEach((item) => {
|
||
// 使用categoryName作为分类,如果没有则使用category
|
||
const category = item.categoryName || `分类${item.category}` || '未分类';
|
||
|
||
if (!categoryMap.has(category)) {
|
||
categoryMap.set(category, []);
|
||
}
|
||
|
||
const treeItem = {
|
||
key: `item-${item.id}`,
|
||
title: item.name,
|
||
isLeaf: true,
|
||
id: item.id,
|
||
name: item.name,
|
||
code: item.code || item.name,
|
||
category: category,
|
||
mapCount: item.mapCount || 0
|
||
};
|
||
|
||
categoryMap.get(category).push(treeItem);
|
||
});
|
||
|
||
// 转换为树形结构
|
||
const treeData = [];
|
||
let categoryIndex = 1;
|
||
|
||
for (const [category, items] of categoryMap) {
|
||
const categoryNode = {
|
||
key: `category-${categoryIndex}`,
|
||
title: category,
|
||
children: items
|
||
};
|
||
treeData.push(categoryNode);
|
||
categoryIndex++;
|
||
}
|
||
|
||
return treeData;
|
||
}
|
||
|
||
// 处理搜索
|
||
function handleSearch() {
|
||
loadInterfaceTree();
|
||
}
|
||
|
||
// 处理下拉菜单选择
|
||
function handleActionSelect(value: string, option: any) {
|
||
try {
|
||
if (option && option.data) {
|
||
const dataRef = option.data;
|
||
if (value === 'edit') {
|
||
compileClick(dataRef);
|
||
} else if (value === 'map') {
|
||
newmapClick(dataRef);
|
||
}
|
||
}
|
||
} catch (error) {
|
||
}
|
||
}
|
||
|
||
// 根据key获取节点数据
|
||
function getNodeData(key: string) {
|
||
if (!key || !interfaceTreeData.value.length) return null;
|
||
|
||
// 递归查找节点
|
||
function findNode(nodes: any[]): any {
|
||
for (const node of nodes) {
|
||
if (node.key === key) {
|
||
return node;
|
||
}
|
||
if (node.children && node.children.length > 0) {
|
||
const found = findNode(node.children);
|
||
if (found) return found;
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
|
||
return findNode(interfaceTreeData.value);
|
||
}
|
||
|
||
</script>
|
||
|
||
<template>
|
||
<div class="interface-layout">
|
||
<!-- 左侧上游接口列表:自定义左菜单 -->
|
||
<CustomLeftMenu
|
||
class="left-sidebar"
|
||
:tree-data="interfaceTreeData"
|
||
v-model:selected-keys="selectedKeys"
|
||
v-model:expanded-keys="expandedKeys"
|
||
:loading="loading"
|
||
v-model:search-keyword="searchKeyword"
|
||
:search-placeholder="$t('system.interface.search.placeholder.interfaceName')"
|
||
:refresh-title="$t('system.interface.actions.refresh')"
|
||
:add-title="$t('system.interface.actions.addInterface')"
|
||
:loading-text="$t('system.interface.loading.interfaceList')"
|
||
:delete-label="$t('system.interface.actions.delete')"
|
||
:actions-title="$t('system.interface.table.actions')"
|
||
method-label="POST"
|
||
@search="handleSearch"
|
||
@refresh="loadInterfaceTree"
|
||
@add="interfaceClick"
|
||
@select="handleInterfaceSelect"
|
||
@expand="handleTreeExpand"
|
||
@action-select="handleActionSelect"
|
||
@delete-node="deleteInterfaceClick"
|
||
@node-click="(key) => selectInterfaceByKey(key)"
|
||
/>
|
||
|
||
<!-- 中间映射列表区域 -->
|
||
<div class="middle-content nav-panel">
|
||
<div class="content-header sidebar-header">
|
||
<div class="mapping-search-box">
|
||
<a-input
|
||
v-model="mappingSearchKeyword"
|
||
:placeholder="$t('system.interface.search.placeholder.providerName')"
|
||
allow-clear
|
||
size="small"
|
||
class="sidebar-search-input"
|
||
@change="() => MapServiceProviderRef?.setSearchKeyword?.(mappingSearchKeyword)"
|
||
@clear="() => MapServiceProviderRef?.setSearchKeyword?.('')"
|
||
>
|
||
<template #prefix>
|
||
<icon-search />
|
||
</template>
|
||
</a-input>
|
||
</div>
|
||
<div class="header-actions">
|
||
<a-button
|
||
type="text"
|
||
size="small"
|
||
class="action-icon-btn refresh-btn"
|
||
@click="MapServiceProviderRef?.tableRef?.reload?.()"
|
||
:disabled="!selectedInterface"
|
||
:title="$t('system.interface.actions.refresh')"
|
||
>
|
||
↻
|
||
</a-button>
|
||
<a-button
|
||
type="text"
|
||
size="small"
|
||
class="action-icon-btn add-btn"
|
||
@click="() => {
|
||
if (selectedInterface) {
|
||
newmapClick(selectedInterface);
|
||
} else {
|
||
$baseMessage($t('system.interface.message.selectInterfaceFirst'), 'warning');
|
||
}
|
||
}"
|
||
:disabled="!selectedInterface"
|
||
:title="$t('system.interface.modal.addMapping')"
|
||
>
|
||
+
|
||
</a-button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="content-body">
|
||
<div v-if="selectedInterface" class="mapping-list-container tree-scroll">
|
||
<MapServiceProvider
|
||
:id="selectedInterface.id"
|
||
ref="MapServiceProviderRef"
|
||
@edit-map="handlerEditMap"
|
||
@row-click="rowClick"
|
||
/>
|
||
</div>
|
||
<div v-else class="empty-state">
|
||
<a-empty :description="$t('system.interface.empty.selectLeftInterfaceForMapping')" />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 右侧内容区域 -->
|
||
<div class="right-content content-panel">
|
||
<div v-if="selectedInterface" class="interface-detail-container">
|
||
<!-- Tab切换 - 完全独立 -->
|
||
<div class="interface-detail-tabs">
|
||
<div class="tabs-header">
|
||
<div class="tabs-list">
|
||
<div
|
||
class="tab-item"
|
||
:class="{ active: activeContentTab === 'mapping' }"
|
||
@click="handleTabChange('mapping'); activeContentTab = 'mapping'"
|
||
>
|
||
{{ $t('system.interface.tabs.mappingDetail') }}
|
||
</div>
|
||
<div
|
||
class="tab-item"
|
||
:class="{ active: activeContentTab === 'debugger' }"
|
||
:style="{ opacity: MapServiceProviderROW ? 1 : 0.5, cursor: MapServiceProviderROW ? 'pointer' : 'not-allowed' }"
|
||
@click="if (MapServiceProviderROW) { handleTabChange('debugger'); activeContentTab = 'debugger' }"
|
||
>
|
||
{{ $t('system.interface.tabs.apiDebugger') }}
|
||
</div>
|
||
<div
|
||
class="tab-item"
|
||
:class="{ active: activeContentTab === 'detail' }"
|
||
@click="handleTabChange('detail'); activeContentTab = 'detail'"
|
||
>
|
||
{{ $t('system.interface.tabs.interfaceDetail') }}
|
||
</div>
|
||
</div>
|
||
<div class="tabs-extra">
|
||
<a-button
|
||
v-if="activeContentTab === 'mapping'"
|
||
size="small"
|
||
type="primary"
|
||
@click="handleTabSave"
|
||
>
|
||
{{ $t('system.interface.actions.save') }}
|
||
</a-button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Tab内容 - 完全独立 -->
|
||
<div class="interface-detail-tab-content">
|
||
<!-- 映射明细内容 -->
|
||
<div v-if="activeContentTab === 'mapping' && selectedInterface" class="mapping-content">
|
||
<EnteredMap
|
||
:key="`entered-map-${selectedInterface?.id}-${serviceProviderId}-${activeContentTab}`"
|
||
ref="enteredMapRef"
|
||
class="h-full w-full"
|
||
:service-provider-id="serviceProviderId"
|
||
:interface-id="upStreamIdRef"
|
||
:service-provider-name="serviceProviderName"
|
||
@save-model-enter="saveModelEnter"
|
||
/>
|
||
</div>
|
||
|
||
<!-- API调试组件 -->
|
||
<div v-if="activeContentTab === 'debugger'" class="debug-content">
|
||
<ApiDebugger
|
||
:key="`api-debugger-${selectedInterface?.id || ''}-${MapServiceProviderROW?.id || MapServiceProviderROW?.serviceProviderId || ''}-${apiDebuggerRefreshKey}`"
|
||
ref="apiDebuggerRef"
|
||
:selected-interface="selectedInterface"
|
||
:selected-service-provider="MapServiceProviderROW"
|
||
:enable-defaults="true"
|
||
:curl-to-import="curlToImport"
|
||
/>
|
||
</div>
|
||
|
||
<!-- 接口明细内容(v-show 保持挂载,便于切换至调试 tab 时获取 cURL) -->
|
||
<div v-show="activeContentTab === 'detail' && selectedInterface" class="detail-content">
|
||
<UpstreamInterfaceModal
|
||
:key="`${selectedInterface?.id}-${detailRefreshKey}`"
|
||
ref="interfaceDetailModalRef"
|
||
@saved="() => { /* 保留在当前tab,按需刷新左侧树 */ }"
|
||
>
|
||
<template #headerActions>
|
||
<a-button size="small" type="primary" @click="handleTabSave">
|
||
{{ $t('system.interface.actions.save') }}
|
||
</a-button>
|
||
<a-button size="small" @click="handleDetailDebugClick">
|
||
调试
|
||
</a-button>
|
||
</template>
|
||
</UpstreamInterfaceModal>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div v-else class="empty-state">
|
||
<a-empty :description="$t('system.interface.empty.selectInterfaceForDetail')" />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<ModalPro
|
||
v-model:visible="editVisible"
|
||
:width="1500"
|
||
:modal-style="{ height: '900px' }"
|
||
:title="editId ? $t('system.interface.modal.editUpstreamInterface') : $t('system.interface.modal.addUpstreamInterface')"
|
||
unmount-on-close
|
||
:footer="false"
|
||
class="upstream-modal-tall"
|
||
>
|
||
<template v-if="editVisible">
|
||
<UpstreamInterfaceModal
|
||
ref="modalForm"
|
||
@saved="handleUpstreamInterfaceSaved"
|
||
/>
|
||
</template>
|
||
<div class="modal-footer">
|
||
<a-space :size="8">
|
||
<a-button @click="editVisible = false">取消</a-button>
|
||
<a-button type="primary" @click="handleModalSave">保存</a-button>
|
||
</a-space>
|
||
</div>
|
||
</ModalPro>
|
||
<ModalPro
|
||
@register="registerAddMap"
|
||
@ok="handleMappingSubmit"
|
||
@cancel="cancelCascader"
|
||
:width="1000"
|
||
:title="`新增映射 - ${mapName || ''}`"
|
||
>
|
||
<div class="mapping-modal-content">
|
||
<!-- 映射列表 -->
|
||
<div class="mapping-selector">
|
||
<div class="selector-tip">
|
||
<span class="tip-text">需要映射多个接口的,请注意排序</span>
|
||
</div>
|
||
|
||
<div class="mapping-list">
|
||
<div
|
||
v-for="(mapping, index) in mappingList"
|
||
:key="index"
|
||
class="mapping-item"
|
||
>
|
||
<span class="mapping-label">第{{ index + 1 }}个映射</span>
|
||
<a-cascader
|
||
v-model="mapping.value"
|
||
:options="getCascaderOptionsForIndex(index)"
|
||
:field-names="{ label: 'label', value: 'value', children: 'children' }"
|
||
:placeholder="$t('system.interface.search.placeholder.selectProviderAndInterface')"
|
||
:show-search="true"
|
||
:filter-option="filterCascaderOption"
|
||
:change-on-select="false"
|
||
style="flex: 1"
|
||
/>
|
||
<a-button
|
||
v-if="index === mappingList.length - 1"
|
||
type="text"
|
||
size="small"
|
||
class="action-icon-btn add-btn"
|
||
@click="addMapping"
|
||
:title="$t('system.interface.actions.addMapping')"
|
||
>
|
||
+
|
||
</a-button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</ModalPro>
|
||
<ModalPro title="映射服务商列表" :width="1200" @register="register">
|
||
<MapServiceProvider
|
||
:id="mapId"
|
||
ref="MapServiceProviderRef"
|
||
@edit-map="handlerEditMap"
|
||
@row-click="rowClick"
|
||
/>
|
||
</ModalPro>
|
||
|
||
<ModalPro
|
||
before-close
|
||
:width="1600"
|
||
:height="1600"
|
||
class="mapping-modal"
|
||
@register="registerEnter"
|
||
@ok="saveModelEnter"
|
||
@reset="() => {}"
|
||
>
|
||
|
||
<div >
|
||
|
||
<EnteredMap
|
||
ref="enteredMapRef"
|
||
|
||
class="h-full w-full"
|
||
:service-provider-id="serviceProviderId"
|
||
:interface-id="upStreamIdRef"
|
||
:service-provider-name="serviceProviderName"
|
||
@save-model-enter="saveModelEnter"
|
||
/>
|
||
</div>
|
||
|
||
</ModalPro>
|
||
</template>
|
||
|
||
<style scoped lang="less">
|
||
.interface-layout {
|
||
display: flex;
|
||
height: calc(100vh - 86px);
|
||
max-height: calc(100vh - 86px);
|
||
padding: 16px;
|
||
gap: 16px;
|
||
background-color: #f0f3f4;
|
||
overflow: hidden;
|
||
box-sizing: border-box;
|
||
width: 100%;
|
||
}
|
||
|
||
/* 左侧使用自定义左菜单组件,仅保留布局 */
|
||
.left-sidebar {
|
||
width: 280px;
|
||
flex-shrink: 0;
|
||
height: 100%;
|
||
}
|
||
|
||
.content-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 10px 12px !important;
|
||
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
|
||
background: transparent;
|
||
min-height: 48px !important;
|
||
color: #000;
|
||
}
|
||
|
||
/* 中间映射列表区域 */
|
||
.middle-content.nav-panel {
|
||
flex: 1;
|
||
max-width: 250px;
|
||
background: #fff;
|
||
border: 1px solid rgba(0, 0, 0, 0.08);
|
||
border-radius: 8px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
overflow: hidden;
|
||
color: #000;
|
||
box-shadow: none;
|
||
}
|
||
|
||
.mapping-list-title {
|
||
margin: 0;
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: #000;
|
||
}
|
||
|
||
.mapping-list-container {
|
||
flex: 1;
|
||
padding: 8px 4px 0 4px;
|
||
overflow-y: auto;
|
||
color: #000;
|
||
background: transparent;
|
||
border-radius: 8px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
|
||
> * {
|
||
width: 100%;
|
||
max-width: 100%;
|
||
}
|
||
}
|
||
|
||
.mapping-list-container::-webkit-scrollbar {
|
||
width: 6px;
|
||
}
|
||
|
||
.mapping-list-container::-webkit-scrollbar-track {
|
||
background: rgba(0, 0, 0, 0.08);
|
||
border-radius: 8px;
|
||
}
|
||
|
||
.mapping-list-container::-webkit-scrollbar-thumb {
|
||
background: rgba(0, 0, 0, 0.15);
|
||
border-radius: 8px;
|
||
}
|
||
|
||
.mapping-list-container::-webkit-scrollbar-thumb:hover {
|
||
background: rgba(0, 0, 0, 0.25);
|
||
}
|
||
|
||
.mapping-search-box {
|
||
flex: 1 1 calc(100% - 22px);
|
||
padding: 0;
|
||
margin-right: 4px;
|
||
box-sizing: border-box;
|
||
min-width: 0;
|
||
}
|
||
|
||
.mapping-search-box :deep(.arco-input-wrapper) {
|
||
background: #fff !important;
|
||
border: 1px solid var(--color-border-2) !important;
|
||
border-radius: 4px !important;
|
||
height: auto !important;
|
||
}
|
||
|
||
.mapping-search-box :deep(.arco-input) {
|
||
background: #fff !important;
|
||
border: none !important;
|
||
border-radius: 4px !important;
|
||
color: var(--color-text-1) !important;
|
||
height: auto !important;
|
||
}
|
||
|
||
.mapping-search-box :deep(.arco-input::placeholder) {
|
||
color: var(--color-text-3) !important;
|
||
}
|
||
|
||
.mapping-search-box :deep(.arco-input:hover),
|
||
.mapping-search-box :deep(.arco-input:focus),
|
||
.mapping-search-box :deep(.arco-input-wrapper:hover),
|
||
.mapping-search-box :deep(.arco-input-wrapper:focus-within) {
|
||
background: #fff !important;
|
||
border-color: var(--app-primary-color) !important;
|
||
}
|
||
|
||
.mapping-search-box :deep(.arco-input-prefix) {
|
||
color: var(--color-text-3) !important;
|
||
}
|
||
|
||
/* 右侧内容区域 */
|
||
.right-content.content-panel {
|
||
flex: 1;
|
||
background-color: #fff;
|
||
border: 1px solid rgba(0, 0, 0, 0.08);
|
||
border-radius: 8px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
overflow: hidden;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||
}
|
||
|
||
.interface-detail-container {
|
||
display: flex;
|
||
flex-direction: column;
|
||
height: 100%;
|
||
|
||
.interface-detail-tabs {
|
||
margin-bottom: 16px;
|
||
flex-shrink: 0;
|
||
border-bottom: 1px solid var(--color-border-2);
|
||
|
||
.tabs-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 0 20px;
|
||
height: 48px;
|
||
|
||
.tabs-list {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 32px;
|
||
flex: 1;
|
||
justify-content: flex-start;
|
||
|
||
.tab-item {
|
||
padding: 8px 0;
|
||
font-size: 14px;
|
||
color: var(--color-text-2);
|
||
cursor: pointer;
|
||
position: relative;
|
||
transition: color 0.2s;
|
||
font-weight: 600;
|
||
white-space: nowrap;
|
||
flex-shrink: 0;
|
||
|
||
&:hover {
|
||
color: var(--color-text-1);
|
||
}
|
||
|
||
&.active {
|
||
color: var(--app-primary-color) !important;
|
||
font-weight: 600 !important;
|
||
|
||
&::after {
|
||
content: '' !important;
|
||
position: absolute !important;
|
||
bottom: -1px !important;
|
||
left: 0 !important;
|
||
right: 0 !important;
|
||
height: 2px !important;
|
||
background-color: var(--app-primary-color) !important;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
.tabs-extra {
|
||
flex-shrink: 0;
|
||
margin-left: auto;
|
||
}
|
||
}
|
||
}
|
||
|
||
.interface-detail-tab-content {
|
||
flex: 1;
|
||
overflow: hidden;
|
||
min-height: 0;
|
||
|
||
.mapping-content {
|
||
height: 100%;
|
||
display: flex;
|
||
flex-direction: column;
|
||
min-height: 0;
|
||
}
|
||
|
||
.detail-content {
|
||
height: 100%;
|
||
display: flex;
|
||
flex-direction: column;
|
||
padding: 20px;
|
||
box-sizing: border-box;
|
||
min-height: 0;
|
||
}
|
||
|
||
.debug-content {
|
||
height: 100%;
|
||
display: flex;
|
||
flex-direction: column;
|
||
padding: 20px;
|
||
min-height: 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
.tab-content {
|
||
height: 100%;
|
||
display: flex;
|
||
flex-direction: column;
|
||
flex: 1;
|
||
}
|
||
|
||
.tab-actions {
|
||
padding: 16px 20px;
|
||
border-bottom: 1px solid var(--color-border-2);
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
align-items: center;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.tab-body {
|
||
flex: 1;
|
||
height: 100%;
|
||
overflow-y: auto;
|
||
padding: 20px;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.middle-content.nav-panel .content-header {
|
||
padding: 10px 12px !important;
|
||
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
|
||
background: transparent;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
min-height: 48px !important;
|
||
color: #000;
|
||
}
|
||
|
||
.header-actions {
|
||
display: flex;
|
||
gap: var(--app-button-gap, 8px);
|
||
flex-shrink: 0;
|
||
margin-left: auto;
|
||
}
|
||
|
||
/* 映射列表区域:刷新、新增按钮为黑色纯符号(无背景无框),两按钮紧挨 */
|
||
.middle-content.nav-panel .header-actions .action-icon-btn.refresh-btn {
|
||
color: #000 !important;
|
||
background-color: transparent !important;
|
||
border-color: transparent !important;
|
||
&:hover:not(:disabled) {
|
||
color: #262626 !important;
|
||
background-color: transparent !important;
|
||
border-color: transparent !important;
|
||
}
|
||
&:disabled {
|
||
color: #d9d9d9 !important;
|
||
background-color: transparent !important;
|
||
border-color: transparent !important;
|
||
}
|
||
}
|
||
.middle-content.nav-panel .header-actions .action-icon-btn.add-btn {
|
||
color: #000 !important;
|
||
background-color: transparent !important;
|
||
border-color: transparent !important;
|
||
&:hover:not(:disabled) {
|
||
color: #262626 !important;
|
||
background-color: transparent !important;
|
||
border-color: transparent !important;
|
||
}
|
||
&:disabled {
|
||
color: #d9d9d9 !important;
|
||
background-color: transparent !important;
|
||
border-color: transparent !important;
|
||
}
|
||
}
|
||
|
||
.content-body {
|
||
flex: 1;
|
||
overflow: hidden;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.empty-state {
|
||
flex: 1; /* 占满父容器剩余空间 */
|
||
min-height: 0; /* 防止高度塌陷 */
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 100%; /* 确保占满宽度 */
|
||
|
||
:deep(.arco-empty) {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
}
|
||
|
||
.loading-state {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
height: 120px;
|
||
color: #666;
|
||
|
||
p {
|
||
margin-top: 8px;
|
||
font-size: 12px;
|
||
}
|
||
}
|
||
|
||
// 新增映射弹窗样式
|
||
.mapping-modal-content {
|
||
padding: 0;
|
||
}
|
||
|
||
.mapping-selector {
|
||
padding: 20px;
|
||
|
||
// 强制设置弹窗内按钮为正方形
|
||
:deep(.arco-btn) {
|
||
width: 28px !important;
|
||
height: 28px !important;
|
||
min-width: 28px !important;
|
||
max-width: 28px !important;
|
||
padding: 0 !important;
|
||
}
|
||
}
|
||
|
||
.selector-tip {
|
||
margin-bottom: 20px;
|
||
|
||
.tip-text {
|
||
color: #ff7800;
|
||
font-size: 14px;
|
||
}
|
||
}
|
||
|
||
.mapping-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 16px;
|
||
}
|
||
|
||
.mapping-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
|
||
.mapping-label {
|
||
font-size: 14px;
|
||
color: #262626;
|
||
min-width: 80px;
|
||
}
|
||
|
||
:deep(.action-icon-btn) {
|
||
width: 28px !important;
|
||
height: 28px !important;
|
||
min-width: 28px !important;
|
||
max-width: 28px !important;
|
||
padding: 0 !important;
|
||
display: flex !important;
|
||
align-items: center !important;
|
||
justify-content: center !important;
|
||
color: #8c8c8c !important;
|
||
border: 1px solid transparent !important;
|
||
border-radius: 4px !important;
|
||
font-size: 16px !important;
|
||
font-weight: bold !important;
|
||
}
|
||
|
||
:deep(.action-icon-btn.add-btn) {
|
||
color: var(--app-primary-color) !important;
|
||
background-color: var(--app-primary-color-light) !important;
|
||
border: 1px solid var(--app-primary-color) !important;
|
||
border-radius: 50% !important;
|
||
width: 28px !important;
|
||
height: 28px !important;
|
||
min-width: 28px !important;
|
||
max-width: 28px !important;
|
||
min-height: 28px !important;
|
||
max-height: 28px !important;
|
||
padding: 0 !important;
|
||
line-height: 28px !important;
|
||
}
|
||
|
||
:deep(.action-icon-btn.add-btn:hover) {
|
||
color: #ffffff !important;
|
||
background-color: var(--app-primary-color) !important;
|
||
border-color: var(--app-primary-color) !important;
|
||
}
|
||
}
|
||
|
||
.mapping-tip {
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.mapping-search {
|
||
margin-bottom: 20px;
|
||
padding: 16px;
|
||
background-color: #fafafa;
|
||
border-radius: 6px;
|
||
border: 1px solid #e8e8e8;
|
||
}
|
||
|
||
.mapping-cascader {
|
||
border: 1px solid #e8e8e8;
|
||
border-radius: 6px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.cascader-header {
|
||
padding: 12px 16px;
|
||
background-color: #f5f5f5;
|
||
border-bottom: 1px solid #e8e8e8;
|
||
|
||
h4 {
|
||
margin: 0 0 4px 0;
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
color: #262626;
|
||
}
|
||
|
||
.cascader-desc {
|
||
font-size: 12px;
|
||
color: #8c8c8c;
|
||
}
|
||
}
|
||
|
||
.cascader-content {
|
||
min-height: 300px;
|
||
max-height: 400px;
|
||
overflow-y: auto;
|
||
}
|
||
|
||
.cascader-empty {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
height: 200px;
|
||
|
||
:deep(.arco-empty) {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
}
|
||
|
||
.span-style1 {
|
||
font-size: 17px;
|
||
font-weight: 700;
|
||
color: #222;
|
||
}
|
||
|
||
.query-box {
|
||
padding: 12px 16px;
|
||
border-radius: 2px;
|
||
}
|
||
|
||
.table-box {
|
||
padding: 0 16px;
|
||
border-radius: 2px;
|
||
}
|
||
|
||
:deep(.vxe-table--header) {
|
||
background-color: #f2f3f5;
|
||
font-weight: bold;
|
||
display: table-header-group !important;
|
||
}
|
||
|
||
// :deep(.vxe-header--column) {
|
||
// height: 48px;
|
||
// padding: 8px;
|
||
// }
|
||
|
||
:deep(.mapping-modal) {
|
||
.arco-modal-body {
|
||
height: 1500px !important;
|
||
max-height: 1500px !important;
|
||
padding: 0 !important;
|
||
}
|
||
|
||
.arco-modal {
|
||
border-radius: 8px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.arco-modal-header {
|
||
padding: 16px 20px;
|
||
border-bottom: 1px solid #e8e8e8;
|
||
}
|
||
|
||
.arco-modal-footer {
|
||
padding: 12px 20px;
|
||
border-top: 1px solid #e8e8e8;
|
||
}
|
||
}
|
||
|
||
/* toolbar spacing and control sizes */
|
||
:deep(.toolbarBtn .arco-btn) {
|
||
height: 28px;
|
||
line-height: 26px;
|
||
padding: 0 10px;
|
||
margin-left: 2px;
|
||
}
|
||
|
||
/* remove outer rectangular wrapper of search (form inline) */
|
||
:deep(.arco-form-item-label-col) {
|
||
display: none !important;
|
||
}
|
||
:deep(.arco-form-item) { margin: 0 8px 0 0; }
|
||
:deep(.arco-input-wrapper) {
|
||
height: 28px;
|
||
}
|
||
:deep(.arco-form-item-content-wrapper) {
|
||
padding-right: 0 !important;
|
||
padding-left: 0 !important;
|
||
margin: 0 !important;
|
||
background: transparent !important;
|
||
border: none !important;
|
||
box-shadow: none !important;
|
||
outline: none !important;
|
||
}
|
||
:deep(.arco-form-item-content) { margin-right: 0; }
|
||
|
||
/* kill double-layer look: remove any outer box/background around inline form items */
|
||
:deep(.arco-form-inline) {
|
||
background: transparent !important;
|
||
border: 0 !important;
|
||
}
|
||
:deep(.vxe-toolbar .arco-form) {
|
||
background: transparent !important;
|
||
border: none !important;
|
||
box-shadow: none !important;
|
||
}
|
||
/* override inline paddings/margins added by arco row */
|
||
:deep(.vxe-toolbar .arco-row[style]) {
|
||
padding-left: 0 !important;
|
||
padding-right: 0 !important;
|
||
margin-left: 0 !important;
|
||
margin-right: 0 !important;
|
||
}
|
||
:deep(.vxe-toolbar .arco-form[style]) {
|
||
padding-left: 0 !important;
|
||
padding-right: 0 !important;
|
||
margin-left: 0 !important;
|
||
margin-right: 0 !important;
|
||
border: none !important;
|
||
box-shadow: none !important;
|
||
background: transparent !important;
|
||
}
|
||
/* remove the extra rounded rectangle on form item content */
|
||
:deep(.vxe-toolbar .arco-form-item-content),
|
||
:deep(.vxe-toolbar .arco-form-item-content-flex) {
|
||
background: transparent !important;
|
||
border: none !important;
|
||
box-shadow: none !important;
|
||
outline: none !important;
|
||
}
|
||
:deep(.arco-form-inline .arco-form-item) {
|
||
background: transparent !important;
|
||
border: 0 !important;
|
||
padding: 0 !important;
|
||
}
|
||
:deep(.arco-form-item-content) {
|
||
background: transparent !important;
|
||
border: none !important;
|
||
box-shadow: none !important;
|
||
}
|
||
/* remove input focus halo so only一个边框可见 */
|
||
:deep(.arco-input-wrapper.arco-input-focus),
|
||
:deep(.arco-input-wrapper:hover) {
|
||
box-shadow: none !important;
|
||
}
|
||
:deep(.arco-input) { outline: none !important; }
|
||
|
||
.modal-footer {
|
||
margin-top: 8px;
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
padding: 6px 0 0 0;
|
||
gap: var(--app-button-gap, 8px);
|
||
}
|
||
|
||
</style>
|