chenlong 1 year ago
parent
commit
c98997cd58
  1. 4
      Attributes/ApiBody.php
  2. 18
      Controller/DemoController.php
  3. 47
      Service/ApiDocService.php
  4. 85
      View/Document/view.blade.php

4
Attributes/ApiBody.php

@ -21,7 +21,6 @@ class ApiBody extends Params
* @param string $describe 参数描述
* @param bool $required 是否必填
* @param float $version 参数版本, 版本废弃用负数表示,例 2.0 版本废弃了 id参数:-2.0
* @param array|ApiBody[] $children 子参数
*/
public function __construct(
public string|array $name,
@ -29,8 +28,7 @@ class ApiBody extends Params
public string $type = 'Integer',
public string $describe = '',
public bool $required = true,
public float $version = 0,
public array $children = []
public float $version = 0
)
{
if (is_array($name)) {

18
Controller/DemoController.php

@ -57,16 +57,14 @@ class DemoController
#[Api]
#[ApiBody('id', 'Integer', 'ID', true)]
#[ApiBody('array', 'Array', '数组', true)]
#[ApiBody('arrayObject', 'Array', '包含对象的数组', true, children: [
new ApiBody('field1', 'String', '字段一'),
new ApiBody('field2', 'String', '字段二'),
new ApiBody('field3', 'String', '字段三'),
])]
#[ApiBody('Object', 'Object', '对象', true, children: [
new ApiBody('field1', 'String', '字段一'),
new ApiBody('field2', 'String', '字段二'),
new ApiBody('field3', 'String', '字段三'),
])]
#[ApiBody('arrayObject', 'Array', '包含对象的数组')]
#[ApiBody('arrayObject.field1', 'String', '字段一')]
#[ApiBody('arrayObject.field2', 'String', '字段二')]
#[ApiBody('arrayObject.field3', 'String', '字段三')]
#[ApiBody('Object', 'Object', '对象')]
#[ApiBody('Object.field1', 'String', '字段一')]
#[ApiBody('Object.field2', 'String', '字段二')]
#[ApiBody('Object.field3', 'String', '字段三')]
#[ApiBody('file', 'File', '文件上传')]
#[ApiReturn('id', 'Integer', 'ID')]
#[ApiReturn('title', 'String', '标题')]

47
Service/ApiDocService.php

@ -96,8 +96,8 @@ class ApiDocService
private function apiResolve(\ReflectionClass $reflectionClass): array
{
$authenticateType = $this->authenticateType($reflectionClass);
$groupTitle = $this->apiTitleResolve($reflectionClass->getDocComment());
$baseUri = $reflectionClass->getAttributes(Controller::class)[0]->newInstance()->prefix;
$groupTitle = $this->apiTitleResolve($reflectionClass->getDocComment());
$baseUri = $reflectionClass->getAttributes(Controller::class)[0]->newInstance()->prefix;
$apis = [];
foreach ($reflectionClass->getMethods() as $reflectionMethod) {
@ -197,10 +197,53 @@ class ApiDocService
$api['version'][] = $attribute->version;
}
}
$api['responseParams'] = $this->childrenParamHandle($api['responseParams']);
if ($api['method'] === 'POST') {
$api['requestParams'] = $this->childrenParamHandle($api['requestParams']);
}
return $api;
}
/**
* @param array $initialData
*
* @return array
*/
private function childrenParamHandle(array $initialData): array
{
$newData = [];
foreach ($initialData as $item) {
$item['children'] = [];
$names = explode('.', $item['name']);
$current = &$newData;
foreach (array_slice($names, 0, -1) as $attr) {
$current = &$current[$attr]['children'];
}
$item['name'] = end($names);
$current[$item['name']] = $item;
}
return $this->childrenData(array_values($newData));
}
/**
* @param array $data
*
* @return array
*/
private function childrenData(array $data): array
{
return array_map(function ($value){
if ($value['children']){
$value['children'] = array_values($this->childrenData($value['children']));
}
return $value;
}, $data);
}
/**
* @param \ReflectionMethod $method
*

85
View/Document/view.blade.php

@ -93,8 +93,10 @@
</el-aside>
<el-container>
<el-header>
<el-button style="margin-top: 8px" type="primary" plain @click="clearCurrentApi">文档说明</el-button>
<el-button style="margin-top: 8px" type="primary" plain @click="docDesVisible = true">文档说明</el-button>
<el-button style="margin-top: 8px" type="primary" plain @click="showMark = !showMark">我的标记</el-button>
检索:<el-input v-model="searchText" style="width: 200px" placeholder="搜索:地址|名字" clearable></el-input>
版本检索:<el-input v-model="version" style="width: 200px" placeholder="版本号" clearable></el-input>
</el-header>
<el-main>
<el-row v-if="currentApiInfo.name" :gutter="10">
@ -221,31 +223,35 @@
</el-col>
</el-row>
<div v-else>
<el-alert title="页面介绍" type="success">
<div><i class="el-icon-star-on" style="color: #E6A23C"></i> 已做标记,可认为是收藏,接口详情点击可切换标记状态</div>
<div><i class="el-icon-star-off" style="color: #E6A23C"></i> 未做标记</div>
<div><i style="color:#F56C6C;" class="el-icon-s-check"></i>必须使用Token验证</div>
<div><i style="color:#67C23A;" class="el-icon-s-check"></i>使用Token数据, 不验证</div>
<div><i style="color:#909399;" class="el-icon-s-check"></i> 无需token</div>
<div><i class="el-icon-s-promotion"></i> 发送模拟请求</div>
</el-alert>
<el-alert title="响应介绍" type="warning">
<div><b>code:</b> 状态码</div>
<div><b>data:</b> 数据</div>
<div><b>msg:</b> 响应提示语</div>
<div><b>ts:</b> 当前时间戳(秒)</div>
<hr>
<div><b>code 值说明:</b></div>
<div><b style="margin-left: 10px">200:</b>成功</div>
<div><b style="margin-left: 10px">202:</b>失败</div>
<div><b style="margin-left: 10px">1001:</b>刷新token,此时响应数据会带上新的token</div>
<div><b style="margin-left: 10px">1002:</b>token无效,应要求用户重新登录</div>
</el-alert>
</div>
</el-main>
</el-container>
</el-container>
<el-dialog title="文档说明" :visible.sync="docDesVisible">
<el-alert title="页面介绍" type="success" :closable="false">
<div><i class="el-icon-star-on" style="color: #E6A23C"></i> 已做标记,可认为是收藏,接口详情点击可切换标记状态</div>
<div><i class="el-icon-star-off" style="color: #E6A23C"></i> 未做标记</div>
<div><i style="color:#F56C6C;" class="el-icon-s-check"></i>必须使用Token验证</div>
<div><i style="color:#67C23A;" class="el-icon-s-check"></i>使用Token数据, 不验证</div>
<div><i style="color:#909399;" class="el-icon-s-check"></i> 无需token</div>
<div><i class="el-icon-s-promotion"></i> 发送模拟请求</div>
</el-alert>
<el-alert title="响应介绍" type="warning" :closable="false">
<div><b>code:</b> 状态码</div>
<div><b>data:</b> 数据</div>
<div><b>msg:</b> 响应提示语</div>
<div><b>ts:</b> 当前时间戳(秒)</div>
<hr>
<div><b>code 值说明:</b></div>
<div><b style="margin-left: 10px">200:</b>成功</div>
<div><b style="margin-left: 10px">202:</b>失败</div>
<div><b style="margin-left: 10px">1001:</b>刷新token,此时响应数据会带上新的token</div>
<div><b style="margin-left: 10px">1002:</b>token无效,应要求用户重新登录</div>
</el-alert>
<el-alert title="认证说明" type="error" :closable="false">
<div>认证方式采用JWT方式, 传输方式使用Header参数: Authenticate</div>
<div><b>例:</b>Authenticate: Bearer G0peyJhbGciF7hOiJyaXB.G0peyJhbGciF7hOiJyaXB.G0peyJhbGciF7hOiJyaXB</div>
</el-alert>
</el-dialog>
</div>
</body>
@ -253,7 +259,7 @@
var APP = new Vue({
el:'#app',
data: {
editApiDoc:false,
docDesVisible:true,
paramInfo: 'request',
paramShowType: 'form',
requestUrl: 'https://vuejs.org/guide/introduction.html',
@ -271,15 +277,27 @@
myMark:[],
showMark:false,
searchText:"",
apiActive:-1
apiActive:-1,
version:''
},
created(){
if (localStorage.getItem("ScBefore")){
this.scriptBeforeRequest = localStorage.getItem("ScBefore");
}
if (!/requestData\.headers/.test(this.scriptBeforeRequest)) {
this.scriptBeforeRequest += `\nif(ApiInfo.auth){\n`;
this.scriptBeforeRequest += ` requestData.headers = { Authenticate: 'Bearer ' + localStorage.getItem("token")};\n`;
this.scriptBeforeRequest += `}`;
}
if (localStorage.getItem("ScAfter")){
this.scriptAfterRequest = localStorage.getItem("ScAfter");
}
if (!/response\.data\.token/.test(this.scriptAfterRequest)) {
this.scriptAfterRequest += `\nif(response.data.token){\n`
+ ` localStorage.setItem("token", response.data.token);\n`
+ `}`;
}
this.getApiInfo();
this.myMark = localStorage.getItem("myMark").split(',');
},
@ -303,7 +321,13 @@
}
if(this.searchText){
let length = item.children.filter(a => {
return a.url.indexOf(this.searchText) < 0 || a.name.indexOf(this.searchText) < 0
return a.url.indexOf(this.searchText) < 0 && a.name.indexOf(this.searchText) < 0
});
if (length > 0) show = false;
}
if (this.version) {
let length = item.children.filter(a => {
return !a.version.includes(this.version * 1)
});
if (length > 0) show = false;
}
@ -314,7 +338,10 @@
if(this.showMark && !this.myMark.includes(a.url)){
show = false;
}
if(this.searchText && (a.url.indexOf(this.searchText) < 0 || a.name.indexOf(this.searchText) < 0)){
if(this.searchText && (a.url.indexOf(this.searchText) < 0 && a.name.indexOf(this.searchText) < 0)){
show = false;
}
if(this.version && !a.version.includes(this.version * 1)) {
show = false;
}
return show;
@ -326,10 +353,6 @@
this.form = this.requestParamsResolve(this.currentApiInfo.requestParams);
this.paramShowJsonEl && this.paramShowJsonEl.set(this.form);
},
clearCurrentApi(){
this.currentApiInfo = {};
this.form = {};
},
getPath(){
try{
return new URL(this.currentApiInfo.url).pathname

Loading…
Cancel
Save