feat: 服务商公共参数配置并回落到接口明细
All checks were successful
Build and Deploy Front / deploy (push) Successful in 6m38s

新增服务商级公共参数入口与弹窗,按位置只读落入 Query/Header/Body,并完善授权入参与调试页按钮样式。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
zzh 2026-09-15 19:50:51 +08:00
parent a0fea7d001
commit a5ef7e542d
9 changed files with 820 additions and 124 deletions

View File

@ -1955,7 +1955,6 @@
</a-button> </a-button>
<a-button <a-button
size="small" size="small"
type="primary"
:loading="isLoading" :loading="isLoading"
:disabled="isLoading" :disabled="isLoading"
@click="sendRequest" @click="sendRequest"

View File

@ -416,7 +416,7 @@
> >
<a-dropdown <a-dropdown
trigger="click" trigger="click"
:popup-max-height="250" :popup-max-height="false"
@select="handleActionSelect" @select="handleActionSelect"
> >
<a-button <a-button
@ -460,7 +460,7 @@
</span> </span>
<a-dropdown <a-dropdown
trigger="click" trigger="click"
:popup-max-height="250" :popup-max-height="false"
@select="handleActionSelect" @select="handleActionSelect"
> >
<a-button <a-button

View File

@ -25,7 +25,7 @@
buildSmartFillResult, buildSmartFillResult,
} from '@/utils/curlToInterfaceFill'; } from '@/utils/curlToInterfaceFill';
import { useTabRowCounts } from '@/utils/useTabRowCounts'; import { useTabRowCounts } from '@/utils/useTabRowCounts';
import { FormKvPlaceholderOptions, ParameterType } from '@/constants/itfxEnums'; import { FormKvPlaceholderOptions, ParameterType, FieldPosition } from '@/constants/itfxEnums';
import { import {
flattenTreeTableRows, flattenTreeTableRows,
getTreeTableFlatRows, getTreeTableFlatRows,
@ -440,6 +440,7 @@
function validateKvParamRows(rows: any[], label: string): string | null { function validateKvParamRows(rows: any[], label: string): string | null {
const seen = new Set<string>(); const seen = new Set<string>();
for (const row of rows || []) { for (const row of rows || []) {
if (isCommonParamRow(row)) continue;
const name = String(row?.name ?? '').trim(); const name = String(row?.name ?? '').trim();
if (!name) return `${label} 存在未填写参数名的行,请补全或删除空行`; if (!name) return `${label} 存在未填写参数名的行,请补全或删除空行`;
const key = name.toLowerCase(); const key = name.toLowerCase();
@ -583,6 +584,138 @@
() => props.showQuery && !isDownstreamMode.value, () => props.showQuery && !isDownstreamMode.value,
); );
const showHeaderTab = computed(() => isDownstreamMode.value && props.showQuery); const showHeaderTab = computed(() => isDownstreamMode.value && props.showQuery);
/** 下游:服务商公共参数按位置回落到 Query/Header/Body(只读置灰) */
const inheritedCommonParams = ref<any[]>([]);
const inheritedCommonParamsLoading = ref(false);
function normalizePositionList(row: any): number[] {
const list = row?.fieldPositionList;
if (Array.isArray(list) && list.length) {
return list.map((x: any) => Number(x)).filter((n: number) => Number.isFinite(n));
}
const fp = row?.fieldPositions;
if (typeof fp === 'string' && fp.trim()) {
return fp
.split(',')
.map((x) => Number(x.trim()))
.filter((n) => Number.isFinite(n));
}
if (Array.isArray(fp)) {
return fp.map((x: any) => Number(x)).filter((n: number) => Number.isFinite(n));
}
return [];
}
function commonParamsForPosition(position: number) {
return inheritedCommonParams.value.filter((row) =>
normalizePositionList(row).includes(position),
);
}
const inheritedQueryParams = computed(() =>
isDownstreamMode.value ? commonParamsForPosition(FieldPosition.Query) : [],
);
const inheritedHeaderParams = computed(() =>
isDownstreamMode.value ? commonParamsForPosition(FieldPosition.Header) : [],
);
const inheritedBodyParams = computed(() =>
isDownstreamMode.value ? commonParamsForPosition(FieldPosition.Body) : [],
);
function isCommonParamRow(row: any) {
return !!row?.__fromCommonParams;
}
function stripCommonRows<T extends Record<string, any>>(rows: T[] | undefined | null): T[] {
return (Array.isArray(rows) ? rows : []).filter((r) => !isCommonParamRow(r));
}
function filterCommonFromActionRows(actions: any) {
const filterRows = (arr: any[]) =>
(Array.isArray(arr) ? arr : []).filter((r) => !isCommonParamRow(r));
return {
insertRecords: filterRows(actions?.insertRecords),
removeRecords: filterRows(actions?.removeRecords),
updateRecords: filterRows(actions?.updateRecords),
};
}
function toCommonKvRows(list: any[]) {
return (list || []).map((row, idx) => ({
__fromCommonParams: true,
disableEdit: true,
id: `common-kv-${row.id ?? idx}`,
name: row.fieldName || '',
value: row.fieldValue || '',
description: row.description || '公共参数',
__commonParamId: row.id,
}));
}
function mergeCommonKvRows(ownRows: any[] | undefined, commonList: any[]) {
return [...toCommonKvRows(commonList), ...stripCommonRows(ownRows)];
}
async function applyKvTableWithCommon(
tableProps: { data: any[] },
tableRef: { value?: { reloadData?: (rows: any[]) => any } },
ownRows: any[] | undefined,
commonList: any[],
) {
const next = mergeCommonKvRows(ownRows, commonList);
tableProps.data = next;
await nextTick();
tableRef.value?.reloadData?.(next);
}
async function remergeInheritedCommonIntoKvTables() {
if (!isDownstreamMode.value) return;
await applyKvTableWithCommon(
queryTableProps,
queryTableRef,
stripCommonRows(queryTableProps.data),
inheritedQueryParams.value,
);
if (showHeaderTab.value) {
await applyKvTableWithCommon(
headerTableProps,
headerTableRef,
stripCommonRows(headerTableProps.data),
inheritedHeaderParams.value,
);
}
await applyKvTableWithCommon(
bodyFormTableProps,
bodyFormTableRef,
stripCommonRows(bodyFormTableProps.data),
inheritedBodyParams.value,
);
refreshTabCounts();
}
async function refreshInheritedCommonParams() {
const spId = Number(props.serviceProviderId);
if (!isDownstreamMode.value || !Number.isFinite(spId) || spId <= 0) {
inheritedCommonParams.value = [];
await remergeInheritedCommonIntoKvTables();
return;
}
inheritedCommonParamsLoading.value = true;
try {
const res: any = await getFixedParameterGetFixedParameterList({
serviceProviderId: spId,
});
const data = res?.data || res || [];
inheritedCommonParams.value = Array.isArray(data) ? data : [];
} catch {
inheritedCommonParams.value = [];
} finally {
inheritedCommonParamsLoading.value = false;
}
await remergeInheritedCommonIntoKvTables();
}
const isNoneBodyContentType = computed( const isNoneBodyContentType = computed(
() => isDownstreamMode.value && isNoneContentType(props.basicFormData?.contentType), () => isDownstreamMode.value && isNoneContentType(props.basicFormData?.contentType),
); );
@ -992,7 +1125,9 @@
mode: 'row', mode: 'row',
showStatus: true, showStatus: true,
autoClear: false, autoClear: false,
beforeEditMethod: ({ row }: { row: any }) => !isCommonParamRow(row),
}, },
rowClassName: ({ row }: { row: any }) => (isCommonParamRow(row) ? 'row-common-param' : ''),
}, },
showIndex: false, showIndex: false,
data: [] as any[], data: [] as any[],
@ -1017,6 +1152,14 @@
data: [] as any[], data: [] as any[],
}); });
watch(
() => [props.serviceProviderId, props.parameterMode] as const,
() => {
refreshInheritedCommonParams();
},
{ immediate: true },
);
// Query 固定配置表格(不传 height,随内容自然增高,与 Query 列表的固定高度区分) // Query 固定配置表格(不传 height,随内容自然增高,与 Query 列表的固定高度区分)
const queryFixedTableProps = computed(() => ({ const queryFixedTableProps = computed(() => ({
columns: [ columns: [
@ -1327,11 +1470,17 @@
const canClearCurrentRequestTab = computed(() => { const canClearCurrentRequestTab = computed(() => {
if (!showRequestTabClearAction.value) return false; if (!showRequestTabClearAction.value) return false;
if (requestTab.value === 'query') return queryTabCount.value > 0; if (requestTab.value === 'query') {
if (requestTab.value === 'header') return headerTabCount.value > 0; return stripCommonRows(getTableFullData(queryTableRef, queryTableProps.data)).length > 0;
}
if (requestTab.value === 'header') {
return stripCommonRows(getTableFullData(headerTableRef, headerTableProps.data)).length > 0;
}
if (requestTab.value === 'fixed') return fixedConfigTabCount.value > 0; if (requestTab.value === 'fixed') return fixedConfigTabCount.value > 0;
if (requestTab.value === 'body') { if (requestTab.value === 'body') {
if (isBodyKvMode.value) return !isBodyKvEmpty.value; if (isBodyKvMode.value) {
return stripCommonRows(getTableFullData(bodyFormTableRef, bodyFormTableProps.data)).length > 0;
}
return !isBodyTreeEmpty.value; return !isBodyTreeEmpty.value;
} }
if (requestTab.value === 'mappingBody') return !isMappingBodyEmpty.value; if (requestTab.value === 'mappingBody') return !isMappingBodyEmpty.value;
@ -1339,48 +1488,65 @@
return false; return false;
}); });
// 监听 queryParams prop 变化,同步到表格数据 // 监听 queryParams prop 变化,同步到表格数据(并合并公共参数)
watch( watch(
() => props.queryParams, () => props.queryParams,
(newVal: any) => { async (newVal: any) => {
if (newVal && Array.isArray(newVal)) { if (!newVal || !Array.isArray(newVal)) return;
// 只在数据真正变化时才更新,避免循环 const own = stripCommonRows(newVal);
const currentData = queryTableProps.data || []; const currentOwn = stripCommonRows(queryTableProps.data || []);
if (currentData.length !== newVal.length || JSON.stringify(currentData) !== JSON.stringify(newVal)) { if (
queryTableProps.data = [...newVal] as any[]; currentOwn.length !== own.length
if (queryTableRef.value) { || JSON.stringify(currentOwn) !== JSON.stringify(own)
queryTableRef.value.reloadData([...newVal]); ) {
} await applyKvTableWithCommon(
} queryTableProps,
queryTableRef,
own,
inheritedQueryParams.value,
);
} }
}, },
{ immediate: true } { immediate: true },
); );
function syncKvTableProps( function syncKvTableProps(
propList: any[] | undefined, propList: any[] | undefined,
tableProps: { data: any[] }, tableProps: { data: any[] },
tableRef: typeof queryTableRef, tableRef: typeof queryTableRef,
commonList: any[],
) { ) {
if (!propList || !Array.isArray(propList)) return; if (!propList || !Array.isArray(propList)) return;
const currentData = tableProps.data || []; const own = stripCommonRows(propList);
const currentOwn = stripCommonRows(tableProps.data || []);
if ( if (
currentData.length !== propList.length currentOwn.length !== own.length
|| JSON.stringify(currentData) !== JSON.stringify(propList) || JSON.stringify(currentOwn) !== JSON.stringify(own)
) { ) {
tableProps.data = [...propList] as any[]; applyKvTableWithCommon(tableProps, tableRef, own, commonList);
tableRef.value?.reloadData?.([...propList]);
} }
} }
watch( watch(
() => props.headerParams, () => props.headerParams,
(newVal) => syncKvTableProps(newVal as any[], headerTableProps, headerTableRef), (newVal) =>
syncKvTableProps(
newVal as any[],
headerTableProps,
headerTableRef,
inheritedHeaderParams.value,
),
{ immediate: true }, { immediate: true },
); );
watch( watch(
() => props.bodyFormParams, () => props.bodyFormParams,
(newVal) => syncKvTableProps(newVal as any[], bodyFormTableProps, bodyFormTableRef), (newVal) =>
syncKvTableProps(
newVal as any[],
bodyFormTableProps,
bodyFormTableRef,
inheritedBodyParams.value,
),
{ immediate: true }, { immediate: true },
); );
watch( watch(
@ -1839,13 +2005,13 @@
tableRef: any, tableRef: any,
keepRow?: (row: any) => boolean, keepRow?: (row: any) => boolean,
) { ) {
const roots = [...(getTableFullData(tableRef) || [])]; const roots = [...(getTableFullData(tableRef) || [])].filter((r) => !isCommonParamRow(r));
if (!keepRow) return roots; if (!keepRow) return roots;
const toRemove: any[] = []; const toRemove: any[] = [];
roots.forEach((row) => { roots.forEach((row) => {
if (keepRow(row)) { if (keepRow(row)) {
if (Array.isArray(row?.children) && row.children.length) { if (Array.isArray(row?.children) && row.children.length) {
toRemove.push(...row.children); toRemove.push(...row.children.filter((c: any) => !isCommonParamRow(c)));
} }
return; return;
} }
@ -1858,17 +2024,32 @@
const tab = requestTab.value; const tab = requestTab.value;
if (tab === 'query') { if (tab === 'query') {
await removeTableRows(queryTableRef, collectRemovableTableRows(queryTableRef)); await removeTableRows(queryTableRef, collectRemovableTableRows(queryTableRef));
queryTableProps.data = []; await applyKvTableWithCommon(
queryTableProps,
queryTableRef,
[],
inheritedQueryParams.value,
);
} else if (tab === 'header') { } else if (tab === 'header') {
await removeTableRows(headerTableRef, collectRemovableTableRows(headerTableRef)); await removeTableRows(headerTableRef, collectRemovableTableRows(headerTableRef));
headerTableProps.data = []; await applyKvTableWithCommon(
headerTableProps,
headerTableRef,
[],
inheritedHeaderParams.value,
);
} else if (tab === 'fixed') { } else if (tab === 'fixed') {
await removeTableRows(queryFixedTableRef, collectRemovableTableRows(queryFixedTableRef)); await removeTableRows(queryFixedTableRef, collectRemovableTableRows(queryFixedTableRef));
fixedParams.value = []; fixedParams.value = [];
fixedParamsHasChanges.value = true; fixedParamsHasChanges.value = true;
} else if (tab === 'body' && isBodyKvMode.value) { } else if (tab === 'body' && isBodyKvMode.value) {
await removeTableRows(bodyFormTableRef, collectRemovableTableRows(bodyFormTableRef)); await removeTableRows(bodyFormTableRef, collectRemovableTableRows(bodyFormTableRef));
bodyFormTableProps.data = []; await applyKvTableWithCommon(
bodyFormTableProps,
bodyFormTableRef,
[],
inheritedBodyParams.value,
);
} else if (tab === 'body' && !isNoneBodyContentType.value) { } else if (tab === 'body' && !isNoneBodyContentType.value) {
if (hoistedBodyArrayWrapper.value) { if (hoistedBodyArrayWrapper.value) {
const persisted = getRequestBodyFlatParams(); const persisted = getRequestBodyFlatParams();
@ -1941,6 +2122,7 @@
// 删除 Query 参数行 // 删除 Query 参数行
async function deleteQueryRow(row: any) { async function deleteQueryRow(row: any) {
if (isCommonParamRow(row)) return;
markDirty(); markDirty();
await queryTableRef.value?.removeRow(row); await queryTableRef.value?.removeRow(row);
refreshTabCounts(); refreshTabCounts();
@ -1955,6 +2137,7 @@
} }
async function deleteHeaderRow(row: any) { async function deleteHeaderRow(row: any) {
if (isCommonParamRow(row)) return;
markDirty(); markDirty();
await headerTableRef.value?.removeRow(row); await headerTableRef.value?.removeRow(row);
refreshTabCounts(); refreshTabCounts();
@ -1969,6 +2152,7 @@
} }
async function deleteBodyFormRow(row: any) { async function deleteBodyFormRow(row: any) {
if (isCommonParamRow(row)) return;
markDirty(); markDirty();
await bodyFormTableRef.value?.removeRow(row); await bodyFormTableRef.value?.removeRow(row);
refreshTabCounts(); refreshTabCounts();
@ -2922,10 +3106,9 @@
tableProps: { data: any[] }, tableProps: { data: any[] },
tableRef: { value?: { reloadData?: (rows: any[]) => void } }, tableRef: { value?: { reloadData?: (rows: any[]) => void } },
rows: any[], rows: any[],
commonList: any[] = [],
) { ) {
tableProps.data = rows; await applyKvTableWithCommon(tableProps, tableRef, rows, commonList);
await nextTick();
tableRef.value?.reloadData?.(rows);
} }
async function loadFlatListIntoTreeTable( async function loadFlatListIntoTreeTable(
@ -3022,11 +3205,26 @@
await nextTick(); await nextTick();
refBasicForm.value?.setFormValue?.(nextBasic); refBasicForm.value?.setFormValue?.(nextBasic);
await reloadKvTable(queryTableProps, queryTableRef, payload.queryParams); await reloadKvTable(
queryTableProps,
queryTableRef,
payload.queryParams,
inheritedQueryParams.value,
);
if (isDownstreamMode.value) { if (isDownstreamMode.value) {
await reloadKvTable(headerTableProps, headerTableRef, payload.headerParams ?? []); await reloadKvTable(
await reloadKvTable(bodyFormTableProps, bodyFormTableRef, isForm ? payload.bodyFormParams : []); headerTableProps,
headerTableRef,
payload.headerParams ?? [],
inheritedHeaderParams.value,
);
await reloadKvTable(
bodyFormTableProps,
bodyFormTableRef,
isForm ? payload.bodyFormParams : [],
inheritedBodyParams.value,
);
if (!isForm && !isNone) { if (!isForm && !isNone) {
mappingBodyTableProps.data = []; mappingBodyTableProps.data = [];
await nextTick(); await nextTick();
@ -3197,20 +3395,27 @@
validateDetailParamsBeforeSave, validateDetailParamsBeforeSave,
captureDirtyBaseline, captureDirtyBaseline,
markDirty, markDirty,
refreshInheritedCommonParams,
isDirty: () => isDirty.value, isDirty: () => isDirty.value,
getSaveSnapshot: (): InterfaceDetailSnapshot => ({ getSaveSnapshot: (): InterfaceDetailSnapshot => ({
queryParams: queryTableRef.value?.getTableData?.()?.fullData queryParams: stripCommonRows(
queryTableRef.value?.getTableData?.()?.fullData
|| queryTableRef.value?.xGrid?.getTableData()?.fullData || queryTableRef.value?.xGrid?.getTableData()?.fullData
|| queryTableProps.data || queryTableProps.data
|| [], || [],
headerParams: headerTableRef.value?.getTableData?.()?.fullData ),
headerParams: stripCommonRows(
headerTableRef.value?.getTableData?.()?.fullData
|| headerTableRef.value?.xGrid?.getTableData()?.fullData || headerTableRef.value?.xGrid?.getTableData()?.fullData
|| headerTableProps.data || headerTableProps.data
|| [], || [],
bodyFormParams: bodyFormTableRef.value?.getTableData?.()?.fullData ),
bodyFormParams: stripCommonRows(
bodyFormTableRef.value?.getTableData?.()?.fullData
|| bodyFormTableRef.value?.xGrid?.getTableData()?.fullData || bodyFormTableRef.value?.xGrid?.getTableData()?.fullData
|| bodyFormTableProps.data || bodyFormTableProps.data
|| [], || [],
),
requestParams: getRequestBodyFlatParams(), requestParams: getRequestBodyFlatParams(),
mappingBodyParams: getTreeTableFlatRows( mappingBodyParams: getTreeTableFlatRows(
mappingBodyTableRef.value?.getTableData?.()?.fullData mappingBodyTableRef.value?.getTableData?.()?.fullData
@ -3221,9 +3426,9 @@
|| responseTableRef.value?.xGrid?.getTableData()?.fullData || responseTableRef.value?.xGrid?.getTableData()?.fullData
|| responseTableProps.data || responseTableProps.data
|| [], || [],
queryActionRows: queryTableRef.value?.getActionRows?.() || { insertRecords: [], removeRecords: [], updateRecords: [] }, queryActionRows: filterCommonFromActionRows(queryTableRef.value?.getActionRows?.()),
headerActionRows: headerTableRef.value?.getActionRows?.() || { insertRecords: [], removeRecords: [], updateRecords: [] }, headerActionRows: filterCommonFromActionRows(headerTableRef.value?.getActionRows?.()),
bodyFormActionRows: bodyFormTableRef.value?.getActionRows?.() || { insertRecords: [], removeRecords: [], updateRecords: [] }, bodyFormActionRows: filterCommonFromActionRows(bodyFormTableRef.value?.getActionRows?.()),
requestActionRows: stripBodyRootFromActionRows( requestActionRows: stripBodyRootFromActionRows(
requestTableRef.value?.getActionRows?.() || { requestTableRef.value?.getActionRows?.() || {
insertRecords: [], insertRecords: [],
@ -3472,7 +3677,10 @@
placeholder="请输入参数名" placeholder="请输入参数名"
/> />
</template> </template>
<span v-else>{{ row.name || '-' }}</span> <span v-else class="kv-name-cell">
{{ row.name || '-' }}
<span v-if="isCommonParamRow(row)" class="common-param-tag">公共</span>
</span>
</template> </template>
<template #name_edit="{ row }"> <template #name_edit="{ row }">
@ -3540,13 +3748,14 @@
</template> </template>
<template #action="{ row }"> <template #action="{ row }">
<a-space> <a-space v-if="!isCommonParamRow(row)">
<a-popconfirm content="确定删除该参数?" @ok="deleteQueryRow(row)"> <a-popconfirm content="确定删除该参数?" @ok="deleteQueryRow(row)">
<a-button type="text" class="row-action-btn"> <a-button type="text" class="row-action-btn">
<IconDelete /> <IconDelete />
</a-button> </a-button>
</a-popconfirm> </a-popconfirm>
</a-space> </a-space>
<span v-else class="common-param-action">—</span>
</template> </template>
</TablePro> </TablePro>
</div> </div>
@ -3640,7 +3849,10 @@
> >
<template #name="{ row }"> <template #name="{ row }">
<a-input v-if="isHeaderRowEditing(row)" v-model="row.name" allow-clear placeholder="请输入参数名" /> <a-input v-if="isHeaderRowEditing(row)" v-model="row.name" allow-clear placeholder="请输入参数名" />
<span v-else>{{ row.name || '-' }}</span> <span v-else class="kv-name-cell">
{{ row.name || '-' }}
<span v-if="isCommonParamRow(row)" class="common-param-tag">公共</span>
</span>
</template> </template>
<template #name_edit="{ row }"> <template #name_edit="{ row }">
<a-input v-model="row.name" allow-clear placeholder="请输入参数名" /> <a-input v-model="row.name" allow-clear placeholder="请输入参数名" />
@ -3671,11 +3883,16 @@
<a-input v-model="row.description" allow-clear placeholder="请输入描述" /> <a-input v-model="row.description" allow-clear placeholder="请输入描述" />
</template> </template>
<template #action="{ row }"> <template #action="{ row }">
<a-popconfirm content="确定删除该参数?" @ok="deleteHeaderRow(row)"> <a-popconfirm
v-if="!isCommonParamRow(row)"
content="确定删除该参数?"
@ok="deleteHeaderRow(row)"
>
<a-button type="text" class="row-action-btn"> <a-button type="text" class="row-action-btn">
<IconDelete /> <IconDelete />
</a-button> </a-button>
</a-popconfirm> </a-popconfirm>
<span v-else class="common-param-action">—</span>
</template> </template>
</TablePro> </TablePro>
</div> </div>
@ -3687,7 +3904,7 @@
</div> </div>
<!-- Body KV(FormUrlEncoded) --> <!-- Body KV(FormUrlEncoded) -->
<div v-show="requestTab === 'body' && isBodyKvMode" class="table-content"> <div v-show="requestTab === 'body' && isBodyKvMode" class="table-content body-kv-pane">
<TablePro <TablePro
ref="bodyFormTableRef" ref="bodyFormTableRef"
v-bind="bodyFormTableProps" v-bind="bodyFormTableProps"
@ -3696,7 +3913,10 @@
> >
<template #name="{ row }"> <template #name="{ row }">
<a-input v-if="isBodyFormRowEditing(row)" v-model="row.name" allow-clear placeholder="参数名" /> <a-input v-if="isBodyFormRowEditing(row)" v-model="row.name" allow-clear placeholder="参数名" />
<span v-else>{{ row.name || '-' }}</span> <span v-else class="kv-name-cell">
{{ row.name || '-' }}
<span v-if="isCommonParamRow(row)" class="common-param-tag">公共</span>
</span>
</template> </template>
<template #name_edit="{ row }"> <template #name_edit="{ row }">
<a-input v-model="row.name" allow-clear placeholder="参数名" /> <a-input v-model="row.name" allow-clear placeholder="参数名" />
@ -3727,11 +3947,16 @@
<a-input v-model="row.description" allow-clear /> <a-input v-model="row.description" allow-clear />
</template> </template>
<template #action="{ row }"> <template #action="{ row }">
<a-popconfirm content="确定删除该参数?" @ok="deleteBodyFormRow(row)"> <a-popconfirm
v-if="!isCommonParamRow(row)"
content="确定删除该参数?"
@ok="deleteBodyFormRow(row)"
>
<a-button type="text" class="row-action-btn"> <a-button type="text" class="row-action-btn">
<IconDelete /> <IconDelete />
</a-button> </a-button>
</a-popconfirm> </a-popconfirm>
<span v-else class="common-param-action">—</span>
</template> </template>
</TablePro> </TablePro>
</div> </div>
@ -4597,6 +4822,46 @@
} }
} }
.kv-name-cell {
display: inline-flex;
align-items: center;
gap: 6px;
max-width: 100%;
}
.common-param-tag {
flex-shrink: 0;
padding: 0 5px;
height: 18px;
line-height: 18px;
border-radius: 3px;
font-size: 11px;
color: var(--color-text-3);
background: var(--color-fill-2);
border: 1px solid var(--color-border-2);
}
.common-param-action {
color: var(--color-text-4);
font-size: 12px;
}
:deep(.row-common-param) {
color: var(--color-text-3) !important;
background: var(--color-fill-1) !important;
td {
color: var(--color-text-3) !important;
background: var(--color-fill-1) !important;
}
}
.body-kv-pane {
display: flex;
flex-direction: column;
min-height: 0;
}
// 请求方式 + 请求地址 - 符合设计规范 // 请求方式 + 请求地址 - 符合设计规范
.api-test-header { .api-test-header {
display: flex; display: flex;

View File

@ -2514,12 +2514,12 @@ export default {
'system.serviceProviders.dropdown.authConfig': 'Auth config', 'system.serviceProviders.dropdown.authConfig': 'Auth config',
'system.serviceProviders.dropdown.accountManage': 'Account management', 'system.serviceProviders.dropdown.accountManage': 'Account management',
'system.serviceProviders.dropdown.export': 'Export', 'system.serviceProviders.dropdown.export': 'Export',
'system.serviceProviders.dropdown.fixedParams': 'Common parameters', 'system.serviceProviders.dropdown.fixedParams': 'Common params',
'system.serviceProviders.dropdown.delete': 'Delete', 'system.serviceProviders.dropdown.delete': 'Delete',
'system.serviceProviders.publish.title': 'Publish mapping cache · {name}', 'system.serviceProviders.publish.title': 'Publish mapping cache · {name}',
'system.serviceProviders.publish.tip': 'After auth/provider changes are saved, remapped system interfaces must be republished to refresh the live cache.', 'system.serviceProviders.publish.tip': 'After auth, common params, or provider changes are saved, remapped system interfaces must be republished to refresh the live cache.',
'system.serviceProviders.publish.confirmAfterSave': 'Saved. Check and publish mapping cache for this provider now?', 'system.serviceProviders.publish.confirmAfterSave': 'Saved. Common params / config take effect online only after publishing mapping cache. Check and publish now?',
'system.serviceProviders.publish.goPublish': 'Publish', 'system.serviceProviders.publish.goPublish': 'Publish',
'system.serviceProviders.publish.later': 'Later', 'system.serviceProviders.publish.later': 'Later',
'system.serviceProviders.publish.close': 'Close', 'system.serviceProviders.publish.close': 'Close',

View File

@ -2412,12 +2412,12 @@ export default {
'system.serviceProviders.dropdown.authConfig': '授权配置', 'system.serviceProviders.dropdown.authConfig': '授权配置',
'system.serviceProviders.dropdown.accountManage': '账户管理', 'system.serviceProviders.dropdown.accountManage': '账户管理',
'system.serviceProviders.dropdown.export': '导出', 'system.serviceProviders.dropdown.export': '导出',
'system.serviceProviders.dropdown.fixedParams': '公共参数配置', 'system.serviceProviders.dropdown.fixedParams': '公共参数',
'system.serviceProviders.dropdown.delete': '删除', 'system.serviceProviders.dropdown.delete': '删除',
'system.serviceProviders.publish.title': '发布映射缓存 · {name}', 'system.serviceProviders.publish.title': '发布映射缓存 · {name}',
'system.serviceProviders.publish.tip': '授权配置、服务商信息等变更写入数据库后,需对已映射的系统接口重新发布,线上缓存才会更新。', 'system.serviceProviders.publish.tip': '授权配置、公共参数、服务商信息等变更写入数据库后,需对已映射的系统接口重新发布,线上缓存才会更新。',
'system.serviceProviders.publish.confirmAfterSave': '保存成功。是否立即检查并发布该服务商相关的映射缓存?', 'system.serviceProviders.publish.confirmAfterSave': '保存成功。公共参数/配置需发布映射缓存后才对线上生效,是否立即检查并发布?',
'system.serviceProviders.publish.goPublish': '去发布', 'system.serviceProviders.publish.goPublish': '去发布',
'system.serviceProviders.publish.later': '稍后', 'system.serviceProviders.publish.later': '稍后',
'system.serviceProviders.publish.close': '关闭', 'system.serviceProviders.publish.close': '关闭',

View File

@ -547,6 +547,8 @@
confirmLeaveUnsaved: (options?: { content?: string }) => confirmLeaveUnsaved: (options?: { content?: string }) =>
interfaceDetailRef.value?.confirmLeaveUnsaved?.(options) ?? Promise.resolve(true), interfaceDetailRef.value?.confirmLeaveUnsaved?.(options) ?? Promise.resolve(true),
captureDirtyBaseline: () => interfaceDetailRef.value?.captureDirtyBaseline?.(), captureDirtyBaseline: () => interfaceDetailRef.value?.captureDirtyBaseline?.(),
refreshInheritedCommonParams: () =>
interfaceDetailRef.value?.refreshInheritedCommonParams?.(),
}); });
</script> </script>

View File

@ -7,7 +7,6 @@
getServiceProviderChangeAuthInterface, getServiceProviderChangeAuthInterface,
getServiceProviderGetAuthInfo, getServiceProviderGetAuthInfo,
} from '@/api/ServiceProviderInterface/serviceProviderAuth'; } from '@/api/ServiceProviderInterface/serviceProviderAuth';
import { getDictionaryGetList } from '@/api/ApplicationServices/dictionary';
import { addModalForm, configData, interfaceId } from './data'; import { addModalForm, configData, interfaceId } from './data';
import { $baseConfirm } from '@/utils/confirm'; import { $baseConfirm } from '@/utils/confirm';
import FormPro from '@/components/FormPro/index.vue'; import FormPro from '@/components/FormPro/index.vue';
@ -234,66 +233,18 @@
key: number; key: number;
}; };
const dictionaryLoaded = ref(false); const restaurants = ref<RestaurantItem[]>([{ value: '$FromAccount', key: 1 }]);
const loadingDictionary = ref(false);
const restaurants = ref<RestaurantItem[]>([]);
async function loadDictionaryData() { function filterOption(inputValue: string, option: any) {
if (dictionaryLoaded.value || loadingDictionary.value) { return (
return; !inputValue ||
} String(option.value || '')
.toLowerCase()
loadingDictionary.value = true; .includes(String(inputValue).toLowerCase())
try { );
const response: any = await getDictionaryGetList({ type: 'ParameterPlaceholder' });
let data: API.EnumCacheEntry[] = [];
if (Array.isArray(response)) {
data = response;
} else if (response && Array.isArray(response.data)) {
data = response.data;
} else if (response && response.data) {
data = Array.isArray(response.data) ? response.data : [];
}
const options = data.map((item: any, index: number) => ({
value: String(item.value || ''),
key: item.key || index,
}));
const defaultOptions = [
{ value: '$FromAccount', key: 1 },
{ value: '$TimeStamp', key: 2 },
];
const allOptions = [...defaultOptions];
options.forEach((option: RestaurantItem) => {
if (!allOptions.find((item) => item.value === option.value)) {
allOptions.push(option);
}
});
restaurants.value = allOptions;
dictionaryLoaded.value = true;
} catch {
restaurants.value = [
{ value: '$FromAccount', key: 1 },
{ value: '$TimeStamp', key: 2 },
];
} finally {
loadingDictionary.value = false;
}
}
function loadAll() {
return [
{ value: '$FromAccount', key: 1 },
{ value: '$TimeStamp', key: 2 },
];
} }
onMounted(async () => { onMounted(async () => {
restaurants.value = loadAll();
if (configData.interfaceCode) { if (configData.interfaceCode) {
oldVal.value = configData.interfaceCode; oldVal.value = configData.interfaceCode;
await nextTick(); await nextTick();
@ -312,15 +263,6 @@
} }
}); });
function filterOption(inputValue: string, option: any) {
return (
!inputValue ||
String(option.value || '')
.toLowerCase()
.includes(String(inputValue).toLowerCase())
);
}
function deleteRow(row: any) { function deleteRow(row: any) {
tableRef.value.removeRow(row); tableRef.value.removeRow(row);
} }
@ -354,8 +296,8 @@
v-model="record.value" v-model="record.value"
:data="restaurants" :data="restaurants"
allow-clear allow-clear
placeholder="请选择或输入 $FromAccount"
:filter-option="filterOption" :filter-option="filterOption"
@focus="() => { if (!dictionaryLoaded && !loadingDictionary) loadDictionaryData(); }"
@select="(val) => (record.value = val)" @select="(val) => (record.value = val)"
@change="(val) => { record.value = val; }" @change="(val) => { record.value = val; }"
/> />

View File

@ -0,0 +1,406 @@
<script lang="ts" setup>
import { ref, computed, nextTick } from 'vue';
import { useI18n } from 'vue-i18n';
import useModal from '@/components/ModalPro/useModal';
import ModalPro from '@/components/ModalPro/index.vue';
import SelectPro from '@/components/SelectPro/index.vue';
import { getDictionaryGetList } from '@/api/ApplicationServices/dictionary';
import {
getFixedParameterGetFixedParameterList,
postFixedParameterMaintainParameter,
} from '@/api/ParameterServices/fixedParameter';
import { IconDelete, IconPlus } from '@arco-design/web-vue/es/icon';
import { $baseMessage } from '@/utils';
defineOptions({
name: 'FixedParameterConfig',
});
const emit = defineEmits<{
(e: 'saved', payload: { id: number; name?: string; code?: string }): void;
}>();
const { t } = useI18n();
const [register, { openModal, setModalProps }] = useModal() as any;
const serviceProviderId = ref(0);
const serviceProviderMeta = ref<{ id: number; name?: string; code?: string } | null>(null);
const loading = ref(false);
const saving = ref(false);
type FixedRow = {
__rowKey: string | number;
id?: number;
fieldName?: string;
fieldValue?: string;
fieldPositionList?: Array<number | string>;
description?: string;
};
const tableData = ref<FixedRow[]>([]);
const originalById = ref<Map<number, FixedRow>>(new Map());
let rowKeySeq = 0;
const placeholderOptions = ref<{ value: string; label: string }[]>([]);
const tableColumns = computed(() => [
{
title: '序号',
width: 64,
align: 'center' as const,
render: ({ rowIndex }: any) => rowIndex + 1,
},
{
title: '参数名',
dataIndex: 'fieldName',
slotName: 'fieldName',
minWidth: 140,
},
{
title: '参数值',
dataIndex: 'fieldValue',
slotName: 'fieldValue',
minWidth: 180,
},
{
title: '位置',
dataIndex: 'fieldPositionList',
slotName: 'fieldPositions',
minWidth: 180,
},
{
title: '描述',
dataIndex: 'description',
slotName: 'description',
minWidth: 140,
},
{
title: '操作',
slotName: 'action',
width: 72,
align: 'center' as const,
fixed: 'right' as const,
},
]);
async function loadDictionaries() {
try {
const phRes: any = await getDictionaryGetList({ type: 'ParameterPlaceholder' });
const phList = phRes?.data || phRes || [];
placeholderOptions.value = (Array.isArray(phList) ? phList : []).map((item: any) => ({
value: String(item.value ?? item.key ?? ''),
label: String(item.value ?? item.key ?? ''),
}));
} catch {
placeholderOptions.value = [];
}
}
function normalizeRow(item: any): FixedRow {
const id = item?.id != null ? Number(item.id) : undefined;
return {
__rowKey: id ?? `new_${++rowKeySeq}`,
id,
fieldName: item?.fieldName ?? '',
fieldValue: item?.fieldValue ?? '',
fieldPositionList: Array.isArray(item?.fieldPositionList)
? [...item.fieldPositionList]
: [],
description: item?.description ?? '',
};
}
async function loadList() {
if (!serviceProviderId.value) return;
loading.value = true;
try {
const res: any = await getFixedParameterGetFixedParameterList({
serviceProviderId: serviceProviderId.value,
});
const data = res?.data || res || [];
const list = (Array.isArray(data) ? data : []).map(normalizeRow);
tableData.value = list;
const map = new Map<number, FixedRow>();
list.forEach((row) => {
if (row.id != null) {
map.set(row.id, {
...row,
fieldPositionList: [...(row.fieldPositionList || [])],
});
}
});
originalById.value = map;
} catch {
tableData.value = [];
originalById.value = new Map();
$baseMessage('加载公共参数失败', 'error');
} finally {
loading.value = false;
}
}
async function init(input: number | { id: number; name?: string; code?: string }) {
const id = typeof input === 'number' ? input : Number(input?.id);
if (!Number.isFinite(id) || id <= 0) {
$baseMessage('请先选择服务商', 'warning');
return;
}
serviceProviderId.value = id;
serviceProviderMeta.value =
typeof input === 'number'
? { id, name: undefined, code: undefined }
: { id, name: input?.name, code: input?.code };
setModalProps?.({
title: `${t('system.serviceProviders.dropdown.fixedParams')}${
serviceProviderMeta.value?.name ? ` - ${serviceProviderMeta.value.name}` : ''
}`,
showFooter: true,
okText: '保存',
confirmLoading: false,
});
openModal();
await nextTick();
await loadDictionaries();
await loadList();
}
function handleAddRow() {
tableData.value.push({
__rowKey: `new_${++rowKeySeq}`,
fieldName: '',
fieldValue: '',
fieldPositionList: [],
description: '',
});
}
function handleDeleteRow(record: FixedRow) {
tableData.value = tableData.value.filter((r) => r.__rowKey !== record.__rowKey);
}
function positionsEqual(a?: Array<number | string>, b?: Array<number | string>) {
const aa = [...(a || [])].map(String).sort();
const bb = [...(b || [])].map(String).sort();
if (aa.length !== bb.length) return false;
return aa.every((v, i) => v === bb[i]);
}
function rowChanged(curr: FixedRow, orig: FixedRow) {
return (
String(curr.fieldName ?? '') !== String(orig.fieldName ?? '') ||
String(curr.fieldValue ?? '') !== String(orig.fieldValue ?? '') ||
String(curr.description ?? '') !== String(orig.description ?? '') ||
!positionsEqual(curr.fieldPositionList, orig.fieldPositionList)
);
}
function buildItems() {
const items: any[] = [];
const currentIds = new Set<number>();
for (const row of tableData.value) {
const name = String(row.fieldName ?? '').trim();
if (!name) continue;
const payload = {
fieldName: name,
fieldValue: row.fieldValue ?? '',
fieldPositionList: row.fieldPositionList || [],
description: row.description ?? '',
};
if (row.id == null) {
items.push({ ...payload, options: 1, id: undefined });
continue;
}
currentIds.add(row.id);
const orig = originalById.value.get(row.id);
if (!orig || rowChanged(row, orig)) {
items.push({ ...payload, options: 3, id: row.id });
}
}
for (const [id, orig] of originalById.value.entries()) {
if (!currentIds.has(id)) {
items.push({
id,
fieldName: orig.fieldName,
fieldValue: orig.fieldValue,
fieldPositionList: orig.fieldPositionList || [],
description: orig.description,
options: 2,
});
}
}
return items;
}
async function onModalSave() {
if (!serviceProviderId.value) return false;
const emptyName = tableData.value.some((r) => !String(r.fieldName ?? '').trim());
if (emptyName) {
$baseMessage('请填写参数名', 'warning');
return false;
}
const names = tableData.value.map((r) => String(r.fieldName ?? '').trim().toLowerCase());
if (new Set(names).size !== names.length) {
$baseMessage('参数名不能重复', 'warning');
return false;
}
const items = buildItems();
if (items.length === 0) {
$baseMessage('暂无变更需要保存', 'warning');
return false;
}
saving.value = true;
setModalProps?.({ confirmLoading: true });
try {
await postFixedParameterMaintainParameter({
serviceProviderId: serviceProviderId.value,
items,
});
$baseMessage('保存成功,如需生效请去发布', 'success');
emit('saved', {
id: Number(serviceProviderId.value),
name: serviceProviderMeta.value?.name,
code: serviceProviderMeta.value?.code,
});
await loadList();
return true;
} catch {
$baseMessage('保存失败', 'error');
return false;
} finally {
saving.value = false;
setModalProps?.({ confirmLoading: false });
}
}
defineExpose({ init });
</script>
<template>
<div class="fixed-parameter-config">
<ModalPro
:width="920"
:title="t('system.serviceProviders.dropdown.fixedParams')"
:loading="loading || saving"
@register="register"
@ok="onModalSave"
>
<div class="fixed-parameter-body">
<a-alert type="info" :show-icon="false" class="modal-form-tip">
配置当前服务商的公共参数,供其下接口共用。路径:修改 → 保存 → 发布映射缓存后才对线上生效。
</a-alert>
<div class="table-toolbar">
<div class="toolbar-left">
<a-button type="primary" class="app-btn-primary-soft" size="small" @click="handleAddRow">
<template #icon><IconPlus /></template>
新增
</a-button>
</div>
<div class="toolbar-right">
<span class="row-count">共 {{ tableData.length }} 条</span>
</div>
</div>
<a-table
size="small"
row-key="__rowKey"
:columns="tableColumns"
:data="tableData"
:loading="loading"
:pagination="false"
:bordered="{ cell: true }"
:scroll="{ y: 420 }"
class="fixed-parameter-table"
>
<template #empty>
<div class="empty-tip">暂无公共参数,点击「新增」添加</div>
</template>
<template #fieldName="{ record }">
<a-input v-model="record.fieldName" allow-clear placeholder="请输入参数名" />
</template>
<template #fieldValue="{ record }">
<a-auto-complete
v-model="record.fieldValue"
:data="placeholderOptions"
allow-clear
placeholder="请选择或输入参数值"
:filter-option="
(inputValue, option) =>
!inputValue ||
String(option.value || '')
.toLowerCase()
.includes(String(inputValue).toLowerCase())
"
/>
</template>
<template #fieldPositions="{ record }">
<SelectPro
v-model="record.fieldPositionList"
:api="getDictionaryGetList"
:extra="{ type: 'FieldPosition' }"
:is-page="false"
:immediate="true"
:label-keys="['value']"
:multiple="true"
id-key="key"
placeholder="请选择位置"
/>
</template>
<template #description="{ record }">
<a-input v-model="record.description" allow-clear placeholder="可选" />
</template>
<template #action="{ record }">
<a-button type="text" size="small" status="danger" title="删除" @click="handleDeleteRow(record)">
<template #icon><IconDelete /></template>
</a-button>
</template>
</a-table>
</div>
</ModalPro>
</div>
</template>
<style lang="less" scoped>
.fixed-parameter-body {
.modal-form-tip {
margin-bottom: 16px;
}
.table-toolbar {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 12px;
}
.row-count {
font-size: 13px;
color: var(--color-text-3);
}
.empty-tip {
padding: 24px 0;
color: var(--color-text-3);
font-size: 13px;
}
.fixed-parameter-table {
:deep(.arco-input-wrapper),
:deep(.arco-select-view),
:deep(.arco-auto-complete) {
width: 100%;
}
}
}
</style>

View File

@ -18,6 +18,7 @@
postServiceProviderImport, postServiceProviderImport,
} from '@/api/interface-serviceProvider/serviceProviderImportExport'; } from '@/api/interface-serviceProvider/serviceProviderImportExport';
import ParameterConfig from '@/views/api/downstreamInterface/comps/ParameterConfig.vue'; import ParameterConfig from '@/views/api/downstreamInterface/comps/ParameterConfig.vue';
import FixedParameterConfig from '@/views/api/downstreamInterface/comps/FixedParameterConfig.vue';
import FormPro from '@/components/FormPro/index.vue'; import FormPro from '@/components/FormPro/index.vue';
import TableButtons from '@/components/TableButtons/index.vue'; import TableButtons from '@/components/TableButtons/index.vue';
import { import {
@ -32,6 +33,7 @@
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { getDictionaryGetList } from '@/api/ApplicationServices/dictionary'; import { getDictionaryGetList } from '@/api/ApplicationServices/dictionary';
import { getAppGetDropDownList } from '@/api/ApplicationServices/apps'; import { getAppGetDropDownList } from '@/api/ApplicationServices/apps';
import { getFixedParameterGetFixedParameterList } from '@/api/ParameterServices/fixedParameter';
import ServiceProviderInterfaceModal from './components/ServiceProviderInterfaceModal.vue'; import ServiceProviderInterfaceModal from './components/ServiceProviderInterfaceModal.vue';
import AccountList from './comps/AccountList.vue'; import AccountList from './comps/AccountList.vue';
import ProviderMapPublishModal from './comps/ProviderMapPublishModal.vue'; import ProviderMapPublishModal from './comps/ProviderMapPublishModal.vue';
@ -48,6 +50,7 @@
IconRefresh, IconRefresh,
IconMore, IconMore,
IconDelete, IconDelete,
IconList,
} from '@arco-design/web-vue/es/icon'; } from '@arco-design/web-vue/es/icon';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
@ -111,6 +114,8 @@
const searchKeyword = ref(''); const searchKeyword = ref('');
const serviceProviderList = ref<any[]>([]); const serviceProviderList = ref<any[]>([]);
const selectedProvider = ref<any>(null); const selectedProvider = ref<any>(null);
const commonParamsCount = ref<number | null>(null);
const commonParamsLoading = ref(false);
const loading = ref(false); const loading = ref(false);
// 接口弹窗相关 // 接口弹窗相关
@ -960,11 +965,63 @@
} }
const accountList = ref(); const accountList = ref();
const fixedParameterConfigRef = ref<InstanceType<typeof FixedParameterConfig> | null>(null);
function accountManageClick(row: any) { function accountManageClick(row: any) {
accountList.value?.init?.(row); accountList.value?.init?.(row);
} }
function handleFixedParamsWithSelect(provider: any) {
handleProviderSelect(provider);
setTimeout(() => {
fixedParameterConfigRef.value?.init?.(provider);
}, 150);
}
async function refreshCommonParamsCount(providerLike?: any) {
const id = Number(providerLike?.id || selectedProvider.value?.id || 0);
if (!Number.isFinite(id) || id <= 0) {
commonParamsCount.value = null;
return;
}
commonParamsLoading.value = true;
try {
const res: any = await getFixedParameterGetFixedParameterList({
serviceProviderId: id,
});
const data = res?.data || res || [];
commonParamsCount.value = Array.isArray(data) ? data.length : 0;
} catch {
commonParamsCount.value = null;
} finally {
commonParamsLoading.value = false;
}
}
watch(
() => selectedProvider.value?.id,
() => {
refreshCommonParamsCount();
},
{ immediate: true },
);
function openCommonParamsFromTabs() {
const provider = selectedProvider.value;
if (!provider?.id) {
$baseMessage('请先选择服务商', 'warning');
return;
}
fixedParameterConfigRef.value?.init?.(provider);
}
function onFixedParamsSaved(payload: any) {
refreshCommonParamsCount(payload);
interfaceDetailModalRef.value?.refreshInheritedCommonParams?.();
interfaceModalRef.value?.refreshInheritedCommonParams?.();
askGoPublish(payload);
}
function mapAccountClick(row: { id: any; code: any; name?: string }) { function mapAccountClick(row: { id: any; code: any; name?: string }) {
accountList.value?.init?.(row); accountList.value?.init?.(row);
} }
@ -1644,6 +1701,10 @@
<IconSettings style="margin-right: 8px;" /> <IconSettings style="margin-right: 8px;" />
{{ t('system.serviceProviders.dropdown.authConfig') }} {{ t('system.serviceProviders.dropdown.authConfig') }}
</a-doption> </a-doption>
<a-doption @click="handleFixedParamsWithSelect(nodeData)">
<IconList style="margin-right: 8px;" />
{{ t('system.serviceProviders.dropdown.fixedParams') }}
</a-doption>
<a-doption @click="accountManageClick(nodeData)"> <a-doption @click="accountManageClick(nodeData)">
<IconUser style="margin-right: 8px;" /> <IconUser style="margin-right: 8px;" />
{{ t('system.serviceProviders.dropdown.accountManage') }} {{ t('system.serviceProviders.dropdown.accountManage') }}
@ -1719,7 +1780,22 @@
:items="interfaceDetailTabItems" :items="interfaceDetailTabItems"
@change="switchInterfaceDetailTab" @change="switchInterfaceDetailTab"
/> />
<div class="tabs-extra" v-if="selectedInterface && interfaceDetailTab !== 'detail'"></div> <div v-if="selectedProvider" class="tabs-extra">
<a-button
size="small"
class="common-params-entry"
title="服务商公共参数,发布映射后对该服务商下接口生效"
@click="openCommonParamsFromTabs"
>
公共参数{{
commonParamsLoading
? ''
: commonParamsCount != null
? ` · ${commonParamsCount}`
: ''
}}
</a-button>
</div>
</div> </div>
</div> </div>
@ -1813,6 +1889,7 @@
ref="parameterConfigFlag" ref="parameterConfigFlag"
@saved="askGoPublish" @saved="askGoPublish"
/> />
<FixedParameterConfig ref="fixedParameterConfigRef" @saved="onFixedParamsSaved" />
<AccountList ref="accountList" @saved="askGoPublish" /> <AccountList ref="accountList" @saved="askGoPublish" />
<ProviderMapPublishModal ref="providerPublishRef" /> <ProviderMapPublishModal ref="providerPublishRef" />
</div> </div>
@ -1995,6 +2072,11 @@
display: flex; display: flex;
align-items: center; align-items: center;
flex-shrink: 0; flex-shrink: 0;
gap: 8px;
.common-params-entry {
color: var(--color-text-2);
}
} }
} }
} }