全局参数化加入请求参数中,参数支持多行编辑,展示值转换,细节优化

This commit is contained in:
暮光:城中城
2021-11-09 22:54:37 +08:00
parent 265ad08d3e
commit cabc693119
10 changed files with 218 additions and 67 deletions

View File

@@ -0,0 +1,32 @@
export default {
formatSeconds(value) {
let result = parseInt(value);
let second = result / 1000;
let s = Math.floor(second % 60);
let ms = (result % 1000);
if (s > 0) return `${s}.${ms} s`;
return `${ms} ms`;
},
formatFileSize(fileSize) {
if (!fileSize) {
return '0 B';
}
let size = "";
if (fileSize < 0.1 * 1024) {
size = fileSize.toFixed(2) + " B"
} else if (fileSize < 0.1 * 1024 * 1024) {
size = (fileSize / 1024).toFixed(2) + " KB"
} else if (fileSize < 0.1 * 1024 * 1024 * 1024) {
size = (fileSize / (1024 * 1024)).toFixed(2) + " MB"
} else {
size = (fileSize / (1024 * 1024 * 1024)).toFixed(2) + " GB"
}
let sizeStr = size + "";
let index = sizeStr.indexOf(".");
let dou = sizeStr.substr(index + 1, 2);
if (dou == "00") {
return sizeStr.substring(0, index) + sizeStr.substr(index + 3, 2)
}
return size;
},
}

View File

@@ -0,0 +1,19 @@
<template>
<router-view v-slot="{ Component, route }">
<keep-alive>
<component :is="Component" :key="route.fullPath" />
</keep-alive>
</router-view>
</template>
<script>
export default {
name: 'EmptyLayout',
components: {},
props: [],
data() {
return {}
},
methods: {}
}
</script>

View File

@@ -35,7 +35,7 @@
<script>
import MenuChildrenLayout from './MenuChildrenLayout.vue'
import {customApi} from '../../api'
import {customApi, zyplayerApi} from '../../api'
import {createTreeViewByTag, getTreeDataForTag} from '../../store/TreeViewByTag'
export default {
@@ -81,6 +81,7 @@
this.openKeys = [matched[1].path];
}
this.getSwaggerResourceList();
this.getGlobalParamList();
},
methods: {
docChecked(val, node) {
@@ -89,6 +90,12 @@
this.$router.push({path: '/doc/view', query: dataRef.query});
}
},
getGlobalParamList() {
zyplayerApi.docSwaggerGlobalParamList().then(res => {
let globalParam = res.data || [];
this.$store.commit('setGlobalParam', globalParam);
});
},
getSwaggerResourceList() {
customApi.get('./swagger-resources').then(res => {
if (res instanceof Array) {

View File

@@ -1,48 +1,61 @@
<template>
<a-table :row-selection="{ selectedRowKeys: queryParamSelectedRowKeys, onChange: queryParamRowSelectionChange }"
:dataSource="queryParamList"
:columns="queryParamListColumns" size="small"
:pagination="false"
:scroll="{ y: '300px' }">
<template #bodyCell="{ column, text, record }">
<template v-if="column.dataIndex === 'name'">
<a-input placeholder="请输入参数名" v-model:value="record.name" @change="queryParamChange(record)"></a-input>
<div v-if="multilineEdit">
<div style="text-align: right;">
<a-button @click="toKeyValueEdit" type="link">表单编辑</a-button>
</div>
<a-textarea v-model:value="multilineEditValue" :auto-size="{ minRows: 14, maxRows: 14 }"></a-textarea>
</div>
<div v-else>
<a-table :row-selection="{ selectedRowKeys: queryParamSelectedRowKeys, onChange: queryParamRowSelectionChange }"
:dataSource="paramListRef"
:columns="paramListColumns" size="small"
:pagination="false"
:scroll="{ y: '300px' }">
<template #headerCell="{ column }">
<template v-if="column.dataIndex === 'action'">
<a-button @click="toMultilineEdit" type="link" style="margin-left: -50px;">多行编辑</a-button>
</template>
</template>
<template v-if="column.dataIndex === 'type'">
<a-select v-if="record.key >= 10000" v-model:value="record.type">
<a-select-option value="integer">Integer</a-select-option>
<a-select-option value="string">String</a-select-option>
<a-select-option value="file">File</a-select-option>
</a-select>
<a-tag color="pink" v-else-if="text === 'integer'">Integer</a-tag>
<a-tag color="red" v-else-if="text === 'string'">String</a-tag>
<a-tag color="green" v-else>{{text||'-'}}</a-tag>
<template #bodyCell="{ column, text, record }">
<template v-if="column.dataIndex === 'name'">
<a-input placeholder="请输入参数名" v-model:value="record.name" @change="queryParamChange(record)"></a-input>
</template>
<template v-if="column.dataIndex === 'type'">
<a-select v-if="record.key >= 10000" v-model:value="record.type">
<a-select-option value="integer">Integer</a-select-option>
<a-select-option value="string">String</a-select-option>
<a-select-option value="file">File</a-select-option>
</a-select>
<a-tag color="pink" v-else-if="text === 'integer'">Integer</a-tag>
<a-tag color="red" v-else-if="text === 'string'">String</a-tag>
<a-tag color="green" v-else>{{text||'-'}}</a-tag>
</template>
<template v-if="column.dataIndex === 'value'">
<a-select v-if="record.enum && record.type === 'array'" v-model:value="record.value" mode="multiple" :placeholder="record.description || '请选择'" style="width: 100%;">
<a-select-option :value="enums" v-for="enums in record.enum">{{enums}}</a-select-option>
</a-select>
<a-select v-else-if="record.enum" v-model:value="record.value" :placeholder="record.description || '请选择'" style="width: 100%;">
<a-select-option :value="enums" v-for="enums in record.enum">{{enums}}</a-select-option>
</a-select>
<a-upload v-else-if="record.type==='file' || record.subType === 'file' || record.subType === 'MultipartFile'"
:file-list="record.value" name="file" :multiple="record.type === 'array'"
:before-upload="file=>{return beforeUpload(file, record)}"
:remove="file=>{return handleRemove(file, record)}"
>
<a-button><upload-outlined></upload-outlined>选择文件</a-button>
</a-upload>
<a-input v-else :placeholder="record.description || '请输入参数值'" v-model:value="record.value" @change="queryParamChange(record)"></a-input>
</template>
<template v-if="column.dataIndex === 'action'">
<CloseOutlined v-if="!record.isLastRow" @click="queryParamRemove(record)" style="cursor: pointer;"/>
</template>
</template>
<template v-if="column.dataIndex === 'value'">
<a-select v-if="record.enum && record.type === 'array'" v-model:value="record.value" mode="multiple" :placeholder="record.description || '请选择'" style="width: 100%;">
<a-select-option :value="enums" v-for="enums in record.enum">{{enums}}</a-select-option>
</a-select>
<a-select v-else-if="record.enum" v-model:value="record.value" :placeholder="record.description || '请选择'" style="width: 100%;">
<a-select-option :value="enums" v-for="enums in record.enum">{{enums}}</a-select-option>
</a-select>
<a-upload v-else-if="record.type==='file' || record.subType === 'file' || record.subType === 'MultipartFile'"
:file-list="record.value" name="file" :multiple="record.type === 'array'"
:before-upload="file=>{return beforeUpload(file, record)}"
:remove="file=>{return handleRemove(file, record)}"
>
<a-button><upload-outlined></upload-outlined>选择文件</a-button>
</a-upload>
<a-input v-else :placeholder="record.description || '请输入参数值'" v-model:value="record.value" @change="queryParamChange(record)"></a-input>
</template>
<template v-if="column.dataIndex === 'action'">
<CloseOutlined v-if="!record.isLastRow" @click="queryParamRemove(record)" style="cursor: pointer;"/>
</template>
</template>
</a-table>
</a-table>
</div>
</template>
<script>
import {toRefs, ref, reactive, onMounted, watch} from 'vue';
import {toRefs, toRef, ref, reactive, onMounted, watch} from 'vue';
import { useRouter, useRoute } from "vue-router";
import {useStore} from 'vuex';
import { message } from 'ant-design-vue';
@@ -66,15 +79,15 @@
},
emits: ['update:selected'],
setup(props, { attrs, slots, emit, expose }) {
let queryParamList = ref(props.paramList);
let paramListRef = ref(props.paramList);
let nextIndex = 10000;
// Query参数处理
if (queryParamList.value.length <= 0 || !queryParamList.value[queryParamList.value.length - 1].isLastRow) {
queryParamList.value.push({name: '', value: undefined, type: 'integer', key: ++nextIndex, isLastRow: true});
if (paramListRef.value.length <= 0 || !paramListRef.value[paramListRef.value.length - 1].isLastRow) {
props.paramList.push({name: '', value: undefined, type: 'integer', key: ++nextIndex, isLastRow: true});
}
let queryParamSelectedRowKeys = ref([]);
queryParamList.value.forEach(item => {
item.value = item.example || undefined;
paramListRef.value.forEach(item => {
item.value = item.value || item.example || undefined;
if ((item.enum && item.type === 'array') || item.type === 'file' || item.subType === 'MultipartFile') {
item.value = [];
}
@@ -86,22 +99,23 @@
const queryParamChange = (record) => {
if (record.isLastRow) {
record.isLastRow = false;
queryParamList.value.push({name: '', value: undefined, type: 'integer', key: ++nextIndex, isLastRow: true});
props.paramList.push({name: '', value: undefined, type: 'integer', key: ++nextIndex, isLastRow: true});
queryParamSelectedRowKeys.value.push(nextIndex);
}
};
const queryParamRemove = (record) => {
if (!record.isLastRow) {
queryParamList.value = queryParamList.value.filter(item => item !== record);
let index = props.paramList.findIndex(item => item === record);
props.paramList.splice(index, 1);
}
};
let queryParamListColumns = ref([]);
queryParamListColumns.value.push({title: '参数名', dataIndex: 'name', width: 250});
let paramListColumns = ref([]);
paramListColumns.value.push({title: '参数名', dataIndex: 'name', width: 250});
if (props.showType) {
queryParamListColumns.value.push({title: '类型', dataIndex: 'type', width: 100});
// paramListColumns.value.push({title: '类型', dataIndex: 'type', width: 100});
}
queryParamListColumns.value.push({title: '参数值', dataIndex: 'value'});
queryParamListColumns.value.push({title: '', dataIndex: 'action', width: 40});
paramListColumns.value.push({title: '参数值', dataIndex: 'value'});
paramListColumns.value.push({title: '', dataIndex: 'action', width: 40});
const beforeUpload = (file, record) => {
if (record.type !== 'array') {
record.value = [file];
@@ -113,20 +127,71 @@
const handleRemove = (file, record) => {
record.value = record.value.filter(item => item !== file);
};
let multilineEdit = ref(false);
let multilineEditValue = ref('');
const toMultilineEdit = () => {
multilineEdit.value = true;
multilineEditValue.value = paramListRef.value.filter(item => item.name || item.value)
.map(item => (item.name || '') + ':' + (item.value || ''))
.join('\n');
};
const toKeyValueEdit = () => {
convertKeyValueEdit();
multilineEdit.value = false;
};
// 将多行转换为表格的方式录入
const convertKeyValueEdit = () => {
if (!multilineEdit.value) return;
let paramMap = {};
props.paramList.forEach(item => paramMap[item.name] = item);
let lineDataArr = multilineEditValue.value.split('\n');
let paramListTemp = [];
lineDataArr.forEach(line => {
if (!line) return;
let index = line.indexOf(':');
if (index >= 0) {
let name = line.substring(0, index);
let value = line.substring(index + 1);
if (name || value) {
let newLine = paramMap[name] || {name: name, value: value, type: 'integer', key: ++nextIndex};
newLine.value = value;
paramListTemp.push(newLine);
}
} else {
let newLine = paramMap[line] || {name: line, value: undefined, type: 'integer', key: ++nextIndex};
paramListTemp.push(newLine);
}
});
paramListTemp.push({name: '', value: undefined, type: 'integer', key: ++nextIndex, isLastRow: true});
queryParamSelectedRowKeys.value = [];
// 修改父对象的值ref引用的值也将一起改变
props.paramList.splice(0, props.paramList.length);
paramListTemp.forEach(item => {
props.paramList.push(item);
queryParamSelectedRowKeys.value.push(item.key);
});
};
const getSelectedRowKeys = () => {
convertKeyValueEdit();
return queryParamSelectedRowKeys.value;
};
return {
queryParamList,
paramListRef,
queryParamSelectedRowKeys,
queryParamRowSelectionChange,
queryParamChange,
queryParamRemove,
beforeUpload,
handleRemove,
queryParamListColumns,
paramListColumns,
// 父组件调用
getSelectedRowKeys,
// 多行编辑
multilineEdit,
multilineEditValue,
toMultilineEdit,
toKeyValueEdit,
convertKeyValueEdit,
};
},
};

View File

@@ -1,5 +1,5 @@
import PageLayout from './components/layouts/PageLayout.vue'
import EmptyLayout from './components/layouts/EmptyLayout.vue'
import EmptyKeepAliveLayout from './components/layouts/EmptyKeepAliveLayout.vue'
let routers = [
{
@@ -35,7 +35,7 @@ let routers = [
meta: {
icon: 'SettingOutlined'
},
component: EmptyLayout,
component: EmptyKeepAliveLayout,
children: [
{
path: '/doc/setting/globalParam',

View File

@@ -12,12 +12,17 @@ export default createStore({
methodStatistic: {},
// 数据库存储的地址信息
swaggerResource: {},
// 全局参数
globalParam: [],
}
},
mutations: {
setUserInfo(state, userInfo) {
state.userInfo = userInfo;
},
setGlobalParam(state, globalParam) {
state.globalParam = globalParam;
},
setSwaggerResource(state, swaggerResource) {
state.swaggerResource = swaggerResource;
},

View File

@@ -62,6 +62,7 @@
zyplayerApi.docSwaggerGlobalParamList().then(res => {
setTimeout(() => docListLoading.value = false, 500);
docList.value = res.data || [];
store.commit('setGlobalParam', docList.value);
});
};
let docEdit = ref({});

View File

@@ -21,7 +21,7 @@
<a-radio value="row">row</a-radio>
<a-radio value="binary">binary</a-radio>
</a-radio-group>
<a-select v-if="bodyParamType === 'row'" v-model:value="consumesParamType" size="small" style="margin-left: 10px;vertical-align: top;width: 90px;">
<a-select v-if="bodyParamType === 'row'" v-model:value="consumesParamType" size="small" style="margin-left: 10px;vertical-align: top;width: 100px;">
<a-select-option value="json">JSON</a-select-option>
<a-select-option value="html">HTML</a-select-option>
<a-select-option value="xml">XML</a-select-option>
@@ -94,6 +94,7 @@
setup(props) {
const store = useStore();
let swaggerResource = store.state.swaggerResource || {};
let globalParam = store.state.globalParam || [];
let swaggerDoc = store.state.swaggerDoc || {};
let urlDomain = swaggerResource.rewriteDomain || swaggerDoc.host;
let docUrl = ref(urlDomain + props.docInfoShow.url);
@@ -104,15 +105,28 @@
let urlParamList = ref([]);
// Header参数处理
const headerParamRef = ref();
let headerParamListGlobal = globalParam.filter(item => item.paramType === 2);
let headerParamListProp = props.requestParamList.filter(item => item.in === 'header');
let nextIndex = 1;
headerParamListGlobal.forEach(item => {
headerParamListProp.push({name: item.paramKey, value: item.paramValue, type: 'string', key: 'g' + (nextIndex++)});
});
let headerParamList = ref(JSON.parse(JSON.stringify(headerParamListProp)));
// cookie参数处理
const cookieParamRef = ref();
let cookieParamListGlobal = globalParam.filter(item => item.paramType === 3);
let cookieParamListProp = props.requestParamList.filter(item => item.in === 'cookie');
cookieParamListGlobal.forEach(item => {
cookieParamListProp.push({name: item.paramKey, value: item.paramValue, type: 'string', key: 'g' + (nextIndex++)});
});
let cookieParamList = ref(JSON.parse(JSON.stringify(cookieParamListProp)));
// form参数处理
const formParamRef= ref();
let formParamListGlobal = globalParam.filter(item => item.paramType === 1);
let formParamListProp = props.requestParamList.filter(item => item.in === 'formData');
formParamListGlobal.forEach(item => {
formParamListProp.push({name: item.paramKey, value: item.paramValue, type: 'string', key: 'g' + (nextIndex++)});
});
let formParamList = ref([]);
if (props.docInfoShow.method === 'post') {
// post的时候参数否放到form里面

View File

@@ -8,7 +8,7 @@
<a-radio-button value="row">原始值</a-radio-button>
<a-radio-button value="preview">预览</a-radio-button>
</a-radio-group>
<a-select v-if="bodyShowType === 'format'" v-model:value="bodyShowFormatType" size="small" style="margin-left: 10px;">
<a-select v-if="bodyShowType === 'format'" v-model:value="bodyShowFormatType" size="small" style="margin-left: 10px;width: 100px;">
<a-select-option value="json">JSON</a-select-option>
<a-select-option value="html">HTML</a-select-option>
<a-select-option value="xml">XML</a-select-option>
@@ -40,7 +40,11 @@
</a-table>
</a-tab-pane>
<template #rightExtra>
<span class="status-info-box">状态码<span>{{resultData.status||'200'}}</span>耗时<span>{{resultData.useTime||0}} ms</span>大小<span>{{resultData.bodyLength||0}} B</span></span>
<span class="status-info-box">
状态码<span>{{resultData.status||'200'}}</span>
耗时<span>{{unitConvert.formatSeconds(resultData.useTime||0)}}</span>
大小<span>{{unitConvert.formatFileSize(resultData.bodyLength||0)}}</span>
</span>
</template>
</a-tabs>
</div>
@@ -60,6 +64,7 @@
import 'mavon-editor/dist/css/index.css'
import {zyplayerApi} from "../../../api";
import aceEditor from "../../../assets/ace-editor";
import unitConvert from "../../../assets/utils/unitConvert.js";
export default {
props: {
@@ -132,8 +137,9 @@
resultDataContentFormat.value = props.result.data.data;
}
} else {
resultDataContentOrigin.value = JSON.stringify(props.result.data);
resultDataContentFormat.value = JSON.stringify(props.result.data, null, 4);
let errorSuffix = '\n// 请求失败,以下为封装的返回值对象,仅供参考\n\n';
resultDataContentOrigin.value = errorSuffix + JSON.stringify(props.result.data);
resultDataContentFormat.value = errorSuffix + JSON.stringify(props.result.data, null, 4);
}
bodyShowTypeChange();
}
@@ -148,6 +154,7 @@
activePage,
bodyShowType,
bodyShowTypeChange,
unitConvert,
bodyShowFormatType,
bodyShowFormatPreview,
previewHtmlRef,