Merge branch 'dev' of github.com:jetlinks/jetlinks-ui-vue into dev

This commit is contained in:
blp 2023-01-12 18:31:40 +08:00
commit f6fd6b38ef
11 changed files with 249 additions and 42 deletions

View File

@ -15,7 +15,7 @@
</div>
</div>
<div class="jtable-content">
<div class="jtable-alert" v-if="rowSelection.selectedRowKeys && rowSelection.selectedRowKeys.length">
<div class="jtable-alert" v-if="rowSelection && rowSelection.selectedRowKeys && rowSelection.selectedRowKeys.length">
<a-alert :message="'已选择' + rowSelection.selectedRowKeys.length + '项'" type="info" :afterClose="handleAlertClose">
<template #closeText>
<a>取消选择</a>

View File

@ -87,6 +87,11 @@ export default [
path: '/link/accessConfig/detail/add',
component: () => import('@/views/link/AccessConfig/Detail/index.vue')
},
// system 系统管理
{
path:'/system/api',
components: ()=>import('@/views/system/apiPage/index')
},
// 初始化
{
path: '/init-home',

View File

@ -25,6 +25,10 @@
:open-number="openAccess"
@confirm="againJumpPage"
/>
<FuncTestDialog
:open-number="openFunc"
@confirm="againJumpPage"
/>
</div>
</a-card>
</template>
@ -35,6 +39,7 @@ import { QuestionCircleOutlined } from '@ant-design/icons-vue';
import { message } from 'ant-design-vue';
import AccessMethodDialog from './dialogs/AccessMethodDialog.vue';
import FuncTestDialog from './dialogs/FuncTestDialog.vue';
import { recommendList } from '../index';

View File

@ -3,7 +3,7 @@
<a-modal
v-model:visible="visible"
title="选择产品"
style="width: 700px"
style="width: 1000px"
@ok="handleOk"
:getContainer="getContainer"
:maskClosable="false"
@ -11,34 +11,31 @@
<div class="search">
<a-select
v-model:value="form.key"
style="width: 100%"
style="width: 100px;margin-right: 20px;"
:options="productList"
/>
<a-select
v-model:value="form.relation"
style="width: 100%"
style="width: 100px;margin-right: 20px;"
:options="productList"
/>
<a-input v-model:value="form.keyValue" allow-clear />
<a-input v-model:value="form.keyValue" allow-clear style="width: 230px;margin-right: 50px;" />
<a-button type="primary" @click="clickSearch">
<a-button type="primary" @click="clickSearch" style="margin-right: 10px;">
<template #icon><SearchOutlined /></template>
搜索
</a-button>
<a-button type="primary" @click="clickReset">
<a-button @click="clickReset">
<template #icon><reload-outlined /></template>
重置
</a-button>
</div>
<JTable :columns="columns">
</JTable>
<JTable :columns="columns" model="TABLE"> </JTable>
<template #footer>
<a-button key="back" @click="visible = false">取消</a-button>
<a-button key="submit" type="primary" @click="handleOk"
>确认</a-button
>
<a-button key="back" @click="visible = false
">取消</a-button>
<a-button key="submit" type="primary" @click="handleOk">确认</a-button>
</template>
</a-modal>
</template>
@ -65,10 +62,10 @@ const handleOk = () => {
watch(
() => props.openNumber,
() => {
visible.value = true;
clickReset();
getOptions();
clickSearch();
visible.value = true;
},
);
@ -122,4 +119,11 @@ const selectItem: deviceInfo | {} = {};
const getList = () => {};
</script>
<style lang="scss" scoped></style>
<style lang="less" scoped>
.func-test-dialog-container {
.search {
display: flex;
}
}
</style>

View File

@ -6,8 +6,7 @@
<!-- <InitHome /> -->
<!-- <DeviceHome /> -->
<!-- <DevOpsHome /> -->
<!-- <ComprehensiveHome /> -->
<ApiPage />
<ComprehensiveHome />
</div>
</div>
</template>
@ -17,7 +16,6 @@ import InitHome from './components/InitHome/index.vue';
import DeviceHome from './components/DeviceHome/index.vue';
import DevOpsHome from './components/DevOpsHome/index.vue';
import ComprehensiveHome from './components/ComprehensiveHome/index.vue';
import ApiPage from '@/views/system/apiPage/index.vue'
</script>

View File

@ -8,27 +8,27 @@
</div>
</div>
<p style="display: flex; justify-content: space-between">
<p>
<span class="label">请求数据类型</span>
<span class="value">{{
<span>{{
getContent(selectApi.requestBody) ||
'application/x-www-form-urlencoded'
}}</span>
<span class="label">响应数据类型</span>
<span class="value">{{ `["/"]` }}</span>
<span>{{ `["/"]` }}</span>
</p>
<div class="api-card">
<h5>请求参数</h5>
<div class="content">
<JTable
:columns="columns.request"
:dataSource="selectApi.parameters"
:columns="requestCard.columns"
:dataSource="requestCard.tableData"
noPagination
model="TABLE"
>
<template #required="slotProps">
<span>{{ slotProps.row.required + '' }}</span>
<span>{{ Boolean(slotProps.row.required) + '' }}</span>
</template>
<template #type="slotProps">
<span>{{ slotProps.row.schema.type }}</span>
@ -36,6 +36,39 @@
</JTable>
</div>
</div>
<div class="api-card">
<h5>响应状态</h5>
<div class="content">
<JTable
:columns="responseStatusCard.columns"
:dataSource="responseStatusCard.tableData"
noPagination
model="TABLE"
>
</JTable>
<a-tabs v-model:activeKey="responseStatusCard.activeKey">
<a-tab-pane
:key="key"
:tab="key"
v-for="key in tabs"
></a-tab-pane>
</a-tabs>
</div>
</div>
<div class="api-card">
<h5>响应参数</h5>
<div class="content">
<JTable
:columns="respParamsCard.columns"
:dataSource="respParamsCard.tableData"
noPagination
model="TABLE"
>
</JTable>
</div>
</div>
</div>
</template>
@ -49,11 +82,21 @@ const props = defineProps({
type: Object as PropType<apiDetailsType>,
required: true,
},
schemas: {
type: Object,
required: true,
},
});
const { selectApi } = toRefs(props);
const columns = {
request: [
type tableCardType = {
columns: object[];
tableData: object[];
activeKey?: any;
getData?: any;
};
const requestCard = reactive<tableCardType>({
columns: [
{
title: '参数名',
dataIndex: 'name',
@ -82,13 +125,136 @@ const columns = {
scopedSlots: true,
},
],
};
tableData: [],
getData: () => {
requestCard.tableData = props.selectApi.parameters;
},
});
const responseStatusCard = reactive<tableCardType>({
activeKey: '',
columns: [
{
title: '状态码',
dataIndex: 'code',
key: 'code',
},
{
title: '说明',
dataIndex: 'desc',
key: 'desc',
},
{
title: 'schema',
dataIndex: 'schema',
key: 'schema',
},
],
tableData: [],
getData: () => {
if (!Object.keys(props.selectApi.responses).length)
return (responseStatusCard.tableData = []);
const tableData = <any>[];
Object.entries(props.selectApi.responses || {}).forEach((item: any) => {
const desc = item[1].description;
const schema = item[1].content['*/*'].schema.$ref?.split('/') || '';
tableData.push({
code: item[0],
desc,
schema: schema && schema.pop(),
});
});
responseStatusCard.activeKey = tableData[0]?.code;
responseStatusCard.tableData = tableData;
},
});
const tabs = computed(() =>
responseStatusCard.tableData
.map((item: any) => item.code + '')
.filter((code: string) => code !== '400'),
);
const respParamsCard = reactive<tableCardType>({
columns: [
{
title: '参数名称',
dataIndex: 'paramsName',
},
{
title: '参数说明',
dataIndex: 'desc',
},
{
title: '类型',
dataIndex: 'paramsType',
},
],
tableData: [],
getData: (code: string) => {
type schemaObjType = {
paramsName: string;
paramsType: string;
desc: string;
children?: schemaObjType[];
};
const schemaName = responseStatusCard.tableData.find(
(item: any) => item.code === code,
).schema;
const schemas = toRaw(props.schemas);
function findData(schemaName: string) {
if (!schemaName || !schemas[schemaName]) {
return [];
}
const result: schemaObjType[] = [];
const schema = schemas[schemaName];
const basicType = ['string', 'integer', 'boolean'];
Object.entries(schema.properties).forEach((item: [string, any]) => {
const paramsType =
item[1].type ||
(item[1].$ref && item[1].$ref.split('/').pop()) ||
(item[1].items && item[1].items.$ref.split('/').pop()) ||
'';
const schemaObj: schemaObjType = {
paramsName: item[0],
paramsType,
desc: item[1].description || '',
};
if (!basicType.includes(paramsType))
schemaObj.children = findData(paramsType);
result.push(schemaObj);
});
console.log(result);
return result;
}
respParamsCard.tableData = findData(schemaName);
// console.log(respParamsCard.tableData);
},
});
console.log(selectApi.value);
const getContent = (data: any) => {
if (!data) return '';
return Object.keys(data.content)[0];
if (data && data.content) {
return Object.keys(data.content || {})[0];
}
return '';
};
onMounted(() => {
requestCard.getData();
responseStatusCard.getData();
});
watch(
() => props.selectApi,
() => {
requestCard.getData();
responseStatusCard.getData();
},
);
watch([() => responseStatusCard.activeKey, () => props.selectApi], (n) => {
n[0] && respParamsCard.getData(n[0]);
});
</script>
<style lang="less" scoped>
@ -103,13 +269,25 @@ const getContent = (data: any) => {
.input {
display: flex;
margin: 24px 0;
}
}
p {
display: flex;
justify-content: space-between;
font-size: 14px;
.label {
font-weight: bold;
}
}
.api-card {
margin-top: 24px;
h5 {
position: relative;
padding-left: 10px;
font-weight: 600;
font-size: 16px;
&::before {
position: absolute;
@ -125,7 +303,6 @@ const getContent = (data: any) => {
.content {
padding-left: 10px;
:deep(.jtable-body) {
padding: 0;

View File

@ -5,6 +5,7 @@
<div class="input">
<InputCard :value="selectApi.method" />
<a-input :value="selectApi?.url" disabled />
<span class="send">发送</span>
</div>
</div>
</div>
@ -36,7 +37,15 @@ const { selectApi } = toRefs(props);
.input {
display: flex;
.send {
width: 65px;
padding: 4px 15px;
font-size: 14px;
color: #fff;
background-color: #1890ff;
}
}
}
}
</style>
</style>

View File

@ -43,9 +43,9 @@ const columns = [
},
];
const rowSelection: TableProps['rowSelection'] = {
// onChange: (selectedRowKeys, selectedRows) => {
// console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
// },
onChange: (selectedRowKeys, selectedRows) => {
console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
},
};
const jump = (row:object) => {

View File

@ -32,13 +32,14 @@ const getTreeData = () => {
Promise.all(allPromise).then((values) => {
values.forEach((item: any, i) => {
tree[i].children = combData(item?.paths);
tree[i].schemas = item.components.schemas
});
treeData.value = tree;
});
});
};
const clickSelectItem: TreeProps['onSelect'] = (key, node: any) => {
emits('select', node.node.dataRef);
emits('select', node.node.dataRef, node.node?.parent.node.schemas);
};
onMounted(() => {

View File

@ -1,9 +1,11 @@
export type treeNodeTpye = {
name: string;
key: string;
schemas?:object;
link?: string;
apiList?: object[];
children?: treeNodeTpye[];
};
export type methodType = {
[key: string]: object
@ -17,6 +19,7 @@ export type apiDetailsType = {
url: string;
method: string;
summary: string;
parameters: [];
parameters: any[];
requestBody?: any;
responses:object;
}

View File

@ -15,12 +15,12 @@
class="api-details"
v-show="selectedApi.url && tableData.length > 0"
>
<a-button @click="selectedApi = initSelectedApi"
<a-button @click="selectedApi = initSelectedApi" style="margin-bottom: 24px;"
>返回</a-button
>
<a-tabs v-model:activeKey="activeKey" type="card">
<a-tab-pane key="does" tab="文档">
<ApiDoes :select-api="selectedApi" />
<ApiDoes :select-api="selectedApi" :schemas="schemas" />
</a-tab-pane>
<a-tab-pane key="test" tab="调试">
<ApiTest :select-api="selectedApi" />
@ -40,7 +40,8 @@ import ApiDoes from './components/ApiDoes.vue';
import ApiTest from './components/ApiTest.vue';
const tableData = ref([]);
const treeSelect = (node: treeNodeTpye) => {
const treeSelect = (node: treeNodeTpye, nodeSchemas:object = {}) => {
schemas.value = nodeSchemas
if (!node.apiList) return;
const apiList: apiObjType[] = node.apiList as apiObjType[];
const table: any = [];
@ -61,10 +62,14 @@ const treeSelect = (node: treeNodeTpye) => {
};
const activeKey = ref('does');
const initSelectedApi = {
const schemas = ref({});
const initSelectedApi:apiDetailsType = {
url: '',
method: '',
summary: '',
parameters: [],
responses: {},
requestBody: {}
};
const selectedApi = ref<apiDetailsType>(initSelectedApi);