Compare commits

...

No commits in common. "server" and "thinkphp" have entirely different histories.

722 changed files with 44793 additions and 11592 deletions

View File

@ -1,18 +0,0 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false
[*.{yml,yaml}]
indent_size = 2
[docker-compose.yml]
indent_size = 4

View File

@ -1,67 +0,0 @@
APP_NAME=CatchAdmin后台管理系统
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost
LOG_CHANNEL=stack
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=
DB_PREFIX=
BROADCAST_DRIVER=log
CACHE_DRIVER=file
FILESYSTEM_DISK=local
QUEUE_CONNECTION=sync
SESSION_DRIVER=file
SESSION_LIFETIME=120
MEMCACHED_HOST=127.0.0.1
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=smtp
MAIL_HOST=mailhog
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="${APP_NAME}"
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_HOST=
PUSHER_PORT=443
PUSHER_SCHEME=https
PUSHER_APP_CLUSTER=mt1
VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
VITE_PUSHER_HOST="${PUSHER_HOST}"
VITE_PUSHER_PORT="${PUSHER_PORT}"
VITE_PUSHER_SCHEME="${PUSHER_SCHEME}"
VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
VITE_BASE_URL=${APP_URL}/api/
VITE_APP_NAME=${APP_NAME}
ALIOSS_BUCKET=
ALIOSS_ACCESS_ID=
ALIOSS_ACCESS_SECRET=
ALIOSS_ENDPOINT=
ALIOSS_UPLOAD_DIR=

1
.example.env Normal file
View File

@ -0,0 +1 @@
APP_DEBUG = false [APP] DEFAULT_TIMEZONE = Asia/Shanghai [DATABASE] TYPE = mysql HOSTNAME = 127.0.0.1 DATABASE = test USERNAME = username PASSWORD = password HOSTPORT = 3306 CHARSET = utf8 DEBUG = true [LANG] default_lang = zh-cn

11
.gitattributes vendored
View File

@ -1,11 +0,0 @@
* text=auto
*.blade.php diff=html
*.css diff=css
*.html diff=html
*.md diff=markdown
*.php diff=php
/.github export-ignore
CHANGELOG.md export-ignore
.styleci.yml export-ignore

View File

@ -1,7 +1,7 @@
# 环境 # 环境
- 操作系统: - 操作系统:
- php 版本: - php 版本:
- Laravel 版本: - thinkphp 版本:
- Mysql 版本: - Mysql 版本:
- web 服务器: - web 服务器:

View File

@ -1,7 +1,7 @@
# 环境 # 环境
- 操作系统: - 操作系统:
- php 版本: - php 版本:
- Laravel 版本: - thinkphp 版本:
- Mysql 版本: - Mysql 版本:
- web 服务器: - web 服务器:

23
.gitignore vendored
View File

@ -1,19 +1,8 @@
/public/hot
/public/storage
/storage/*.key
/vendor
/fixer
/catch
.env
nohup.out
.env.backup
.env.production
.phpunit.result.cache
.php-cs-fixer.cache
Homestead.json
Homestead.yaml
composer.lock
/.fleet
/.idea /.idea
/.vscode /.vscode
/web /vendor
/package
*.log
.env
composer.lock

View File

@ -1,101 +0,0 @@
<?php
require_once __DIR__.DIRECTORY_SEPARATOR.'fixer'.DIRECTORY_SEPARATOR.'vendor'.DIRECTORY_SEPARATOR.'autoload.php';
use PhpCsFixer\Finder;
use PhpCsFixer\Config;
$finder = Finder::create()
// 排除目录
//->exclude('packages')
//// ->notPath('./packages/test.php')
// in 配置需要规则的目录
->in([
__DIR__.DIRECTORY_SEPARATOR.'app',
__DIR__.DIRECTORY_SEPARATOR.'catch',
__DIR__.DIRECTORY_SEPARATOR.'modules',
])
// 排除 . 开头的文件
->ignoreDotFiles(true)
// vcs 文件
->ignoreVCS(true);
$config = new Config();
return $config->setRules([
'@PSR1' => true, // psr1
'@PSR2' => true, // psr2 规范
'@PSR12' => true, // psr12 规范
'binary_operator_spaces' => true, // 二元操作符号空格 $a=1 => $a = 1;
'array_syntax' => [
'syntax' => 'short', // array('1') => ['1']
],
'no_trailing_comma_in_singleline_array' => true, // -$a = array('sample', ); => $a = array('sample');
'trim_array_spaces' => true, // array( 'a', 'b' ); => array('a', 'b')
'single_trait_insert_per_statement' => false,
'standardize_not_equals' => true, // "!=" => "<>"
'magic_constant_casing' => true, // __dir__ => __DIR__
'native_function_casing' => true, // STRLEN($str); => strlen($str);
'cast_spaces' => true, // (int)$b => (int) $b
'simplified_if_return' => true, // if ($foo) { return true; } return false; => return (bool) ($foo) ;
'no_unused_imports' => true, // use \DateTime; -use \Exception; => use \DateTime;
'not_operator_with_successor_space' => true, // if (!$bar) => if (! $bar)
/**
* // function example($b) {
if ($b) {
return;
}
- return;
*/
'no_useless_return' => true,
/**
* function a() {
- $a = 1;
- return $a;
+ return 1;
*/
'return_assignment' => true,
/**
-<?php return null;
+<?php return;
*/
'simplified_null_return' => true,
/**
* $foo = [
- 'bar' => [
- 'baz' => true,
- ],
+ 'bar' => [
+ 'baz' => true,
+ ],
*/
'array_indentation' => true,
/**
* -$sample = $b [ 'a' ] [ 'b' ];
+$sample = $b['a']['b'];
*/
'no_spaces_around_offset' => true,
'concat_space' => true, // $a.$b => $a . $b
])->setFinder($finder);

42
.travis.yml Normal file
View File

@ -0,0 +1,42 @@
sudo: false
language: php
branches:
only:
- stable
cache:
directories:
- $HOME/.composer/cache
before_install:
- composer self-update
install:
- composer install --no-dev --no-interaction --ignore-platform-reqs
- zip -r --exclude='*.git*' --exclude='*.zip' --exclude='*.travis.yml' ThinkPHP_Core.zip .
- composer require --update-no-dev --no-interaction "topthink/think-image:^1.0"
- composer require --update-no-dev --no-interaction "topthink/think-migration:^1.0"
- composer require --update-no-dev --no-interaction "topthink/think-captcha:^1.0"
- composer require --update-no-dev --no-interaction "topthink/think-mongo:^1.0"
- composer require --update-no-dev --no-interaction "topthink/think-worker:^1.0"
- composer require --update-no-dev --no-interaction "topthink/think-helper:^1.0"
- composer require --update-no-dev --no-interaction "topthink/think-queue:^1.0"
- composer require --update-no-dev --no-interaction "topthink/think-angular:^1.0"
- composer require --dev --update-no-dev --no-interaction "topthink/think-testing:^1.0"
- zip -r --exclude='*.git*' --exclude='*.zip' --exclude='*.travis.yml' ThinkPHP_Full.zip .
script:
- php think unit
deploy:
provider: releases
api_key:
secure: TSF6bnl2JYN72UQOORAJYL+CqIryP2gHVKt6grfveQ7d9rleAEoxlq6PWxbvTI4jZ5nrPpUcBUpWIJHNgVcs+bzLFtyh5THaLqm39uCgBbrW7M8rI26L8sBh/6nsdtGgdeQrO/cLu31QoTzbwuz1WfAVoCdCkOSZeXyT/CclH99qV6RYyQYqaD2wpRjrhA5O4fSsEkiPVuk0GaOogFlrQHx+C+lHnf6pa1KxEoN1A0UxxVfGX6K4y5g4WQDO5zT4bLeubkWOXK0G51XSvACDOZVIyLdjApaOFTwamPcD3S1tfvuxRWWvsCD5ljFvb2kSmx5BIBNwN80MzuBmrGIC27XLGOxyMerwKxB6DskNUO9PflKHDPI61DRq0FTy1fv70SFMSiAtUv9aJRT41NQh9iJJ0vC8dl+xcxrWIjU1GG6+l/ZcRqVx9V1VuGQsLKndGhja7SQ+X1slHl76fRq223sMOql7MFCd0vvvxVQ2V39CcFKao/LB1aPH3VhODDEyxwx6aXoTznvC/QPepgWsHOWQzKj9ftsgDbsNiyFlXL4cu8DWUty6rQy8zT2b4O8b1xjcwSUCsy+auEjBamzQkMJFNlZAIUrukL/NbUhQU37TAbwsFyz7X0E/u/VMle/nBCNAzgkMwAUjiHM6FqrKKBRWFbPrSIixjfjkCnrMEPw=
file:
- ThinkPHP_Core.zip
- ThinkPHP_Full.zip
skip_cleanup: true
on:
tags: true

View File

@ -1,51 +0,0 @@
version: '1.0'
name: branch-pipeline
displayName: BranchPipeline
stages:
- stage:
name: compile
displayName: 编译
steps:
- step: build@php
name: build_php
displayName: PHP 构建
# 支持5.0、7.0、7.1、7.2、7.3、7.4、8.0、8.1八个版本
phpVersion: 8.0
# 构建命令
commands:
- php --version
# 非必填字段开启后表示将构建产物暂存但不会上传到制品库中7天后自动清除
artifacts:
# 构建产物名字作为产物的唯一标识可向下传递支持自定义默认为BUILD_ARTIFACT。在下游可以通过${BUILD_ARTIFACT}方式引用来获取构建物地址
- name: BUILD_ARTIFACT
# 构建产物获取路径,是指代码编译完毕之后构建物的所在路径
path:
- ./
- step: publish@general_artifacts
name: publish_general_artifacts
displayName: 上传制品
# 上游构建任务定义的产物名默认BUILD_ARTIFACT
dependArtifact: BUILD_ARTIFACT
# 上传到制品库时的制品命名默认output
artifactName: output
dependsOn: build_php
- stage:
name: release
displayName: 发布
steps:
- step: publish@release_artifacts
name: publish_release_artifacts
displayName: '发布'
# 上游上传制品任务的产出
dependArtifact: output
# 发布制品版本号
version: '1.0.0.0'
# 是否开启版本号自增,默认开启
autoIncrement: true
triggers:
push:
branches:
exclude:
- master
include:
- .*

View File

@ -1,49 +0,0 @@
version: '1.0'
name: master-pipeline
displayName: MasterPipeline
stages:
- stage:
name: compile
displayName: 编译
steps:
- step: build@php
name: build_php
displayName: PHP 构建
# 支持5.0、7.0、7.1、7.2、7.3、7.4、8.0、8.1八个版本
phpVersion: 8.0
# 构建命令
commands:
- php --version
# 非必填字段开启后表示将构建产物暂存但不会上传到制品库中7天后自动清除
artifacts:
# 构建产物名字作为产物的唯一标识可向下传递支持自定义默认为BUILD_ARTIFACT。在下游可以通过${BUILD_ARTIFACT}方式引用来获取构建物地址
- name: BUILD_ARTIFACT
# 构建产物获取路径,是指代码编译完毕之后构建物的所在路径
path:
- ./
- step: publish@general_artifacts
name: publish_general_artifacts
displayName: 上传制品
# 上游构建任务定义的产物名默认BUILD_ARTIFACT
dependArtifact: BUILD_ARTIFACT
# 上传到制品库时的制品命名默认output
artifactName: output
dependsOn: build_php
- stage:
name: release
displayName: 发布
steps:
- step: publish@release_artifacts
name: publish_release_artifacts
displayName: '发布'
# 上游上传制品任务的产出
dependArtifact: output
# 发布制品版本号
version: '1.0.0.0'
# 是否开启版本号自增,默认开启
autoIncrement: true
triggers:
push:
branches:
include:
- master

View File

@ -1,36 +0,0 @@
version: '1.0'
name: pr-pipeline
displayName: PRPipeline
stages:
- stage:
name: compile
displayName: 编译
steps:
- step: build@php
name: build_php
displayName: PHP 构建
# 支持5.0、7.0、7.1、7.2、7.3、7.4、8.0、8.1八个版本
phpVersion: 8.0
# 构建命令
commands:
- php --version
# 非必填字段开启后表示将构建产物暂存但不会上传到制品库中7天后自动清除
artifacts:
# 构建产物名字作为产物的唯一标识可向下传递支持自定义默认为BUILD_ARTIFACT。在下游可以通过${BUILD_ARTIFACT}方式引用来获取构建物地址
- name: BUILD_ARTIFACT
# 构建产物获取路径,是指代码编译完毕之后构建物的所在路径
path:
- ./
- step: publish@general_artifacts
name: publish_general_artifacts
displayName: 上传制品
# 上游构建任务定义的产物名默认BUILD_ARTIFACT
dependArtifact: BUILD_ARTIFACT
# 上传到制品库时的制品命名默认output
artifactName: output
dependsOn: build_php
triggers:
pr:
branches:
include:
- master

View File

@ -186,6 +186,8 @@
same "printed page" as the copyright notice for easier same "printed page" as the copyright notice for easier
identification within third-party archives. identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
You may obtain a copy of the License at You may obtain a copy of the License at

View File

@ -1,64 +0,0 @@
## Introduce
`CatchAdmin` is a background management system based on secondary development of [Laravel](https://laravel.com) and [Element Plus](https://element-plus.org). The `Laravel` community also has many excellent background management systems, such as `Nova`, an official product, which is of course charged, and `Filament` based on `Livewire` is free, and `Laravel Admin` has to be said. `CatchAdmin` still adopts the traditional front-end and back-end separation strategy, and the `Laravel` framework is only exported as `Api`. Coupling between management system modules is minimized. Each module has independent controllers, routes, models, and data tables. In terms of development, the influence between modules is minimized as much as possible, which reduces the difficulty of development. Based on `CatchAdmin`, systems such as `CMS`, `CRM`, `OA`, etc. can be developed. It also encapsulates many practical tools to enhance the development experience.
[Chinese](./README.md)|[English](./README-en.md)
## Function
- [x] User management Background
- [x] Department Management Configure the company's department structure, support tree structure
- [x] Position Management Configure the position of background users
- [x] Menu Management Configure system menus, buttons, etc.
- [x] Role management Configure user roles and assign permissions
- [x] Operation log Background user operation records
- [x] Login log The login record of background system users
- [x] Code Generation Generate CURD operations on the API side
- [x] Schema management Generate table structure
- [x] module management system
## Project Address
- [github catch admin](https://github.com/jaguarjack/catch-admin)
-
## Document Address
- [Document Address](https://catchadmin.com/docs/3.0/intro)
## Preview
![zRrjNd.png](https://i.imgtg.com/2023/02/16/dASpg.png)
![zRsAEQ.png](https://i.imgtg.com/2023/02/16/dAsKK.png)
![zRsUv6.png](https://i.imgtg.com/2023/02/16/dA0fB.png)
![zRsV4s.png](https://i.imgtg.com/2023/02/16/dAd5s.png)
## Demo Address
[demo address](https://v3.catchadmin.com)
- Account: `catch@admin.com`
- Password: `catchadmin`
## Sponsorship
If the project helps you, or saves you development time at work. If you can, you can support the `Catchadmin` project, thank you very much 🙏
<img src="https://i.imgtg.com/2023/02/16/dAV0a.jpg" width = "200" alt="support"/>
## Specification
###PHP
Use fixer for code checking, please refer to the specifications of the `.php-cs-fixer.dist.php` file in the root directory for details, and the following two steps are required
```shell
mkdir path
```
```shell
composer require --working-dir=path friendsofphp/php-cs-fixer
```
After the installation is complete, you can use
```shell
composer cs
```
Format the code, this command will directly modify the file to complete the correction, if you only need to check whether the format is correct, then use
```shell
composer cs-diff
```
## Thanks 🙏
> Ranked in no particular order
- [Laravel](https://laravel.com)
- [Vue](https://cn.vuejs.org/)
- [ElementPlus](https://element-plus.org)
- [Docusaurus](https://docusaurus.com)
- [JetBrains](https://www.jetbrains.com/)

228
README.md
View File

@ -1,97 +1,165 @@
## 介绍 <p align="center">
### 这是 catchadmin 完全分离的版本 <img src="https://cdn.learnku.com/uploads/images/202005/17/18206/zSuf7Ce5kM.png!large">
`CatchAdmin`是一款基于[Laravel](https://laravel.com)和[Element Plus](https://element-plus.org)二次开发而成后台管理系统。`Laravel` 社区也有许多非常优秀的后台管理系统,例如 `Nova`, 官方出品,当然是收费的,免费的有基于 `Livewire``Filament`,还有不得不说的 `Laravel Admin``CatchAdmin` 还是采用传统的前后端分离策略,`Laravel` 框架仅仅作为 `Api` 输出。将管理系统模块之间的耦合降到了最低限度。每个模块之间都有独立的控制器,路由,模型,数据表。在开发上尽可能将模块之间的影响降到最低,降低了开发上的难度。基于 `CatchAdmin `可以开发 `CMS``CRM``OA` 等 等系统。也封装了很多实用的工具,提升开发体验。 </p>
## 前端项目
[catchadmin-vue](https://gitee.com/catchadmin/catch-admin-vue)
## Laravel 入门教程 <p align="center"><code>CatchAdmin</code>是一款基于<a href="http://www.thinkphp.cn/" target="_blank">thinkphp framework</a>
[Laravel 免费入门教程](https://laravel-study.catchadmin.com) <a href="https://github.com/PanJiaChen/vue-element-admin/">element admin</a>开发而成的后台管理系统。因为 thinkphp 的简单高效,文档齐全。在看了很多 thinkphp 生态中的后台管理系统,发现没有一款合适的前后端分离系统。遂开发了 CatchAdmin。
基于新版 thinkphp6 服务者的特性,后台的每个模块都可以独立成一个服务,模块之间的功能职责更加清晰。同时后台也兼顾了后台开发前端页面,减少了前端页面代码的输出。将表格和表单都封装成了后台组件,可在后台中通过 Json 数据轻松渲染出前端页面大大提高了开发效率。CatchAdmin 同时也提供 Http 客户端Excel 的导入导出,敏感词检测,大量的内置 commands 等一系列组件工具,提高使用后台的开发体验。
</p>
[中文](./README.md)|[英文](./README-en.md) <p align="center">
## 其他版本 <a href="https://catchadmin.com/">文档</a> |
- [tp8 新版本](https://gitee.com/catchamin/catchadmin-tp) <a href="https://demo.catchadmin.com">演示地址</a> |
- [webman 高性能版本](https://gitee.com/catchamin/catchadmin-webman) <a href="https://bbs.catchadmin.com">论坛交流</a> |
<a href="https://gitee.com/jaguarjack/catchAdmin">项目源码</a> |
<a href="https://www.kancloud.cn/akasishikelu/thinkphp6">看云分析</a>
<a href="#extensions">扩展</a>
</p>
## 新功能 <p align="center">
- [动态表单](https://catchadmin.com/docs/3.0/front/catch-form) <a href="https://gitee.com/jaguarjack/catchAdmin" target="_blank">
- [动态表格](https://catchadmin.com/docs/3.0/front/catch-table) <img src="https://svg.hamm.cn/gitee.svg?type=star&user=jaguarjack&project=catchAdmin"/>
</a >
<a href="https://gitee.com/jaguarjack/catchAdmin" target="_blank">
<img src="https://svg.hamm.cn/gitee.svg?type=fork&user=jaguarjack&project=catchAdmin"/>
</a >
<img src="https://svg.hamm.cn/badge.svg?key=Base&value=ThinkPHP6"/>
<img src="https://svg.hamm.cn/badge.svg?key=Data&value=MySQL5.5"/>
<img src="https://svg.hamm.cn/badge.svg?key=Runtime&value=PHP7.1"/>
<img src="https://svg.hamm.cn/badge.svg?key=License&value=Apache-2.0"/>
</p >
## 专业版 ## 后台启动流程
[专业版本官方地址](https://license.catchadmin.com) [![gSrLz6.png](https://z3.ax1x.com/2021/04/26/gSrLz6.png)](https://imgtu.com/i/gSrLz6)
## AntDV 版本
首先感谢一直以来对 `CatchAdmin` 开源项目的支持和使用。作为一名开源工作者,我一直致力于开发出功能强大且易于使用的后台管理系统,以帮助您简化业务流程和提升工作效率。然而,由于某些原因,我不得不做出一些调整。为了能够继续开发和维护这个项目,我将推出一款付费的后台管理系统,以确保我能够持续为您提供高质量的服务和支持。 - 请使用 `v1`分支
专业版本不会在开源版本做一些破坏性变更,所以当您从开源版本切换到专业版本,不会有任何开发心智负担。但是使用专业版本会有新的组件来配合您的工作。
我深信,付费后台管理系统将为您带来更多的价值和便利,帮助您提升工作效率
## 功能 ## 功能
- [x] 用户管理 后台用户管理 - [x] `用户管理` 后台用户管理
- [x] 部门管理 配置公司的部门结构,支持树形结构 - [x] `部门管理` 配置公司的部门结构,支持树形结构
- [x] 岗位管理 配置后台用户的职务 - [x] `岗位管理` 配置后台用户的职务
- [x] 菜单管理 配置系统菜单,按钮等等 - [x] `菜单管理` 配置系统菜单,按钮等等
- [x] 角色管理 配置用户担当的角色,分配权限 - [x] `角色管理` 配置用户担当的角色,分配权限
- [x] 操作日志 后台用户操作记录 - [x] `数据字典` 管理后台表结构
- [x] 登录日志 后台系统用户的登录记录 - [x] `操作日志` 后台用户操作记录
- [x] 代码生成 生成 API 端的 CURD 操作 - [x] `登录日志` 后台系统用户的登录记录
- [x] Schema 管理 生成表结构 - [x] `代码生成` 生成 API 端的 CURD 操作
- [x] 模块管理 系统模块管理 - [x] `敏感词` 支持敏感词配置
- [x] `附件管理` 可管理上传的文件
- [x] `定时任务` 可管理定时任务,而不依赖于 Crontab
## 讨论 - [x] `短信平台` 短信云管理,支持 阿里大于腾讯云UcloudSubmail
- 可以提 `ISSUE`,请按照 `issue` 模板提问 - [x] `云上传` 支持云上传七牛OSS腾讯
- 加入 Q 群 `302266230` 暗号 `catchadmin` - [ ] `微信管理`
- 加微信入群,新建🆕
<img src="wechat.png" width="200"/>
## 项目地址 ## 项目地址
- [github catchadmin](https://github.com/jaguarjack/catch-admin) - [github 地址](https://github.com/yanwenwu/catch-admin)
## 文档地址 - [gitee 地址](https://gitee.com/jaguarjack/catchAdmin)
- [文档地址](https://catchadmin.com/docs/3.0/intro) - [前端 Vue 项目地址](https://github.com/yanwenwu/catch-admin-vue)
## 预览 - [文档地址](https://github.com/catch-admin/document)[个人精力实在有限,希望可以小伙伴们可以一起维护文档]
![zRrjNd.png](https://i.imgtg.com/2023/02/16/dASpg.png) ## 支持创作
![zRsAEQ.png](https://i.imgtg.com/2023/02/16/dAsKK.png) 如果项目对你有帮助,可以订阅支持我❤️。你的每一份支持是对我最大的鼓励。开源不易,感谢支持。可以通过使用 [**🎉 爱发电**](https://afdian.net/@jaguarjack)订阅支持创作。
![zRsUv6.png](https://i.imgtg.com/2023/02/16/dA0fB.png)
![zRsV4s.png](https://i.imgtg.com/2023/02/16/dAd5s.png) ## 预览
<table>
<tr>
<td><img src="https://s1.ax1x.com/2020/09/07/wucNXq.md.png"></td>
<td><img src="https://s1.ax1x.com/2020/09/07/wucm6I.md.png"></td>
</tr>
<tr>
<td><img src="https://s1.ax1x.com/2020/09/07/wucZpd.md.png"></td>
<td><img src="https://s1.ax1x.com/2020/09/07/wuce1A.md.png"></td>
</tr>
<tr>
<td><img src="https://s1.ax1x.com/2020/09/07/wucnXt.md.png"></td>
<td><img src="https://s1.ax1x.com/2020/09/07/wucKnP.md.png"></td>
</tr>
<tr>
<td><img src="https://s1.ax1x.com/2020/09/07/wuc3tg.md.png"></td>
<td><img src="https://s1.ax1x.com/2020/09/07/wucM0f.md.png"></td>
</tr>
<tr>
<td><img src="https://s1.ax1x.com/2020/09/07/wucQ78.md.png"></td>
<td><img src="https://s1.ax1x.com/2020/09/07/wuc1AS.md.png"></td>
</tr>
<tr>
<td><img src="https://s1.ax1x.com/2020/09/07/wuc8hQ.md.png"></td>
<td><img src="https://s1.ax1x.com/2020/09/07/wucY1s.md.png"></td>
</tr>
<tr>
<td><img src="https://s1.ax1x.com/2020/09/07/wucJpj.md.png"></td>
<td><img src="https://s1.ax1x.com/2020/09/07/wuctcn.md.png"></td>
</tr>
</table>
## 环境要求
- php7.1+ (需以下扩展)
- [x] mbstring
- [x] json
- [x] openssl
- [x] xml
- [x] pdo
- nginx
- mysql
### 如何安装
> 安装之前请确保已安装 Composer
#### 下载项目
- 通过 Git 下载(推荐)
```shell
git clone https://gitee.com/jaguarjack/catchAdmin && cd catchAdmin
curl -sS https://install.phpcomposer.com/installer | php
composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/
composer install --ignore-platform-reqs
```
- composer 安装
```shell
composer create-project jaguarjack/catchadmin:dev-master
```
#### 安装
下载完成之后通过命令来进行安装, 一键安装 🚀
```shell
php think catch:install
```
## 体验地址 ## 体验地址
[demo 地址](https://v3.catchadmin.com)
- 账户: `catch@admin.com`
- 密码: `catchadmin`
## 视频教程(😂记得一键三连哦) [体验地址](https://demo.catchadmin.com)
- [catchadmin 安装](https://www.bilibili.com/video/BV1eY411v71J/) - 账号: catch@admin.com
- [catchadmin 开发之模块创建](https://www.bilibili.com/video/BV1jP41127aW/) - 密码: catchadmin
- [catchadmin 之快速开发](https://www.bilibili.com/video/BV1Qh4y1J7eB/)
## 规范 [catchadmin 文档地址](https://catchadmin.com)
### PHP
使用 fixer 进行代码检查, 具体请查看根目录下 `.php-cs-fixer.dist.php` 文件的规范,还需要进行以下两步骤
```shell
mkdir path
```
```shell
composer require --working-dir=path friendsofphp/php-cs-fixer
```
安装完成之后可以使用
```shell
composer cs
```
进行代码格式化,这个命令会直接修改文件完成修正,如果只需要查看格式是否正确,那么使用
```shell
composer cs-diff
```
## 感谢🙏 ### 系列文章
如果是刚开始使用 thinkphp6, 以下文章可能会对你有些许帮助,文章基于 RC3 版本。整体架构是不变的。
- [Tp6 启动分析](https://www.kancloud.cn/akasishikelu/thinkphp6/1129385)
- [Tp6 Request 解析](https://www.kancloud.cn/akasishikelu/thinkphp6/1134496)
- [TP6 应用初始化](https://www.kancloud.cn/akasishikelu/thinkphp6/1130427)
- [Tp6 中间件分析](https://www.kancloud.cn/akasishikelu/thinkphp6/1136616)
- [Tp6 请求流程](https://www.kancloud.cn/akasishikelu/thinkphp6/1136608)
### Who used
- [uctoo 应用开发管理后台](https://gitee.com/uctoo/uctoo)
### Talking
- [论坛讨论](https://bbs.catchadmin.com)
- 可以提 `ISSUE`,请按照 `issue` 模板提问
- 加入 Q 群 `302266230` 前请先 star 项目支持一下, 备注填写用户名 + 平台。例如: JaguarJack Github
### Thanks
- 感谢 [JetBrains](https://www.jetbrains.com) 提供生产力巨高的 `PHPStorm``WebStorm`
> 排名不分先后 > 排名不分先后
- [Laravel](https://laravel.com) - [top-think/think](https://github.com/top-think/think)
- [Vue](https://cn.vuejs.org/) - [element-admin](https://panjiachen.gitee.io/vue-element-admin-site/zh/)
- [ElementPlus](https://element-plus.org) - [thans/tp-jwt-auth](https://packagist.org/packages/thans/tp-jwt-auth)
- [VitePress](https://vitepress.dev/zh/) - [jaguarjack/think-filesystem-cloud](https://github.com/yanwenwu/think-filesystem-cloud)
- [JetBrains](https://www.jetbrains.com/) - [overtrue/wechat](https://github.com/overtrue/wechat)
- [jaguarjack/migration-generator](https://github.com/yanwenwu/migration-generator)
- [phpoffice/phpspreadsheet](https://github.com/PHPOffice/PhpSpreadsheet)

1
app/.htaccess Normal file
View File

@ -0,0 +1 @@
deny from all

93
app/BaseController.php Normal file
View File

@ -0,0 +1,93 @@
<?php
declare (strict_types = 1);
namespace app;
use think\App;
use think\exception\ValidateException;
use think\Validate;
/**
* 控制器基础类
*/
abstract class BaseController
{
/**
* Request实例
* @var \think\Request
*/
protected $request;
/**
* 应用实例
* @var \think\App
*/
protected $app;
/**
* 是否批量验证
* @var bool
*/
protected $batchValidate = false;
/**
* 控制器中间件
* @var array
*/
protected $middleware = [];
/**
* 构造方法
* @access public
* @param App $app 应用对象
*/
public function __construct(App $app)
{
$this->app = $app;
$this->request = $this->app->request;
// 控制器初始化
$this->initialize();
}
// 初始化
protected function initialize()
{}
/**
* 验证数据
* @access protected
* @param array $data 数据
* @param string|array $validate 验证器名或者验证规则数组
* @param array $message 提示信息
* @param bool $batch 是否批量验证
* @return array|string|true
* @throws ValidateException
*/
protected function validate(array $data, $validate, array $message = [], bool $batch = false)
{
if (is_array($validate)) {
$v = new Validate();
$v->rule($validate);
} else {
if (strpos($validate, '.')) {
// 支持场景
list($validate, $scene) = explode('.', $validate);
}
$class = false !== strpos($validate, '\\') ? $validate : $this->app->parseClass('validate', $validate);
$v = new $class();
if (!empty($scene)) {
$v->scene($scene);
}
}
$v->message($message);
// 是否批量验证
if ($batch || $this->batchValidate) {
$v->batch(true);
}
return $v->failException(true)->check($data);
}
}

View File

@ -1,32 +0,0 @@
<?php
namespace App\Console;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
/**
* Define the application's command schedule.
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void
*/
protected function schedule(Schedule $schedule)
{
// $schedule->command('inspire')->hourly();
}
/**
* Register the commands for the application.
*
* @return void
*/
protected function commands()
{
$this->load(__DIR__.'/Commands');
require base_path('routes/console.php');
}
}

View File

@ -1,33 +0,0 @@
<?php
namespace App\Events;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class Create
{
use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('channel-name');
}
}

View File

@ -1,33 +0,0 @@
<?php
namespace App\Events;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class Test
{
use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('channel-name');
}
}

57
app/ExceptionHandle.php Normal file
View File

@ -0,0 +1,57 @@
<?php
namespace app;
use think\db\exception\DataNotFoundException;
use think\db\exception\ModelNotFoundException;
use think\exception\Handle;
use think\exception\HttpException;
use think\exception\HttpResponseException;
use think\exception\ValidateException;
use think\Response;
use Throwable;
/**
* 应用异常处理类
*/
class ExceptionHandle extends Handle
{
/**
* 不需要记录信息(日志)的异常类列表
* @var array
*/
protected $ignoreReport = [
HttpException::class,
HttpResponseException::class,
ModelNotFoundException::class,
DataNotFoundException::class,
ValidateException::class,
];
/**
* 记录异常信息(包括日志或者其它方式记录)
*
* @access public
* @param Throwable $exception
* @return void
*/
public function report(Throwable $exception): void
{
// 使用内置的方式记录异常日志
parent::report($exception);
}
/**
* Render an exception into an HTTP response.
*
* @access public
* @param \think\Request $request
* @param Throwable $e
* @return Response
* @throws \Exception
*/
public function render($request, Throwable $e): Response
{
// 其他错误交给系统处理
return parent::render($request, $e);
}
}

View File

@ -1,85 +0,0 @@
<?php
namespace App\Exceptions;
use Catch\Enums\Code;
use Catch\Exceptions\CatchException;
use Catch\Exceptions\FailedException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Http\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Throwable;
class Handler extends ExceptionHandler
{
/**
* A list of exception types with their corresponding custom log levels.
*
* @var array<class-string<\Throwable>, \Psr\Log\LogLevel::*>
*/
protected $levels = [
//
];
/**
* A list of the exception types that are not reported.
*
* @var array<int, class-string<\Throwable>>
*/
protected $dontReport = [
//
];
/**
* A list of the inputs that are never flashed to the session on validation exceptions.
*
* @var array<int, string>
*/
protected $dontFlash = [
'current_password',
'password',
'password_confirmation',
];
/**
* Register the exception handling callbacks for the application.
*
* @return void
*/
public function register()
{
$this->reportable(function (Throwable $e) {
//
});
}
/**
* render
*
* @param $request
* @param Throwable $e
* @return JsonResponse|Response
* @throws Throwable
*/
public function render($request, Throwable $e): JsonResponse|Response
{
$message = $e->getMessage();
if (method_exists($e, 'getStatusCode')) {
if ($e->getStatusCode() == Response::HTTP_NOT_FOUND) {
$message = '路由未找到或未注册';
}
}
$e = new FailedException($message ?: 'Server Error', $e instanceof CatchException ? $e->getCode() : Code::FAILED);
$response = parent::render($request, $e);
$response->header('Access-Control-Allow-Origin', '*');
$response->header('Access-Control-Allow-Methods', '*');
$response->header('Access-Control-Allow-Headers', '*');
return $response;
}
}

View File

@ -1,13 +0,0 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
}

View File

@ -1,67 +0,0 @@
<?php
namespace App\Http;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
class Kernel extends HttpKernel
{
/**
* The application's global HTTP middleware stack.
*
* These middleware are run during every request to your application.
*
* @var array<int, class-string|string>
*/
protected $middleware = [
// \App\Http\Middleware\TrustHosts::class,
\App\Http\Middleware\TrustProxies::class,
\Illuminate\Http\Middleware\HandleCors::class,
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
];
/**
* The application's route middleware groups.
*
* @var array<string, array<int, class-string|string>>
*/
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'api' => [
// \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class
],
];
/**
* The application's route middleware.
*
* These middleware may be assigned to groups or used individually.
*
* @var array<string, class-string|string>
*/
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
'signed' => \App\Http\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
];
}

View File

@ -1,21 +0,0 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Auth\Middleware\Authenticate as Middleware;
class Authenticate extends Middleware
{
/**
* Get the path the user should be redirected to when they are not authenticated.
*
* @param \Illuminate\Http\Request $request
* @return string|null
*/
protected function redirectTo($request)
{
if (! $request->expectsJson()) {
return route('login');
}
}
}

View File

@ -1,17 +0,0 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;
class EncryptCookies extends Middleware
{
/**
* The names of the cookies that should not be encrypted.
*
* @var array<int, string>
*/
protected $except = [
//
];
}

View File

@ -1,17 +0,0 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance as Middleware;
class PreventRequestsDuringMaintenance extends Middleware
{
/**
* The URIs that should be reachable while maintenance mode is enabled.
*
* @var array<int, string>
*/
protected $except = [
//
];
}

View File

@ -1,32 +0,0 @@
<?php
namespace App\Http\Middleware;
use App\Providers\RouteServiceProvider;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class RedirectIfAuthenticated
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
* @param string|null ...$guards
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
public function handle(Request $request, Closure $next, ...$guards)
{
$guards = empty($guards) ? [null] : $guards;
foreach ($guards as $guard) {
if (Auth::guard($guard)->check()) {
return redirect(RouteServiceProvider::HOME);
}
}
return $next($request);
}
}

View File

@ -1,19 +0,0 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware;
class TrimStrings extends Middleware
{
/**
* The names of the attributes that should not be trimmed.
*
* @var array<int, string>
*/
protected $except = [
'current_password',
'password',
'password_confirmation',
];
}

View File

@ -1,20 +0,0 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Middleware\TrustHosts as Middleware;
class TrustHosts extends Middleware
{
/**
* Get the host patterns that should be trusted.
*
* @return array<int, string|null>
*/
public function hosts()
{
return [
$this->allSubdomainsOfApplicationUrl(),
];
}
}

View File

@ -1,28 +0,0 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Middleware\TrustProxies as Middleware;
use Illuminate\Http\Request;
class TrustProxies extends Middleware
{
/**
* The trusted proxies for this application.
*
* @var array<int, string>|string|null
*/
protected $proxies;
/**
* The headers that should be used to detect proxies.
*
* @var int
*/
protected $headers =
Request::HEADER_X_FORWARDED_FOR |
Request::HEADER_X_FORWARDED_HOST |
Request::HEADER_X_FORWARDED_PORT |
Request::HEADER_X_FORWARDED_PROTO |
Request::HEADER_X_FORWARDED_AWS_ELB;
}

View File

@ -1,22 +0,0 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Routing\Middleware\ValidateSignature as Middleware;
class ValidateSignature extends Middleware
{
/**
* The names of the query string parameters that should be ignored.
*
* @var array<int, string>
*/
protected $except = [
// 'fbclid',
// 'utm_campaign',
// 'utm_content',
// 'utm_medium',
// 'utm_source',
// 'utm_term',
];
}

View File

@ -1,17 +0,0 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
class VerifyCsrfToken extends Middleware
{
/**
* The URIs that should be excluded from CSRF verification.
*
* @var array<int, string>
*/
protected $except = [
//
];
}

View File

@ -1,30 +0,0 @@
<?php
namespace App\Listeners;
use Illuminate\Console\Events\CommandFinished;
class Command
{
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param CommandFinished $event
* @return void
*/
public function handle(CommandFinished $event)
{
//
// dd($event->command);
}
}

View File

@ -1,28 +0,0 @@
<?php
namespace App\Listeners;
class RouteMatched
{
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param object $event
* @return void
*/
public function handle(\Illuminate\Routing\Events\RouteMatched $event)
{
//
// dd($event->route);
}
}

View File

@ -1,27 +0,0 @@
<?php
namespace App\Listeners;
class test
{
/**
* Create the event listener.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Handle the event.
*
* @param object $event
* @return void
*/
public function handle($event)
{
//
}
}

View File

@ -1,11 +0,0 @@
<?php
namespace App\Models\Modules\Users\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class CatchController extends Model
{
use HasFactory;
}

View File

@ -1,45 +0,0 @@
<?php
namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
class User extends Authenticatable
{
use HasFactory, Notifiable;
protected $table = 'users';
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
}

View File

@ -1,28 +0,0 @@
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
//
}
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
//
}
}

View File

@ -1,30 +0,0 @@
<?php
namespace App\Providers;
// use Illuminate\Support\Facades\Gate;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
/**
* The model to policy mappings for the application.
*
* @var array<class-string, class-string>
*/
protected $policies = [
// 'App\Models\Model' => 'App\Policies\ModelPolicy',
];
/**
* Register any authentication / authorization services.
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
//
}
}

View File

@ -1,21 +0,0 @@
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Broadcast;
use Illuminate\Support\ServiceProvider;
class BroadcastServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Broadcast::routes();
require base_path('routes/channels.php');
}
}

View File

@ -1,53 +0,0 @@
<?php
namespace App\Providers;
use App\Listeners\Command;
use Illuminate\Auth\Events\Registered;
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
use Illuminate\Console\Events\CommandFinished;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use Illuminate\Routing\Events\RouteMatched;
use Illuminate\Support\Facades\Event;
class EventServiceProvider extends ServiceProvider
{
/**
* The event to listener mappings for the application.
*
* @var array<class-string, array<int, class-string>>
*/
protected $listen = [
Registered::class => [
SendEmailVerificationNotification::class,
],
RouteMatched::class => [
\App\Listeners\RouteMatched::class
],
CommandFinished::class => [
Command::class
]
];
/**
* Register any events for your application.
*
* @return void
*/
public function boot()
{
//
}
/**
* Determine if events and listeners should be automatically discovered.
*
* @return bool
*/
public function shouldDiscoverEvents()
{
return false;
}
}

View File

@ -1,62 +0,0 @@
<?php
namespace App\Providers;
use Catch\CatchAdmin;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Http\Request;
use Illuminate\Routing\CompiledRouteCollection;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Facades\Route;
class RouteServiceProvider extends ServiceProvider
{
/**
* The path to the "home" route for your application.
*
* Typically, users are redirected here after authentication.
*
* @var string
*/
public const HOME = '/home';
/**
* Define your route model bindings, pattern filters, and other route configuration.
*
* @return void
*/
public function boot()
{
$this->configureRateLimiting();
$this->routes(function () {
Route::middleware('api')
->prefix('api')
->group(base_path('routes/api.php'));
Route::middleware('web')
->group(base_path('routes/web.php'));
});
$this->booted(function(){
$this->app->booted(function (){
if (file_exists('loadCachedAdminRoutes')) {
loadCachedAdminRoutes();
}
});
});
}
/**
* Configure the rate limiters for the application.
*
* @return void
*/
protected function configureRateLimiting()
{
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
});
}
}

53
app/Request.php Normal file
View File

@ -0,0 +1,53 @@
<?php
namespace app;
// 应用请求对象类
use catchAdmin\permissions\model\Users;
use catcher\CatchAuth;
use catcher\Code;
use catcher\exceptions\FailedException;
use catcher\exceptions\LoginFailedException;
use thans\jwt\exception\TokenBlacklistException;
use thans\jwt\exception\TokenExpiredException;
use thans\jwt\exception\TokenInvalidException;
class Request extends \think\Request
{
protected $auth;
/**
* login user
*
* @time 2020年01月09日
* @param null $guard
* @return mixed
*/
public function user($guard = null)
{
if (!$this->auth) {
$this->auth = new CatchAuth;
}
try {
$user = $this->auth->guard($guard ? : config('catch.auth.default.guard'))->user();
if ($user->status == Users::DISABLE) {
throw new LoginFailedException('该用户已被禁用', Code::USER_FORBIDDEN);
}
} catch (\Exception $e) {
if ($e instanceof TokenExpiredException) {
throw new FailedException('token 过期', Code::LOGIN_EXPIRED);
}
if ($e instanceof TokenBlacklistException) {
throw new FailedException('token 被加入黑名单', Code::LOGIN_BLACKLIST);
}
if ($e instanceof TokenInvalidException) {
throw new FailedException('token 不合法', Code::LOST_LOGIN);
}
throw new FailedException('认证失败: '. $e->getMessage(), $e->getCode());
}
return $user;
}
}

2
app/common.php Normal file
View File

@ -0,0 +1,2 @@
<?php
// 应用公共文件

18
app/event.php Normal file
View File

@ -0,0 +1,18 @@
<?php
// 事件定义文件
return [
'bind' => [],
'listen' => [
'AppInit' => [],
'HttpRun' => [],
'HttpEnd' => [],
'LogLevel' => [],
'LogWrite' => [],
'RouteLoaded' => [],
],
'subscribe' => [
],
];

11
app/middleware.php Normal file
View File

@ -0,0 +1,11 @@
<?php
// 全局中间件定义文件
return [
// 全局请求缓存
// \think\middleware\CheckRequestCache::class,
// 多语言加载
// \think\middleware\LoadLangPack::class,
// Session初始化
// \think\middleware\SessionInit::class
\think\middleware\AllowCrossDomain::class,
];

9
app/provider.php Normal file
View File

@ -0,0 +1,9 @@
<?php
use app\ExceptionHandle;
use app\Request;
// 容器Provider定义文件
return [
'think\Request' => Request::class,
'think\exception\Handle' => ExceptionHandle::class,
];

4
app/service.php Normal file
View File

@ -0,0 +1,4 @@
<?php
return [
\catcher\CatchAdminService::class,
];

53
artisan
View File

@ -1,53 +0,0 @@
#!/usr/bin/env php
<?php
define('LARAVEL_START', microtime(true));
/*
|--------------------------------------------------------------------------
| Register The Auto Loader
|--------------------------------------------------------------------------
|
| Composer provides a convenient, automatically generated class loader
| for our application. We just need to utilize it! We'll require it
| into the script here so that we do not have to worry about the
| loading of any of our classes manually. It's great to relax.
|
*/
require __DIR__.'/vendor/autoload.php';
$app = require_once __DIR__.'/bootstrap/app.php';
/*
|--------------------------------------------------------------------------
| Run The Artisan Application
|--------------------------------------------------------------------------
|
| When we run the console application, the current CLI command will be
| executed in this console and the response sent back to a terminal
| or another output device for the developers. Here goes nothing!
|
*/
$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
$status = $kernel->handle(
$input = new Symfony\Component\Console\Input\ArgvInput,
new Symfony\Component\Console\Output\ConsoleOutput
);
/*
|--------------------------------------------------------------------------
| Shutdown The Application
|--------------------------------------------------------------------------
|
| Once Artisan has finished running, we will fire off the shutdown events
| so that any final work may be done by the application before we shut
| down the process. This is the last thing to happen to the request.
|
*/
$kernel->terminate($input, $status);
exit($status);

View File

@ -1,55 +0,0 @@
<?php
/*
|--------------------------------------------------------------------------
| Create The Application
|--------------------------------------------------------------------------
|
| The first thing we will do is create a new Laravel application instance
| which serves as the "glue" for all the components of Laravel, and is
| the IoC container for the system binding all of the various parts.
|
*/
$app = new Illuminate\Foundation\Application(
$_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);
/*
|--------------------------------------------------------------------------
| Bind Important Interfaces
|--------------------------------------------------------------------------
|
| Next, we need to bind some important interfaces into the container so
| we will be able to resolve them when needed. The kernels serve the
| incoming requests to this application from both the web and CLI.
|
*/
$app->singleton(
Illuminate\Contracts\Http\Kernel::class,
App\Http\Kernel::class
);
$app->singleton(
Illuminate\Contracts\Console\Kernel::class,
App\Console\Kernel::class
);
$app->singleton(
Illuminate\Contracts\Debug\ExceptionHandler::class,
App\Exceptions\Handler::class
);
/*
|--------------------------------------------------------------------------
| Return The Application
|--------------------------------------------------------------------------
|
| This script returns the application instance. The instance is given to
| the calling script so we can separate the building of the instances
| from the actual running of the application and sending responses.
|
*/
return $app;

View File

@ -1,49 +0,0 @@
<?php return array (
'laravel/tinker' =>
array (
'providers' =>
array (
0 => 'Laravel\\Tinker\\TinkerServiceProvider',
),
),
'nesbot/carbon' =>
array (
'providers' =>
array (
0 => 'Carbon\\Laravel\\ServiceProvider',
),
),
'nunomaduro/collision' =>
array (
'providers' =>
array (
0 => 'NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider',
),
),
'nunomaduro/termwind' =>
array (
'providers' =>
array (
0 => 'Termwind\\Laravel\\TermwindServiceProvider',
),
),
'pestphp/pest' =>
array (
'providers' =>
array (
0 => 'Pest\\Laravel\\PestServiceProvider',
),
),
'tymon/jwt-auth' =>
array (
'aliases' =>
array (
'JWTAuth' => 'Tymon\\JWTAuth\\Facades\\JWTAuth',
'JWTFactory' => 'Tymon\\JWTAuth\\Facades\\JWTFactory',
),
'providers' =>
array (
0 => 'Tymon\\JWTAuth\\Providers\\LaravelServiceProvider',
),
),
);

View File

@ -1,237 +0,0 @@
<?php return array (
'providers' =>
array (
0 => 'Illuminate\\Auth\\AuthServiceProvider',
1 => 'Illuminate\\Broadcasting\\BroadcastServiceProvider',
2 => 'Illuminate\\Bus\\BusServiceProvider',
3 => 'Illuminate\\Cache\\CacheServiceProvider',
4 => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
5 => 'Illuminate\\Cookie\\CookieServiceProvider',
6 => 'Illuminate\\Database\\DatabaseServiceProvider',
7 => 'Illuminate\\Encryption\\EncryptionServiceProvider',
8 => 'Illuminate\\Filesystem\\FilesystemServiceProvider',
9 => 'Illuminate\\Foundation\\Providers\\FoundationServiceProvider',
10 => 'Illuminate\\Hashing\\HashServiceProvider',
11 => 'Illuminate\\Mail\\MailServiceProvider',
12 => 'Illuminate\\Notifications\\NotificationServiceProvider',
13 => 'Illuminate\\Pagination\\PaginationServiceProvider',
14 => 'Illuminate\\Pipeline\\PipelineServiceProvider',
15 => 'Illuminate\\Queue\\QueueServiceProvider',
16 => 'Illuminate\\Redis\\RedisServiceProvider',
17 => 'Illuminate\\Auth\\Passwords\\PasswordResetServiceProvider',
18 => 'Illuminate\\Session\\SessionServiceProvider',
19 => 'Illuminate\\Translation\\TranslationServiceProvider',
20 => 'Illuminate\\Validation\\ValidationServiceProvider',
21 => 'Illuminate\\View\\ViewServiceProvider',
22 => 'Laravel\\Tinker\\TinkerServiceProvider',
23 => 'Carbon\\Laravel\\ServiceProvider',
24 => 'NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider',
25 => 'Termwind\\Laravel\\TermwindServiceProvider',
26 => 'Pest\\Laravel\\PestServiceProvider',
27 => 'Tymon\\JWTAuth\\Providers\\LaravelServiceProvider',
28 => 'Catch\\Providers\\CatchAdminServiceProvider',
29 => 'App\\Providers\\AppServiceProvider',
30 => 'App\\Providers\\AuthServiceProvider',
31 => 'App\\Providers\\EventServiceProvider',
32 => 'App\\Providers\\RouteServiceProvider',
),
'eager' =>
array (
0 => 'Illuminate\\Auth\\AuthServiceProvider',
1 => 'Illuminate\\Cookie\\CookieServiceProvider',
2 => 'Illuminate\\Database\\DatabaseServiceProvider',
3 => 'Illuminate\\Encryption\\EncryptionServiceProvider',
4 => 'Illuminate\\Filesystem\\FilesystemServiceProvider',
5 => 'Illuminate\\Foundation\\Providers\\FoundationServiceProvider',
6 => 'Illuminate\\Notifications\\NotificationServiceProvider',
7 => 'Illuminate\\Pagination\\PaginationServiceProvider',
8 => 'Illuminate\\Session\\SessionServiceProvider',
9 => 'Illuminate\\View\\ViewServiceProvider',
10 => 'Carbon\\Laravel\\ServiceProvider',
11 => 'NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider',
12 => 'Termwind\\Laravel\\TermwindServiceProvider',
13 => 'Pest\\Laravel\\PestServiceProvider',
14 => 'Tymon\\JWTAuth\\Providers\\LaravelServiceProvider',
15 => 'Catch\\Providers\\CatchAdminServiceProvider',
16 => 'App\\Providers\\AppServiceProvider',
17 => 'App\\Providers\\AuthServiceProvider',
18 => 'App\\Providers\\EventServiceProvider',
19 => 'App\\Providers\\RouteServiceProvider',
),
'deferred' =>
array (
'Illuminate\\Broadcasting\\BroadcastManager' => 'Illuminate\\Broadcasting\\BroadcastServiceProvider',
'Illuminate\\Contracts\\Broadcasting\\Factory' => 'Illuminate\\Broadcasting\\BroadcastServiceProvider',
'Illuminate\\Contracts\\Broadcasting\\Broadcaster' => 'Illuminate\\Broadcasting\\BroadcastServiceProvider',
'Illuminate\\Bus\\Dispatcher' => 'Illuminate\\Bus\\BusServiceProvider',
'Illuminate\\Contracts\\Bus\\Dispatcher' => 'Illuminate\\Bus\\BusServiceProvider',
'Illuminate\\Contracts\\Bus\\QueueingDispatcher' => 'Illuminate\\Bus\\BusServiceProvider',
'Illuminate\\Bus\\BatchRepository' => 'Illuminate\\Bus\\BusServiceProvider',
'Illuminate\\Bus\\DatabaseBatchRepository' => 'Illuminate\\Bus\\BusServiceProvider',
'cache' => 'Illuminate\\Cache\\CacheServiceProvider',
'cache.store' => 'Illuminate\\Cache\\CacheServiceProvider',
'cache.psr6' => 'Illuminate\\Cache\\CacheServiceProvider',
'memcached.connector' => 'Illuminate\\Cache\\CacheServiceProvider',
'Illuminate\\Cache\\RateLimiter' => 'Illuminate\\Cache\\CacheServiceProvider',
'Illuminate\\Foundation\\Console\\AboutCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Cache\\Console\\ClearCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Cache\\Console\\ForgetCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ClearCompiledCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Auth\\Console\\ClearResetsCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ConfigCacheCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ConfigClearCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\DbCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\MonitorCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\PruneCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\ShowCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\TableCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\WipeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\DownCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\EnvironmentCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\EnvironmentDecryptCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\EnvironmentEncryptCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\EventCacheCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\EventClearCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\EventListCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\KeyGenerateCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\OptimizeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\OptimizeClearCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\PackageDiscoverCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\ClearCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\ListFailedCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\FlushFailedCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\ForgetFailedCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\ListenCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\MonitorCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\PruneBatchesCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\PruneFailedJobsCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\RestartCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\RetryCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\RetryBatchCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\WorkCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\RouteCacheCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\RouteClearCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\RouteListCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\DumpCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\Seeds\\SeedCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Console\\Scheduling\\ScheduleFinishCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Console\\Scheduling\\ScheduleListCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Console\\Scheduling\\ScheduleRunCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Console\\Scheduling\\ScheduleClearCacheCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Console\\Scheduling\\ScheduleTestCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Console\\Scheduling\\ScheduleWorkCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ShowModelCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\StorageLinkCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\UpCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ViewCacheCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ViewClearCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Cache\\Console\\CacheTableCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\CastMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ChannelMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ComponentMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ConsoleMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Routing\\Console\\ControllerMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\DocsCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\EventGenerateCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\EventMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ExceptionMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\Factories\\FactoryMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\JobMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ListenerMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\MailMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Routing\\Console\\MiddlewareMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ModelMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\NotificationMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Notifications\\Console\\NotificationTableCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ObserverMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\PolicyMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ProviderMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\FailedTableCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\TableCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Queue\\Console\\BatchesTableCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\RequestMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ResourceMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\RuleMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ScopeMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\Seeds\\SeederMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Session\\Console\\SessionTableCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\ServeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\StubPublishCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\TestMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Foundation\\Console\\VendorPublishCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'migrator' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'migration.repository' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'migration.creator' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\Migrations\\MigrateCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\Migrations\\FreshCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\Migrations\\InstallCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\Migrations\\RefreshCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\Migrations\\ResetCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\Migrations\\RollbackCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\Migrations\\StatusCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'Illuminate\\Database\\Console\\Migrations\\MigrateMakeCommand' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'composer' => 'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider',
'hash' => 'Illuminate\\Hashing\\HashServiceProvider',
'hash.driver' => 'Illuminate\\Hashing\\HashServiceProvider',
'mail.manager' => 'Illuminate\\Mail\\MailServiceProvider',
'mailer' => 'Illuminate\\Mail\\MailServiceProvider',
'Illuminate\\Mail\\Markdown' => 'Illuminate\\Mail\\MailServiceProvider',
'Illuminate\\Contracts\\Pipeline\\Hub' => 'Illuminate\\Pipeline\\PipelineServiceProvider',
'queue' => 'Illuminate\\Queue\\QueueServiceProvider',
'queue.connection' => 'Illuminate\\Queue\\QueueServiceProvider',
'queue.failer' => 'Illuminate\\Queue\\QueueServiceProvider',
'queue.listener' => 'Illuminate\\Queue\\QueueServiceProvider',
'queue.worker' => 'Illuminate\\Queue\\QueueServiceProvider',
'redis' => 'Illuminate\\Redis\\RedisServiceProvider',
'redis.connection' => 'Illuminate\\Redis\\RedisServiceProvider',
'auth.password' => 'Illuminate\\Auth\\Passwords\\PasswordResetServiceProvider',
'auth.password.broker' => 'Illuminate\\Auth\\Passwords\\PasswordResetServiceProvider',
'translator' => 'Illuminate\\Translation\\TranslationServiceProvider',
'translation.loader' => 'Illuminate\\Translation\\TranslationServiceProvider',
'validator' => 'Illuminate\\Validation\\ValidationServiceProvider',
'validation.presence' => 'Illuminate\\Validation\\ValidationServiceProvider',
'command.tinker' => 'Laravel\\Tinker\\TinkerServiceProvider',
),
'when' =>
array (
'Illuminate\\Broadcasting\\BroadcastServiceProvider' =>
array (
),
'Illuminate\\Bus\\BusServiceProvider' =>
array (
),
'Illuminate\\Cache\\CacheServiceProvider' =>
array (
),
'Illuminate\\Foundation\\Providers\\ConsoleSupportServiceProvider' =>
array (
),
'Illuminate\\Hashing\\HashServiceProvider' =>
array (
),
'Illuminate\\Mail\\MailServiceProvider' =>
array (
),
'Illuminate\\Pipeline\\PipelineServiceProvider' =>
array (
),
'Illuminate\\Queue\\QueueServiceProvider' =>
array (
),
'Illuminate\\Redis\\RedisServiceProvider' =>
array (
),
'Illuminate\\Auth\\Passwords\\PasswordResetServiceProvider' =>
array (
),
'Illuminate\\Translation\\TranslationServiceProvider' =>
array (
),
'Illuminate\\Validation\\ValidationServiceProvider' =>
array (
),
'Laravel\\Tinker\\TinkerServiceProvider' =>
array (
),
),
);

View File

@ -0,0 +1,23 @@
<?php
// +----------------------------------------------------------------------
// | UCToo [ Universal Convergence Technology ]
// +----------------------------------------------------------------------
// | Copyright (c) 2014-2021 https://www.uctoo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: UCToo <contact@uctoo.com>
// +----------------------------------------------------------------------
namespace catchAdmin\apimanager;
use catcher\ModuleService;
class ApimanagerService extends ModuleService
{
public function loadRouteFrom()
{
// TODO: Implement loadRouteFrom() method.
return __DIR__ . DIRECTORY_SEPARATOR . 'route.php';
}
}

View File

@ -0,0 +1,96 @@
apimanager 模块是一个用于API管理、测试的模块。
# 概述
本模块的设计目标是提供开发人员、产品人员等相关角色可以管理和测试API可以将系统内部或外部API信息保存在系统内使得产品具有自完备的特性和持续交付的特性并可进行灵活的二次开发。
## 主要特性
1. 支持API分类管理支持自定义用户环境变量支持API测试用例管理。
2. 支持HTTP、HTTPS接口测试用例的在线运行。更多协议支持规划在模块roadmap中
3. 支持接口文档管理。
4. 已集成微信第三方平台相关接口测试用例,开发者可快速进行第三方平台应用开发。
5. 支持多帐号多应用使用环境,易于团队协作,不限制接口数量、用户数量、请求数量。
6. 基于catchadmin开发模块安装简单使用便捷支持模块数据导入导出。
7. 开源开放易于二次开发测试用例可共享形成产品API知识库。
8. 支持私有化部署、云原生部署。
9. 可视化管理系统路由列表并可与API测试工具结合可视化测试系统接口。
演示地址demo.uctoo.com 控制台使用demo帐号登录
模块使用界面截图:
<table>
<tr>
<td><img src="https://gitee.com/UCT_admin/materials/raw/master/uctoo_apitester/images/api%20category%20list.png"></td>
</tr>
<tr>
<td><img src="https://gitee.com/UCT_admin/materials/raw/master/uctoo_apitester/images/api%20category%20edit.png"></td>
</tr>
<tr>
<td><img src="https://gitee.com/UCT_admin/materials/raw/master/uctoo_apitester/images/api%20user%20env%20list.png"></td>
</tr>
<tr>
<td><img src="https://gitee.com/UCT_admin/materials/raw/master/uctoo_apitester/images/api%20user%20env%20edit.png"></td>
</tr>
<tr>
<td><img src="https://gitee.com/UCT_admin/materials/raw/master/uctoo_apitester/images/api%20test%20case%20list.png"></td>
</tr>
<tr>
<td><img src="https://gitee.com/UCT_admin/materials/raw/master/uctoo_apitester/images/api%20test%20case%20edit.png"></td>
</tr>
<tr>
<td><img src="https://gitee.com/UCT_admin/materials/raw/master/uctoo_apitester/images/apirun.png"></td>
</tr>
<tr>
<td><img src="https://gitee.com/UCT_admin/materials/raw/master/uctoo_apitester/images/routelist.png"></td>
</tr>
</table>
## 产品架构
1. 基于catchadmin标准模块开发方式开发可在管理后台一键安装模块和初始化模块数据。
2. 前端采用axios技术选型前端可形成标准客户端接口库。
3. 本地接口数据源类型local主要沿用catchadmin基于用户身份的接口鉴权方案需在API测试用例header添加authorization参数其值为登录接口返回的值。
4. 在扫码登录后注册用户帐号接口测试用例演示了采用微信扫码登录后获取到的用户access_token进行接口鉴权的示例。
5. 微信相关开发使用了[uctoo/think-easywechat SDK](https://gitee.com/UCT/think-easywechat) 集成catchadmin (TP6+VUE) 和 easywechat 4支持微信第三方平台、微信小程序云开发、微信支付服务商等特性。
## 安装教程
### 运行环境依赖
PHP >= 7.1.0
Mysql >= 5.5.0 (需支持innodb引擎)
PDO PHP Extension
MBstring PHP Extension
CURL PHP Extension
ZIP Extension
Composer
catchadmin
### 分步骤安装
1. 从https://gitee.com/jaguarjack/catchAdmin 或 https://gitee.com/uctoo/uctoo 下载https://gitee.com/uctoo/uctoo/tree/master/catch/apimanager 目录模块复制到catchadmin对应目录
2. apimanager/catch-admin-vue 目录内是模块前端vue项目代码复制到前端VUE项目对应目录注意如和原前端vue项目目录的文件有冲突需自行合并代码版本。
3. 前端package.json文件请谨慎覆盖原项目文件。请使用命令 npm install --save @smallwei/avue npm install --save vue-json-editor npm install --save vue-json-views 添加模块依赖等效于手动合并package.json版本。如模块新依赖了第三方组件需要在前端项目目录重新运行 yarn install 命令。
3. 登录管理后台,在系统管理->模块管理启用API管理模块即可安装模块和初始化模块数据。
## 使用手册
1. 可以通过API管理->API分类功能增删改查API分类。
2. 可以通过API环境变量功能增删改查用户环境变量。环境变量的key值以{{key}}方式定义在API测试用例中对应的{{key}}值将替换为环境变量的value值。每个用户可以创建多组环境变量可以切换当前选中的环境变量组。
3. 可以通过API列表功能增删改查API测试用例。api_url、header、body、query、auth字段支持环境变量。新增API测试用例时标识字段请与路由列表name字段保持一致以便API测试用例与路由一一对应快速检索。
4. 可以对已添加的API测试用例执行测试操作在API测试界面可以对api_url、header、body、query、auth等字段进行自定义编辑。发送按钮可以实际执行API测试用例获得接口返回值。
5. 可以使用路由列表->同步至数据库功能,将系统内所有路由信息保存至数据库,以便可视化管理和测试。与 php think route:list -m 命令相同效果。
6. 可以使用路由列表->API测试功能以路由name字段为请求参数跳转至API测试列表页面以便快速查询出对应的API测试用例进行API测试。需更新前端vue项目layout/mixin/formOperate.js文件修复了页面初始化传参bug
具体请参考 https://www.kancloud.cn/doc_uctoo/manual
## 开发说明
### 模块roadmap
1. 通过解析路由文件router.php中的数据自动生成系统接口system类型的所有测试用例。即实现系统接口的可视化测试。
2. 实现API管理功能即可通过界面配置进行基于appid的接口权限管理OAUTH2接口鉴权方案。
3. 实现API测试用例中API文档字段支持markdown编辑和展示。
4. 实现除POST、GET、PUT、DELETE之外的其他接口请求方式。
5. 实现全部content-type类型的支持。
6. 实现测试数据的保存、历史记录等功能。
7. 实现notify类型接口的测试目前还没有在市面上见过类似功能的产品但是实际开发中notify类型的接口在微信第三方平台、各种支付回调、硬件数据上传等很多场景都有遇到。
8. 实现API测试用例的公开共享、私有、保护有偿获取等特性。
具体请参考开源版开发手册 https://www.kancloud.cn/doc_uctoo/uctoo_dev 及 本开源项目示例

View File

@ -0,0 +1,116 @@
{
"name": "catch-admin",
"version": "4.4.0",
"description": "catch-admin manage system on element-admin-vue",
"author": "JaguarJack <njphper@gmail.com>",
"scripts": {
"dev": "vue-cli-service serve",
"lint": "eslint --ext .js,.vue src",
"build:prod": "vue-cli-service build",
"build:stage": "vue-cli-service build --mode staging",
"preview": "node build/index.js --preview",
"new": "plop",
"svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml",
"test:unit": "jest --clearCache && vue-cli-service test:unit",
"test:ci": "npm run lint && npm run test:unit"
},
"dependencies": {
"@form-create/element-ui": "^2.5.4",
"axios": "0.18.1",
"clipboard": "2.0.4",
"codemirror": "5.45.0",
"core-js": "3.6.5",
"driver.js": "0.9.5",
"dropzone": "5.5.1",
"echarts": "4.2.1",
"element-ui": "2.13.2",
"file-saver": "2.0.1",
"fuse.js": "3.4.4",
"js-cookie": "2.2.0",
"jsonlint": "1.6.3",
"jszip": "3.2.1",
"normalize.css": "7.0.0",
"nprogress": "0.2.0",
"path-to-regexp": "2.4.0",
"screenfull": "4.2.0",
"script-loader": "0.7.2",
"sortablejs": "1.8.4",
"vue": "2.6.10",
"vue-count-to": "1.0.13",
"vue-highlightjs": "^1.3.3",
"vue-router": "3.0.2",
"vue-splitpane": "1.0.4",
"vuedraggable": "2.20.0",
"vuex": "3.1.0",
"xlsx": "0.14.1"
},
"devDependencies": {
"@smallwei/avue": "^2.8.17",
"@vue/cli-plugin-babel": "4.4.4",
"@vue/cli-plugin-eslint": "4.4.4",
"@vue/cli-plugin-unit-jest": "4.4.4",
"@vue/cli-service": "4.4.4",
"@vue/test-utils": "1.0.0-beta.29",
"autoprefixer": "9.5.1",
"babel-eslint": "10.1.0",
"babel-jest": "^26.3.0",
"babel-plugin-dynamic-import-node": "2.3.3",
"chalk": "2.4.2",
"chokidar": "2.1.5",
"connect": "3.6.6",
"eslint": "6.7.2",
"eslint-plugin-vue": "6.2.2",
"highlight.js": "^10.2.0",
"html-webpack-plugin": "3.2.0",
"husky": "1.3.1",
"lint-staged": "8.1.5",
"lodash": "^4.17.20",
"mockjs": "1.0.1-beta3",
"plop": "2.3.0",
"runjs": "4.3.2",
"sass": "1.26.2",
"sass-loader": "8.0.2",
"script-ext-html-webpack-plugin": "2.1.3",
"serve-static": "1.13.2",
"svg-sprite-loader": "4.1.3",
"svgo": "1.2.0",
"vue-highlight.js": "^3.1.0",
"vue-json-editor": "^1.4.3",
"vue-json-views": "^1.3.0",
"vue-template-compiler": "2.6.10"
},
"browserslist": [
"> 1%",
"last 2 versions"
],
"bugs": {
"url": "https://github.com/JaguarJack/catch-admin-vue/issues"
},
"engines": {
"node": ">=8.9",
"npm": ">= 3.0.0"
},
"keywords": [
"vue",
"admin",
"dashboard",
"element-ui",
"boilerplate",
"admin-template",
"management-system"
],
"license": "MIT",
"lint-staged": {
"src/**/*.{js,vue}": [
"eslint --fix",
"git add"
]
},
"husky": {
"hooks": {}
},
"repository": {
"type": "git",
"url": "git+https://github.com/JaguarJack/catch-admin-vue"
}
}

View File

@ -0,0 +1,7 @@
import request from "@/utils/request";
export function userenvList() {
return request({
url: "/apiTesterUserenv",
method: "get"
});
}

View File

@ -0,0 +1,48 @@
import Vue from 'vue'
import 'normalize.css/normalize.css' // a modern alternative to CSS resets
import Element from 'element-ui'
import Avue from '@smallwei/avue'
import 'element-ui/lib/theme-chalk/index.css'
import './styles/element-variables.scss'
// import enLang from 'element-ui/lib/locale/lang/en'// 如果使用中文语言包请默认支持,无需额外引入,请删除该依赖
import '@/styles/index.scss' // global css
import App from './App'
import store from './store'
import router from './router'
import './icons' // icon
import './permission' // permission control
import './utils/error-log' // error log
import request from '@/utils/request'
import * as filters from './filters' // global filters
import catchAdmin from '@/components/Catch'
Vue.use(Element, {
size: 'small'// set element-ui default size
// locale: enLang // 如果使用中文,无需设置,请删除
})
window.axios = request;
Vue.use(Avue, { request });
// register global utility filters
Object.keys(filters).forEach(key => {
Vue.filter(key, filters[key])
})
// 后台启动
catchAdmin.boot()
Vue.config.productionTip = false
Vue.prototype.$http = request
Vue.prototype.admin = catchAdmin
new Vue({
el: '#app',
router,
store,
render: h => h(App)
})

View File

@ -0,0 +1,44 @@
<template>
<catch-table
:ref="table.ref"
:headers="table.headers"
:border="true"
:search="table.search"
:filter-params="table.filterParams"
:hide-pagination="true"
:form-create="formCreate"
:actions="table.actions"
:api-route="table.apiRoute"
:dialog-width="table.dialog.width"
default-expand-all
row-key="id"
:tree-props="table.tree.props"
/>
</template>
<script>
import renderTable from '@/views/render-table-form'
export default {
name:'apimanager_apicategory',
mixins: [renderTable],
data() {
return {
tableFrom: 'table/apimanager/ApiCategory',
}
},
methods: {
beforeSubmit(row) {
if (row.form.parent_id instanceof Array) {
row.form.parent_id = row.form.parent_id.length > 0 ? row.form.parent_id.pop() : 0
}
return row
},
afterHandleResponse() {
this.$http.get('table/apimanager/ApiCategory', {params: { only: 'form'}}).then(response => {
this.formCreate.rule = response.data.form
})
}
}
}
</script>

View File

@ -0,0 +1,217 @@
<template>
<div class="app-container">
<div class="filter-container">
<el-input v-model="queryParam.env_name" placeholder="环境名称" clearable class="filter-item form-search-input" />
<el-button class="filter-item search" icon="el-icon-search" @click="handleSearch">
搜索
</el-button>
<el-button class="filter-item" icon="el-icon-refresh" @click="handleRefresh">
重置
</el-button>
<el-button class="filter-item fr" type="primary" icon="el-icon-plus" @click="handleCreateEnv">
新增
</el-button>
</div>
<el-table ref="multipleTable" :data="data" tooltip-effect="dark" style="width: 100%" border fit @selection-change="handleSelectMulti">
<el-table-column type="selection" width="55" />
<el-table-column prop="env_name" label="环境名称" />
<el-table-column prop="selected" label="当前环境" />
<el-table-column prop="creator" label="创建人" />
<el-table-column prop="created_at" label="创建时间" />
<el-table-column prop="updated_at" label="更新时间" />
<el-table-column label="操作" fixed="right" width="300">
<template slot-scope="module">
<el-button type="primary" icon="el-icon-refresh" @click="selectAPIenv(module.row.id)" >切换</el-button>
<el-button type="primary" icon="el-icon-edit" @click="handleUpdate(module.row)" />
<el-button type="danger" icon="el-icon-delete" @click="handleDelete(module.row.id)" />
</template>
</el-table-column>
</el-table>
<el-pagination
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="paginate.current"
hide-on-single-page
:page-sizes="paginate.sizes"
:page-size="paginate.limit"
:layout="paginate.layout"
:total="paginate.total"/>
<!----------------------------------- 编辑 ---------------------------------------------->
<el-dialog :close-on-click-modal="false" :title="title" :visible.sync="formVisible" @close="handleCancel">
<el-form label-position="top" :ref="formName" :model="formFieldsData" :rules="rules">
<el-form-item label="env_name" :label-width="formLabelWidth" prop="env_name">
<el-input v-model="formFieldsData.env_name" placeholder="请输入环境名称" autocomplete="off" clearable />
</el-form-item>
<!-- <el-form-item label="appid" :label-width="formLabelWidth" prop="appid">
<el-input v-model="formFieldsData.appid" placeholder="请输入appid" autocomplete="off" clearable />
</el-form-item>
<el-form-item label="project_id" :label-width="formLabelWidth" prop="project_id">
<el-input v-model="formFieldsData.project_id" placeholder="请输入project_id" autocomplete="off" clearable />
</el-form-item> -->
<el-form-item label="env_json" :label-width="formLabelWidth" prop="env_json">
<avue-crud
ref="crudJSON"
:option="tableOption"
:data="jsonTableData"
@row-update="addUpdateJSON"
@row-del="rowDelJSON"
@row-save="rowSaveJSON"
>
<template slot-scope="{ row, index }" slot="menu">
<el-button
type="text"
size="small"
@click="rowCellJSON(row, index)"
>{{ row.$cellEdit ? "自定义保存" : "自定义修改" }}</el-button
>
</template>
</avue-crud>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="handleCancel"> </el-button>
<el-button type="primary" @click="handleSubmit"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import formOperate from '@/layout/mixin/formOperate'
import { parseTime } from '@/utils'
export default {
name:'apimanager_apienv',
mixins: [formOperate],
data() {
return {
formName: 'apiEnv',
formLabelWidth: '120px',
//
refreshRoute: true,
//
queryParam: {
env_name: '',
},
formVisible: false,
formFieldsData: {
env_name: '',
env_json: ''
},
url: 'apiTesterUserenv',
//
rules: {
env_name: [
{ required: true, message: '请输入环境名称' }
],
env_json: [
{ required: true, message: '请输入环境变量' }
]
},
jsonTableData:[],
tableOption: {
refreshBtn:false,
addBtn: false,
editBtn: false,
addRowBtn: true,
cancelBtn: false,
border: true,
column: [
{
label: "Key",
prop: "key",
cell: true,
rules: [
{
required: true,
message: "Key值示例:{{KeyName}}",
trigger: "blur"
}
]
},
{
label: "Value",
prop: "value",
cell: true,
rules: [
{
required: true,
message: "请输入Value值",
trigger: "blur"
}
]
}
]
},
}
},
watch:{
formFieldsData:{
deep:true,
handler(data){
if(data.env_json){
let obj = this.JsonToObject(data.env_json)
let arr = Object.entries(obj).map(item => {
return { key: item[0], value: item[1], $cellEdit: false };
});
this.jsonTableData = arr;
}
}
}
},
methods: {
handleCreateEnv(){
this.jsonTableData = []
this.handleCreate()
},
selectAPIenv(id) {
this.$http.get( 'apiTesterUserenv/selectAPIenv/' + id).then(response => {
this.$message.success(response.message)
this.handleRefresh()
})
},
// ApiBaseInfo Json Object
JsonToObject(json) {
if (json) {
let flag = /\'/.test(json);
if (flag) {
return JSON.parse(json.replace(/\'/gi, '"'));
} else {
return JSON.parse(json);
}
} else {
return null;
}
},
// JSON
rowCellJSON(row, index) {
this.$refs.crudJSON.rowCell(row, index);
},
// JSON
addUpdateJSON(form, index, done, loading) {
loading();
done();
},
// JSON
rowSaveJSON(form, done) {
done();
this.formFieldsData.env_json = this.handlerJson(this.jsonTableData);
},
// JSON
rowDelJSON(form, index, done) {
this.jsonTableData.splice(index, 1);
this.formFieldsData.env_json = this.handlerJson(this.jsonTableData);
},
handlerJson(arrData){
let cache = {};
arrData.forEach(item => {
cache[item.key] = item.value;
});
if(Object.keys(cache).length){
return JSON.stringify(cache);
}else{
return null;
}
}
}
}
</script>

View File

@ -0,0 +1,659 @@
<template>
<div class="run-container">
<el-card class="box-card">
<el-row style="margin-bottom:5px">
<el-button
@click="dialogTableVisible = true"
class="filter-item fr"
type="primary"
icon="el-icon-s-grid"
>
</el-button>
<el-select
style="margin-right:5px"
class="filter-item fr"
@change="changeUserenv"
v-model="currentEnvId"
placeholder="用户环境变量"
>
<el-option
:value="env.id"
:label="env.env_name"
v-for="env in userEnvInfos"
:key="env.id"
/>
</el-select>
</el-row>
<el-input
placeholder="请输入内容"
v-model="currentInputUrl"
class="input-with-select"
:disabled="userable"
>
<el-select
class="method_select"
:disabled="userable"
v-model="currentSelectMethod"
slot="prepend"
placeholder="请选择"
>
<el-option
v-for="(mth, index) in apiMethods"
:key="index"
:label="mth"
:value="mth"
></el-option>
</el-select>
<el-button
type="primary"
class="apisend"
slot="append"
icon="el-icon-s-promotion"
@click="_runapi"
:disabled="sendAble"
>发送</el-button
>
</el-input>
<el-tabs class="mt30 tab-liut" type="border-card">
<el-tab-pane label="Header">
<el-table :data="headerTableData">
<el-table-column width="50">
<template slot-scope="{ row }">
<el-checkbox v-model="row.open"></el-checkbox>
</template>
</el-table-column>
<el-table-column label="KEY">
<template slot-scope="{ row }">
<item-btn v-model="row.key" />
</template>
</el-table-column>
<el-table-column label="VALUE">
<template slot-scope="{ row }">
<item-btn :selectshow="true" v-model="row.value" />
</template>
</el-table-column>
<el-table-column width="50">
<template slot="header" slot-scope="scope">
<el-button
type="primary"
icon="el-icon-plus"
circle
@click="addRow(headerTableData, scope)"
></el-button>
</template>
<template slot-scope="{ row }">
<el-button
icon="el-icon-delete"
circle
type="danger"
@click="delRow(row, headerTableData)"
></el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="Query">
<el-table :data="queryTableData">
<el-table-column width="50">
<template slot-scope="{ row }">
<el-checkbox v-model="row.open"></el-checkbox>
</template>
</el-table-column>
<el-table-column label="KEY">
<template slot-scope="{ row }">
<item-btn v-model="row.key" />
</template>
</el-table-column>
<el-table-column label="VALUE">
<template slot-scope="{ row }">
<item-btn :selectshow="true" v-model="row.value" />
</template>
</el-table-column>
<el-table-column width="50">
<template slot="header" slot-scope="scope">
<el-button
type="primary"
icon="el-icon-plus"
circle
@click="addRow(queryTableData, scope)"
></el-button>
</template>
<template slot-scope="{ row }">
<el-button
icon="el-icon-delete"
circle
type="danger"
@click="delRow(row, queryTableData)"
></el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane label="Body">
<el-radio-group v-model="radio">
<el-radio :label="0">none</el-radio>
<el-radio :label="1">form-data</el-radio>
<el-radio :label="2">x-www-form-urlencoded</el-radio>
<el-radio :label="3">json</el-radio>
<el-radio :label="4">raw(json)</el-radio>
</el-radio-group>
<vue-json-editor
class="vjd"
v-if="radio === 4"
v-model="rawJson"
:mode="'code'"
lang="zh"
></vue-json-editor>
<el-table
v-else
v-loading="loading"
element-loading-text="This request dose not have a body"
element-loading-spinner="el-icon-warning"
element-loading-background="rgba(0, 0, 0, 0.8)"
:data="bodyTableData"
>
<el-table-column width="50">
<template slot-scope="{ row }">
<el-checkbox v-model="row.open"></el-checkbox>
</template>
</el-table-column>
<el-table-column label="KEY">
<template slot-scope="{ row }">
<item-btn v-model="row.key" />
</template>
</el-table-column>
<el-table-column label="VALUE">
<template slot-scope="{ row }">
<item-btn :selectshow="true" v-model="row.value" />
</template>
</el-table-column>
<el-table-column width="50">
<template slot="header" slot-scope="scope">
<el-button
type="primary"
icon="el-icon-plus"
circle
@click="addRow(bodyTableData, scope)"
></el-button>
</template>
<template slot-scope="{ row }">
<el-button
icon="el-icon-delete"
circle
type="danger"
@click="delRow(row, bodyTableData)"
></el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
</el-tabs>
<el-card v-if="json" class="box-card mt30">
<json-view :data="json" />
</el-card>
</el-card>
<el-dialog :title="currentUserEnvName" :visible.sync="dialogTableVisible">
<el-table :data="currentUserEnvJson" border style="width: 100%">
<el-table-column prop="key" label="变量" fit> </el-table-column>
<el-table-column prop="value" label="值" fit> </el-table-column>
</el-table>
</el-dialog>
</div>
</template>
<script>
import vueJsonEditor from "vue-json-editor";
import { userenvList } from "@/api/userenv";
import ItemBtn from "./itemBtn.vue";
import jsonView from "vue-json-views";
import axios from "axios";
import qs from "qs";
export default {
components: {
jsonView,
ItemBtn,
vueJsonEditor
},
data() {
return {
//
sendAble: false,
//
userable: false,
//
dialogTableVisible: false,
// Api Mthods
apiMethods: [
"POST",
"GET",
"PUT",
"PATCH",
"DELETE",
"COPY",
"HEAD",
"OPTIONS"
],
// Api
json: null,
// api
apiBaseInfo: {},
//
userEnvInfos: [],
// id
currentEnvId: null,
// Url
currentInputUrl: "",
// Api Method
currentSelectMethod: "GET",
regEnv: /\{\{(.+?)\}\}/g,
// body 0:none 1:form-data 2:x-www-form-urlencoded
radio: 2,
checked: true,
input: "",
// Body
bodyTableData: [],
headerTableData: [],
queryTableData: [],
radioLabel: ["none", "form-data", "x-www-form-urlencoded", "json"],
rawJson: {},
headers: null,
params: null
};
},
computed: {
//
currentUserEnvInfo() {
if (this.userEnvInfos.length !== 0) {
return this.userEnvInfos.filter(env => env.id === this.currentEnvId)[0];
} else {
return null;
}
},
//
currentUserEnvJson() {
if (this.currentUserEnvInfo && this.currentUserEnvInfo.env_json) {
let obj = JSON.parse(this.currentUserEnvInfo.env_json);
return Object.entries(obj).map(item => {
return { key: item[0], value: item[1] };
});
} else {
return null;
}
},
//
currentUserEnvName() {
if (this.currentUserEnvInfo && this.currentUserEnvInfo.env_name) {
return this.currentUserEnvInfo.env_name;
} else {
return "未定义名称";
}
},
// Map
currentUserEnvMap() {
if (this.currentUserEnvInfo && this.currentUserEnvInfo.env_json) {
let obj = JSON.parse(this.currentUserEnvInfo.env_json);
return obj;
} else {
return null;
}
},
// Apiurl (base_url + url)
currentApiUrl() {
// reg formula
let regEnv = /\{\{(.+?)\}\}/g;
// Url
let curInputUrl = this.currentInputUrl;
// 使
let flag = regEnv.test(curInputUrl);
if (flag) {
if (this.currentUserEnvMap) {
let new_url = this.replaceUserenv(curInputUrl); //curInputUrl.replace(regEnv,this.currentUserEnvMap["{{host}}"]);
return new_url;
} else {
return null;
}
} else {
return curInputUrl;
}
},
loading() {
return !Boolean(this.radio);
}
},
mounted() {
this.init();
},
methods: {
init() {
let id = this.$route.query.id;
this.$http.get("apitester/" + id).then(response => {
this.apiBaseInfo = response.data;
this.apiBaseInfo.body = this.apiBaseInfo.body.replace(/'/g, '"');
if (this.apiBaseInfo.body) {
// let resstr = this.apiBaseInfo.body
// .replace(/\\/g, "")
// .replace(/"{/g, "{")
// .replace(/}"/g, "}");
this.rawJson = JSON.parse(this.apiBaseInfo.body);
} else {
this.rawJson = {};
}
this.resetMethodAndUrl();
this.initTable();
});
userenvList().then(response => {
this.userEnvInfos = response.data;
if (response.data.length !== 0) {
response.data.forEach(env => {
if (env.selected) {
this.currentEnvId = env.id;
}
});
}
});
},
/**@dis 初始化table表格 */
initTable() {
let { header, query, body, content_type } = this.apiBaseInfo;
this.radio = this.radioLabel.findIndex(el => {
if (content_type.indexOf(el) !== -1) return true;
return false;
});
let resTable = [header, query, body].map(el => {
return el ? JSON.parse(el) : false;
});
[this.headerTableData, this.queryTableData, this.bodyTableData].some(
(el, index) => {
if (resTable[index] === false) return false;
for (const [key, value] of Object.entries(resTable[index])) {
if (typeof value === "string") {
el.push({ open: true, key: key.trim(), value: value.trim() });
} else if (typeof value === "object") {
el.push({
open: true,
key: key.trim(),
value: JSON.stringify(value)
});
} else {
el.push({ open: true, key: key.trim(), value });
}
}
}
);
},
watchParam(arr) {
let regEnv = /\{\{(.+?)\}\}/g;
let watchCeche = {};
arr.forEach(item => {
let regFlag = regEnv.test(item.value);
let cacheFlag = this.currentUserEnvMap[item.value];
if (regFlag && cacheFlag) {
watchCeche[item.key] = cacheFlag;
}
});
return watchCeche;
},
// Api
_runapi() {
this.requestBeforeHook();
switch (this.currentSelectMethod) {
case "POST":
this.apiPost();
break;
case "GET":
this.apiGet();
break;
case "PUT":
this.apiPut();
break;
case "DELETE":
this.apiDelete();
break;
case "PATCH":
case "COPY":
case "HEAD":
case "OPTIONS":
this.$notify({
title: "消息",
message: "通知开发人员进行扩展",
type: "info"
});
break;
default:
this.$notify({
title: "消息",
message: "平台版本暂时不支持该请求方法",
type: "info"
});
break;
}
},
headFactory(ctype, args) {
let product = null;
switch (ctype) {
case "x-www-form-urlencoded":
product = qs.stringify(args);
break;
case "form-data":
let data = new FormData();
for (const key in args) {
data.append(key, args[key]);
}
product = data;
break;
case "raw":
this.$notify({
title: "消息",
message: "平台版本暂时还未支持raw数据格式",
type: "info"
});
product = args;
break;
case "json":
default:
product = args;
break;
}
return product;
},
tbDataToObj(tb) {
const params = {};
tb.filter(el => el.open)
.map(el =>
Object.defineProperty({}, el.key, {
value: el.value,
writable: true,
enumerable: true,
configurable: true
})
)
.forEach(el => Object.assign(params, el));
return params;
},
async apiGet() {
let { status, data } = await axios
.get(this.currentApiUrl, {
headers: this.headers,
params: this.params
})
.catch(err => {
this.json = err;
this.$notify({
title: "失败",
message: "请求发送失败",
type: "error"
});
});
if (status === 200) {
this.json = data;
this.$notify({
title: "成功",
message: "请求发送成功",
type: "success"
});
}
},
async apiPost() {
const data =
this.radio === 4
? this.rawJson
: this.headFactory(
this.radioLabel[this.radio],
this.tbDataToObj(this.bodyTableData)
);
let res = await axios
.post(this.currentApiUrl, data, {
headers: this.headers,
params: this.params
})
.catch(err => {
this.json = {};
this.$notify({
title: "失败",
message: "请求发送失败",
type: "error"
});
});
if (res.status === 200) {
this.json = res.data;
this.$notify({
title: "成功",
message: "请求发送成功",
type: "success"
});
}
},
async apiPut() {
const data = this.headFactory(
this.radioLabel[this.radio],
this.tbDataToObj(this.bodyTableData)
);
let res = await axios
.put(this.currentApiUrl, data, {
headers: this.headers,
params: this.params
})
.catch(err => {
this.json = err;
this.$notify({
title: "失败",
message: "请求发送失败",
type: "error"
});
});
if (res.status === 200) {
this.json = res.data;
this.$notify({
title: "成功",
message: "请求发送成功",
type: "success"
});
}
},
async apiDelete() {
const data =
this.radio === 4
? this.rawJson
: this.headFactory(
this.radioLabel[this.radio],
this.tbDataToObj(this.bodyTableData)
);
let res = await axios
.delete(this.currentApiUrl, {
data,
headers: this.headers,
params: this.params
})
.catch(err => {
this.json = err;
this.$notify({
title: "失败",
message: "请求发送失败",
type: "error"
});
});
if (res.status === 200) {
this.json = res.data;
this.$notify({
title: "成功",
message: "请求发送成功",
type: "success"
});
}
},
requestBeforeHook() {
this.headers = this.watchParam(this.headerTableData);
this.params = Object.assign(
this.tbDataToObj(this.queryTableData),
this.watchParam(this.queryTableData)
);
},
//
changeUserenv(env) {
this.$http.get("apiTesterUserenv/selectAPIenv/" + env).then(response => {
this.$message.success(response.message);
});
},
// Api Methods Api Url
resetMethodAndUrl() {
this.currentInputUrl = this.apiBaseInfo.api_url;
this.currentSelectMethod = this.apiBaseInfo.methods.toLocaleUpperCase();
},
//
replaceUserenv(orgStr) {
let userEnv = this.currentUserEnvJson;
for (let envelement of userEnv) {
orgStr = orgStr.replace(envelement.key, envelement.value);
}
return orgStr;
},
/**@dis 删除行 */
delRow(row, _table) {
let index = _table.findIndex(_row => row === _row);
_table.splice(index, 1);
},
addRow(_table) {
_table.push({ open: false, key: "KEY", value: "VALUE" });
}
},
watch: {
// Api Url
currentApiUrl(url) {
let flag = /http|https/.test(url);
if (url && flag) {
this.sendAble = false;
} else {
this.sendAble = true;
}
}
}
};
</script>
<style lang="scss" scoped>
.run-container {
margin: 20px;
.method_select {
width: 100px;
}
.apisend {
background-color: #70b9eb;
color: white;
}
.mt30 {
margin-top: 30px;
}
.tab-liut {
min-height: 200px;
}
}
</style>
<style lang="scss">
.vjd {
.jsoneditor-vue {
height: 430px;
}
.jsoneditor-poweredBy {
display: none;
}
}
</style>

View File

@ -0,0 +1,114 @@
<template>
<div class="item-btn-container">
<el-row>
<el-col v-show="!selectValue" :span="18">
<el-button @click="clickBtn" v-if="isShow">{{ value }}</el-button>
<el-input
ref="inputRef"
placeholder="请输入内容"
v-else
:value="value"
@blur="inputBlur"
@input="value => this.$emit('input', value)"
></el-input>
</el-col>
<el-col v-show="selectValue" :span="18">
<el-tag v-if="filename" @close="delFile" closable type="success">{{
filename
}}</el-tag>
<el-upload
v-else
action="/upload/image"
:limit="1"
ref="upload"
:show-file-list="false"
:http-request="uploadGuard"
>
<el-button size="small" type="primary">点击上传</el-button>
</el-upload>
</el-col>
<el-col :span="6">
<el-select v-if="selectshow" v-model="selectValue">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
>
</el-option>
</el-select>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
props: {
value: {
type: [String, File, Number],
default() {
return null;
}
},
selectshow: {
value: Boolean,
default() {
return false;
}
}
},
data() {
return {
isShow: true,
options: [
{
value: false,
label: "Text"
},
{
value: true,
label: "File"
}
],
selectValue: false,
filename: ""
};
},
computed: {
uploadFile() {
return this.$refs.upload;
}
},
methods: {
/**@dis 切换状态 */
clickBtn() {
this.isShow = false;
this.$nextTick(() => {
this.$refs.inputRef.focus();
});
},
inputBlur() {
this.isShow = true;
},
uploadGuard({ file }) {
this.filename = file.name;
this.$emit("input", file);
},
delFile() {
this.filename = "";
this.$emit("input", null);
}
}
};
</script>
<style lang="scss">
.item-btn-container {
.el-button {
max-width: 100%;
overflow: hidden;
text-align: left;
}
}
</style>

View File

@ -0,0 +1,816 @@
<template>
<div class="app-container">
<el-row :gutter="12">
<el-col :span="6">
<el-card shadow="never">
<div slot="header" class="clearfix">
<span>分类</span>
</div>
<div class="block">
<el-tree
:data="apicategory"
:props="apicategoryProps"
node-key="id"
default-expand-all
:expand-on-click-node="false"
@node-click="getApicategoryData"
/>
</div>
</el-card>
</el-col>
<el-col :span="18">
<div class="filter-container">
<el-row>
<el-input
v-model="queryParam.api_title"
placeholder="名称"
clearable
class="filter-item form-search-input"
/>
<el-input
v-model="queryParam.api_name"
placeholder="标识"
clearable
class="filter-item form-search-input"
/>
<el-select
v-model="queryParam.type"
clearable
placeholder="请选择数据源类型"
class="filter-item"
style="margin-right: 5px"
>
<el-option value="1" label="remote" />
<el-option value="2" label="local" />
</el-select>
<el-button
class="filter-item fr"
icon="el-icon-refresh"
@click="handleRefresh"
>
重置
</el-button>
<el-button
style="margin-right: 5px"
class="filter-item fr search"
icon="el-icon-search"
@click="handleSearch"
>
搜索
</el-button>
</el-row>
<el-row style="margin-top: 5px">
<el-select
class="filter-item "
@change="changeUserenv"
v-model="userenvid"
placeholder="用户环境变量"
>
<el-option
:value="env.id"
:label="env.env_name"
v-for="env in userenvs"
:key="env.id"
/>
</el-select>
<el-button
class="filter-item fr"
type="primary"
icon="el-icon-plus"
@click="handleCreate"
>
新增
</el-button>
</el-row>
</div>
<el-button
v-if="this.selectedIds.length"
size="small"
class="filter-item mb-5"
type="danger"
icon="el-icon-delete"
@click="handleMultiDelete"
>
批量删除
</el-button>
<el-table
ref="multipleTable"
:data="data"
tooltip-effect="dark"
style="width: 100%"
border
fit
@selection-change="handleSelectMulti"
>
<el-table-column
type="selection"
width="55"
:selectable="selectInit"
/>
<el-table-column label="名称">
<template slot-scope="api">{{ api.row.api_title }}</template>
</el-table-column>
<el-table-column prop="methods" label="methods" />
<el-table-column prop="api_name" label="标识" />
<!-- <el-table-column prop="status" label="状态">
<template slot-scope="api">
<el-switch
v-if="api.row.id === 0"
v-model="api.row.status"
disabled
active-text="启用"
:active-value="1"
/>
<el-switch
v-else
v-model="api.row.status"
active-text="启用"
inactive-text="禁用"
:active-value="1"
:inactive-value="2"
@change="disOrEnableUser(api.row)"
/>
</template>
</el-table-column> -->
<el-table-column prop="type" label="数据源类型">
<template slot-scope="api">
<el-tag v-if="api.row.type === 1" type="success">remote</el-tag>
<el-tag v-if="api.row.type === 2" type="danger">local</el-tag>
</template>
</el-table-column>
<el-table-column prop="created_at" label="创建时间" />
<el-table-column label="操作" fixed="right" width="300">
<template slot-scope="api">
<el-button
type="primary"
icon="el-icon-refresh"
@click="testApi(api.row.id)"
>测试</el-button
>
<el-button
type="primary"
icon="el-icon-edit"
v-if="api.row.id === 0"
disabled
/>
<el-button
type="primary"
icon="el-icon-edit"
v-else
@click="beforeHandleUpdate(api.row)"
/>
<el-button
type="danger"
icon="el-icon-edit"
v-if="api.row.id === 0"
disabled
/>
<el-button
type="danger"
icon="el-icon-delete"
v-else
@click="handleDelete(api.row.id)"
/>
</template>
</el-table-column>
</el-table>
<el-pagination
background
class="pagination-container"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="paginate.current"
hide-on-single-page
:page-sizes="paginate.sizes"
:page-size="paginate.limit"
:layout="paginate.layout"
:total="paginate.total"
/>
</el-col>
</el-row>
<!----------------------------------- API ---------------------------------------------->
<el-dialog
:close-on-click-modal="false"
:title="title"
:visible.sync="formVisible"
:destroy-on-close="true"
@close="handleCancel()"
>
<el-form :ref="formName" :model="formFieldsData" :rules="rules">
<el-row :gutter="12">
<el-form-item
label="分类"
:label-width="formLabelWidth"
prop="category_id"
>
<el-cascader
v-model="formFieldsData.category_id"
:options="treeCategory.data"
:props="treeCategory.prop"
:show-all-levels="false"
style="width: 85%"
clearable
/>
</el-form-item>
<el-form-item label="type" :label-width="formLabelWidth" prop="type">
<el-select
v-model="formFieldsData.type"
style="width: 85%"
placeholder="请选择数据源类型"
>
<el-option
v-for="(item, key) in type"
:key="key"
:label="item"
:value="key"
/>
</el-select>
</el-form-item>
<el-form-item
label="名称"
:label-width="formLabelWidth"
prop="api_title"
>
<el-input
v-model="formFieldsData.api_title"
placeholder="请输入名称"
autocomplete="off"
clearable
/>
</el-form-item>
<el-form-item
label="标识路由name"
:label-width="formLabelWidth"
prop="api_name"
>
<el-input
v-model="formFieldsData.api_name"
placeholder="请输入英文唯一标识请与路由name字段一致"
autocomplete="off"
clearable
/>
</el-form-item>
<el-form-item
label="methods类型"
:label-width="formLabelWidth"
prop="methods"
>
<el-select
v-model="formFieldsData.methods"
placeholder="请选择methods类型"
>
<el-option
v-for="(item, key) in methodsTypes"
:key="key"
:label="item"
:value="key"
/>
</el-select>
</el-form-item>
<el-form-item
label="api_url"
:label-width="formLabelWidth"
prop="api_url"
>
<el-input
v-model="formFieldsData.api_url"
placeholder="请输入api地址"
autocomplete="off"
clearable
/>
</el-form-item>
<el-form-item label="Header">
<avue-crud
ref="crudHeader"
:option="tableOption"
:data="headerTableData"
@row-update="addUpdateHeader"
@row-del="rowDelHeader"
@row-save="rowSaveHeader"
>
<template slot-scope="{ row, index }" slot="menu">
<el-button
type="text"
size="small"
@click="rowCellHeader(row, index)"
>{{ row.$cellEdit ? "自定义保存" : "自定义修改" }}</el-button
>
</template>
</avue-crud>
</el-form-item>
<el-form-item label="Body">
<avue-crud
ref="crudBody"
:option="tableOption"
:data="bodyTableData"
@row-update="addUpdateBody"
@row-del="rowDelBody"
@row-save="rowSaveBody"
>
<template slot-scope="{ row, index }" slot="menu">
<el-button
type="text"
size="small"
@click="rowCellBody(row, index)"
>{{ row.$cellEdit ? "自定义保存" : "自定义修改" }}</el-button
>
</template>
</avue-crud>
</el-form-item>
<el-form-item label="Query">
<avue-crud
ref="crudQuery"
:option="tableOption"
:data="queryTableData"
@row-update="addUpdateQuery"
@row-del="rowDelQuery"
@row-save="rowSaveQuery"
>
<template slot-scope="{ row, index }" slot="menu">
<el-button
type="text"
size="small"
@click="rowCellQuery(row, index)"
>{{ row.$cellEdit ? "自定义保存" : "自定义修改" }}</el-button
>
</template>
</avue-crud>
</el-form-item>
<el-form-item label="Auth">
<avue-crud
ref="crudAuth"
:option="tableOption"
:data="authTableData"
@row-update="addUpdateAuth"
@row-del="rowDelAuth"
@row-save="rowSaveAuth"
>
<template slot-scope="{ row, index }" slot="menu">
<el-button
type="text"
size="small"
@click="rowCellAuth(row, index)"
>{{ row.$cellEdit ? "自定义保存" : "自定义修改" }}</el-button
>
</template>
</avue-crud>
</el-form-item>
<el-form-item
label="content-type"
:label-width="formLabelWidth"
prop="content_type"
>
<el-select
v-model="formFieldsData.content_type"
style="width: 85%"
placeholder="请选择content_type类型"
>
<el-option
v-for="(item, key) in content_types"
:key="key"
:label="item"
:value="key"
/>
</el-select>
</el-form-item>
<el-form-item
label="文档url"
:label-width="formLabelWidth"
prop="doc_url"
>
<el-input
v-model="formFieldsData.doc_url"
placeholder="请输入文档url地址"
autocomplete="off"
clearable
/>
</el-form-item>
<el-form-item
label="文档"
:label-width="formLabelWidth"
prop="document"
>
<el-input
type="textarea"
:rows="5"
v-model="formFieldsData.document"
placeholder="请输入文档内容markdown格式"
autocomplete="off"
clearable
/>
</el-form-item>
<el-form-item
label="示例请求数据"
:label-width="formLabelWidth"
prop="sample_data"
>
<el-input
type="textarea"
:rows="5"
v-model="formFieldsData.sample_data"
placeholder="请输入示例请求数据"
autocomplete="off"
clearable
/>
</el-form-item>
<el-form-item
label="示例返回数据"
:label-width="formLabelWidth"
prop="sample_result"
>
<el-input
type="textarea"
:rows="5"
v-model="formFieldsData.sample_result"
placeholder="请输入示例返回数据"
autocomplete="off"
clearable
/>
</el-form-item>
<el-form-item label="排序" :label-width="formLabelWidth" prop="sort">
<el-input-number
v-model="formFieldsData.sort"
:min="1"
:max="100000"
/>
</el-form-item>
<el-form-item label="状态" :label-width="formLabelWidth">
<el-radio v-model="formFieldsData.status" :label="1" checked
>已完成</el-radio
>
<el-radio v-model="formFieldsData.status" :label="2"
>待开发</el-radio
>
<el-radio v-model="formFieldsData.status" :label="3"
>开发中</el-radio
>
<el-radio v-model="formFieldsData.status" :label="4"
>已废弃</el-radio
>
</el-form-item>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="handleCancel()"> </el-button>
<el-button type="primary" @click="submit"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import formOperate from "@/layout/mixin/formOperate";
import { userenvList } from "@/api/userenv";
export default {
name: "apimanager_apitester",
mixins: [formOperate],
data() {
return {
formName: "apis",
//
refreshRoute: true,
apicategoryProps: {
label: "category_title"
},
formLabelWidth: "120px",
// api
queryParam: {
api_title: "",
api_name: "",
status: "",
type: "",
category_id: ""
},
formVisible: false,
formFieldsData: {
api_title: "",
api_name: "",
api_url: "",
category_id: 0,
type: "",
methods: "",
auth: "",
header: "",
query: "",
body: "",
doc_url: "",
document: "",
sample_data: "",
sample_result: "",
sort: "",
status: "",
content_type: "",
env_id: "",
memo: ""
},
url: "apitester",
data: [],
//
treeCategory: {
data: [],
default: [],
prop: {
label: "category_title",
value: "id",
emitPath: false,
checkStrictly: true
}
},
// methods
type: {
1: "remote",
2: "local"
},
// methods
methodsTypes: {
POST: "POST",
GET: "GET",
PUT: "PUT",
PATCH: "PATCH",
DELETE: "DELETE",
COPY: "COPY",
HEAD: "HEAD",
OPTIONS: "OPTIONS"
},
content_types: {
"application/x-www-form-urlencoded":
"application/x-www-form-urlencoded",
"application/json; charset=utf-8": "application/json; charset=utf-8",
"multipart/form-data": "multipart/form-data",
raw: "raw"
},
//
rules: {
api_title: [
{ required: true, message: "请输入名称", trigger: "blur" },
{ min: 3, max: 20, message: "长度在 3 到 20 个字符", trigger: "blur" }
],
api_name: [
{ required: true, message: "请输入英文唯一标识", trigger: "blur" }
]
},
//
apicategory: [],
userenvs: [],
userenvid: {},
// api form
headerTableData: [],
bodyTableData: [],
queryTableData: [],
authTableData: [],
// api form Options
tableOption: {
refreshBtn: false,
addBtn: false,
editBtn: false,
addRowBtn: true,
cancelBtn: false,
border: true,
column: [
{
label: "Key",
prop: "key",
cell: true,
rules: [
{
required: true,
message: "请输入Key值",
trigger: "blur"
}
]
},
{
label: "Value",
prop: "value",
cell: true,
rules: [
{
required: true,
message: "请输入Value值",
trigger: "blur"
}
]
}
]
}
};
},
//
mounted() {
this.$http.get("apicategory").then(response => {
this.apicategory = response.data;
});
userenvList().then(response => {
if (response.data.length !== 0) {
response.data.forEach(env => {
if (env.selected) {
this.userenvid = env.id;
}
});
}
this.userenvs = response.data;
});
},
methods: {
testApi(id) {
this.$router.push({ path: "/apimanager/apirun", query: { id } });
},
// API
getApicategoryData(data, node, self) {
this.queryParam.category_id = data.id;
this.handleSearch();
},
// /
disOrEnableApi(api) {
this.$http.put("apitester/switch/status/" + api.id).then(response => {
this.$message({
message: response.message,
type: "success"
});
});
},
beforeCreate() {
this.$http.get("apicategory").then(response => {
this.treeCategory.data = response.data;
});
},
beforeHandleUpdate(api) {
this.beforeCreate();
this.$http.get(this.url + "/" + api.id).then(response => {
const api = response.data;
this.handleUpdate(api);
});
},
selectInit(row, index) {
return row.id !== 0;
},
submit() {
this.handleSubmit();
},
onJsonChange(value) {
console.log("value:", value);
},
onJsonSave(value) {
console.log("value:", value);
},
changeUserenv(env) {
this.$http
.get("apiTesterUserenv/selectAPIenv/" + env)
.then(response => {});
},
// Header
rowCellHeader(row, index) {
this.$refs.crudHeader.rowCell(row, index);
},
rowCellBody(row, index) {
this.$refs.crudBody.rowCell(row, index);
},
rowCellQuery(row, index) {
this.$refs.crudQuery.rowCell(row, index);
},
rowCellAuth(row, index) {
this.$refs.crudAuth.rowCell(row, index);
},
// Header
addUpdateHeader(form, index, done, loading) {
loading();
done();
},
addUpdateBody(form, index, done, loading) {
loading();
done();
},
addUpdateQuery(form, index, done, loading) {
loading();
done();
},
addUpdateAuth(form, index, done, loading) {
loading();
done();
},
afterCancel() {
setTimeout(() => {
this.headerTableData = [];
this.bodyTableData = [];
this.queryTableData = [];
this.authTableData = [];
}, 400);
Object.keys(this.formFieldsData).forEach(k => {
switch (k) {
case "category_id":
this.formFieldsData[k] = null;
break;
case "type":
this.formFieldsData[k] = "1";
break;
default:
break;
}
});
},
// Header
rowSaveHeader(form, done) {
done();
let json = this.handlerTable(this.headerTableData);
this.formFieldsData.header = json;
},
rowSaveBody(form, done) {
done();
let json = this.handlerTable(this.bodyTableData);
this.formFieldsData.body = json;
},
rowSaveQuery(form, done) {
done();
let json = this.handlerTable(this.queryTableData);
this.formFieldsData.query = json;
},
rowSaveAuth(form, done) {
done();
let json = this.handlerTable(this.authTableData);
this.formFieldsData.auth = json;
},
// Header
rowDelHeader(form, index, done) {
this.headerTableData.splice(index, 1);
let json = this.handlerTable(this.headerTableData);
this.formFieldsData.header = json;
},
rowDelBody(form, index, done) {
this.bodyTableData.splice(index, 1);
let json = this.handlerTable(this.bodyTableData);
this.formFieldsData.body = json;
},
rowDelQuery(form, index, done) {
this.queryTableData.splice(index, 1);
let json = this.handlerTable(this.queryTableData);
this.formFieldsData.query = json;
},
rowDelAuth(form, index, done) {
this.authTableData.splice(index, 1);
let json = this.handlerTable(this.authTableData);
this.formFieldsData.auth = json;
},
// ApiBaseInfo Json Object
JsonToObject(json) {
if (json && json !== "") {
let flag = /\'/.test(json);
if (flag) {
return JSON.parse(json.replace(/\'/gi, '"'));
} else {
return JSON.parse(json);
}
} else {
return null;
}
},
initTableData(json) {
let obj = this.JsonToObject(json);
let arr = Object.entries(obj).map(item => {
return { key: item[0], value: item[1], $cellEdit: false };
});
return arr;
},
handlerTable(arr) {
let obj = {};
if (arr) {
arr.forEach(item => {
return (obj[item.key] = item.value);
});
}
if (Object.keys(obj).length) {
return JSON.stringify(obj);
} else {
return "";
}
}
},
watch: {
formFieldsData: {
handler(data) {
if (data.header) {
this.headerTableData = this.initTableData(data.header);
}
if (data.body) {
this.bodyTableData = this.initTableData(data.body);
}
if (data.auth) {
this.authTableData = this.initTableData(data.auth);
}
if (data.query) {
this.queryTableData = this.initTableData(data.query);
}
},
deep: true
}
}
};
</script>
<style>
.custom-tree-node {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
padding-right: 8px;
}
</style>

View File

@ -0,0 +1,159 @@
<template>
<div class="app-container">
<el-form ref="form" :model="queryParam" :inline="true">
<el-form-item prop="rule" label="rule" :label-width="formLabelWidth">
<el-input v-model="queryParam.rule" placeholder="rule" type="input"></el-input>
</el-form-item><el-form-item prop="route" label="route" :label-width="formLabelWidth">
<el-input v-model="queryParam.route" placeholder="route" type="input"></el-input>
</el-form-item><el-form-item prop="method" label="method" :label-width="formLabelWidth">
<el-input v-model="queryParam.method" placeholder="method" type="input"></el-input>
</el-form-item><el-form-item prop="name" label="name" :label-width="formLabelWidth">
<el-input v-model="queryParam.name" placeholder="name" type="input"></el-input>
</el-form-item><el-form-item prop="domain" label="domain" :label-width="formLabelWidth">
<el-input v-model="queryParam.domain" placeholder="domain" type="input"></el-input>
</el-form-item><el-form-item prop="option" label="option" :label-width="formLabelWidth">
<el-input v-model="queryParam.option" placeholder="option" type="input"></el-input>
</el-form-item><el-form-item prop="pattern" label="pattern" :label-width="formLabelWidth">
<el-input v-model="queryParam.pattern" placeholder="pattern" type="input"></el-input>
</el-form-item>
<el-form-item>
<el-button icon="el-icon-search" type="primary" @click="handleSearch">
查询
</el-button>
</el-form-item>
</el-form>
<el-divider content-position="center"></el-divider>
<div class="filter-container">
<el-row>
<el-col :span="12">
<div class="grid-content">
<el-button class="filter-item" icon="el-icon-refresh" @click="handleRefresh">刷新</el-button>
<!-- <el-button class="filter-item" type="primary" icon="el-icon-plus" @click="handleCreate()">添加</el-button> -->
<el-button type="primary" class="filter-item" icon="el-icon-refresh" @click="sync">
同步至数据库
</el-button>
<el-button v-if="this.selectedIds.length" size="small" class="filter-item mb-5" type="danger" icon="el-icon-delete" @click="handleMultiDelete">批量删除</el-button>
<el-button @click="clearFilter">清除所有过滤器</el-button>
</div>
</el-col>
<el-col :span="12">
<el-button icon="el-icon-info" circle @click="templateVersion" style="float: right; padding: 3px 0"></el-button>
<el-dropdown @command="handleTableCommand" style="float: right; padding: 3px 0">
<span class="el-dropdown-link">
<i class="el-icon-more el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="a">配置表格</el-dropdown-item>
<el-dropdown-item command="b" divided>移除</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<div class="grid-content">
<el-dropdown @command="handleDropdownCommand" style="float: right; padding: 3px 0">
<span class="el-dropdown-link">
<i class="el-icon-menu el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-checkbox-group v-model="checkList">
<el-dropdown-item command="a"><el-checkbox label="rule"></el-checkbox></el-dropdown-item><el-dropdown-item command="a"><el-checkbox label="route"></el-checkbox></el-dropdown-item><el-dropdown-item command="a"><el-checkbox label="method"></el-checkbox></el-dropdown-item><el-dropdown-item command="a"><el-checkbox label="name"></el-checkbox></el-dropdown-item><el-dropdown-item command="a"><el-checkbox label="domain"></el-checkbox></el-dropdown-item><el-dropdown-item command="a"><el-checkbox label="option"></el-checkbox></el-dropdown-item><el-dropdown-item command="a"><el-checkbox label="pattern"></el-checkbox></el-dropdown-item>
</el-checkbox-group>
</el-dropdown-menu>
</el-dropdown>
</div>
</el-col>
</el-row>
</div>
<el-table ref="multipleTable" :data="data" tooltip-effect="dark" style="width: 100%" fit @selection-change="handleSelectMulti">
<el-table-column type="selection" width="55" v-if="true"></el-table-column>
<el-table-column prop="rule" label="rule" sortable="true" v-if="true"></el-table-column>
<el-table-column prop="route" label="route" sortable="true" v-if="true"></el-table-column>
<el-table-column prop="method" label="method" sortable="true" v-if="true"></el-table-column>
<el-table-column prop="name" label="name" sortable="true" v-if="true"></el-table-column>
<el-table-column prop="domain" label="domain" sortable="true" v-if="true"></el-table-column>
<el-table-column prop="option" label="option" sortable="true" v-if="true"></el-table-column>
<el-table-column prop="pattern" label="pattern" sortable="true" v-if="true"></el-table-column>
<el-table-column prop="creator" label="创建人" v-if="true"></el-table-column>
<el-table-column prop="created_at" label="创建时间" v-if="true"></el-table-column>
<el-table-column prop="updated_at" label="更新时间" v-if="true"></el-table-column>
<el-table-column label="操作" v-if="true" fixed="right">
<template slot-scope="module">
<el-button type="primary" icon="el-icon-stopwatch" @click="testApi(module.row.name)">API测试</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination background @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="paginate.current" hide-on-single-page :page-sizes="paginate.sizes" :page-size="paginate.limit" :layout="paginate.layout" :total="paginate.total"></el-pagination>
<!----------------------------------- 新增/编辑 ---------------------------------------------->
<el-drawer ref="drawer" size="60%" :title="drawerTitle" :visible.sync="formVisible" :before-close="handleClose" direction="rtl" @close="handleCancel">
<div class="demo-drawer__content">
<el-form :ref="formName" :model="formFieldsData" :rules="rules">
<el-form-item prop="rule" label="rule" :label-width="formLabelWidth">
<el-input v-model="formFieldsData.rule" placeholder="rule" autocomplete="off" clearable type="input"></el-input>
</el-form-item><el-form-item prop="route" label="route" :label-width="formLabelWidth">
<el-input v-model="formFieldsData.route" placeholder="route" autocomplete="off" clearable type="input"></el-input>
</el-form-item><el-form-item prop="method" label="method" :label-width="formLabelWidth">
<el-input v-model="formFieldsData.method" placeholder="method" autocomplete="off" clearable type="input"></el-input>
</el-form-item><el-form-item prop="name" label="name" :label-width="formLabelWidth">
<el-input v-model="formFieldsData.name" placeholder="name" autocomplete="off" clearable type="input"></el-input>
</el-form-item><el-form-item prop="domain" label="domain" :label-width="formLabelWidth">
<el-input v-model="formFieldsData.domain" placeholder="domain" autocomplete="off" clearable type="input"></el-input>
</el-form-item><el-form-item prop="option" label="option" :label-width="formLabelWidth">
<el-input v-model="formFieldsData.option" placeholder="option" autocomplete="off" clearable type="input"></el-input>
</el-form-item><el-form-item prop="pattern" label="pattern" :label-width="formLabelWidth">
<el-input v-model="formFieldsData.pattern" placeholder="pattern" autocomplete="off" clearable type="input"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="drawer__footer">
<el-button @click="handleCancel"> </el-button>
<el-button type="primary" @click="handleSubmit" :loading="loading">{{ loading ? '提交中 ...' : '确 定' }}</el-button>
</div>
</div>
</el-drawer>
</div>
</template>
<script>
import formOperate from '@/layout/mixin/formOperate'
export default {
name:'apimanager_routeList',
mixins: [formOperate],
data() {
return {
url: 'routeList',
formName: 'route_list',
formLabelWidth: '120px',
//
queryParam: {
rule:'',route:'',method:'',name:'',domain:'',option:'',pattern:'',
},
formVisible: false,
formFieldsData: {
rule:'',route:'',method:'',name:'',domain:'',option:'',pattern:'',
},
loading: false,
checkList: [],
search: '',
drawerTitle: 'route_list',
form: {rule:'',route:'',method:'',name:'',domain:'',option:'',pattern:'', },
timer: null,
//
rules: {
}
}
},
mounted() {},
methods: {
testApi(name) {
let api_name = name.replace(/\\/g,"\\\\") //
this.$router.push({ path: "/apitester", query: { api_name} });
},
sync() {
this.$http.post('apimanager/routelist/sync').then(res => {
this.$message.success(res.message)
this.handleRefresh()
})
},
},
}
</script>

View File

@ -0,0 +1,9 @@
export default {
// api分类
apicategory: () => import('@/views/apimanager/apicategory'),
// api测试
apitester: () => import('@/views/apimanager/apitester'),
apirun: () => import('@/views/apimanager/apirun'),
apienv: () => import('@/views/apimanager/apienv'),
apimanager_routeList: () => import('@/views/apimanager/route_list/route_list'),
}

View File

@ -0,0 +1,83 @@
<?php
// +----------------------------------------------------------------------
// | UCToo [ Universal Convergence Technology ]
// +----------------------------------------------------------------------
// | Copyright (c) 2014-2021 https://www.uctoo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: UCToo <contact@uctoo.com>
// +----------------------------------------------------------------------
namespace catchAdmin\apimanager\controller;
use catcher\base\CatchController;
use catchAdmin\apimanager\model\ApiCategory as ApiCategoryModel;
use catcher\base\CatchRequest as Request;
use catcher\CatchResponse;
use catcher\exceptions\FailedException;
class ApiCategory extends CatchController
{
protected $ApiCategoryModel;
public function __construct(ApiCategoryModel $ApiCategoryModel)
{
$this->ApiCategoryModel = $ApiCategoryModel;
}
/**
* 列表
* @time 2021年05月19日 15:21
* @param Request $request
*/
public function index(): \think\response\Json
{
return CatchResponse::success($this->ApiCategoryModel->getList());
}
/**
* 保存信息
* @time 2021年05月19日 15:21
* @param Request $request
*/
public function save(Request $request) : \think\response\Json
{
return CatchResponse::success($this->ApiCategoryModel->storeBy($request->post()));
}
/**
* 读取
* @time 2021年05月19日 15:21
* @param $id
*/
public function read($id) : \think\Response
{
return CatchResponse::success($this->ApiCategoryModel->findBy($id));
}
/**
* 更新
* @time 2021年05月19日 15:21
* @param Request $request
* @param $id
*/
public function update(Request $request, $id) : \think\response\Json
{
return CatchResponse::success($this->ApiCategoryModel->updateBy($id, $request->post()));
}
/**
* 删除
* @time 2021年05月19日 15:21
* @param $id
*/
public function delete($id) : \think\Response
{
if ($this->ApiCategoryModel->where('parent_id', $id)->find()) {
throw new FailedException('存在子分类,无法删除');
}
return CatchResponse::success($this->ApiCategoryModel->deleteBy($id));
}
}

View File

@ -0,0 +1,79 @@
<?php
// +----------------------------------------------------------------------
// | UCToo [ Universal Convergence Technology ]
// +----------------------------------------------------------------------
// | Copyright (c) 2014-2021 https://www.uctoo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: UCToo <contact@uctoo.com>
// +----------------------------------------------------------------------
namespace catchAdmin\apimanager\controller;
use catcher\base\CatchRequest as Request;
use catcher\CatchResponse;
use catcher\base\CatchController;
use catchAdmin\apimanager\model\ApiTester as ApiTesterModel;
use catchAdmin\apimanager\model\ApiCategory;
class ApiTester extends CatchController
{
protected $ApiTesterModel;
public function __construct(ApiTesterModel $ApiTesterModel)
{
$this->ApiTesterModel = $ApiTesterModel;
}
/**
* 列表
* @time 2021年05月20日 11:41
* @param Request $request
*/
public function index(Request $request) : \think\Response
{
return CatchResponse::paginate($this->ApiTesterModel->getList());
}
/**
* 保存信息
* @time 2021年05月20日 11:41
* @param Request $request
*/
public function save(Request $request) : \think\Response
{
return CatchResponse::success($this->ApiTesterModel->storeBy($request->post()));
}
/**
* 读取
* @time 2021年05月20日 11:41
* @param $id
*/
public function read($id) : \think\Response
{
return CatchResponse::success($this->ApiTesterModel->findBy($id));
}
/**
* 更新
* @time 2021年05月20日 11:41
* @param Request $request
* @param $id
*/
public function update(Request $request, $id) : \think\Response
{
return CatchResponse::success($this->ApiTesterModel->updateBy($id, $request->post()));
}
/**
* 删除
* @time 2021年05月20日 11:41
* @param $id
*/
public function delete($id) : \think\Response
{
return CatchResponse::success($this->ApiTesterModel->deleteBy($id));
}
}

View File

@ -0,0 +1,78 @@
<?php
// +----------------------------------------------------------------------
// | UCToo [ Universal Convergence Technology ]
// +----------------------------------------------------------------------
// | Copyright (c) 2014-2021 https://www.uctoo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: UCToo <contact@uctoo.com>
// +----------------------------------------------------------------------
namespace catchAdmin\apimanager\controller;
use catcher\base\CatchRequest as Request;
use catcher\CatchResponse;
use catcher\base\CatchController;
use catchAdmin\apimanager\model\ApiTesterLog as ApiTesterLogModel;
class ApiTesterLog extends CatchController
{
protected $ApiTesterLogModel;
public function __construct(ApiTesterLogModel $ApiTesterLogModel)
{
$this->ApiTesterLogModel = $ApiTesterLogModel;
}
/**
* 列表
* @time 2021年06月10日 19:20
* @param Request $request
*/
public function index(Request $request) : \think\Response
{
return CatchResponse::paginate($this->ApiTesterLogModel->getList());
}
/**
* 保存信息
* @time 2021年06月10日 19:20
* @param Request $request
*/
public function save(Request $request) : \think\Response
{
return CatchResponse::success($this->ApiTesterLogModel->storeBy($request->post()));
}
/**
* 读取
* @time 2021年06月10日 19:20
* @param $id
*/
public function read($id) : \think\Response
{
return CatchResponse::success($this->ApiTesterLogModel->findBy($id));
}
/**
* 更新
* @time 2021年06月10日 19:20
* @param Request $request
* @param $id
*/
public function update(Request $request, $id) : \think\Response
{
return CatchResponse::success($this->ApiTesterLogModel->updateBy($id, $request->post()));
}
/**
* 删除
* @time 2021年06月10日 19:20
* @param $id
*/
public function delete($id) : \think\Response
{
return CatchResponse::success($this->ApiTesterLogModel->deleteBy($id));
}
}

View File

@ -0,0 +1,99 @@
<?php
// +----------------------------------------------------------------------
// | UCToo [ Universal Convergence Technology ]
// +----------------------------------------------------------------------
// | Copyright (c) 2014-2021 https://www.uctoo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: UCToo <contact@uctoo.com>
// +----------------------------------------------------------------------
namespace catchAdmin\apimanager\controller;
use catcher\base\CatchRequest as Request;
use catcher\CatchResponse;
use catcher\base\CatchController;
use catchAdmin\apimanager\model\ApiTesterUserenv as ApiTesterUserenvModel;
use think\facade\Log;
class ApiTesterUserenv extends CatchController
{
protected $ApiTesterUserenvModel;
public function __construct(ApiTesterUserenvModel $ApiTesterUserenvModel)
{
$this->ApiTesterUserenvModel = $ApiTesterUserenvModel;
}
/**
* 列表
* @time 2021年05月26日 18:28
* @param Request $request
*/
public function index(Request $request) : \think\Response
{
return CatchResponse::paginate($this->ApiTesterUserenvModel->getList());
}
/**
* 保存信息
* @time 2021年05月26日 18:28
* @param Request $request
*/
public function save(Request $request) : \think\Response
{
return CatchResponse::success($this->ApiTesterUserenvModel->storeBy($request->post()));
}
/**
* 读取
* @time 2021年05月26日 18:28
* @param $id
*/
public function read($id) : \think\Response
{
return CatchResponse::success($this->ApiTesterUserenvModel->findBy($id));
}
/**
* 更新
* @time 2021年05月26日 18:28
* @param Request $request
* @param $id
*/
public function update(Request $request, $id) : \think\Response
{
return CatchResponse::success($this->ApiTesterUserenvModel->updateBy($id, $request->post()));
}
/**
* 删除
* @time 2021年05月26日 18:28
* @param $id
*/
public function delete($id) : \think\Response
{
return CatchResponse::success($this->ApiTesterUserenvModel->deleteBy($id));
}
/**
* 切换API环境
* @param Request $request
* @param $id
*/
public function selectAPIenv(Request $request,$id = "") : \think\Response
{
if ($id)
{
$creator_id = $request->user()->id;
$this->ApiTesterUserenvModel->update(['selected' => 0], ['creator_id' => $creator_id]); //全不选
$res = $this->ApiTesterUserenvModel->update(['selected' => 1], ['id' => $id]); //选中当前
//设置为管理员当前选中的applet
if($res){
return CatchResponse::success($res,'切换API环境成功');
}
}
return CatchResponse::fail('切换API环境错误');
}
}

View File

@ -0,0 +1,93 @@
<?php
namespace catchAdmin\apimanager\controller;
use catcher\base\CatchRequest as Request;
use catcher\CatchResponse;
use catcher\base\CatchController;
use catchAdmin\apimanager\repository\RouteListRepository as RouteListModel;
use think\Response;
class RouteList extends CatchController
{
protected $routeListModel;
/**
*
* @time 2021/11/11 17:47
* @param RouteListModel $routeListModel
* @return mixed
*/
public function __construct(RouteListModel $routeListModel)
{
$this->routeListModel = $routeListModel;
}
/**
*
* @time 2021/11/11 17:47
* @return Response
*/
public function index() : Response
{
return CatchResponse::paginate($this->routeListModel->getList());
}
/**
*
* @time 2021/11/11 17:47
* @param Request $request
* @return Response
*/
public function save(Request $request) : Response
{
return CatchResponse::success($this->routeListModel->storeBy($request->post()));
}
/**
*
* @time 2021/11/11 17:47
* @param $id
* @return Response
*/
public function read($id) : Response
{
return CatchResponse::success($this->routeListModel->findBy($id));
}
/**
*
* @time 2021/11/11 17:47
* @param $id
* @param Request $request
* @return Response
*/
public function update($id, Request $request) : Response
{
return CatchResponse::success($this->routeListModel->updateBy($id, $request->post()));
}
/**
*
* @time 2021/11/11 17:47
* @param $id
* @return Response
*/
public function delete($id) : Response
{
return CatchResponse::success($this->routeListModel->deleteBy($id));
}
/**
* 同步
*
* @time 2021/11/11 17:47
* @return \think\response\Json
* @throws \Exception
*/
public function sync()
{
return CatchResponse::success($this->routeListModel->sync());
}
}

View File

@ -0,0 +1,45 @@
<?php
use think\migration\Migrator;
use think\migration\db\Column;
use Phinx\Db\Adapter\MysqlAdapter;
class ApiCategory extends Migrator
{
/**
* Change Method.
*
* Write your reversible migrations using this method.
*
* More information on writing migrations is available here:
* http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
*
* The following commands can be used in this method and Phinx will
* automatically reverse them when rolling back:
*
* createTable
* renameTable
* addColumn
* renameColumn
* addIndex
* addForeignKey
*
* Remember to call "create()" or "update()" and NOT "save()" when working
* with the Table class.
*/
public function change()
{
$table = $this->table('api_category', ['engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => 'API分类' ,'id' => 'id' ,'primary_key' => ['id']]);
$table->addColumn('category_title', 'string', ['limit' => 64,'null' => false,'default' => '','signed' => true,'comment' => '分类标题',])
->addColumn('parent_id', 'integer', ['limit' => MysqlAdapter::INT_REGULAR,'null' => false,'default' => 0,'signed' => true,'comment' => '父级ID',])
->addColumn('category_name', 'string', ['limit' => 128,'null' => false,'default' => '','signed' => true,'comment' => '分类唯一标识',])
->addColumn('status', 'boolean', ['null' => false,'default' => 1,'signed' => true,'comment' => '状态:1=正常;2=停用',])
->addColumn('sort', 'integer', ['limit' => MysqlAdapter::INT_REGULAR,'null' => false,'default' => 0,'signed' => true,'comment' => '排序字段',])
->addColumn('created_at', 'integer', ['limit' => MysqlAdapter::INT_REGULAR,'null' => false,'default' => 0,'signed' => false,'comment' => '创建时间',])
->addColumn('updated_at', 'integer', ['limit' => MysqlAdapter::INT_REGULAR,'null' => false,'default' => 0,'signed' => false,'comment' => '更新时间',])
->addColumn('deleted_at', 'integer', ['limit' => MysqlAdapter::INT_REGULAR,'null' => false,'default' => 0,'signed' => false,'comment' => '软删除字段',])
->addColumn('creator_id', 'integer', ['limit' => MysqlAdapter::INT_REGULAR,'null' => false,'default' => 0,'signed' => false,'comment' => '创建人ID',])
->addIndex(['category_name'], ['unique' => true,'name' => 'api_category_category_name'])
->create();
}
}

View File

@ -0,0 +1,60 @@
<?php
use think\migration\Migrator;
use think\migration\db\Column;
use Phinx\Db\Adapter\MysqlAdapter;
class ApiTester extends Migrator
{
/**
* Change Method.
*
* Write your reversible migrations using this method.
*
* More information on writing migrations is available here:
* http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
*
* The following commands can be used in this method and Phinx will
* automatically reverse them when rolling back:
*
* createTable
* renameTable
* addColumn
* renameColumn
* addIndex
* addForeignKey
*
* Remember to call "create()" or "update()" and NOT "save()" when working
* with the Table class.
*/
public function change()
{
$table = $this->table('api_tester', ['engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => 'api测试表' ,'id' => 'id' ,'primary_key' => ['id']]);
$table->addColumn('api_title', 'string', ['limit' => 128,'null' => false,'default' => '新建接口','signed' => true,'comment' => '标题',])
->addColumn('api_name', 'string', ['limit' => 128,'null' => false,'default' => '','signed' => true,'comment' => '英文唯一标识',])
->addColumn('category_id', 'integer', ['limit' => MysqlAdapter::INT_REGULAR,'null' => false,'default' => 0,'signed' => true,'comment' => '分类',])
->addColumn('type', 'boolean', ['null' => false,'default' => 1,'signed' => true,'comment' => '数据源类型:1=remote,2=local',])
->addColumn('appid', 'string', ['limit' => 64,'null' => true,'signed' => true,'comment' => 'appid',])
->addColumn('project_id', 'string', ['limit' => 64,'null' => true,'signed' => true,'comment' => '项目ID',])
->addColumn('api_url', 'string', ['limit' => 512,'null' => false,'default' => 'https://127.0.0.1','signed' => true,'comment' => 'API URL',])
->addColumn('methods', 'string', ['limit' => 128,'null' => false,'default' => 'POST','signed' => true,'comment' => '方法:POST,GET,PUT,PATCH,DELETE,COPY,HEAD,OPTIONS',])
->addColumn('auth', 'text', ['limit' => MysqlAdapter::TEXT_REGULAR,'null' => true,'signed' => true,'comment' => '鉴权',])
->addColumn('header', 'text', ['limit' => MysqlAdapter::TEXT_REGULAR,'null' => true,'signed' => true,'comment' => 'header',])
->addColumn('query', 'text', ['limit' => MysqlAdapter::TEXT_REGULAR,'null' => true,'signed' => true,'comment' => 'query',])
->addColumn('body', 'text', ['limit' => MysqlAdapter::TEXT_REGULAR,'null' => true,'signed' => true,'comment' => 'body',])
->addColumn('doc_url', 'string', ['limit' => 512,'null' => true,'signed' => true,'comment' => '文档URL',])
->addColumn('document', 'text', ['limit' => MysqlAdapter::TEXT_REGULAR,'null' => true,'signed' => true,'comment' => '文档',])
->addColumn('sample_data', 'text', ['limit' => MysqlAdapter::TEXT_REGULAR,'null' => true,'signed' => true,'comment' => '示例数据',])
->addColumn('sample_result', 'text', ['limit' => MysqlAdapter::TEXT_REGULAR,'null' => true,'signed' => true,'comment' => '示例返回数据',])
->addColumn('sort', 'integer', ['limit' => MysqlAdapter::INT_REGULAR,'null' => true,'signed' => true,'comment' => '排序',])
->addColumn('status', 'boolean', ['null' => false,'default' => 1,'signed' => true,'comment' => '状态:1=已完成,2=待开发,3=开发中,4=已废弃',])
->addColumn('content_type', 'string', ['limit' => 128,'null' => false,'default' => 'application/x-www-form-urlencoded','signed' => true,'comment' => 'content-type:application/x-www-form-urlencoded,multipart/form-data,raw',])
->addColumn('env_id', 'integer', ['limit' => MysqlAdapter::INT_REGULAR,'null' => true,'signed' => true,'comment' => '环境ID',])
->addColumn('memo', 'text', ['limit' => MysqlAdapter::TEXT_REGULAR,'null' => true,'signed' => true,'comment' => '备注',])
->addColumn('created_at', 'integer', ['limit' => MysqlAdapter::INT_REGULAR,'null' => false,'default' => 0,'signed' => false,'comment' => '创建时间',])
->addColumn('updated_at', 'integer', ['limit' => MysqlAdapter::INT_REGULAR,'null' => false,'default' => 0,'signed' => false,'comment' => '更新时间',])
->addColumn('deleted_at', 'integer', ['limit' => MysqlAdapter::INT_REGULAR,'null' => false,'default' => 0,'signed' => false,'comment' => '软删除字段',])
->addColumn('creator_id', 'integer', ['limit' => MysqlAdapter::INT_REGULAR,'null' => false,'default' => 0,'signed' => false,'comment' => '创建人ID',])
->create();
}
}

View File

@ -0,0 +1,44 @@
<?php
use think\migration\Migrator;
use think\migration\db\Column;
use Phinx\Db\Adapter\MysqlAdapter;
class ApiTesterUserenv extends Migrator
{
/**
* Change Method.
*
* Write your reversible migrations using this method.
*
* More information on writing migrations is available here:
* http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
*
* The following commands can be used in this method and Phinx will
* automatically reverse them when rolling back:
*
* createTable
* renameTable
* addColumn
* renameColumn
* addIndex
* addForeignKey
*
* Remember to call "create()" or "update()" and NOT "save()" when working
* with the Table class.
*/
public function change()
{
$table = $this->table('api_tester_userenv', ['engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => 'API测试用户环境' ,'id' => 'id' ,'primary_key' => ['id']]);
$table->addColumn('env_name', 'string', ['limit' => 128,'null' => false,'default' => '','signed' => true,'comment' => '环境名称',])
->addColumn('appid', 'string', ['limit' => 64,'null' => false,'default' => '','signed' => true,'comment' => 'appid',])
->addColumn('project_id', 'string', ['limit' => 64,'null' => false,'default' => '','signed' => true,'comment' => '项目ID',])
->addColumn('env_json', 'text', ['limit' => MysqlAdapter::TEXT_REGULAR,'null' => false,'signed' => true,'comment' => '环境变量json',])
->addColumn('selected', 'boolean', ['null' => false,'default' => 0,'signed' => true,'comment' => '是否当前选中:0=否,1=是',])
->addColumn('created_at', 'integer', ['limit' => MysqlAdapter::INT_REGULAR,'null' => false,'default' => 0,'signed' => false,'comment' => '创建时间',])
->addColumn('updated_at', 'integer', ['limit' => MysqlAdapter::INT_REGULAR,'null' => false,'default' => 0,'signed' => false,'comment' => '更新时间',])
->addColumn('deleted_at', 'integer', ['limit' => MysqlAdapter::INT_REGULAR,'null' => false,'default' => 0,'signed' => false,'comment' => '软删除字段',])
->addColumn('creator_id', 'integer', ['limit' => MysqlAdapter::INT_REGULAR,'null' => false,'default' => 0,'signed' => false,'comment' => '创建人ID',])
->create();
}
}

View File

@ -0,0 +1,46 @@
<?php
use think\migration\Migrator;
use think\migration\db\Column;
use Phinx\Db\Adapter\MysqlAdapter;
class ApiTesterLog extends Migrator
{
/**
* Change Method.
*
* Write your reversible migrations using this method.
*
* More information on writing migrations is available here:
* http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
*
* The following commands can be used in this method and Phinx will
* automatically reverse them when rolling back:
*
* createTable
* renameTable
* addColumn
* renameColumn
* addIndex
* addForeignKey
*
* Remember to call "create()" or "update()" and NOT "save()" when working
* with the Table class.
*/
public function change()
{
$table = $this->table('api_tester_log', ['engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => 'API测试记录' ,'id' => 'id' ,'primary_key' => ['id']]);
$table->addColumn('appid', 'string', ['limit' => 50,'null' => true,'signed' => true,'comment' => 'appid',])
->addColumn('user_id', 'integer', ['limit' => MysqlAdapter::INT_REGULAR,'null' => true,'signed' => true,'comment' => 'users表id',])
->addColumn('api_id', 'integer', ['limit' => MysqlAdapter::INT_REGULAR,'null' => true,'signed' => true,'comment' => 'api_tester表id',])
->addColumn('params', 'text', ['limit' => MysqlAdapter::TEXT_REGULAR,'null' => true,'signed' => true,'comment' => 'api参数',])
->addColumn('result', 'text', ['limit' => MysqlAdapter::TEXT_REGULAR,'null' => true,'signed' => true,'comment' => '返回值',])
->addColumn('request_data', 'text', ['limit' => MysqlAdapter::TEXT_REGULAR,'null' => true,'signed' => true,'comment' => '请求数据',])
->addColumn('response_data', 'text', ['limit' => MysqlAdapter::TEXT_REGULAR,'null' => true,'signed' => true,'comment' => '响应数据',])
->addColumn('created_at', 'integer', ['limit' => MysqlAdapter::INT_REGULAR,'null' => false,'default' => 0,'signed' => false,'comment' => '创建时间',])
->addColumn('updated_at', 'integer', ['limit' => MysqlAdapter::INT_REGULAR,'null' => false,'default' => 0,'signed' => false,'comment' => '更新时间',])
->addColumn('deleted_at', 'integer', ['limit' => MysqlAdapter::INT_REGULAR,'null' => false,'default' => 0,'signed' => false,'comment' => '软删除字段',])
->addColumn('creator_id', 'integer', ['limit' => MysqlAdapter::INT_REGULAR,'null' => false,'default' => 0,'signed' => false,'comment' => '创建人ID',])
->create();
}
}

View File

@ -0,0 +1,48 @@
<?php
use think\migration\Migrator;
use think\migration\db\Column;
use Phinx\Db\Adapter\MysqlAdapter;
class RouteList extends Migrator
{
/**
* Change Method.
*
* Write your reversible migrations using this method.
*
* More information on writing migrations is available here:
* http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class
*
* The following commands can be used in this method and Phinx will
* automatically reverse them when rolling back:
*
* createTable
* renameTable
* addColumn
* renameColumn
* addIndex
* addForeignKey
*
* Remember to call "create()" or "update()" and NOT "save()" when working
* with the Table class.
*/
public function change()
{
$table = $this->table('route_list', ['engine' => 'InnoDB', 'collation' => 'utf8mb4_general_ci', 'comment' => '路由表' ,'id' => 'id' ,'primary_key' => ['id']]);
$table->addColumn('rule', 'string', ['limit' => 128,'null' => true,'signed' => true,'comment' => 'rule',])
->addColumn('route', 'string', ['limit' => 256,'null' => true,'signed' => true,'comment' => 'route',])
->addColumn('method', 'string', ['limit' => 16,'null' => true,'signed' => true,'comment' => 'method',])
->addColumn('name', 'string', ['limit' => 256,'null' => true,'signed' => true,'comment' => 'name',])
->addColumn('domain', 'string', ['limit' => 128,'null' => true,'signed' => true,'comment' => 'domain',])
->addColumn('option', 'string', ['limit' => 256,'null' => true,'signed' => true,'comment' => 'option',])
->addColumn('pattern', 'string', ['limit' => 128,'null' => true,'signed' => true,'comment' => 'pattern',])
->addColumn('title', 'string', ['limit' => 128,'null' => true,'signed' => true,'comment' => 'title',])
->addColumn('created_at', 'integer', ['limit' => MysqlAdapter::INT_REGULAR,'null' => false,'default' => 0,'signed' => false,'comment' => '创建时间',])
->addColumn('updated_at', 'integer', ['limit' => MysqlAdapter::INT_REGULAR,'null' => false,'default' => 0,'signed' => false,'comment' => '更新时间',])
->addColumn('deleted_at', 'integer', ['limit' => MysqlAdapter::INT_REGULAR,'null' => false,'default' => 0,'signed' => false,'comment' => '软删除字段',])
->addColumn('creator_id', 'integer', ['limit' => MysqlAdapter::INT_REGULAR,'null' => false,'default' => 0,'signed' => false,'comment' => '创建人ID',])
->addIndex(['name'], ['unique' => true,'name' => 'route_list_name'])
->create();
}
}

View File

@ -0,0 +1,98 @@
<?php
// +----------------------------------------------------------------------
// | UCToo [ Universal Convergence Technology ]
// +----------------------------------------------------------------------
// | Copyright (c) 2014-2021 https://www.uctoo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: UCToo <contact@uctoo.com>
// +----------------------------------------------------------------------
use think\migration\Seeder;
class ApiCategorySeed extends Seeder
{
/**
* Run Method.
*
* Write your database seeder using this method.
*
* More information on writing seeders is available here:
* http://docs.phinx.org/en/latest/seeding.html
*/
public function run(): void
{
$data = array (
0 =>
array (
'id' => 1,
'category_title' => '微信第三方平台',
'parent_id' => 0,
'category_name' => 'wechatopen',
'status' => 1,
'sort' => 1,
'created_at' => 1621414770,
'updated_at' => 1621414770,
'deleted_at' => 0,
'creator_id' => 1,
),
1 =>
array (
'id' => 2,
'category_title' => '微信交易组件标准版',
'parent_id' => 1,
'category_name' => 'MiniShop_Base',
'status' => 1,
'sort' => 1,
'created_at' => 1621415897,
'updated_at' => 1621415897,
'deleted_at' => 0,
'creator_id' => 1,
),
2 =>
array (
'id' => 3,
'category_title' => '腾讯AI开放平台',
'parent_id' => 0,
'category_name' => 'tencentAI',
'status' => 1,
'sort' => 1,
'created_at' => 1621493345,
'updated_at' => 1621493345,
'deleted_at' => 0,
'creator_id' => 1,
),
3 =>
array (
'id' => 4,
'category_title' => '批量代云开发',
'parent_id' => 1,
'category_name' => 'componenttcb',
'status' => 1,
'sort' => 1,
'created_at' => 1621494287,
'updated_at' => 1621494287,
'deleted_at' => 0,
'creator_id' => 1,
),
4 =>
array (
'id' => 5,
'category_title' => '本地接口',
'parent_id' => 0,
'category_name' => 'local',
'status' => 1,
'sort' => 2,
'created_at' => 1621494287,
'updated_at' => 1621494287,
'deleted_at' => 0,
'creator_id' => 1,
),
);
foreach ($data as $item) {
\catchAdmin\apimanager\model\ApiCategory::create($item);
}
}
}

View File

@ -0,0 +1,785 @@
<?php
// +----------------------------------------------------------------------
// | UCToo [ Universal Convergence Technology ]
// +----------------------------------------------------------------------
// | Copyright (c) 2014-2021 https://www.uctoo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: UCToo <contact@uctoo.com>
// +----------------------------------------------------------------------
use think\migration\Seeder;
class ApiTesterSeed extends Seeder
{
/**
* Run Method.
*
* Write your database seeder using this method.
*
* More information on writing seeders is available here:
* http://docs.phinx.org/en/latest/seeding.html
*/
public function run(): void
{
$data = array (
0 =>
array (
'id' => 1,
'api_title' => '获取类目详情',
'api_name' => 'product/category/get',
'category_id' => 2,
'type' => 1,
'appid' => '',
'project_id' => '',
'api_url' => '{{host}}/api/wechatopen/product/category/get',
'methods' => 'POST',
'auth' => NULL,
'header' => '{"authorization":"{{authorization}}"}',
'query' => '{"appid":"{{appid}}"}',
'body' => '',
'doc_url' => 'https://developers.weixin.qq.com/miniprogram/dev/framework/ministore/minishopopencomponent/API/cat/get_cat_list.html',
'document' => '参考文档url',
'sample_data' => '无',
'sample_result' => '无',
'sort' => 2,
'status' => 1,
'content_type' => 'application/x-www-form-urlencoded',
'env_id' => 0,
'memo' => NULL,
'created_at' => 1621484754,
'updated_at' => 1622461634,
'deleted_at' => 0,
'creator_id' => 1,
),
1 =>
array (
'id' => 2,
'api_title' => '获取品牌列表',
'api_name' => 'product/brand/get',
'category_id' => 2,
'type' => 1,
'appid' => '',
'project_id' => '',
'api_url' => '{{host}}/api/wechatopen/product/brand/get',
'methods' => 'POST',
'auth' => '',
'header' => '{"authorization":"{{authorization}}"}',
'query' => '{"appid":"{{appid}}"}',
'body' => '',
'doc_url' => 'https://developers.weixin.qq.com/miniprogram/dev/framework/ministore/minishopopencomponent/API/cat/get_brand.html',
'document' => '详见文档url地址',
'sample_data' => '无',
'sample_result' => '返回',
'sort' => 1,
'status' => 1,
'content_type' => 'application/x-www-form-urlencoded',
'env_id' => 0,
'memo' => NULL,
'created_at' => 1621485017,
'updated_at' => 1622378856,
'deleted_at' => 0,
'creator_id' => 1,
),
2 =>
array (
'id' => 3,
'api_title' => '登录日志',
'api_name' => '/log/login',
'category_id' => 5,
'type' => 2,
'appid' => '',
'project_id' => '',
'api_url' => '{{host}}/log/login',
'methods' => 'GET',
'auth' => '',
'header' => '{"authorization":"{{authorization}}"}',
'query' => '',
'body' => '',
'doc_url' => 'http://apidoc.catchadmin.com/web/#/5?page_id=23',
'document' => '无',
'sample_data' => '无',
'sample_result' => '无',
'sort' => 1,
'status' => 1,
'content_type' => 'application/x-www-form-urlencoded',
'env_id' => 0,
'memo' => '',
'created_at' => 1621598744,
'updated_at' => 1622965477,
'deleted_at' => 0,
'creator_id' => 1,
),
3 =>
array (
'id' => 4,
'api_title' => '操作日志',
'api_name' => 'log/operate',
'category_id' => 5,
'type' => 2,
'appid' => '',
'project_id' => '',
'api_url' => '{{host}}/log/operate',
'methods' => 'GET',
'auth' => '',
'header' => '{"authorization":"{{authorization}}"}',
'query' => '',
'body' => '',
'doc_url' => 'http://apidoc.catchadmin.com/web/#/5?page_id=53',
'document' => '无',
'sample_data' => '无',
'sample_result' => '无',
'sort' => 1,
'status' => 1,
'content_type' => 'application/x-www-form-urlencoded',
'env_id' => 0,
'memo' => '',
'created_at' => 1621598935,
'updated_at' => 1622965460,
'deleted_at' => 0,
'creator_id' => 1,
),
4 =>
array (
'id' => 5,
'api_title' => '获取运费模板',
'api_name' => 'product/delivery/get_freight_template',
'category_id' => 2,
'type' => 1,
'appid' => '',
'project_id' => '',
'api_url' => '{{host}}/api/wechatopen/product/delivery/get_freight_template',
'methods' => 'POST',
'auth' => '',
'header' => '{"authorization":"{{authorization}}"}',
'query' => '{"appid":"{{appid}}"}',
'body' => '',
'doc_url' => 'https://developers.weixin.qq.com/doc/ministore/minishopopencomponent/API/cat/get_freight_template.html',
'document' => '详见文档URL',
'sample_data' => '无',
'sample_result' => '无',
'sort' => 1,
'status' => 1,
'content_type' => 'application/x-www-form-urlencoded',
'env_id' => 0,
'memo' => '',
'created_at' => 1621599469,
'updated_at' => 1622378826,
'deleted_at' => 0,
'creator_id' => 1,
),
5 =>
array (
'id' => 6,
'api_title' => '获取店铺的商品分类',
'api_name' => 'product/store/get_shopcat',
'category_id' => 2,
'type' => 1,
'appid' => '',
'project_id' => '',
'api_url' => '{{host}}/api/wechatopen/product/store/get_shopcat',
'methods' => 'POST',
'auth' => '',
'header' => '{"authorization":"{{authorization}}"}',
'query' => '{"appid":"{{appid}}"}',
'body' => '',
'doc_url' => 'https://developers.weixin.qq.com/miniprogram/dev/framework/ministore/minishopopencomponent/API/store/get_shopcat.html',
'document' => '参考文档url',
'sample_data' => '无',
'sample_result' => '无',
'sort' => 1,
'status' => 1,
'content_type' => 'application/x-www-form-urlencoded',
'env_id' => 0,
'memo' => '',
'created_at' => 1621762669,
'updated_at' => 1622378816,
'deleted_at' => 0,
'creator_id' => 1,
),
6 =>
array (
'id' => 7,
'api_title' => '添加商品',
'api_name' => 'product/spu/add',
'category_id' => 2,
'type' => 1,
'appid' => '',
'project_id' => '',
'api_url' => '{{host}}/api/wechatopen/product/spu/add',
'methods' => 'POST',
'auth' => '',
'header' => '{"authorization":"{{authorization}}"}',
'query' => '{"appid":"{{appid}}"}',
'body' => '{
"out_product_id": "1234566",
"title": "任天堂 Nintendo Switch 国行续航增强版 NS家用体感游戏机掌机 便携掌上游戏机 红蓝主机",
"sub_title": "JD自营更放心】【国行Switch更安心的保修服务更快的国行服务器】一台主机三种模式游戏掌机随时随地一起趣玩。",
"head_img":
[
"http://img10.360buyimg.com/n1/s450x450_jfs/t1/85865/39/13611/488083/5e590a40E4bdf69c0/55c9bf645ea2b727.jpg"
],
"desc_info":
{
"imgs":
[
"http://img10.360buyimg.com/n1/s450x450_jfs/t1/85865/39/13611/488083/5e590a40E4bdf69c0/55c9bf645ea2b727.jpg"
]
},
"brand_id": 2100000000,
"cats":
[
{
"cat_id": 6033,
"level": 1
},
{
"cat_id": 6057,
"level": 2
},
{
"cat_id": 6091,
"level": 3
}
],
"attrs":
[
{
"attr_key": "商品毛重",
"attr_value": "380g"
},
{
"attr_key": "商品产地",
"attr_value": "中国大陆"
}
],
"model": "国行续航增强版",
"express_info":
{
"template_id": 5189
},
"skus":
[
{
"out_product_id": "1234566",
"out_sku_id": "1024",
"thumb_img": "http://img10.360buyimg.com/n1/s450x450_jfs/t1/100778/17/13648/424215/5e590a40E2d68e774/e171d222a0c9b763.jpg",
"sale_price": 1300,
"market_price": 1500,
"stock_num": 100,
"sku_code": "A24525252",
"barcode": "13251454",
"sku_attrs":
[
{
"attr_key": "选择颜色",
"attr_value": "红蓝主机"
},
{
"attr_key": "选择套装",
"attr_value": "主机+保护套"
}
]
}
]
}',
'doc_url' => 'https://developers.weixin.qq.com/miniprogram/dev/framework/ministore/minishopopencomponent/API/spu/add_spu.html',
'document' => '参考文档url',
'sample_data' => '无',
'sample_result' => '无',
'sort' => 1,
'status' => 1,
'content_type' => 'application/x-www-form-urlencoded',
'env_id' => 0,
'memo' => '',
'created_at' => 1621764682,
'updated_at' => 1622378805,
'deleted_at' => 0,
'creator_id' => 1,
),
7 =>
array (
'id' => 8,
'api_title' => '获取商品',
'api_name' => 'product/spu/get',
'category_id' => 2,
'type' => 1,
'appid' => '',
'project_id' => '',
'api_url' => '{{host}}/api/wechatopen/product/spu/get',
'methods' => 'POST',
'auth' => '',
'header' => '{"authorization":"{{authorization}}"}',
'query' => '{"appid":"{{appid}}"}',
'body' => '{\'product_id\':\'31334398\'}',
'doc_url' => 'https://developers.weixin.qq.com/miniprogram/dev/framework/ministore/minishopopencomponent/API/spu/get_spu.html',
'document' => '参考文档URL',
'sample_data' => '无',
'sample_result' => '无',
'sort' => 1,
'status' => 1,
'content_type' => 'application/x-www-form-urlencoded',
'env_id' => 0,
'memo' => '',
'created_at' => 1621774347,
'updated_at' => 1622378781,
'deleted_at' => 0,
'creator_id' => 1,
),
8 =>
array (
'id' => 9,
'api_title' => '获取商品列表',
'api_name' => 'product/spu/get_list',
'category_id' => 2,
'type' => 1,
'appid' => '',
'project_id' => '',
'api_url' => '{{host}}/api/wechatopen/product/spu/get_list',
'methods' => 'POST',
'auth' => '',
'header' => '{"authorization":"{{authorization}}"}',
'query' => '{"appid":"{{appid}}"}',
'body' => '{"status":5}',
'doc_url' => 'https://developers.weixin.qq.com/miniprogram/dev/framework/ministore/minishopopencomponent/API/spu/get_spu_list.html',
'document' => '参考文档URL',
'sample_data' => '无',
'sample_result' => '无',
'sort' => 1,
'status' => 1,
'content_type' => 'application/x-www-form-urlencoded',
'env_id' => 0,
'memo' => '',
'created_at' => 1621777668,
'updated_at' => 1622378754,
'deleted_at' => 0,
'creator_id' => 1,
),
9 =>
array (
'id' => 10,
'api_title' => '搜索商品',
'api_name' => 'product/spu/search',
'category_id' => 2,
'type' => 1,
'appid' => '',
'project_id' => '',
'api_url' => '{{host}}/api/wechatopen/product/spu/search',
'methods' => 'POST',
'auth' => NULL,
'header' => '{"authorization":"{{authorization}}"}',
'query' => '{"appid":"{{appid}}"}',
'body' => '{\'status\':5,\'keyword\':\'UI\',\'page\':1}',
'doc_url' => 'https://developers.weixin.qq.com/miniprogram/dev/framework/ministore/minishopopencomponent/API/spu/search_spu.html',
'document' => '参考文档url',
'sample_data' => '无',
'sample_result' => '无',
'sort' => 1,
'status' => 1,
'content_type' => 'application/x-www-form-urlencoded',
'env_id' => 1,
'memo' => NULL,
'created_at' => 1622000389,
'updated_at' => 1622378719,
'deleted_at' => 0,
'creator_id' => 1,
),
10 =>
array (
'id' => 11,
'api_title' => '上架商品',
'api_name' => 'product/spu/listing',
'category_id' => 2,
'type' => 1,
'appid' => '',
'project_id' => '',
'api_url' => '{{host}}/api/wechatopen/product/spu/listing',
'methods' => 'POST',
'auth' => '',
'header' => '{"authorization":"{{authorization}}"}',
'query' => '{"appid":"{{appid}}"}',
'body' => '{\'product_id\':\'33541981\'}',
'doc_url' => 'https://developers.weixin.qq.com/miniprogram/dev/framework/ministore/minishopopencomponent/API/spu/up_spu_listing.html',
'document' => '参考文档URL',
'sample_data' => '无',
'sample_result' => '无',
'sort' => 1,
'status' => 1,
'content_type' => 'application/x-www-form-urlencoded',
'env_id' => 0,
'memo' => '',
'created_at' => 1622019456,
'updated_at' => 1622378710,
'deleted_at' => 0,
'creator_id' => 1,
),
11 =>
array (
'id' => 12,
'api_title' => '下架商品',
'api_name' => 'product/spu/delisting',
'category_id' => 2,
'type' => 1,
'appid' => '',
'project_id' => '',
'api_url' => '{{host}}/api/wechatopen/product/spu/delisting',
'methods' => 'POST',
'auth' => '',
'header' => '{"authorization":"{{authorization}}"}',
'query' => '{"appid":"{{appid}}"}',
'body' => '{\'product_id\':33541981}',
'doc_url' => 'https://developers.weixin.qq.com/miniprogram/dev/framework/ministore/minishopopencomponent/API/spu/up_spu_delisting.html',
'document' => '参考文档URL',
'sample_data' => '无',
'sample_result' => '无',
'sort' => 1,
'status' => 1,
'content_type' => 'application/x-www-form-urlencoded',
'env_id' => 0,
'memo' => '',
'created_at' => 1622019791,
'updated_at' => 1622378701,
'deleted_at' => 0,
'creator_id' => 1,
),
12 =>
array (
'id' => 13,
'api_title' => 'API测试用户环境新增',
'api_name' => 'ApiTesterUserenv/save',
'category_id' => 5,
'type' => 2,
'appid' => '',
'project_id' => '',
'api_url' => '{{host}}/apiTesterUserenv',
'methods' => 'POST',
'auth' => '',
'header' => '{"authorization":"{{authorization}}"}',
'query' => '',
'body' => '{\'env_name\':\'api.server.local\',\'appid\':\'wx407e\',\'project_id\':1,\'selected\':0}',
'doc_url' => '无',
'document' => '新增一条API测试用户环境数据',
'sample_data' => '{\'env_name\':\'appapi.uctoo.local\',\'appid\':\'wx407e\',\'project_id\':1,\'env_json\':\'{"{{host}}":"api.server.local","{{appid}}":"wx407e","{{authorization}}":"BearereyJ0eXA"}\',\'selected\':0}',
'sample_result' => '无',
'sort' => 1,
'status' => 4,
'content_type' => 'application/x-www-form-urlencoded',
'env_id' => 1,
'memo' => '',
'created_at' => 1622030621,
'updated_at' => 1622965370,
'deleted_at' => 0,
'creator_id' => 1,
),
13 =>
array (
'id' => 14,
'api_title' => 'API测试用户环境列表',
'api_name' => 'apiTesterUserenv/index',
'category_id' => 5,
'type' => 2,
'appid' => '',
'project_id' => '',
'api_url' => '{{host}}/apiTesterUserenv',
'methods' => 'GET',
'auth' => '',
'header' => '{"authorization":"{{authorization}}"}',
'query' => '{\'creator\':\'admin\'}',
'body' => '',
'doc_url' => '无',
'document' => 'API测试用户环境列表。可以按用户名检索环境列表在query部分增加creator筛选字段。',
'sample_data' => '无',
'sample_result' => '无',
'sort' => 1,
'status' => 1,
'content_type' => 'application/x-www-form-urlencoded',
'env_id' => 0,
'memo' => '',
'created_at' => 1622031131,
'updated_at' => 1622965325,
'deleted_at' => 0,
'creator_id' => 1,
),
14 =>
array (
'id' => 15,
'api_title' => 'API测试用户环境更新',
'api_name' => 'apiTesterUserenv/update',
'category_id' => 5,
'type' => 2,
'appid' => '',
'project_id' => '',
'api_url' => '{{host}}/apiTesterUserenv/3',
'methods' => 'PUT',
'auth' => '',
'header' => '{"authorization":"{{authorization}}"}',
'query' => '',
'body' => '{\'selected\':1}',
'doc_url' => '无',
'document' => 'API地址http://127.0.0.1/apiTesterUserenv/{id}
id为数据主键。body部分为要更新的字段。',
'sample_data' => '无',
'sample_result' => '无',
'sort' => 1,
'status' => 1,
'content_type' => 'application/x-www-form-urlencoded',
'env_id' => 0,
'memo' => '',
'created_at' => 1622033581,
'updated_at' => 1622965309,
'deleted_at' => 0,
'creator_id' => 1,
),
15 =>
array (
'id' => 16,
'api_title' => '微应用列表',
'api_name' => 'applet/index',
'category_id' => 5,
'type' => 2,
'appid' => '',
'project_id' => '',
'api_url' => '{{host}}/applet',
'methods' => 'GET',
'auth' => '',
'header' => '{"authorization":"{{authorization}}"}',
'query' => '{\'creator\':\'demo\'}',
'body' => '',
'doc_url' => '无',
'document' => '可在query字段用creator用户名、name应用名称、appid字段进行筛选。',
'sample_data' => '无',
'sample_result' => '无',
'sort' => 1,
'status' => 1,
'content_type' => 'application/x-www-form-urlencoded',
'env_id' => 0,
'memo' => '',
'created_at' => 1622083381,
'updated_at' => 1622965295,
'deleted_at' => 0,
'creator_id' => 1,
),
16 =>
array (
'id' => 17,
'api_title' => '设置选中微应用',
'api_name' => 'applet/setapplet/<id>',
'category_id' => 5,
'type' => 2,
'appid' => '',
'project_id' => '',
'api_url' => '{{host}}/applet/setapplet/1',
'methods' => 'GET',
'auth' => '',
'header' => '{"authorization":"{{authorization}}"}',
'query' => '',
'body' => '',
'doc_url' => '无',
'document' => '<id>参数为数据主键',
'sample_data' => '无',
'sample_result' => '无',
'sort' => 1,
'status' => 1,
'content_type' => 'application/x-www-form-urlencoded',
'env_id' => 0,
'memo' => '',
'created_at' => 1622085296,
'updated_at' => 1622965277,
'deleted_at' => 0,
'creator_id' => 1,
),
17 =>
array (
'id' => 18,
'api_title' => '获取当前选中应用',
'api_name' => 'admin/applet/<creator_id>',
'category_id' => 5,
'type' => 2,
'appid' => '',
'project_id' => '',
'api_url' => '{{host}}/admin/applet/1',
'methods' => 'GET',
'auth' => '',
'header' => '{"authorization":"{{authorization}}"}',
'query' => '',
'body' => '',
'doc_url' => '无',
'document' => '<creator_id>参数是后台用户主键ID',
'sample_data' => '无',
'sample_result' => '无',
'sort' => 1,
'status' => 1,
'content_type' => 'application/x-www-form-urlencoded',
'env_id' => 0,
'memo' => '',
'created_at' => 1622097323,
'updated_at' => 1622965263,
'deleted_at' => 0,
'creator_id' => 1,
),
18 =>
array (
'id' => 19,
'api_title' => '切换API环境',
'api_name' => 'apiTesterUserenv/selectAPIenv/<id>',
'category_id' => 5,
'type' => 2,
'appid' => '',
'project_id' => '',
'api_url' => '{{host}}/apiTesterUserenv/selectAPIenv/1',
'methods' => 'GET',
'auth' => '',
'header' => '{"authorization":"{{authorization}}"}',
'query' => '',
'body' => '',
'doc_url' => '无',
'document' => '<id>为API环境数据主键',
'sample_data' => '无',
'sample_result' => '无',
'sort' => 1,
'status' => 1,
'content_type' => 'application/x-www-form-urlencoded',
'env_id' => 0,
'memo' => '',
'created_at' => 1622176574,
'updated_at' => 1622965231,
'deleted_at' => 0,
'creator_id' => 1,
),
19 =>
array (
'id' => 20,
'api_title' => '帐号登录',
'api_name' => 'login',
'category_id' => 5,
'type' => 2,
'appid' => '',
'project_id' => '',
'api_url' => '{{host}}/login',
'methods' => 'POST',
'auth' => '',
'header' => '',
'query' => '',
'body' => '{"email":"catch@admin.com","password":"catchadmin"}',
'doc_url' => 'http://apidoc.catchadmin.com/web/#/5?page_id=24',
'document' => '无',
'sample_data' => '无',
'sample_result' => '无',
'sort' => 1,
'status' => 1,
'content_type' => 'application/json; charset=utf-8',
'env_id' => 0,
'memo' => '',
'created_at' => 1622429937,
'updated_at' => 1622460840,
'deleted_at' => 0,
'creator_id' => 1,
),
20 =>
array (
'id' => 21,
'api_title' => '微信扫码登录后获取后台帐号',
'api_name' => 'wechatlogin/wechatoauth',
'category_id' => 5,
'type' => 2,
'appid' => '',
'project_id' => '',
'api_url' => '{{host}}/wechatlogin/wechatoauth',
'methods' => 'GET',
'auth' => '',
'header' => '',
'query' => '{"code":"0712VE1"}',
'body' => '',
'doc_url' => 'https://developers.weixin.qq.com/doc/oplatform/Website_App/WeChat_Login/Wechat_Login.html',
'document' => '微信扫码登录后,在跳转到的页面需要先调用此接口获取微信用户关联的后台管理员帐号列表。根据此接口返回的帐号列表信息,展示帐号名,用户可以点选登录对应帐号。如此接口返回空值,则展示用户注册后台帐号页面。
Query参数
必填参数
code微信扫码登录后微信服务器通知跳转到redirect_url时添加的code参数用于获取微信用户access_token
非必填参数
state扫码登录时添加的随机验证字符串。前端自定义。前端自验证。
返回值accountList 包含扫码登录后获得的 openidaccess_token以及关联的后台帐号列表
',
'sample_data' => '无',
'sample_result' => '无',
'sort' => 1,
'status' => 1,
'content_type' => 'application/x-www-form-urlencoded',
'env_id' => 0,
'memo' => '',
'created_at' => 1622539671,
'updated_at' => 1622709792,
'deleted_at' => 0,
'creator_id' => 1,
),
21 =>
array (
'id' => 22,
'api_title' => '扫码登录后注册用户帐号',
'api_name' => 'wechatlogin/wechatregist',
'category_id' => 5,
'type' => 2,
'appid' => '',
'project_id' => '',
'api_url' => '{{host}}/wechatlogin/wechatregist',
'methods' => 'POST',
'auth' => '',
'header' => '',
'query' => '',
'body' => '{"username":"13688888888","password":"123456","email":"demo@uctoo.com","openid":"openid","access_token":"access_token"}',
'doc_url' => '无',
'document' => 'openid和access_token是微信扫码登录后获取到的值用于调用帐号注册接口权限验证。
username建议采用用户手机号码并进行短信验证。
password建议校验密码复杂度。
email建议进行邮箱验证。可作为密码找回方式之一。',
'sample_data' => '无',
'sample_result' => '无',
'sort' => 1,
'status' => 1,
'content_type' => 'application/json; charset=utf-8',
'env_id' => 0,
'memo' => '',
'created_at' => 1622710277,
'updated_at' => 1622876680,
'deleted_at' => 0,
'creator_id' => 1,
),
22 =>
array (
'id' => 23,
'api_title' => '微信扫码登录后选择帐号登录',
'api_name' => 'wechatlogin/wechatlogin',
'category_id' => 5,
'type' => 2,
'appid' => '',
'project_id' => '',
'api_url' => '{{host}}/wechatlogin/wechatlogin',
'methods' => 'POST',
'auth' => '',
'header' => '',
'query' => '',
'body' => '{"email":"demo@uctoo.com","openid":"openid","access_token":"access_token","password":"any"}',
'doc_url' => '无',
'document' => '微信扫码后,选择帐号登录。
openid和access_token是在扫码授权后获得。
password参数必须传可以是任意值不做验证。',
'sample_data' => '无',
'sample_result' => '无',
'sort' => 1,
'status' => 1,
'content_type' => 'application/json; charset=utf-8',
'env_id' => 0,
'memo' => '',
'created_at' => 1622713987,
'updated_at' => 1622876660,
'deleted_at' => 0,
'creator_id' => 1,
),
);
foreach ($data as $item) {
\catchAdmin\apimanager\model\ApiTester::create($item);
}
}
}

View File

@ -0,0 +1,59 @@
<?php
// +----------------------------------------------------------------------
// | UCToo [ Universal Convergence Technology ]
// +----------------------------------------------------------------------
// | Copyright (c) 2014-2021 https://www.uctoo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: UCToo <contact@uctoo.com>
// +----------------------------------------------------------------------
use think\migration\Seeder;
class ApiTesterUserenvSeed extends Seeder
{
/**
* Run Method.
*
* Write your database seeder using this method.
*
* More information on writing seeders is available here:
* http://docs.phinx.org/en/latest/seeding.html
*/
public function run(): void
{
$data = array (
0 =>
array (
'id' => 1,
'env_name' => 'localhost',
'appid' => 'wx407e4',
'project_id' => '1',
'env_json' => '{"{{host}}":"http://127.0.0.1:8000","{{status}}":"5","{{appid}}":"wx407","{{authorization}}":"Bearer{{手动替换为login接口的token}}"}',
'selected' => 1,
'created_at' => 1622029539,
'updated_at' => 1622386890,
'deleted_at' => 0,
'creator_id' => 1,
),
1 =>
array (
'id' => 2,
'env_name' => 'api.server.local',
'appid' => 'wx407',
'project_id' => '1',
'env_json' => '{"{{host}}":"http://api.server.local"}',
'selected' => 0,
'created_at' => 1622030904,
'updated_at' => 1622386890,
'deleted_at' => 0,
'creator_id' => 1,
),
);
foreach ($data as $item) {
\catchAdmin\apimanager\model\ApiTesterUserenv::create($item);
}
}
}

View File

@ -0,0 +1,460 @@
<?php
// +----------------------------------------------------------------------
// | UCToo [ Universal Convergence Technology ]
// +----------------------------------------------------------------------
// | Copyright (c) 2014-2021 https://www.uctoo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: UCToo <contact@uctoo.com>
// +----------------------------------------------------------------------
use think\migration\Seeder;
class ApimanagerMenusSeed extends Seeder {
/**
* Run Method.
*
* Write your database seeder using this method.
*
* More information on writing seeders is available here:
* http://docs.phinx.org/en/latest/seeding.html
*/
public function run(): void
{
\catcher\Utils::importTreeData($this->getPermissions(), 'permissions', 'parent_id');
}
protected function getPermissions() {
return [
0 => [
'id' => 143,
'permission_name' => 'API管理',
'parent_id' => 0,
'level' => '',
'route' => '/apimanager',
'icon' => 'el-icon-sort',
'module' => 'apimanager',
'creator_id' => 1,
'permission_mark' => 'apimanager',
'component' => 'layout',
'redirect' => '',
'keepalive' => 1,
'type' => 1,
'hidden' => 1,
'sort' => 1,
'created_at' => 1621425807,
'updated_at' => 1621427128,
'deleted_at' => 0,
'children' => [
0 => [
'id' => 144,
'permission_name' => 'API分类',
'parent_id' => 143,
'level' => '',
'route' => '/apicategory',
'icon' => 'el-icon-s-grid',
'module' => 'apimanager',
'creator_id' => 1,
'permission_mark' => 'apicategory',
'component' => 'apicategory',
'redirect' => '',
'keepalive' => 1,
'type' => 1,
'hidden' => 1,
'sort' => 10,
'created_at' => 1621413029,
'updated_at' => 1624010103,
'deleted_at' => 0,
'children' => [
0 => [
'id' => 151,
'permission_name' => '列表',
'parent_id' => 144,
'level' => '',
'route' => '',
'icon' => '',
'module' => 'apimanager',
'creator_id' => 1,
'permission_mark' => 'apicategory@index',
'component' => '',
'redirect' => '',
'keepalive' => 1,
'type' => 2,
'hidden' => 1,
'sort' => 1,
'created_at' => 1621779121,
'updated_at' => 1624010103,
'deleted_at' => 0,
],
1 => [
'id' => 152,
'permission_name' => '创建',
'parent_id' => 144,
'level' => '',
'route' => '',
'icon' => '',
'module' => 'apimanager',
'creator_id' => 1,
'permission_mark' => 'apicategory@save',
'component' => '',
'redirect' => '',
'keepalive' => 1,
'type' => 2,
'hidden' => 1,
'sort' => 1,
'created_at' => 1621779137,
'updated_at' => 1624010103,
'deleted_at' => 0,
],
2 => [
'id' => 153,
'permission_name' => '更新',
'parent_id' => 144,
'level' => '',
'route' => '',
'icon' => '',
'module' => 'apimanager',
'creator_id' => 1,
'permission_mark' => 'apicategory@update',
'component' => '',
'redirect' => '',
'keepalive' => 1,
'type' => 2,
'hidden' => 1,
'sort' => 1,
'created_at' => 1621779154,
'updated_at' => 1624010103,
'deleted_at' => 0,
],
3 => [
'id' => 154,
'permission_name' => '读取',
'parent_id' => 144,
'level' => '',
'route' => '',
'icon' => '',
'module' => 'apimanager',
'creator_id' => 1,
'permission_mark' => 'apicategory@read',
'component' => '',
'redirect' => '',
'keepalive' => 1,
'type' => 2,
'hidden' => 1,
'sort' => 1,
'created_at' => 1621779171,
'updated_at' => 1624010103,
'deleted_at' => 0,
],
4 => [
'id' => 155,
'permission_name' => '删除',
'parent_id' => 144,
'level' => '',
'route' => '',
'icon' => '',
'module' => 'apimanager',
'creator_id' => 1,
'permission_mark' => 'apicategory@delete',
'component' => '',
'redirect' => '',
'keepalive' => 1,
'type' => 2,
'hidden' => 1,
'sort' => 1,
'created_at' => 1621779186,
'updated_at' => 1624010103,
'deleted_at' => 0,
],
],
],
1 => [
'id' => 145,
'permission_name' => 'API测试列表',
'parent_id' => 143,
'level' => '',
'route' => '/apitester',
'icon' => 'el-icon-stopwatch',
'module' => 'apimanager',
'creator_id' => 1,
'permission_mark' => 'apitester',
'component' => 'apitester',
'redirect' => '',
'keepalive' => 1,
'type' => 1,
'hidden' => 1,
'sort' => 1,
'created_at' => 1621479275,
'updated_at' => 1624010086,
'deleted_at' => 0,
'children' => [
0 => [
'id' => 146,
'permission_name' => '列表',
'parent_id' => 145,
'level' => '',
'route' => '',
'icon' => '',
'module' => 'apimanager',
'creator_id' => 1,
'permission_mark' => 'apitester@index',
'component' => '',
'redirect' => '',
'keepalive' => 1,
'type' => 2,
'hidden' => 1,
'sort' => 1,
'created_at' => 1621778966,
'updated_at' => 1624010086,
'deleted_at' => 0,
],
1 => [
'id' => 147,
'permission_name' => '创建',
'parent_id' => 145,
'level' => '',
'route' => '',
'icon' => '',
'module' => 'apimanager',
'creator_id' => 1,
'permission_mark' => 'apitester@save',
'component' => '',
'redirect' => '',
'keepalive' => 1,
'type' => 2,
'hidden' => 1,
'sort' => 1,
'created_at' => 1621779011,
'updated_at' => 1624010086,
'deleted_at' => 0,
],
5 => [
'id' => 148,
'permission_name' => '更新',
'parent_id' => 145,
'level' => '',
'route' => '',
'icon' => '',
'module' => 'apimanager',
'creator_id' => 1,
'permission_mark' => 'apitester@update',
'component' => '',
'redirect' => '',
'keepalive' => 1,
'type' => 2,
'hidden' => 1,
'sort' => 1,
'created_at' => 1621779033,
'updated_at' => 1624010086,
'deleted_at' => 0,
],
2 => [
'id' => 149,
'permission_name' => '读取',
'parent_id' => 145,
'level' => '',
'route' => '',
'icon' => '',
'module' => 'apimanager',
'creator_id' => 1,
'permission_mark' => 'apitester@read',
'component' => '',
'redirect' => '',
'keepalive' => 1,
'type' => 2,
'hidden' => 1,
'sort' => 1,
'created_at' => 1621779051,
'updated_at' => 1624010086,
'deleted_at' => 0,
],
3 => [
'id' => 150,
'permission_name' => '删除',
'parent_id' => 145,
'level' => '',
'route' => '',
'icon' => '',
'module' => 'apimanager',
'creator_id' => 1,
'permission_mark' => 'apitester@delete',
'component' => '',
'redirect' => '',
'keepalive' => 1,
'type' => 2,
'hidden' => 1,
'sort' => 1,
'created_at' => 1621779083,
'updated_at' => 1624010086,
'deleted_at' => 0,
],
],
],
2 => [
'id' => 156,
'permission_name' => 'API运行',
'parent_id' => 143,
'level' => '',
'route' => '/apimanager/apirun',
'icon' => 'el-icon-position',
'module' => 'apimanager',
'creator_id' => 1,
'permission_mark' => 'apirun',
'component' => 'apirun',
'redirect' => '',
'keepalive' => 1,
'type' => 1,
'hidden' => 2,
'sort' => 1,
'created_at' => 1621798022,
'updated_at' => 1621831249,
'deleted_at' => 0,
],
3 => [
'id' => 161,
'permission_name' => 'API环境变量',
'parent_id' => 143,
'level' => '',
'route' => '/apienv',
'icon' => 'el-icon-setting',
'module' => 'apimanager',
'creator_id' => 1,
'permission_mark' => 'apienv',
'component' => 'apienv',
'redirect' => '',
'keepalive' => 1,
'type' => 1,
'hidden' => 1,
'sort' => 1,
'created_at' => 1622176953,
'updated_at' => 1622177106,
'deleted_at' => 0,
],
4 => [
'id' => 281,
'permission_name' => '路由列表',
'parent_id' => 143,
'level' => '143',
'route' => '/apimanager/routeList/curd',
'icon' => 'el-icon-link',
'module' => 'apimanager',
'creator_id' => 1,
'permission_mark' => 'routeList',
'component' => 'apimanager_routeList',
'redirect' => '',
'keepalive' => 1,
'type' => 1,
'hidden' => 1,
'sort' => 0,
'created_at' => 1636624116,
'updated_at' => 1636689266,
'deleted_at' => 0,
'children' => [
0 => [
'id' => 282,
'permission_name' => '列表',
'parent_id' => 281,
'level' => '143-281',
'route' => '',
'icon' => '',
'module' => 'apimanager',
'creator_id' => 1,
'permission_mark' => 'routeList@index',
'component' => '',
'redirect' => '',
'keepalive' => 1,
'type' => 2,
'hidden' => 1,
'sort' => 1,
'created_at' => 1636624117,
'updated_at' => 1636689266,
'deleted_at' => 0,
],
1 => [
'id' => 283,
'permission_name' => '保存',
'parent_id' => 281,
'level' => '143-281',
'route' => '',
'icon' => '',
'module' => 'apimanager',
'creator_id' => 1,
'permission_mark' => 'routeList@save',
'component' => '',
'redirect' => '',
'keepalive' => 1,
'type' => 2,
'hidden' => 1,
'sort' => 1,
'created_at' => 1636624117,
'updated_at' => 1636689266,
'deleted_at' => 0,
],
2 => [
'id' => 284,
'permission_name' => '读取',
'parent_id' => 281,
'level' => '143-281',
'route' => '',
'icon' => '',
'module' => 'apimanager',
'creator_id' => 1,
'permission_mark' => 'routeList@read',
'component' => '',
'redirect' => '',
'keepalive' => 1,
'type' => 2,
'hidden' => 1,
'sort' => 1,
'created_at' => 1636624118,
'updated_at' => 1636689266,
'deleted_at' => 0,
],
3 => [
'id' => 285,
'permission_name' => '更新',
'parent_id' => 281,
'level' => '143-281',
'route' => '',
'icon' => '',
'module' => 'apimanager',
'creator_id' => 1,
'permission_mark' => 'routeList@update',
'component' => '',
'redirect' => '',
'keepalive' => 1,
'type' => 2,
'hidden' => 1,
'sort' => 1,
'created_at' => 1636624118,
'updated_at' => 1636689266,
'deleted_at' => 0,
],
4 => [
'id' => 286,
'permission_name' => '删除',
'parent_id' => 281,
'level' => '143-281',
'route' => '',
'icon' => '',
'module' => 'apimanager',
'creator_id' => 1,
'permission_mark' => 'routeList@delete',
'component' => '',
'redirect' => '',
'keepalive' => 1,
'type' => 2,
'hidden' => 1,
'sort' => 1,
'created_at' => 1636624119,
'updated_at' => 1636689266,
'deleted_at' => 0,
],
],
],
],
],
];
}
}

View File

@ -0,0 +1,92 @@
<?php
// +----------------------------------------------------------------------
// | UCToo [ Universal Convergence Technology ]
// +----------------------------------------------------------------------
// | Copyright (c) 2014-2021 https://www.uctoo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: UCToo <contact@uctoo.com>
// +----------------------------------------------------------------------
namespace catchAdmin\apimanager\model;
use catchAdmin\apimanager\model\search\ApiCategorySearch;
use catcher\base\CatchModel as Model;
use think\db\exception\DbException;
/**
*
* @property int $id
* @property string $category_title
* @property int $parent_id
* @property string $category_name
* @property int $status
* @property int $sort
* @property int $created_at
* @property int $updated_at
* @property int $deleted_at
* @property int $creator_id
*/
class ApiCategory extends Model
{
use ApiCategorySearch;
// 表名
public $name = 'api_category';
// 数据库字段映射
public $field = array(
'id',
// 分类标题
'category_title',
// 父级ID
'parent_id',
// 分类唯一标识
'category_name',
// 状态:1=正常;2=停用
'status',
// 排序字段
'sort',
// 创建时间
'created_at',
// 更新时间
'updated_at',
// 软删除字段
'deleted_at',
// 创建人ID
'creator_id',
);
protected $updateChildrenFields = 'status';
/**
* 列表数据
*
* @time 2020年01月09日
* @return array
* @throws DbException
*/
public function getList(): array
{
return $this->catchSearch()
->catchOrder()
->select()->toTree();
}
/**
* 获取子分类IDS
*
* @time 2020年11月04日
* @param $id
* @throws DbException
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\ModelNotFoundException
* @return mixed
*/
public static function getChildrenCategoryIds($id)
{
$categoryIds = ApiCategory::field(['id', 'parent_id'])->select()->getAllChildrenIds([$id]);
$categoryIds[] = $id;
return $categoryIds;
}
}

View File

@ -0,0 +1,104 @@
<?php
// +----------------------------------------------------------------------
// | UCToo [ Universal Convergence Technology ]
// +----------------------------------------------------------------------
// | Copyright (c) 2014-2021 https://www.uctoo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: UCToo <contact@uctoo.com>
// +----------------------------------------------------------------------
namespace catchAdmin\apimanager\model;
use catcher\base\CatchModel as Model;
use catchAdmin\apimanager\model\search\ApiTesterSearch;
/**
*
* @property int $id
* @property string $api_title
* @property string $api_name
* @property int $category_id
* @property int $type
* @property string $appid
* @property string $project_id
* @property string $api_url
* @property string $methods
* @property string $auth
* @property string $header
* @property string $query
* @property string $body
* @property string $doc_url
* @property string $document
* @property string $sample_data
* @property string $sample_result
* @property int $sort
* @property int $status
* @property string $content_type
* @property int $env_id
* @property string $memo
* @property int $created_at
* @property int $updated_at
* @property int $deleted_at
* @property int $creator_id
*/
class ApiTester extends Model
{
use ApiTesterSearch;
// 表名
public $name = 'api_tester';
// 数据库字段映射
public $field = array(
'id',
// 标题
'api_title',
// 英文唯一标识
'api_name',
// 分类
'category_id',
// 数据源类型:1=remote,2=local
'type',
// appid
'appid',
// 项目ID
'project_id',
// API URL
'api_url',
// 方法:POST,GET,PUT,PATCH,DELETE,COPY,HEAD,OPTIONS
'methods',
// 鉴权
'auth',
// header
'header',
// query
'query',
// body
'body',
// 文档URL
'doc_url',
// 文档
'document',
// 示例数据
'sample_data',
// 示例返回数据
'sample_result',
// 排序
'sort',
// 状态:1=已完成,2=待开发,3=开发中,4=已废弃
'status',
// content-type:application/x-www-form-urlencoded,multipart/form-data,raw
'content_type',
// 环境ID
'env_id',
// 备注
'memo',
// 创建时间
'created_at',
// 更新时间
'updated_at',
// 软删除字段
'deleted_at',
// 创建人ID
'creator_id',
);
}

View File

@ -0,0 +1,60 @@
<?php
// +----------------------------------------------------------------------
// | UCToo [ Universal Convergence Technology ]
// +----------------------------------------------------------------------
// | Copyright (c) 2014-2021 https://www.uctoo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: UCToo <contact@uctoo.com>
// +----------------------------------------------------------------------
namespace catchAdmin\apimanager\model;
use catcher\base\CatchModel as Model;
/**
*
* @property int $id
* @property string $appid
* @property int $user_id
* @property int $api_id
* @property string $params
* @property string $result
* @property string $request_data
* @property string $response_data
* @property int $created_at
* @property int $updated_at
* @property int $deleted_at
* @property int $creator_id
*/
class ApiTesterLog extends Model
{
// 表名
public $name = 'api_tester_log';
// 数据库字段映射
public $field = array(
'id',
// appid
'appid',
// users表id
'user_id',
// api_tester表id
'api_id',
// api参数
'params',
// 返回值
'result',
// 请求数据
'request_data',
// 响应数据
'response_data',
// 创建时间
'created_at',
// 更新时间
'updated_at',
// 软删除字段
'deleted_at',
// 创建人ID
'creator_id',
);
}

View File

@ -0,0 +1,76 @@
<?php
// +----------------------------------------------------------------------
// | UCToo [ Universal Convergence Technology ]
// +----------------------------------------------------------------------
// | Copyright (c) 2014-2021 https://www.uctoo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: UCToo <contact@uctoo.com>
// +----------------------------------------------------------------------
namespace catchAdmin\apimanager\model;
use catchAdmin\permissions\model\DataRangScopeTrait;
use catchAdmin\permissions\model\Users;
use catchAdmin\apimanager\model\search\ApiTesterUserenvSearch;
use catcher\base\CatchModel as Model;
/**
*
* @property int $id
* @property string $env_name
* @property string $appid
* @property string $project_id
* @property string $env_json
* @property int $selected
* @property int $created_at
* @property int $updated_at
* @property int $deleted_at
* @property int $creator_id
*/
class ApiTesterUserenv extends Model
{
use ApiTesterUserenvSearch;
use DataRangScopeTrait;
// 表名
public $name = 'api_tester_userenv';
// 数据库字段映射
public $field = array(
'id',
// 环境名称
'env_name',
// appid
'appid',
// 项目ID
'project_id',
// 环境变量json
'env_json',
// 是否当前选中:0=否,1=是
'selected',
// 创建时间
'created_at',
// 更新时间
'updated_at',
// 软删除字段
'deleted_at',
// 创建人ID
'creator_id',
);
/**
* get list
*
* @time 2020年04月28日
* @param $params
* @throws \think\db\exception\DbException
* @return void
*/
public function getList()
{
return $this->dataRange()->field([$this->aliasField('*')])
->catchJoin(Users::class, 'id', 'creator_id', ['username as creator'])
->catchSearch()
->order($this->aliasField('id'), 'desc')
->paginate();
}
}

View File

@ -0,0 +1,66 @@
<?php
// +----------------------------------------------------------------------
// | UCToo [ Universal Convergence Technology ]
// +----------------------------------------------------------------------
// | Copyright (c) 2014-2021 https://www.uctoo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: UCToo <contact@uctoo.com>
// +----------------------------------------------------------------------
namespace catchAdmin\apimanager\model;
use catchAdmin\apimanager\model\search\RouteListSearch;
use catcher\base\CatchModel as Model;
/**
*
* @property int $id
* @property string $rule
* @property string $route
* @property string $method
* @property string $name
* @property string $domain
* @property string $option
* @property string $pattern
* @property string $title
* @property int $created_at
* @property int $updated_at
* @property int $deleted_at
* @property int $creator_id
*/
class RouteList extends Model
{
use RouteListSearch;
public $field = [
//
'id',
//
'rule',
//
'route',
//
'method',
//
'name',
//
'domain',
//
'option',
//
'pattern',
//
'title',
// 创建时间
'created_at',
// 更新时间
'updated_at',
// 软删除字段
'deleted_at',
// 创建人ID
'creator_id',
];
public $name = 'route_list';
}

View File

@ -0,0 +1,25 @@
<?php
// +----------------------------------------------------------------------
// | UCToo [ Universal Convergence Technology ]
// +----------------------------------------------------------------------
// | Copyright (c) 2014-2021 https://www.uctoo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: UCToo <contact@uctoo.com>
// +----------------------------------------------------------------------
namespace catchAdmin\apimanager\model\search;
trait ApiCategorySearch
{
public function searchCategoryTitleAttr($query, $value, $data)
{
return $query->whereLike('category_title', $value);
}
public function searchStatusAttr($query, $value, $data)
{
return $query->where('status', $value);
}
}

View File

@ -0,0 +1,54 @@
<?php
// +----------------------------------------------------------------------
// | UCToo [ Universal Convergence Technology ]
// +----------------------------------------------------------------------
// | Copyright (c) 2014-2021 https://www.uctoo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: UCToo <contact@uctoo.com>
// +----------------------------------------------------------------------
namespace catchAdmin\apimanager\model\search;
use catchAdmin\apimanager\model\ApiCategory;
trait ApiTesterSearch
{
public function searchApiTitleAttr($query, $value, $data)
{
return $query->whereLike('api_title', $value);
}
public function searchApiNameAttr($query, $value, $data)
{
return $query->whereLike('api_name', $value);
}
public function searchStatusAttr($query, $value, $data)
{
return $query->where($this->aliasField('status'), $value);
}
public function searchTypeAttr($query, $value, $data)
{
return $query->where($this->aliasField('type'), $value);
}
/**
* 查询分类下的API
*
* @time 2021年05月20日
* @param $query
* @param $value
* @param $data
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @return mixed
*/
public function searchCategoryIdAttr($query, $value, $data)
{
return $query->whereIn($this->aliasField('category_id'), ApiCategory::getChildrenCategoryIds($value));
}
}

View File

@ -0,0 +1,28 @@
<?php
// +----------------------------------------------------------------------
// | UCToo [ Universal Convergence Technology ]
// +----------------------------------------------------------------------
// | Copyright (c) 2014-2021 https://www.uctoo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: UCToo <contact@uctoo.com>
// +----------------------------------------------------------------------
namespace catchAdmin\apimanager\model\search;
use catchAdmin\permissions\model\Users;
trait ApiTesterUserenvSearch
{
public function searchCreatorAttr($query, $value, $data)
{
return $query->whereLike(app(Users::class)->getTable() . '.username', $value);
}
public function searchEnvNameAttr($query, $value, $data)
{
return $query->whereLike('env_name', $value);
}
}

View File

@ -0,0 +1,50 @@
<?php
// +----------------------------------------------------------------------
// | UCToo [ Universal Convergence Technology ]
// +----------------------------------------------------------------------
// | Copyright (c) 2014-2021 https://www.uctoo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: UCToo <contact@uctoo.com>
// +----------------------------------------------------------------------
namespace catchAdmin\apimanager\model\search;
use catchAdmin\apimanager\model\ApiCategory;
trait RouteListSearch
{
public function searchRuleAttr($query, $value, $data)
{
return $query->whereLike('rule', $value);
}
public function searchRouteAttr($query, $value, $data)
{
return $query->whereLike('route', $value);
}
public function searchMethodAttr($query, $value, $data)
{
return $query->whereLike('method', $value);
}
public function searchNameAttr($query, $value, $data)
{
return $query->whereLike('name', $value);
}
public function searchDomainAttr($query, $value, $data)
{
return $query->whereLike('domain', $value);
}
public function searchOptionAttr($query, $value, $data)
{
return $query->whereLike('option', $value);
}
public function searchPatternAttr($query, $value, $data)
{
return $query->whereLike('pattern', $value);
}
}

View File

@ -0,0 +1,17 @@
{
"name": "API管理",
"alias": "apimanager",
"description": "UCToo API管理",
"version": "1.0.0",
"keywords": [
"API"
],
"order": 0,
"services": [
"\\catchAdmin\\apimanager\\ApimanagerService"
],
"aliases": [],
"files": [],
"requires": [],
"enable": true
}

View File

@ -0,0 +1,66 @@
<?php
// +----------------------------------------------------------------------
// | UCToo [ Universal Convergence Technology ]
// +----------------------------------------------------------------------
// | Copyright (c) 2014-2021 https://www.uctoo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: UCToo <contact@uctoo.com>
// +----------------------------------------------------------------------
namespace catchAdmin\apimanager\repository;
use catchAdmin\permissions\middleware\PermissionsMiddleware;
use catchAdmin\apimanager\model\RouteList;
use catcher\base\CatchRepository;
use catcher\exceptions\FailedException;
use think\facade\Console;
use think\facade\Log;
use think\facade\Db;
class RouteListRepository extends CatchRepository
{
protected $routeList;
public function __construct(RouteList $routeList)
{
$this->routeList = $routeList;
}
protected function model()
{
return $this->routeList;
}
public function all()
{
$routeList = $this->routeList->select();
return $routeList->toArray();
}
/**
* 同步
*
* @time 2020年06月26日
* @throws \Exception
* @return bool
*/
public function sync()
{
DB::table('route_list')->delete(true);
Console::call('route:list', ['-m']); //没用也不是从命令生成的route_list文件读的数据就是想执行一下命令
$routeList = app()->route->getRuleList();
$rows = [];
foreach ($routeList as $item) {
$item['route'] = $item['route'] instanceof \Closure ? '<Closure>' : $item['route'];
$item['option'] = json_encode($item['option']);
$item['pattern'] = json_encode($item['pattern']);
$rows[] = $item;
}
$res = $this->routeList->saveAll($rows);
return true;
}
}

View File

@ -0,0 +1,29 @@
<?php
// +----------------------------------------------------------------------
// | UCToo [ Universal Convergence Technology ]
// +----------------------------------------------------------------------
// | Copyright (c) 2014-2021 https://www.uctoo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: UCToo <contact@uctoo.com>
// +----------------------------------------------------------------------
// you should use `$router`
/* @var think\Route $router */
$router->group(function () use ($router){
// apiCategory路由
$router->resource('apicategory', '\catchAdmin\apimanager\controller\ApiCategory');
// apiTester路由
$router->resource('apitester', '\catchAdmin\apimanager\controller\ApiTester');
// apiTesterUserenv路由
$router->resource('apiTesterUserenv', '\catchAdmin\apimanager\controller\ApiTesterUserenv');
// 切换API环境
$router->get('apiTesterUserenv/selectAPIenv/<id>', '\catchAdmin\apimanager\controller\ApiTesterUserenv@selectAPIenv');
// apiTesterLog路由
$router->resource('apiTesterLog', '\catchAdmin\apimanager\controller\ApiTesterLog');
// routeList 路由
$router->resource('routeList', catchAdmin\apimanager\controller\RouteList::class);
$router->post('apimanager/routelist/sync', 'catchAdmin\apimanager\controller\RouteList@sync');
})->middleware('auth');

View File

@ -0,0 +1,61 @@
<?php
// +----------------------------------------------------------------------
// | UCToo [ Universal Convergence Technology ]
// +----------------------------------------------------------------------
// | Copyright (c) 2014-2021 https://www.uctoo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: UCToo <contact@uctoo.com>
// +----------------------------------------------------------------------
namespace catchAdmin\apimanager\tables;
use catchAdmin\apimanager\tables\forms\Factory;
use catcher\CatchTable;
use catcher\library\table\Actions;
use catcher\library\table\HeaderItem;
use catcher\library\table\Search;
use catcher\library\table\Table;
class ApiCategory extends CatchTable
{
/**
* table
*
* @time 2021年03月29日
* @return array
*/
protected function table(): array
{
// TODO: Implement table() method.
return $this->getTable('api_category')->header([
HeaderItem::label('分类标题')->prop('category_title'),
HeaderItem::label('分类唯一标识')->prop('category_name'),
HeaderItem::label('排序')->prop('sort')->withEditNumberComponent(),
HeaderItem::label('状态')->prop('status')->withSwitchComponent(),
HeaderItem::label('创建时间')->prop('created_at'),
HeaderItem::label('操作')->width(260)->actions([
Actions::update(),
Actions::delete(),
])
])->withApiRoute('apicategory')->withActions([
Actions::create()
])->withSearch([
Search::label('分类标题')->text('category_title', '请输入分类标题'),
Search::label('状态')->status()
])->withDialogWidth('35%')
->toTreeTable()->render();
}
/**
* form 方式
*
* @time 2021年03月29日
* @return array
*/
protected function form(): array
{
return Factory::create('ApiCategory');
}
}

View File

@ -0,0 +1,40 @@
<?php
// +----------------------------------------------------------------------
// | UCToo [ Universal Convergence Technology ]
// +----------------------------------------------------------------------
// | Copyright (c) 2014-2021 https://www.uctoo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: UCToo <contact@uctoo.com>
// +----------------------------------------------------------------------
namespace catchAdmin\apimanager\tables\forms;
use catchAdmin\apimanager\model\ApiCategory as ApiCategoryModel;
use catcher\library\form\Form;
class ApiCategory extends Form
{
public function fields(): array
{
return [
// TODO: Implement fields() method
Form::cascader('parent_id', '上级分类', [0])->options(
ApiCategoryModel::field(['id', 'parent_id', 'category_title'])->select()->toTree()
)->clearable(true)->filterable(true)->props([
'props' => [
'value' => 'id',
'label' => 'category_title',
'checkStrictly' => true
],
])->style(['width' => '100%']),
Form::input('category_title', '分类标题')->required()->placeholder('分类标题'),
Form::input('category_name', '分类唯一标识'),
Form::radio('status', '状态')->value(1)->options(
Form::options()->add('启用', 1)->add('禁用', 2)->render()
),
Form::number('sort', '排序')->value(1)->min(1)->max(10000),
];
}
}

View File

@ -0,0 +1,22 @@
<?php
// +----------------------------------------------------------------------
// | UCToo [ Universal Convergence Technology ]
// +----------------------------------------------------------------------
// | Copyright (c) 2014-2021 https://www.uctoo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: UCToo <contact@uctoo.com>
// +----------------------------------------------------------------------
namespace catchAdmin\apimanager\tables\forms;
use catcher\library\form\FormFactory;
class Factory extends FormFactory
{
public static function from(): string
{
return __NAMESPACE__;
}
}

25
catch/cms/CmsService.php Normal file
View File

@ -0,0 +1,25 @@
<?php
// +----------------------------------------------------------------------
// | Catch-CMS Design On 2020
// +----------------------------------------------------------------------
// | CatchAdmin [Just Like ]
// +----------------------------------------------------------------------
// | Copyright (c) 2017~2020 http://catchadmin.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( https://github.com/yanwenwu/catch-admin/blob/master/LICENSE.txt )
// +----------------------------------------------------------------------
// | Author: JaguarJack [ njphper@gmail.com ]
// +----------------------------------------------------------------------
namespace catchAdmin\cms;
use catcher\ModuleService;
class CmsService extends ModuleService
{
public function loadRouteFrom()
{
// TODO: Implement loadRouteFrom() method.
return __DIR__ . DIRECTORY_SEPARATOR . 'route.php';
}
}

6
catch/cms/README.md Normal file
View File

@ -0,0 +1,6 @@
## 内容管理系统
#### 安装
```shell
composer require xaboy/form-builder:~2.0
```

View File

@ -0,0 +1,80 @@
<?php
// +----------------------------------------------------------------------
// | Catch-CMS Design On 2020
// +----------------------------------------------------------------------
// | CatchAdmin [Just Like ]
// +----------------------------------------------------------------------
// | Copyright (c) 2017~2020 http://catchadmin.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( https://github.com/yanwenwu/catch-admin/blob/master/LICENSE.txt )
// +----------------------------------------------------------------------
// | Author: JaguarJack [ njphper@gmail.com ]
// +----------------------------------------------------------------------
namespace catchAdmin\cms\controller;
use catcher\base\CatchRequest as Request;
use catcher\CatchResponse;
use catcher\base\CatchController;
use catchAdmin\cms\model\Articles as articlesModel;
class Articles extends CatchController
{
protected $articlesModel;
public function __construct(ArticlesModel $articlesModel)
{
$this->articlesModel = $articlesModel;
}
/**
* 列表
* @time 2020年12月27日 19:40
* @param Request $request
*/
public function index(Request $request) : \think\Response
{
return CatchResponse::paginate($this->articlesModel->getList());
}
/**
* 保存信息
* @time 2020年12月27日 19:40
* @param Request $request
*/
public function save(Request $request) : \think\Response
{
return CatchResponse::success($this->articlesModel->storeBy($request->post()));
}
/**
* 读取
* @time 2020年12月27日 19:40
* @param $id
*/
public function read($id) : \think\Response
{
return CatchResponse::success($this->articlesModel->findBy($id));
}
/**
* 更新
* @time 2020年12月27日 19:40
* @param Request $request
* @param $id
*/
public function update(Request $request, $id) : \think\Response
{
return CatchResponse::success($this->articlesModel->updateBy($id, $request->post()));
}
/**
* 删除
* @time 2020年12月27日 19:40
* @param $id
*/
public function delete($id) : \think\Response
{
return CatchResponse::success($this->articlesModel->deleteBy($id));
}
}

View File

@ -0,0 +1,80 @@
<?php
// +----------------------------------------------------------------------
// | Catch-CMS Design On 2020
// +----------------------------------------------------------------------
// | CatchAdmin [Just Like ]
// +----------------------------------------------------------------------
// | Copyright (c) 2017~2020 http://catchadmin.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( https://github.com/yanwenwu/catch-admin/blob/master/LICENSE.txt )
// +----------------------------------------------------------------------
// | Author: JaguarJack [ njphper@gmail.com ]
// +----------------------------------------------------------------------
namespace catchAdmin\cms\controller;
use catcher\base\CatchRequest as Request;
use catcher\CatchResponse;
use catcher\base\CatchController;
use catchAdmin\cms\model\Banners as bannersModel;
class Banners extends CatchController
{
protected $bannersModel;
public function __construct(BannersModel $bannersModel)
{
$this->bannersModel = $bannersModel;
}
/**
* 列表
* @time 2020年12月27日 19:58
* @param Request $request
*/
public function index(Request $request) : \think\Response
{
return CatchResponse::paginate($this->bannersModel->getList());
}
/**
* 保存信息
* @time 2020年12月27日 19:58
* @param Request $request
*/
public function save(Request $request) : \think\Response
{
return CatchResponse::success($this->bannersModel->storeBy($request->post()));
}
/**
* 读取
* @time 2020年12月27日 19:58
* @param $id
*/
public function read($id) : \think\Response
{
return CatchResponse::success($this->bannersModel->findBy($id));
}
/**
* 更新
* @time 2020年12月27日 19:58
* @param Request $request
* @param $id
*/
public function update(Request $request, $id) : \think\Response
{
return CatchResponse::success($this->bannersModel->updateBy($id, $request->post()));
}
/**
* 删除
* @time 2020年12月27日 19:58
* @param $id
*/
public function delete($id) : \think\Response
{
return CatchResponse::success($this->bannersModel->deleteBy($id));
}
}

View File

@ -0,0 +1,91 @@
<?php
// +----------------------------------------------------------------------
// | Catch-CMS Design On 2020
// +----------------------------------------------------------------------
// | CatchAdmin [Just Like ]
// +----------------------------------------------------------------------
// | Copyright (c) 2017~2020 http://catchadmin.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( https://github.com/yanwenwu/catch-admin/blob/master/LICENSE.txt )
// +----------------------------------------------------------------------
// | Author: JaguarJack [ njphper@gmail.com ]
// +----------------------------------------------------------------------
namespace catchAdmin\cms\controller;
use catcher\base\CatchRequest as Request;
use catcher\CatchAdmin;
use catcher\CatchResponse;
use catcher\base\CatchController;
use catchAdmin\cms\model\Category as categoryModel;
use catcher\CatchUpload;
use catcher\library\excel\reader\Reader;
use think\Exception;
use think\facade\Db;
class Category extends CatchController
{
protected $categoryModel;
public function __construct(CategoryModel $categoryModel)
{
$this->categoryModel = $categoryModel;
}
/**
* 列表
* @time 2020年12月27日 19:15
* @param Request $request
* @return \think\response\Json
*/
public function index(Request $request)
{
return CatchResponse::success($this->categoryModel->getList());
}
/**
* 保存信息
* @time 2020年12月27日 19:15
* @param Request $request
* @return \think\response\Json
*/
public function save(Request $request)
{
return CatchResponse::success($this->categoryModel->storeBy($request->post()));
}
/**
* 读取
* @time 2020年12月27日 19:15
* @param $id
* @return \think\response\Json
*/
public function read($id)
{
return CatchResponse::success($this->categoryModel->findBy($id));
}
/**
* 更新
* @time 2020年12月27日 19:15
* @param Request $request
* @param $id
* @return \think\response\Json
*/
public function update(Request $request, $id)
{
return CatchResponse::success($this->categoryModel->updateBy($id, $request->post()));
}
/**
* 删除
* @time 2020年12月27日 19:15
* @param $id
* @return \think\response\Json
*/
public function delete($id)
{
return CatchResponse::success($this->categoryModel->deleteBy($id));
}
}

View File

@ -0,0 +1,80 @@
<?php
// +----------------------------------------------------------------------
// | Catch-CMS Design On 2020
// +----------------------------------------------------------------------
// | CatchAdmin [Just Like ]
// +----------------------------------------------------------------------
// | Copyright (c) 2017~2020 http://catchadmin.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( https://github.com/yanwenwu/catch-admin/blob/master/LICENSE.txt )
// +----------------------------------------------------------------------
// | Author: JaguarJack [ njphper@gmail.com ]
// +----------------------------------------------------------------------
namespace catchAdmin\cms\controller;
use catcher\base\CatchRequest as Request;
use catcher\CatchResponse;
use catcher\base\CatchController;
use catchAdmin\cms\model\Comments as commentsModel;
class Comments extends CatchController
{
protected $commentsModel;
public function __construct(CommentsModel $commentsModel)
{
$this->commentsModel = $commentsModel;
}
/**
* 列表
* @time 2020年12月27日 19:53
* @param Request $request
*/
public function index(Request $request) : \think\Response
{
return CatchResponse::paginate($this->commentsModel->getList());
}
/**
* 保存信息
* @time 2020年12月27日 19:53
* @param Request $request
*/
public function save(Request $request) : \think\Response
{
return CatchResponse::success($this->commentsModel->storeBy($request->post()));
}
/**
* 读取
* @time 2020年12月27日 19:53
* @param $id
*/
public function read($id) : \think\Response
{
return CatchResponse::success($this->commentsModel->findBy($id));
}
/**
* 更新
* @time 2020年12月27日 19:53
* @param Request $request
* @param $id
*/
public function update(Request $request, $id) : \think\Response
{
return CatchResponse::success($this->commentsModel->updateBy($id, $request->post()));
}
/**
* 删除
* @time 2020年12月27日 19:53
* @param $id
*/
public function delete($id) : \think\Response
{
return CatchResponse::success($this->commentsModel->deleteBy($id));
}
}

Some files were not shown because too many files have changed in this diff Show More