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> </div>
<div class="jtable-content"> <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"> <a-alert :message="'已选择' + rowSelection.selectedRowKeys.length + '项'" type="info" :afterClose="handleAlertClose">
<template #closeText> <template #closeText>
<a>取消选择</a> <a>取消选择</a>

View File

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

View File

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

View File

@ -3,7 +3,7 @@
<a-modal <a-modal
v-model:visible="visible" v-model:visible="visible"
title="选择产品" title="选择产品"
style="width: 700px" style="width: 1000px"
@ok="handleOk" @ok="handleOk"
:getContainer="getContainer" :getContainer="getContainer"
:maskClosable="false" :maskClosable="false"
@ -11,34 +11,31 @@
<div class="search"> <div class="search">
<a-select <a-select
v-model:value="form.key" v-model:value="form.key"
style="width: 100%" style="width: 100px;margin-right: 20px;"
:options="productList" :options="productList"
/> />
<a-select <a-select
v-model:value="form.relation" v-model:value="form.relation"
style="width: 100%" style="width: 100px;margin-right: 20px;"
:options="productList" :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> <template #icon><SearchOutlined /></template>
搜索 搜索
</a-button> </a-button>
<a-button type="primary" @click="clickReset"> <a-button @click="clickReset">
<template #icon><reload-outlined /></template> <template #icon><reload-outlined /></template>
重置 重置
</a-button> </a-button>
</div> </div>
<JTable :columns="columns"> <JTable :columns="columns" model="TABLE"> </JTable>
</JTable>
<template #footer> <template #footer>
<a-button key="back" @click="visible = false">取消</a-button> <a-button key="back" @click="visible = false
<a-button key="submit" type="primary" @click="handleOk" ">取消</a-button>
>确认</a-button <a-button key="submit" type="primary" @click="handleOk">确认</a-button>
>
</template> </template>
</a-modal> </a-modal>
</template> </template>
@ -65,10 +62,10 @@ const handleOk = () => {
watch( watch(
() => props.openNumber, () => props.openNumber,
() => { () => {
visible.value = true;
clickReset(); clickReset();
getOptions(); getOptions();
clickSearch(); clickSearch();
visible.value = true;
}, },
); );
@ -122,4 +119,11 @@ const selectItem: deviceInfo | {} = {};
const getList = () => {}; const getList = () => {};
</script> </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 /> --> <!-- <InitHome /> -->
<!-- <DeviceHome /> --> <!-- <DeviceHome /> -->
<!-- <DevOpsHome /> --> <!-- <DevOpsHome /> -->
<!-- <ComprehensiveHome /> --> <ComprehensiveHome />
<ApiPage />
</div> </div>
</div> </div>
</template> </template>
@ -17,7 +16,6 @@ import InitHome from './components/InitHome/index.vue';
import DeviceHome from './components/DeviceHome/index.vue'; import DeviceHome from './components/DeviceHome/index.vue';
import DevOpsHome from './components/DevOpsHome/index.vue'; import DevOpsHome from './components/DevOpsHome/index.vue';
import ComprehensiveHome from './components/ComprehensiveHome/index.vue'; import ComprehensiveHome from './components/ComprehensiveHome/index.vue';
import ApiPage from '@/views/system/apiPage/index.vue'
</script> </script>

View File

@ -8,27 +8,27 @@
</div> </div>
</div> </div>
<p style="display: flex; justify-content: space-between"> <p>
<span class="label">请求数据类型</span> <span class="label">请求数据类型</span>
<span class="value">{{ <span>{{
getContent(selectApi.requestBody) || getContent(selectApi.requestBody) ||
'application/x-www-form-urlencoded' 'application/x-www-form-urlencoded'
}}</span> }}</span>
<span class="label">响应数据类型</span> <span class="label">响应数据类型</span>
<span class="value">{{ `["/"]` }}</span> <span>{{ `["/"]` }}</span>
</p> </p>
<div class="api-card"> <div class="api-card">
<h5>请求参数</h5> <h5>请求参数</h5>
<div class="content"> <div class="content">
<JTable <JTable
:columns="columns.request" :columns="requestCard.columns"
:dataSource="selectApi.parameters" :dataSource="requestCard.tableData"
noPagination noPagination
model="TABLE" model="TABLE"
> >
<template #required="slotProps"> <template #required="slotProps">
<span>{{ slotProps.row.required + '' }}</span> <span>{{ Boolean(slotProps.row.required) + '' }}</span>
</template> </template>
<template #type="slotProps"> <template #type="slotProps">
<span>{{ slotProps.row.schema.type }}</span> <span>{{ slotProps.row.schema.type }}</span>
@ -36,6 +36,39 @@
</JTable> </JTable>
</div> </div>
</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> </div>
</template> </template>
@ -49,11 +82,21 @@ const props = defineProps({
type: Object as PropType<apiDetailsType>, type: Object as PropType<apiDetailsType>,
required: true, required: true,
}, },
schemas: {
type: Object,
required: true,
},
}); });
const { selectApi } = toRefs(props); const { selectApi } = toRefs(props);
const columns = { type tableCardType = {
request: [ columns: object[];
tableData: object[];
activeKey?: any;
getData?: any;
};
const requestCard = reactive<tableCardType>({
columns: [
{ {
title: '参数名', title: '参数名',
dataIndex: 'name', dataIndex: 'name',
@ -82,13 +125,136 @@ const columns = {
scopedSlots: true, 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) => { const getContent = (data: any) => {
if (!data) return ''; if (data && data.content) {
return Object.keys(data.content)[0]; 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> </script>
<style lang="less" scoped> <style lang="less" scoped>
@ -103,13 +269,25 @@ const getContent = (data: any) => {
.input { .input {
display: flex; display: flex;
margin: 24px 0;
} }
} }
p {
display: flex;
justify-content: space-between;
font-size: 14px;
.label {
font-weight: bold;
}
}
.api-card { .api-card {
margin-top: 24px;
h5 { h5 {
position: relative; position: relative;
padding-left: 10px; padding-left: 10px;
font-weight: 600;
font-size: 16px;
&::before { &::before {
position: absolute; position: absolute;
@ -125,7 +303,6 @@ const getContent = (data: any) => {
.content { .content {
padding-left: 10px; padding-left: 10px;
:deep(.jtable-body) { :deep(.jtable-body) {
padding: 0; padding: 0;

View File

@ -5,6 +5,7 @@
<div class="input"> <div class="input">
<InputCard :value="selectApi.method" /> <InputCard :value="selectApi.method" />
<a-input :value="selectApi?.url" disabled /> <a-input :value="selectApi?.url" disabled />
<span class="send">发送</span>
</div> </div>
</div> </div>
</div> </div>
@ -36,7 +37,15 @@ const { selectApi } = toRefs(props);
.input { .input {
display: flex; 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'] = { const rowSelection: TableProps['rowSelection'] = {
// onChange: (selectedRowKeys, selectedRows) => { onChange: (selectedRowKeys, selectedRows) => {
// console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows); console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows);
// }, },
}; };
const jump = (row:object) => { const jump = (row:object) => {

View File

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

View File

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

View File

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