front/src/views/system/serviceProvidersList/components/ServiceProviderInterfaceModal.vue
2025-11-03 19:39:54 +08:00

1549 lines
42 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script lang="ts" setup>
import Sortable from 'sortablejs';
import XEUtils from 'xe-utils';
import { getCurrentInstance, nextTick, onMounted, reactive, ref } from 'vue';
import { cloneDeep, random } from 'lodash';
import { Message } from '@arco-design/web-vue';
import { IconDragArrow, IconPlus, IconDelete } from '@arco-design/web-vue/es/icon';
import {
postParameterParse,
postParameterValidateParameter,
} from '@/api/ParameterServices/parameter';
import { getInterfaceGetInterfaceById } from '@/api/InterfaceServices/interface';
import { postInterfaceSaveServiceProviderInterface } from '@/api/InterfaceServices/serviceProviderInterface';
import { getDictionaryGetList } from '@/api/ApplicationServices/dictionary';
import { $baseMessage, $baseConfirm } from '@/utils';
import { useI18n } from 'vue-i18n';
import { paramColumns, formItems, formRules, validRulesEdit } from '../../interface/config';
// Import API types
import type { ParameterInput } from '@/api/types';
const { t } = useI18n();
const emit = defineEmits(['close', 'saved']);
defineOptions({
name: 'ServiceProviderInterfaceModal',
});
// 添加类型注解的响应式变量
const drawerVisible = ref<boolean>(false); // 参数弹窗(布尔类型)
const drawerVisible1 = ref<boolean>(false); // 参数弹窗(布尔类型)
const analysisLoading = ref<boolean>(false); // 解析loading(布尔类型)
const importLoading = ref<boolean>(false); // 导入loading(布尔类型)
const flag = ref<boolean>(false); // 标识(布尔类型)
const { proxy } = getCurrentInstance() || { proxy: null };
const refSearchForm = ref<any | undefined>(); // 表单组件引用(可能为undefined)
const tabs = ref<'Json' | 'Xml'>('Json'); // 标签页状态(仅允许"Json"/"Xml")
const tabs1 = ref<'Json' | 'Xml'>('Json'); // 标签页状态(仅允许"Json"/"Xml")
const textareaJson = ref<string>(''); // Json文本域(字符串类型)
const textareaXml = ref<string>(''); // Xml文本域(字符串类型)
const textareaJson1 = ref<string>(''); // 返回参数Json文本域(字符串类型)
const textareaXml1 = ref<string>(''); // 返回参数Xml文本域(字符串类型)
const dataList = ref<any[] | undefined>(); // 解析后的数据列表(任意数组或undefined)
const dataList1 = ref<any[] | undefined>(); // 返回参数解析后的数据列表(任意数组或undefined)
const tableRef = ref(); // 请求参数表格引用(可能为undefined)
const tableRef1 = ref(); // 返回参数表格引用(可能为undefined)
// 服务商接口特有的字段
const serviceProviderId = ref<number>(0);
const serviceProviderName = ref<string>('');
const isEditMode = ref<boolean>(false);
// 添加类型注解的响应式对象
const tableForm = reactive<any>({
optionType: 1,
id: 0,
name: '',
abbreviation: '',
requestAddress: '',
description: '',
isBatch: true,
requestProtocol: undefined,
requestMethod: undefined,
category: undefined,
contentType: undefined,
isReturnMessage: true,
// 服务商接口特有字段
serviceProviderId: 0,
qps: 0,
isDisableAuth: false,
mappedParamType: undefined,
responseContentType: undefined,
inParameterList: [],
outParameterList: [],
});
const tableProps = reactive<any>({
columns: [
...paramColumns.map((col: any) => ({ ...col })),
// 在末尾追加“必填”列(单选框)
{
field: 'isRequired',
title: '必填',
width: 80,
align: 'center',
slots: { default: 'isRequired' },
},
],
isCellSort: false,
showToolbar: false,
isColumnsDrag: false,
showPager: false,
tableConfig: {
editConfig: {
trigger: 'click',
mode: 'row',
showStatus: true,
autoClear: false,
},
},
showIndex: false,
data: [],
minHeight: 200,
keepSource: true,
size: 'small',
useForm: false,
autoHeight: false,
maxHeight: 500,
editRules: validRulesEdit.value,
toolbarConfig: {
setting: false
},
});
const tableProps1 = reactive<any>({
columns: [
...paramColumns.map((col: any) => ({ ...col })),
{
field: 'isRequired',
title: '必填',
width: 80,
align: 'center',
slots: { default: 'isRequired' },
},
],
isCellSort: false,
showToolbar: false,
isColumnsDrag: false,
showPager: false,
tableConfig: {
editConfig: {
trigger: 'click',
mode: 'row',
showStatus: true,
autoClear: false,
},
},
showIndex: false,
data: [],
minHeight: 200,
keepSource: true,
size: 'small',
useForm: false,
autoHeight: false,
maxHeight: 500,
editRules: validRulesEdit.value,
toolbarConfig: {
setting: false
},
});
let sortable: any = null;
let sortable1: any = null;
// 请求参数排序
const requestSort = ref<any[]>([]);
// 返回参数排序
const backSort = ref<any[]>([]);
// 获取行号
function getRowSeq(row: any, refTable: any) {
console.log(refTable, 'refTable.xGrid');
console.log(
refTable.xGrid?.getRowSeq(row),
'refTable.xGrid?.getRowSeq(row)'
);
return `${refTable.xGrid?.getRowSeq(row)}` || '0';
}
const ParameterTypeList = ref<any[]>([]);
async function EnumValueInfoGetList() {
const list = await getDictionaryGetList({
type: 'ParameterType',
});
ParameterTypeList.value = list;
}
// 服务商接口特有的表单配置
const serviceProviderFormItems = [
{
comp: 'a-input',
field: 'name',
label: '',
span: 6,
compProps: {
placeholder: '* 请填写接口中文名称',
},
},
{
comp: 'a-input',
field: 'abbreviation',
label: '',
span: 6,
required: true,
compProps: {
placeholder: '* 请填写接口code',
},
},
{
comp: 'SelectPro',
field: 'requestProtocol',
label: '',
span: 6,
compProps: {
placeholder: '* 请选择请求协议',
api: getDictionaryGetList,
extra: { type: 'RequestProtocol' },
labelKeys: ['value'],
idKey: 'key',
isPage: false,
showHeader: false,
},
},
{
comp: 'SelectPro',
field: 'requestMethod',
label: '',
span: 6,
compProps: {
placeholder: '* 请选择请求方式',
api: getDictionaryGetList,
extra: { type: 'RequestMethod' },
labelKeys: ['value'],
idKey: 'key',
isPage: false,
showHeader: false,
},
},
{
comp: 'a-input',
field: 'requestAddress',
label: '',
span: 6,
compProps: {
placeholder: '* 例: http://xx/api/CreateOrder',
},
},
{
comp: 'SelectPro',
field: 'contentType',
label: '',
span: 6,
compProps: {
placeholder: '* 请选择Content-Type',
api: getDictionaryGetList,
extra: { type: 'ContentType' },
labelKeys: ['value'],
idKey: 'key',
isPage: false,
showHeader: false,
},
},
{
comp: 'SelectPro',
field: 'responseContentType',
label: '',
span: 6,
compProps: {
placeholder: '请选择Response-Content-Type',
api: getDictionaryGetList,
extra: { type: 'ResponseContentType' },
labelKeys: ['value'],
idKey: 'key',
isPage: false,
showHeader: false,
},
},
{
comp: 'SelectPro',
field: 'isDisableAuth',
label: '',
span: 6,
compProps: {
options: [
{ value: true, label: '是' },
{ value: false, label: '否' },
],
labelKeys: ['label'],
idKey: 'value',
isPage: false,
showHeader: false,
},
},
{
comp: 'a-input-number',
field: 'qps',
label: '',
span: 6,
compProps: {
min: 0,
precision: 0,
controlsPosition: 'right',
valueOnClear: 0,
controls: false,
placeholder: 'QPS',
style: { width: '100%' },
},
},
{
comp: 'SelectPro',
field: 'mappedParamType',
label: '',
span: 6,
compProps: {
api: getDictionaryGetList,
extra: { type: 'ParameterType' },
placeholder: '请选择根节点',
labelKeys: ['value'],
idKey: 'key',
isPage: false,
showHeader: false,
style: { width: '100%' },
},
},
{
comp: 'a-input',
field: 'description',
label: '',
span: 6,
compProps: {
placeholder: '请输入接口备注',
allowClear: true,
style: { width: '100%' },
},
},
];
// 服务商接口特有的验证规则
const serviceProviderFormRules = {
name: [{ required: true, message: '输入不能为空', trigger: 'change' }],
abbreviation: [
{ required: true, message: '输入不能为空', trigger: 'change' },
{
max: 30,
message: '请控制长度在30个字符之内',
trigger: 'change',
},
],
requestAddress: [
{ required: true, message: '输入不能为空', trigger: 'change' },
],
requestProtocol: [
{ required: true, message: '请选择请求协议', trigger: 'change' },
],
requestMethod: [
{ required: true, message: '请选择请求方式', trigger: 'change' },
],
contentType: [
{ required: true, message: '请选择Content-Type', trigger: 'change' },
],
description: [
{
max: 200,
message: '请控制长度在200个字符之内',
trigger: 'change',
},
],
};
// 初始化函数
async function init(data?: any) {
if (data) {
serviceProviderId.value = data.serviceProviderId;
serviceProviderName.value = data.serviceProviderName;
isEditMode.value = data.mode === 'edit';
if (isEditMode.value && data.id) {
// 编辑模式,加载接口数据
const interfaceData = await getInterfaceGetInterfaceById({
id: data.id,
});
refSearchForm.value?.setFormValue(interfaceData);
// 确保编辑时携带正确的标识与操作类型
tableForm.id = interfaceData.id || data.id;
tableForm.optionType = 3;
tableForm.inParameterList = interfaceData.inParameterList || [];
tableForm.outParameterList = interfaceData.outParameterList || [];
tableProps.data = interfaceData.inParameterList || [];
tableProps1.data = interfaceData.outParameterList || [];
tableRef.value?.reloadData(interfaceData.inParameterList || []);
tableRef1.value?.reloadData(interfaceData.outParameterList || []);
} else {
// 新增模式,设置默认值
tableForm.serviceProviderId = serviceProviderId.value;
tableForm.id = 0;
tableForm.optionType = 1;
}
}
await EnumValueInfoGetList();
}
// 保存函数
async function saveInterface() {
try {
// 结束编辑,确保勾选/输入值写回行数据
await tableRef.value?.clearActived?.();
await tableRef1.value?.clearActived?.();
const formData = refSearchForm.value?.getValues();
if (!formData) {
$baseMessage('请填写完整信息', 'error');
throw new Error('请填写完整信息');
}
// 规范化参数列表:只保留后端需要的字段,并写入排序,去除 children/_X_ROW_CHILD 等UI字段
const normalizeList = (arr: any[] = [], isInPara: boolean) => {
return (arr || []).map((item: any, idx: number) => ({
id: item.id ?? 0,
pId: item.pId ?? 0,
alias: item.alias ?? item.name ?? '',
name: item.name ?? '',
cnName: item.cnName ?? null,
type: item.type,
isRequired: !!item.isRequired,
description: item.description ?? null,
isInPara: isInPara,
sort: String(idx + 1),
optionType: item.optionType ?? 3, // 编辑模式下默认为修改操作
}));
};
// 从表单容器中取最新数据(表格在编辑模式下)
const inList = (tableRef.value?.getTableData?.().fullData ?? tableForm.inParameterList) as any[];
const outList = (tableRef1.value?.getTableData?.().fullData ?? tableForm.outParameterList) as any[];
// 同步表单双向数据,保证界面与提交一致
// 规范化参数列表:去除UI字段、isRequired转数值、sort转字符串
const normalizedIn = normalizeList(inList, true).map((it: any, idx: number) => {
const { _X_ROW_CHILD, _X_ROW_KEY, children, ...rest } = it || {};
const payload: any = {
...rest,
// 按工作版请求:布尔必填、字符串 sort、强制入参标识
isRequired: Boolean(it?.isRequired),
sort: String(rest?.sort ?? (idx + 1)),
isInPara: true,
pId: rest?.pId ?? 0,
};
// 需要带回 _X_ROW_KEY(后端可能用来定位行)
payload._X_ROW_KEY = _X_ROW_KEY ?? `row_${idx + 1}`;
// id 未生成时不传,避免发送 undefined
if (!payload.id && payload.id !== 0) delete payload.id;
return payload;
});
const normalizedOut = normalizeList(outList, false).map((it: any, idx: number) => {
const { _X_ROW_CHILD, _X_ROW_KEY, children, ...rest } = it || {};
const payload: any = {
...rest,
isRequired: Boolean(it?.isRequired),
sort: String(rest?.sort ?? (idx + 1)),
isInPara: false,
pId: rest?.pId ?? 0,
};
payload._X_ROW_KEY = _X_ROW_KEY ?? `row_${idx + 1}`;
if (!payload.id && payload.id !== 0) delete payload.id;
return payload;
});
tableForm.inParameterList = normalizedIn;
tableForm.outParameterList = normalizedOut;
// 获取二次请求参数数据
const formParameterList = tableRef2.value?.getTableData().fullData || [];
// 参数过滤函数,name 和 value 都不能为空(更严格,避免只传 name)
function isValidParam(param: any) {
return param
&& param.name && String(param.name).trim() !== ''
&& param.value && String(param.value).trim() !== '';
}
const filteredInList = (tableForm.inParameterList || []).filter(isValidParam);
const filteredOutList = (tableForm.outParameterList || []).filter(isValidParam);
const filteredFormList = (formParameterList || []).filter(isValidParam);
const saveData: any = {
...formData,
serviceProviderId: serviceProviderId.value,
inParameterList: filteredInList,
outParameterList: filteredOutList,
formParameterList: filteredFormList,
};
// 规范化新增/编辑的标识字段
if (isEditMode.value) {
saveData.id = formData.id || tableForm.id; // 编辑时必须带id
saveData.optionType = 3; // 编辑
} else {
// 新增时,有的接口要求不传id/或传0;这里优先不传
delete saveData.id;
saveData.optionType = 1; // 新增
}
await postInterfaceSaveServiceProviderInterface(saveData);
$baseMessage('保存成功', 'success');
emit('saved');
emit('close');
return true; // 返回成功状态
} catch (error) {
$baseMessage('保存失败', 'error');
throw error; // 重新抛出错误
}
}
// 关闭弹窗
function closeModal() {
emit('close');
}
// 新增请求参数
async function addNewRequestParam() {
try {
const record = {
id: random(0, 2099999999),
pId: 0,
type: 1,
name: '',
cnName: '',
description: '',
isRequired: false,
};
await nextTick();
const { row: newRow } = await tableRef.value!.addRow(record);
await nextTick();
await tableRef.value?.editRowEvent(newRow);
} catch (error) {
console.error('新增参数失败', error);
$baseMessage('新增参数失败,请重试', 'error');
}
}
// 新增返回参数
async function addNewResponseParam() {
try {
const record = {
id: random(0, 2099999999),
pId: 0,
type: 1,
name: '',
cnName: '',
description: '',
isRequired: false,
};
await nextTick();
const { row: newRow } = await tableRef1.value!.addRow(record);
await nextTick();
await tableRef1.value?.editRowEvent(newRow);
} catch (error) {
console.error('新增参数失败', error);
$baseMessage('新增参数失败,请重试', 'error');
}
}
// 判断行是否处于编辑状态
function isRowEditing(row: any): boolean {
if (!row || !tableRef.value) return false;
try {
return tableRef.value.isEditByRow(row);
} catch (error) {
console.error('检查编辑状态失败', error);
return false;
}
}
// 判断返回参数表格行是否处于编辑状态
function isRowEditing1(row: any): boolean {
if (!row || !tableRef1.value) return false;
try {
return tableRef1.value.isEditByRow(row);
} catch (error) {
console.error('检查编辑状态失败', error);
return false;
}
}
// 编辑行
function editRow(row: any) {
tableRef.value?.editRowEvent(row);
}
// 保存行
function saveRow(row: any) {
tableRef.value?.saveRowEvent(row);
}
// 删除行
function deleteRow(row: any) {
tableRef.value?.removeRowEvent(row);
}
// 编辑返回参数行
function editRow1(row: any) {
tableRef1.value?.editRowEvent(row);
}
// 保存返回参数行
function saveRow1(row: any) {
tableRef1.value?.saveRowEvent(row);
}
// 删除返回参数行
function deleteRow1(row: any) {
tableRef1.value?.removeRowEvent(row);
}
// 获取参数类型名称
function getParameterTypeName(type: any) {
const paramType = ParameterTypeList.value.find(item => item.key === type);
return paramType ? paramType.value : type;
}
// 打开参数解析弹窗
function drawerClick() {
drawerVisible.value = true;
}
// 打开返回参数解析弹窗
function drawerClick1() {
drawerVisible1.value = true;
}
// 取消参数解析
function cancelClick() {
drawerVisible.value = false;
textareaJson.value = '';
textareaXml.value = '';
}
// 取消返回参数解析
function cancelClick1() {
drawerVisible1.value = false;
textareaJson1.value = '';
textareaXml1.value = '';
}
// 解析参数
async function JsonXmlClick() {
if (!textareaJson.value && !textareaXml.value) {
$baseMessage('请先输入要解析的内容', 'warning');
return;
}
analysisLoading.value = true;
try {
const content = tabs.value === 'Json' ? textareaJson.value : textareaXml.value;
const data = await postParameterParse({
parameterParseType: tabs.value === 'Json' ? 1 : 2,
parameterContent: content,
});
dataList.value = data;
$baseMessage('解析成功', 'success');
} catch (error) {
$baseMessage('解析失败', 'error');
} finally {
analysisLoading.value = false;
}
}
// 解析返回参数
async function JsonXmlClick1() {
if (!textareaJson1.value && !textareaXml1.value) {
$baseMessage('请先输入要解析的内容', 'warning');
return;
}
analysisLoading.value = true;
try {
const content = tabs1.value === 'Json' ? textareaJson1.value : textareaXml1.value;
const data = await postParameterParse({
parameterParseType: tabs1.value === 'Json' ? 1 : 2,
parameterContent: content,
});
dataList1.value = data;
$baseMessage('解析成功', 'success');
} catch (error) {
$baseMessage('解析失败', 'error');
} finally {
analysisLoading.value = false;
}
}
// 导入参数
async function importClick() {
if (!dataList.value) {
$baseMessage('导入失败,请先解析', 'error');
return;
}
importLoading.value = true;
try {
const rc: any[] = cloneDeep(dataList.value);
const arr: any[] = rc.map((i) => {
return {
...i,
cnName: '',
description: '',
optionType: 1,
alias: i?.alias || i?.name,
sort: `${i}`,
};
});
await postParameterValidateParameter(arr);
$baseMessage('导入成功', 'success');
dataList.value.sort((a: any, b: any) => a.id - b.id);
dataList.value.forEach((node: any) => {
tableRef.value?.addRowsAt(node, -1);
});
drawerVisible.value = false;
textareaJson.value = '';
textareaXml.value = '';
} catch (error) {
$baseMessage('导入失败', 'error');
} finally {
importLoading.value = false;
}
}
// 导入返回参数
async function importClick1() {
if (!dataList1.value) {
$baseMessage('导入失败,请先解析', 'error');
return;
}
importLoading.value = true;
try {
const rc: any[] = cloneDeep(dataList1.value);
const arr: any[] = rc.map((i) => {
return {
...i,
cnName: '',
description: '',
optionType: 0,
alias: i?.alias || i?.name,
sort: `${i}`,
};
});
await postParameterValidateParameter(arr);
$baseMessage('导入成功', 'success');
dataList1.value.sort((a: any, b: any) => a.id - b.id);
dataList1.value.forEach((node: any) => {
tableRef1.value?.addRowsAt(node, -1);
});
drawerVisible1.value = false;
textareaJson1.value = '';
textareaXml1.value = '';
} catch (error) {
$baseMessage('导入失败', 'error');
} finally {
importLoading.value = false;
}
}
// 添加子级行(请求参数)
async function addRow(currRow: any) {
// 如果当前行没有内容,则添加一个顶级行
if (!currRow.name) {
const record = {
id: random(0, 2099999999),
pId: 0,
type: 1,
name: '',
};
try {
const result = await tableRef.value?.addRow(record);
if (result && result.row) {
await nextTick();
await tableRef.value?.editRowEvent(result.row);
}
} catch (error) {
console.error('新增参数失败', error);
$baseMessage('新增参数失败,请重试', 'error');
}
return undefined;
}
// 如果当前行有内容,则添加子级行
const record = {
id: random(0, 2099999999),
pId: currRow.id,
type: 1,
name: '',
};
if (currRow.type === 8 || currRow.type === 9 || currRow.type === 11) {
$baseMessage('该参数类型不可新增下一级', 'error');
return undefined;
}
await tableRef.value?.addRowsAt(record, -1);
await tableRef.value?.setTreeExpand(currRow, true);
return undefined;
}
// 删除行(请求参数)
async function delRow(currRow: any) {
await tableRef.value?.removeRow(currRow);
if (!currRow.children) currRow.type.disabled = false;
return undefined;
}
// 添加子级行(返回参数)
async function addRow1(currRow: any) {
// 如果当前行没有内容,则添加一个顶级行
if (!currRow.name) {
const record = {
id: random(0, 2099999999),
pId: 0,
type: 1,
name: '',
};
try {
const result = await tableRef1.value?.addRow(record);
if (result && result.row) {
await nextTick();
await tableRef1.value?.editRowEvent(result.row);
}
} catch (error) {
console.error('新增参数失败', error);
$baseMessage('新增参数失败,请重试', 'error');
}
return undefined;
}
// 如果当前行有内容,则添加子级行
const record = {
id: random(0, 2099999999),
pId: currRow.id,
type: 1,
name: '',
};
if (currRow.type === 8 || currRow.type === 9 || currRow.type === 11) {
$baseMessage('该参数类型不可新增下一级', 'error');
return undefined;
}
await tableRef1.value?.addRowsAt(record, -1);
await tableRef1.value?.setTreeExpand(currRow, true);
return undefined;
}
// 删除行(返回参数)
async function delRow1(currRow: any) {
await tableRef1.value?.removeRow(currRow);
if (!currRow.children) currRow.type.disabled = false;
return undefined;
}
// 二次请求参数相关
const tableRef2 = ref();
const tableProps2 = reactive({
columns: [
{ field: 'name', title: '参数名', slots: { edit: 'name' } },
{ field: 'value', title: '参数值', slots: { edit: 'value' } },
{ field: 'description', title: '描述', slots: { edit: 'description' } },
{ field: 'action', title: '操作', width: 120, slots: { default: 'action' } },
],
editConfig: { trigger: 'click', mode: 'row' },
tableConfig: { data: [] },
showPager: false,
});
// 新增二次请求参数
async function addNewFormParam() {
try {
const record = {
id: random(0, 2099999999),
name: '',
value: '',
description: '',
};
await nextTick();
const { row: newRow } = await tableRef2.value!.addRow(record);
await nextTick();
await tableRef2.value?.editRowEvent(newRow);
} catch (error) {
console.error('新增二次请求参数失败', error);
$baseMessage('新增二次请求参数失败,请重试', 'error');
}
}
// 判断行是否在编辑状态(二次请求参数)
function isRowEditing2(row: any) {
return tableRef2.value?.isRowEditing(row) || false;
}
// 添加子级行(二次请求参数)
async function addRow2(currRow: any) {
const record = {
id: random(0, 2099999999),
name: '',
value: '',
description: '',
};
await tableRef2.value?.addRowsAt(record, -1);
return undefined;
}
// 删除行(二次请求参数)
async function delRow2(currRow: any) {
await tableRef2.value?.removeRow(currRow);
return undefined;
}
// 暴露方法给父组件
defineExpose({
init,
saveInterface,
closeModal,
});
onMounted(() => {
EnumValueInfoGetList();
});
</script>
<template>
<div class="service-provider-interface-modal">
<!-- 基础信息 -->
<div class="section">
<div class="section-content">
<FormPro
ref="refSearchForm"
v-model="tableForm"
:button-config="{ showBtn: false }"
:default-span="6"
:row-gutter="8"
:form-layout="'inline'"
:item-list="serviceProviderFormItems"
:label-col-props="{
span: 6,
}"
:required-position="'none'"
:rules="serviceProviderFormRules"
:wrapper-col-props="{ span: 18 }"
size="small"
/>
</div>
</div>
<!-- 请求参数 -->
<div class="section">
<div class="section-content">
<div class="table-header">
<span class="table-title">请求体</span>
<div class="table-toolbar">
<a-space :size="8">
<a-button status="success" @click="addNewRequestParam">
新增
</a-button>
<a-button status="warning" @click="drawerClick()"> 解析</a-button>
</a-space>
</div>
</div>
<TablePro
ref="tableRef"
v-bind="tableProps"
:tree-config="{
transform: true,
rowField: 'id',
parentField: 'pId',
lazy: false,
showIcon: true,
}"
:row-config="{ isHover: true }"
:mouse-config="{
area: false,
}"
class="request-params-table"
>
<!-- 表格模板插槽 -->
<template #asdsad="{ row }">
<div class="flex justify-center items-center">
<IconDragArrow style="font-size: 24px" />
</div>
</template>
<template #name="{ row }">
<template v-if="isRowEditing(row)">
<a-input
v-model="row.name"
allow-clear
placeholder="请输入参数名"
/>
</template>
<span v-else>{{ row.name }}</span>
</template>
<template #cnName="{ row }">
<template v-if="isRowEditing(row)">
<a-input
v-model="row.cnName"
allow-clear
placeholder="请输入中文名称"
/>
</template>
<span v-else>{{ row.cnName }}</span>
</template>
<template #type="{ row }">
<template v-if="isRowEditing(row)">
<a-select
v-model="row.type"
:disabled="!!row.children?.length"
placeholder="请选择类型"
>
<a-option
v-for="option in ParameterTypeList"
:key="option.key"
:value="option.key"
>
{{ option.value }}
</a-option>
</a-select>
</template>
<span v-else>{{ getParameterTypeName(row.type) }}</span>
</template>
<template #description="{ row }">
<template v-if="isRowEditing(row)">
<a-input
v-model="row.description"
allow-clear
placeholder="请输入描述"
/>
</template>
<span v-else>{{ row.description }}</span>
</template>
<template #isRequired="{ row }">
<a-checkbox v-model="row.isRequired" />
</template>
<template #action="{ row }">
<a-space>
<a-button type="text" status="success" @click="addRow(row)">
<IconPlus />
</a-button>
<a-button type="text" status="danger" @click="delRow(row)">
<IconDelete />
</a-button>
</a-space>
</template>
</TablePro>
</div>
</div>
<!-- 返回参数 -->
<div class="section">
<div class="section-content">
<div class="table-header">
<span class="table-title">返回体</span>
<div class="table-toolbar">
<a-space :size="8">
<a-button status="success" @click="addNewResponseParam">
新增
</a-button>
<a-button status="warning" @click="drawerClick1()"> 解析</a-button>
</a-space>
</div>
</div>
<TablePro
ref="tableRef1"
v-bind="tableProps1"
:tree-config="{
transform: true,
rowField: 'id',
parentField: 'pId',
lazy: false,
showIcon: true,
}"
:row-config="{ isHover: true }"
:mouse-config="{
area: false,
}"
class="response-params-table"
>
<!-- 返回参数表格模板插槽 -->
<template #asdsad>
<div class="flex justify-center items-center">
<IconDragArrow style="font-size: 24px" />
</div>
</template>
<template #name="{ row }">
<template v-if="isRowEditing1(row)">
<a-input
v-model="row.name"
allow-clear
placeholder="请输入参数名"
/>
</template>
<span v-else>{{ row.name }}</span>
</template>
<template #cnName="{ row }">
<template v-if="isRowEditing1(row)">
<a-input
v-model="row.cnName"
allow-clear
placeholder="请输入中文名称"
/>
</template>
<span v-else>{{ row.cnName }}</span>
</template>
<template #type="{ row }">
<template v-if="isRowEditing1(row)">
<a-select
v-model="row.type"
:disabled="!!row.children?.length"
placeholder="请选择类型"
>
<a-option
v-for="option in ParameterTypeList"
:key="option.key"
:value="option.key"
>
{{ option.value }}
</a-option>
</a-select>
</template>
<span v-else>{{ getParameterTypeName(row.type) }}</span>
</template>
<template #description="{ row }">
<template v-if="isRowEditing1(row)">
<a-input
v-model="row.description"
allow-clear
placeholder="请输入描述"
/>
</template>
<span v-else>{{ row.description }}</span>
</template>
<template #isRequired="{ row }">
<a-checkbox v-model="row.isRequired" />
</template>
<template #action="{ row }">
<a-space>
<a-button type="text" status="success" @click="addRow1(row)">
<IconPlus />
</a-button>
<a-button type="text" status="danger" @click="delRow1(row)">
<IconDelete />
</a-button>
</a-space>
</template>
</TablePro>
</div>
</div>
<!-- 二次请求参数 -->
<div class="section">
<div class="section-content">
<div class="table-header">
<span class="table-title">二次请求参数</span>
<div class="table-toolbar">
<a-space :size="8">
<a-button status="success" @click="addNewFormParam">
新增
</a-button>
</a-space>
</div>
</div>
<TablePro
ref="tableRef2"
v-bind="tableProps2"
:row-config="{ isHover: true }"
:mouse-config="{
area: false,
}"
:use-form="false"
class="form-params-table"
>
<template #name="{ row }">
<template v-if="isRowEditing2(row)">
<a-input
v-model="row.name"
allow-clear
placeholder="请输入参数名"
/>
</template>
<span v-else>{{ row.name }}</span>
</template>
<template #value="{ row }">
<template v-if="isRowEditing2(row)">
<a-input
v-model="row.value"
allow-clear
placeholder="请输入参数值"
/>
</template>
<span v-else>{{ row.value }}</span>
</template>
<template #description="{ row }">
<template v-if="isRowEditing2(row)">
<a-input
v-model="row.description"
allow-clear
placeholder="请输入描述"
/>
</template>
<span v-else>{{ row.description }}</span>
</template>
<template #action="{ row }">
<a-space>
<a-button type="text" status="success" @click="addRow2(row)">
<IconPlus />
</a-button>
<a-button type="text" status="danger" @click="delRow2(row)">
<IconDelete />
</a-button>
</a-space>
</template>
</TablePro>
</div>
</div>
<!-- 参数解析弹窗 -->
<a-drawer
v-model:visible="drawerVisible"
:width="500"
title="参数解析"
class="analysis-drawer"
:footer="true"
>
<div class="drawer-content-wrapper">
<div class="drawer-tabs-container">
<a-tabs v-model="tabs" class="drawer-tabs">
<a-tab-pane key="Json" title="Json" />
<a-tab-pane key="Xml" title="Xml" />
</a-tabs>
</div>
<div class="drawer-textarea-container">
<div v-if="tabs === 'Json'" class="textarea-wrapper">
<a-textarea
v-model="textareaJson"
type="textarea"
placeholder="请输入JSON格式数据"
resize="none"
height="100%"
/>
</div>
<div v-else class="textarea-wrapper">
<a-textarea
v-model="textareaXml"
type="textarea"
placeholder="请输入XML格式数据"
resize="none"
height="100%"
/>
</div>
</div>
</div>
<template #footer>
<div class="drawer-footer">
<a-button @click="cancelClick">取消</a-button>
<a-button
type="primary"
:loading="analysisLoading"
class="ml-12px"
@click="JsonXmlClick"
>
解析
</a-button>
<a-button
type="primary"
:loading="importLoading"
class="ml-12px"
@click="importClick"
>
导入
</a-button>
</div>
</template>
</a-drawer>
<!-- 返回参数解析弹窗 -->
<a-drawer
v-model:visible="drawerVisible1"
:width="500"
title="参数解析"
class="analysis-drawer"
:footer="true"
>
<div class="drawer-content-wrapper">
<div class="drawer-tabs-container">
<a-tabs v-model="tabs1" class="drawer-tabs">
<a-tab-pane key="Json" title="Json" />
<a-tab-pane key="Xml" title="Xml" />
</a-tabs>
</div>
<div class="drawer-textarea-container">
<div v-if="tabs1 === 'Json'" class="textarea-wrapper">
<a-textarea
v-model="textareaJson1"
type="textarea"
placeholder="请输入JSON格式数据"
resize="none"
height="100%"
/>
</div>
<div v-else class="textarea-wrapper">
<a-textarea
v-model="textareaXml1"
type="textarea"
placeholder="请输入XML格式数据"
resize="none"
height="100%"
/>
</div>
</div>
</div>
<template #footer>
<div class="drawer-footer">
<a-button @click="cancelClick1">取消</a-button>
<a-button
type="primary"
:loading="analysisLoading"
class="ml-12px"
@click="JsonXmlClick1"
>
解析
</a-button>
<a-button
type="primary"
:loading="importLoading"
class="ml-12px"
@click="importClick1"
>
导入
</a-button>
</div>
</template>
</a-drawer>
</div>
</template>
<style lang="less" scoped>
.service-provider-interface-modal {
width: 100%;
height: 100%;
background-color: var(--color-fill-2);
display: flex;
flex-direction: column;
position: relative;
padding: 12px;
}
.section {
background-color: #fff;
border-radius: 8px;
margin-bottom: 16px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
}
.section-content {
padding: 20px;
}
.table-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 4px;
padding-bottom: 4px;
border-bottom: 1px solid #e5e6eb;
}
.table-title {
font-size: 16px;
font-weight: bold;
color: #8c8c8c;
}
.table-toolbar {
margin-bottom: 0;
}
.action-footer {
display: flex;
justify-content: center;
padding: 24px 0;
margin-top: 24px;
background-color: #fff;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
position: sticky;
bottom: 20px;
z-index: 10;
}
.mr-10px {
margin-right: 12px;
}
.ml-12px {
margin-left: 12px;
}
.analysis-drawer {
:deep(.arco-drawer-body) {
padding: 0;
overflow: hidden;
display: flex;
flex-direction: column;
}
:deep(.arco-drawer-footer) {
border-top: 1px solid #e5e6eb;
padding: 16px 24px;
background-color: #fff;
min-height: 68px;
flex-shrink: 0;
}
:deep(.arco-drawer-header) {
border-bottom: 1px solid #e5e6eb;
padding: 16px 24px;
min-height: 56px;
flex-shrink: 0;
}
}
.drawer-content-wrapper {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
}
.drawer-tabs-container {
flex-shrink: 0;
border-bottom: 1px solid #e5e6eb;
}
.drawer-tabs {
:deep(.arco-tabs-content) {
display: none;
}
}
.drawer-textarea-container {
flex: 1;
padding: 16px;
overflow: hidden;
}
.textarea-wrapper {
height: 100%;
}
.drawer-footer {
display: flex;
justify-content: flex-end;
align-items: center;
}
:deep(.arco-form-item) {
margin-bottom: 20px;
.arco-input {
font-size: 12px;
}
.arco-select-view-value {
font-size: 12px;
}
.arco-select-view-single.arco-select-view-size-medium .arco-select-view-input {
font-size: 12px;
}
}
/* 强制移除输入框左侧的必填星号及预留空位(仅作用于此弹窗作用域) */
:deep(.arco-form-item-label-col) {
.arco-form-item-label-required,
.arco-form-item-label-asterisk,
.arco-form-item-label-required-symbol {
display: none !important;
}
/* 去除为星号预留的空白 */
.arco-form-item-label {
padding-left: 0 !important;
}
}
:deep(.arco-form-item-label-col) {
font-weight: 500;
}
:deep(.arco-btn) {
height: 36px;
padding: 0 16px;
font-weight: 500;
font-size: 14px;
&.arco-btn-primary {
background-color: #1684fc;
&:hover {
background-color: #0e76e6;
}
}
&.arco-btn-text {
padding: 0 8px;
}
}
:deep(.vxe-table) {
.vxe-table--header {
background-color: #f8f9fa;
}
.vxe-table--body {
.vxe-body--row {
&:hover {
background-color: #f0f9ff;
}
}
}
}
</style>