├── .browserslistrc
├── .env
├── .eslintrc.js
├── .gitignore
├── LICENSE
├── README.md
├── babel.config.js
├── package-lock.json
├── package.json
├── postcss.config.js
├── public
├── favicon.ico
├── index.html
└── robots.txt
├── src
├── App.vue
├── api
│ ├── application.js
│ ├── document.js
│ ├── interface.js
│ └── user.js
├── assets
│ ├── default_avatar.jpg
│ ├── logo-icon.png
│ ├── logo.png
│ ├── logo2.png
│ ├── sponsor
│ │ ├── alipay_qr_code.jpg
│ │ ├── alipay_qr_code.png
│ │ ├── wxpay_qr_code.jpg
│ │ └── wxpay_qr_code.png
│ ├── theme.less
│ └── theme_param.less
├── components
│ ├── ApiItem.vue
│ ├── Footer.vue
│ ├── Header.vue
│ └── MonacoEditor.vue
├── main.js
├── router.js
├── store.js
├── store
│ ├── app.js
│ ├── getters.js
│ └── user.js
├── utils
│ ├── handle.js
│ ├── pictureProcess.js
│ └── request.js
└── views
│ ├── Home.vue
│ ├── Login.vue
│ ├── application
│ ├── Create.vue
│ ├── Detail.vue
│ ├── Layout.vue
│ └── List.vue
│ ├── center
│ ├── Information.vue
│ ├── Layout.vue
│ ├── Personal.vue
│ └── Security.vue
│ ├── data_analysis
│ ├── Log.vue
│ └── Statistics.vue
│ ├── interface
│ ├── Create.vue
│ ├── Debug.vue
│ └── List.vue
│ ├── management
│ ├── Export.vue
│ ├── Other.vue
│ └── Setting.vue
│ └── other
│ └── 404.vue
└── vue.config.js
/.browserslistrc:
--------------------------------------------------------------------------------
1 | > 1%
2 | last 2 versions
3 | not ie <= 8
4 |
--------------------------------------------------------------------------------
/.env:
--------------------------------------------------------------------------------
1 | # 环境配置
2 | VUE_APP_REQUEST_URL=/ajax
3 |
4 | # 网站版本编号
5 | VUE_APP_VERSION=0.0.1
6 |
7 | # 虚拟API接口请求URI
8 | VUE_APP_API_REQUEST_URI=/api/
9 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: {
4 | node: true
5 | },
6 | 'extends': [
7 | 'plugin:vue/essential',
8 | 'eslint:recommended'
9 | ],
10 | rules: {
11 | 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
12 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
13 | },
14 | parserOptions: {
15 | parser: 'babel-eslint'
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 |
5 | # local env files
6 | .env.local
7 | .env.*.local
8 |
9 | # Log files
10 | npm-debug.log*
11 | yarn-debug.log*
12 | yarn-error.log*
13 |
14 | # Editor directories and files
15 | .idea
16 | .vscode
17 | *.suo
18 | *.ntvs*
19 | *.njsproj
20 | *.sln
21 | *.sw*
22 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
VirAPI——在线虚拟数据云接口平台
6 |
7 | 内置MockJs语法支持,请求接口即可获得自定义规则的虚拟数据。帮助开发者,特别是前端开发者,提供很好的快速开发体验。
8 |
9 | [VirAPI官网 »](http://www.virapi.com/?_from=github)
10 |
11 |
12 |
13 | [文章](http://www.virapi.com/article.html?_from=github)
14 | ·
15 | [关于](http://www.virapi.com/about.html?_from=github)
16 | ·
17 | [控制台](http://console.virapi.com/?_from=github)
18 |
19 | [](https://nodejs.org/en/)
20 | [](https://cn.vuejs.org/)
21 | [](https://www.antdv.com/docs/vue/introduce-cn/)
22 | [](http://www.apache.org/licenses/LICENSE-2.0.html)
23 |
24 |
25 |
26 |
27 | ## VirAPI简介
28 |
29 | VirAPI(Virtual API)—— 在线虚拟数据云接口平台;非侵入式虚拟数据在线请求响应生成接口,支持MockJs语法,请求即可得自定义规则的响应数据;帮助你本地测试或演示项目之用。
30 |
31 | 通过VirAPI你可以通过远程接口URL访问直接获得自定义的随机虚拟响应数据,若是只想做前端客户端(App、小程序、网页等)的功能演示或业务模拟测试,而又不想耗费时间精力去等待后端接口的开发完成,甚至不想搭建后端服务逻辑,那么VirAPI会是你的好帮手。
32 |
33 | **VirAPI的功能特色:**
34 | + 内嵌Mock语法支持,可快速定义虚拟数据结构
35 | + 可视化操作,可视即可得,低门槛快速上手
36 | + 支持多种请求类型(GET、POST、PUT、DELETE)
37 | + 接口请求权限验证,阻止非法请求虚拟接口
38 | + 接口项目应用化管理,还原实际开发场景
39 | + 虚拟接口请求日志数据查看及统计
40 | + 提供应用接口文档管理,高效管理接口及项目计划
41 | + 零污染无侵入,而无需在项目代码中引入Mock包
42 | + 免费开源,可独立部署搭建
43 | + ......
44 |
45 |
46 |
47 | ## VirAPI开源版--前端代码仓库
48 |
49 | 对应后端逻辑代码仓库:[https://github.com/bluvenr/open_virapi](https://github.com/bluvenr/open_virapi)
50 |
51 | ### 运行&打包
52 | 当前前端框架主要使用的是VueJS进行开发。若需要本地运行,请先安装NPM环境。
53 |
54 | 下载后,请`npm install`安装前端项目所需依赖包。
55 |
56 | 本地测试运行,则请执行:`npm serve`
57 |
58 | 打包正式环境,请执行:`npm build`,且打包后的代码生成在`dist`目录下,若需要替换[后端代码](https://github.com/bluvenr/open_virapi),请将该目录所有文件替换到你的后端代码项目下的`/app/public/console/`目录下。
59 |
60 | ### .env 文件相关
61 |
62 | 默认`.env`文件内容为:
63 |
64 | ``` bash
65 | # 环境配置
66 | VUE_APP_REQUEST_URL=/ajax
67 |
68 | # 网站版本编号
69 | VUE_APP_VERSION=0.0.1
70 |
71 | # 虚拟API接口请求URI
72 | VUE_APP_API_REQUEST_URI=/api/
73 | ```
74 |
75 | 其中,`VUE_APP_REQUEST_URL`变量即为对应后端服务接口的地址,默认`/ajax`,即表示为当前域名下的`ajax`地址。你可以根据实际情况进行调整。
76 |
77 | `VUE_APP_API_REQUEST_URI`为当前项目所生成的虚拟数据接口的地址域名,默认`/api/`表示所创建的虚拟数据接口的请求域名为当前域名的`api`路径下。你可以根据实际情况进行调整。
78 |
79 | 建议若是本地测试,可复制`.env`文件并重命名为`.env.development.local`,这样当执行`npm serve`,即进行本地测试运行时,会读取该文件的配置信息。
80 |
81 | 若是要打包到真实环境,则可复制`.env`文件并重命名为`.env.production.local`,这样当执行`npm build`,即进行打包时,会读取该文件的配置信息。
82 |
83 |
84 |
85 |
86 | ## 若觉得VirAPI有帮到你,请赞助一下以示支持哦~
87 | 😁请备注`virapi`。
88 |
89 | | 支付宝赞助
| 微信赞助
|
90 | | --------- | --------- |
91 | | | |
92 |
93 |
94 |
95 |
96 | 欢迎大家通过[Gitter](https://gitter.im/virapi/feedback)与我们沟通和联系。
97 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@vue/app'
4 | ],
5 | plugins: [
6 | [
7 | 'import',
8 | {
9 | libraryName: 'ant-design-vue',
10 | libraryDirectory: 'es',
11 | style: 'css'
12 | }
13 | ]
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Open-VirAPI-Front-End",
3 | "version": "0.1.0",
4 | "description": "VirAPI在线云虚拟Api平台开源版",
5 | "homepage": "https://virapi.com/",
6 | "author": "Bluvenr ",
7 | "scripts": {
8 | "serve": "vue-cli-service serve",
9 | "build": "vue-cli-service build",
10 | "lint": "vue-cli-service lint"
11 | },
12 | "dependencies": {
13 | "ant-design-vue": "^1.4.10",
14 | "axios": "^0.18.0",
15 | "babel-plugin-import": "^1.13.0",
16 | "clipboard": "^2.0.4",
17 | "echarts": "^4.7.0",
18 | "jsonlint": "^1.6.3",
19 | "monaco-editor": "^0.20.0",
20 | "monaco-editor-webpack-plugin": "^1.9.0",
21 | "pinyin": "^2.9.0",
22 | "vue": "^2.6.6",
23 | "vue-echarts": "^5.0.0-beta.0",
24 | "vue-router": "^3.0.1",
25 | "vuex": "^3.0.1"
26 | },
27 | "devDependencies": {
28 | "@vue/cli-plugin-babel": "^3.5.0",
29 | "@vue/cli-plugin-eslint": "^3.5.0",
30 | "@vue/cli-service": "^3.5.0",
31 | "babel-eslint": "^10.0.1",
32 | "eslint": "^5.8.0",
33 | "eslint-plugin-vue": "^5.0.0",
34 | "less": "^3.0.4",
35 | "less-loader": "^4.1.0",
36 | "vue-template-compiler": "^2.5.21"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | autoprefixer: {}
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bluvenr/open_virapi_front_end/bdccf974afc6a7ef2fd9648ddf44676898a4a6bf/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | VirAPI -- 虚拟数据接口系统 [开源版]
10 |
11 |
12 |
13 |
14 | We're sorry but Open-VirAPI doesn't work properly without JavaScript enabled. Please enable it to continue.
15 |
16 |
17 |
18 |
19 |
20 | __ ___ _____ _____
21 | \ \ / (_) /\ | __ \_ _|
22 | \ \ / / _ _ __ / \ | |__) || |
23 | \ \/ / | | '__/ /\ \ | ___/ | |
24 | \ / | | | / ____ \| | _| |_
25 | \/ |_|_|/_/ \_\_| |_____|
26 |
27 |
28 | 页面加载中,请稍后~~
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow: /
3 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
77 |
78 |
115 |
--------------------------------------------------------------------------------
/src/api/application.js:
--------------------------------------------------------------------------------
1 | import request from "@/utils/request";
2 |
3 | // 获取应用列表
4 | export function getAppsRequest() {
5 | return request({
6 | url: "/application",
7 | method: "get"
8 | });
9 | }
10 |
11 | // 获取应用列表
12 | export function getAppsListRequest() {
13 | return request({
14 | url: "/application/list",
15 | method: "get"
16 | });
17 | }
18 |
19 | // 查看应用详情
20 | export function getAppInfoRequest(slug) {
21 | return request({
22 | url: "/application/" + slug,
23 | method: "get"
24 | });
25 | }
26 |
27 | // 获取应用基本
28 | export function getAppBaseInfoRequest(slug) {
29 | return request({
30 | url: "/application/" + slug + "/base_info",
31 | method: "get"
32 | });
33 | }
34 |
35 | // 创建我的应用
36 | export function createAppRequest(data) {
37 | return request({
38 | url: "/application",
39 | method: "post",
40 | data: data
41 | });
42 | }
43 |
44 | // 删除我的应用
45 | export function delAppRequest(id) {
46 | return request({
47 | url: "/application/" + id,
48 | method: "delete"
49 | });
50 | }
51 |
52 | // 更新应用信息
53 | export function updateAppRequest(id, data) {
54 | return request({
55 | url: "/application/" + id,
56 | method: "put",
57 | data: data
58 | });
59 | }
60 |
61 | // 更新应用app key
62 | export function changeAppKeyRequest(id) {
63 | return request({
64 | url: "/change_application_key",
65 | method: "post",
66 | data: {
67 | id
68 | }
69 | });
70 | }
71 |
72 | // 拷贝应用
73 | export function copyAppRequest(data) {
74 | return request({
75 | url: "/application/copy",
76 | method: "post",
77 | data: data
78 | });
79 | }
80 |
--------------------------------------------------------------------------------
/src/api/document.js:
--------------------------------------------------------------------------------
1 | import request from "@/utils/request";
2 |
3 | // 获取文档列表
4 | export function getDocumentListRequest(params) {
5 | return request({
6 | url: "/document",
7 | method: "get",
8 | params
9 | });
10 | }
11 |
12 | // 查看文档详情
13 | export function getDocumentDetailRequest(id, params = null) {
14 | return request({
15 | url: "/document/" + id,
16 | method: "get",
17 | params
18 | });
19 | }
20 |
21 | // 新建文档
22 | export function insertDocumentRequest(data) {
23 | return request({
24 | url: "/document",
25 | method: "post",
26 | data: data
27 | });
28 | }
29 |
30 | // 更新文档信息
31 | export function updateDocumentRequest(id, data) {
32 | return request({
33 | url: "/document/" + id,
34 | method: "put",
35 | data: data
36 | });
37 | }
38 |
39 | // 删除我的文档
40 | export function delDocumentRequest(id, params = null) {
41 | return request({
42 | url: "/document/" + id,
43 | method: "delete",
44 | params
45 | });
46 | }
47 |
--------------------------------------------------------------------------------
/src/api/interface.js:
--------------------------------------------------------------------------------
1 | import request from "@/utils/request";
2 |
3 | // 获取接口列表
4 | export function getApisRequest(params) {
5 | return request({
6 | url: "/interface",
7 | method: "get",
8 | params
9 | });
10 | }
11 |
12 | // 获取接口Map
13 | export function getApisMapRequest(params) {
14 | return request({
15 | url: "/interface_map",
16 | method: "get",
17 | params
18 | });
19 | }
20 |
21 | // 获取接口简易list
22 | export function getApisListRequest(params) {
23 | return request({
24 | url: "/interface/list",
25 | method: "get",
26 | params
27 | });
28 | }
29 |
30 | // 查看接口详情
31 | export function getApiInfoRequest(id) {
32 | return request({
33 | url: "/interface/" + id,
34 | method: "get"
35 | });
36 | }
37 |
38 | // 创建我的接口
39 | export function createApiRequest(data) {
40 | return request({
41 | url: "/interface",
42 | method: "post",
43 | data: data
44 | });
45 | }
46 |
47 | // 清空我的应用接口
48 | export function emptyApisRequest(data) {
49 | return request({
50 | url: "/interface/empty",
51 | method: "post",
52 | data: data
53 | });
54 | }
55 |
56 | // 删除我的接口
57 | export function delApiRequest(id) {
58 | return request({
59 | url: "/interface/" + id,
60 | method: "delete"
61 | });
62 | }
63 |
64 | // 更新接口信息
65 | export function updateApiRequest(id, data) {
66 | return request({
67 | url: "/interface/" + id,
68 | method: "put",
69 | data: data
70 | });
71 | }
72 |
73 | // 测试接口
74 | export function debugApiRequest(data) {
75 | return request({
76 | url: "/interface_debug",
77 | method: "post",
78 | data: data
79 | });
80 | }
81 |
82 | // 拷贝接口
83 | export function copyApiRequest(data) {
84 | return request({
85 | url: "/interface/copy",
86 | method: "post",
87 | data: data
88 | });
89 | }
90 |
91 | // 转移接口
92 | export function moveApiRequest(data) {
93 | return request({
94 | url: "/interface/move",
95 | method: "post",
96 | data: data
97 | });
98 | }
99 |
100 | // 查看接口请求历史记录
101 | export function getRequestLogsRequest(params) {
102 | return request({
103 | url: "/request_log",
104 | method: "get",
105 | params
106 | });
107 | }
108 |
109 | // 查看接口相关数据统计
110 | export function getStatisticsRequest(params) {
111 | return request({
112 | url: "/statistics",
113 | method: "get",
114 | params
115 | });
116 | }
117 |
--------------------------------------------------------------------------------
/src/api/user.js:
--------------------------------------------------------------------------------
1 | import request from "@/utils/request";
2 |
3 | // 登录
4 | export function loginRequest(data) {
5 | return request({
6 | url: "/login",
7 | method: "post",
8 | data: data
9 | });
10 | }
11 |
12 | // 获取当前登录用户信息
13 | export function getMyInfoRequest() {
14 | return request({
15 | url: "/account",
16 | method: "get"
17 | });
18 | }
19 |
20 | // 编辑个人资料
21 | export function updateUserInfoRequest(data) {
22 | return request({
23 | url: "/user/profile",
24 | method: "post",
25 | data
26 | });
27 | }
28 |
29 | // 获取我的登录历史日志
30 | export function getLoginLogRequest() {
31 | return request({
32 | url: "/login_log",
33 | method: "get",
34 | });
35 | }
36 |
37 | // 退出登录
38 | export function logoutRequest() {
39 | return request({
40 | url: "/session",
41 | method: "delete"
42 | });
43 | }
44 |
45 | // 获取用户主页数据信息
46 | export function getUserHomeRequest(uid) {
47 | return request({
48 | url: "/user/home",
49 | method: "get",
50 | params: { uid: uid }
51 | });
52 | }
53 |
54 | // 获取当前用户信息--来自编辑页
55 | export function getUserInfoByEditRequest() {
56 | return request({
57 | url: "/user/edit",
58 | method: "get"
59 | });
60 | }
61 |
62 | // 更新用户信息
63 | export function updateUserDataRequest(uid, data) {
64 | return request({
65 | url: "/user/" + uid,
66 | method: "put",
67 | data: data
68 | });
69 | }
70 |
71 | // 更新用户登录密码
72 | export function updateUserPwdRequest(data) {
73 | return request({
74 | url: "/user_pwd",
75 | method: "put",
76 | data: data
77 | });
78 | }
79 |
--------------------------------------------------------------------------------
/src/assets/default_avatar.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bluvenr/open_virapi_front_end/bdccf974afc6a7ef2fd9648ddf44676898a4a6bf/src/assets/default_avatar.jpg
--------------------------------------------------------------------------------
/src/assets/logo-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bluvenr/open_virapi_front_end/bdccf974afc6a7ef2fd9648ddf44676898a4a6bf/src/assets/logo-icon.png
--------------------------------------------------------------------------------
/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bluvenr/open_virapi_front_end/bdccf974afc6a7ef2fd9648ddf44676898a4a6bf/src/assets/logo.png
--------------------------------------------------------------------------------
/src/assets/logo2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bluvenr/open_virapi_front_end/bdccf974afc6a7ef2fd9648ddf44676898a4a6bf/src/assets/logo2.png
--------------------------------------------------------------------------------
/src/assets/sponsor/alipay_qr_code.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bluvenr/open_virapi_front_end/bdccf974afc6a7ef2fd9648ddf44676898a4a6bf/src/assets/sponsor/alipay_qr_code.jpg
--------------------------------------------------------------------------------
/src/assets/sponsor/alipay_qr_code.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bluvenr/open_virapi_front_end/bdccf974afc6a7ef2fd9648ddf44676898a4a6bf/src/assets/sponsor/alipay_qr_code.png
--------------------------------------------------------------------------------
/src/assets/sponsor/wxpay_qr_code.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bluvenr/open_virapi_front_end/bdccf974afc6a7ef2fd9648ddf44676898a4a6bf/src/assets/sponsor/wxpay_qr_code.jpg
--------------------------------------------------------------------------------
/src/assets/sponsor/wxpay_qr_code.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bluvenr/open_virapi_front_end/bdccf974afc6a7ef2fd9648ddf44676898a4a6bf/src/assets/sponsor/wxpay_qr_code.png
--------------------------------------------------------------------------------
/src/assets/theme.less:
--------------------------------------------------------------------------------
1 | @import "~ant-design-vue/dist/antd.less";
2 |
3 | @import "./theme_param.less";
4 |
5 | .ant-menu-inline,.ant-menu-vertical {
6 | border-width: 0;
7 | }
8 | .ant-menu-horizontal {
9 | border-bottom-width: 0;
10 | }
11 | .ant-menu-horizontal.ant-menu-sub, .ant-menu-vertical.ant-menu-sub, .ant-menu-vertical-left.ant-menu-sub, .ant-menu-vertical-right.ant-menu-sub{
12 | min-width: 80px;
13 | }
14 | .ant-cascader-menu{
15 | height: auto;
16 | max-height: 240px;
17 | min-height: 32px;
18 | }
19 | .ant-table-body{
20 | overflow-x: auto !important;
21 | }
22 | .ant-table-fixed-left,.ant-table-fixed-right{
23 | z-index: 1;
24 | }
25 |
--------------------------------------------------------------------------------
/src/assets/theme_param.less:
--------------------------------------------------------------------------------
1 | @primary-color: #2570ff; // 全局主色
2 | @link-color: #2570ff; // 链接色
3 | @success-color: #52c41a; // 成功色
4 | @warning-color: #faad14; // 警告色
5 | @error-color: #f5222d; // 错误色
6 | @font-size-base: 14px; // 主字号
7 | @heading-color: rgba(0, 0, 0, .85); // 标题色
8 | @text-color: rgba(0, 0, 0, .65); // 主文本色
9 | @text-color-secondary : rgba(0, 0, 0, .45); // 次文本色
10 | @disabled-color : rgba(0, 0, 0, .25); // 失效色
11 | @border-radius-base: 4px; // 组件/浮层圆角
12 | @border-color-base: #d9d9d9; // 边框色
13 | @box-shadow-base: 0 2px 8px rgba(0, 0, 0, .15); // 浮层阴影
14 |
--------------------------------------------------------------------------------
/src/components/ApiItem.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
31 |
32 | {{ apiInfo.describe || '--无接口描述--' }}
33 |
34 |
35 |
36 | URI
37 | {{ `/${appSlug || '--'}/${apiInfo.uri}` }}
38 |
46 |
47 |
48 | 请求方式
49 | {{ apiInfo.method }}
50 |
51 |
52 | 响应结构(data数据)
53 | 该接口无data数据响应
54 |
55 |
56 |
MockJS
57 |
{{ responseData }}
58 |
59 |
60 |
61 |
62 |
63 | 最终结构示例
64 | (请求成功)
65 |
66 |
{{ responseSucceed }}
67 |
68 | 最终结构示例
69 | (请求失败)
70 |
71 |
{{ responseFailed }}
72 |
73 |
74 |
75 |
76 |
77 |
78 |
112 |
113 |
114 |
115 |
128 |
129 |
130 |
131 |
132 |
133 |
134 | URI
135 |
136 |
137 |
138 |
139 |
140 |
157 |
158 |
159 |
160 |
161 | 请求方式
162 |
163 |
164 |
174 | GET
175 | POST
176 | PUT
177 | DELETE
178 |
179 |
180 |
181 |
182 |
183 |
184 | 对应响应数据(data)
185 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
366 |
367 |
584 |
--------------------------------------------------------------------------------
/src/components/Footer.vue:
--------------------------------------------------------------------------------
1 |
2 |
90 |
91 |
92 |
102 |
103 |
202 |
--------------------------------------------------------------------------------
/src/components/Header.vue:
--------------------------------------------------------------------------------
1 |
2 |
87 |
88 |
89 |
108 |
109 |
182 |
--------------------------------------------------------------------------------
/src/components/MonacoEditor.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
100 |
101 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | // import Antd from 'ant-design-vue'
3 | import '@/assets/theme.less'
4 | import App from './App.vue'
5 | import router from './router'
6 | import store from './store'
7 | import {
8 | Base,
9 | Button,
10 | Icon,
11 | Modal,
12 | Layout,
13 | message,
14 | notification,
15 | Anchor,
16 | AutoComplete,
17 | Alert,
18 | BackTop,
19 | Badge,
20 | Breadcrumb,
21 | Carousel,
22 | Cascader,
23 | Checkbox,
24 | Divider,
25 | Form,
26 | Input,
27 | InputNumber,
28 | LocaleProvider,
29 | Menu,
30 | Pagination,
31 | Popconfirm,
32 | Popover,
33 | Radio,
34 | Select,
35 | Spin,
36 | Table,
37 | Tabs,
38 | Tag,
39 | Tooltip,
40 | Upload,
41 | Drawer,
42 | ConfigProvider,
43 | Empty,
44 | } from 'ant-design-vue'
45 |
46 | Vue.config.productionTip = false
47 |
48 | // Vue.use(Antd)
49 |
50 | Vue.use(Base);
51 | Vue.use(LocaleProvider);
52 | Vue.use(Divider);
53 | Vue.use(Anchor);
54 | Vue.use(Alert);
55 | Vue.use(AutoComplete);
56 | Vue.use(Breadcrumb);
57 | Vue.use(Carousel);
58 | Vue.use(BackTop);
59 | Vue.use(Badge);
60 | Vue.use(Menu);
61 | Vue.use(Form);
62 | Vue.use(Button);
63 | Vue.use(Pagination);
64 | Vue.use(Popconfirm);
65 | Vue.use(Popover);
66 | Vue.use(Modal);
67 | Vue.use(Icon);
68 | Vue.use(Table);
69 | Vue.use(Tag);
70 | Vue.use(Cascader);
71 | Vue.use(Checkbox);
72 | Vue.use(Input);
73 | Vue.use(InputNumber);
74 | Vue.use(Radio);
75 | Vue.use(Select);
76 | Vue.use(Spin);
77 | Vue.use(Tabs);
78 | Vue.use(Layout);
79 | Vue.use(Upload);
80 | Vue.use(Drawer);
81 | Vue.use(Tooltip);
82 | Vue.use(Empty);
83 | Vue.use(ConfigProvider);
84 |
85 | Vue.prototype.$message = message;
86 | Vue.prototype.$notification = notification;
87 | Vue.prototype.$info = Modal.info;
88 | Vue.prototype.$success = Modal.success;
89 | Vue.prototype.$error = Modal.error;
90 | Vue.prototype.$warning = Modal.warning;
91 | Vue.prototype.$confirm = Modal.confirm;
92 |
93 | new Vue({
94 | router,
95 | store,
96 | render: h => h(App)
97 | }).$mount('#var-app')
98 |
--------------------------------------------------------------------------------
/src/router.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import VueRouter from 'vue-router'
3 | import store from './store'
4 | import Home from './views/Home.vue'
5 | import Login from './views/Login.vue'
6 | import AppList from './views/application/List'
7 | import AppLayout from './views/application/Layout'
8 | import AppDetail from './views/application/Detail'
9 | import AppCreate from './views/application/Create'
10 | import ApiList from './views/interface/List'
11 | import ApiCreate from './views/interface/Create'
12 | import ApiDebug from './views/interface/Debug'
13 | import AppStatistics from './views/data_analysis/Statistics'
14 | import AppLog from './views/data_analysis/Log'
15 | import BasicSetting from './views/management/Setting'
16 | import OtherSetting from './views/management/Other'
17 | import Export from './views/management/Export'
18 | import CenterLayout from './views/center/Layout'
19 | import Personal from './views/center/Personal'
20 | import Security from './views/center/Security'
21 | import Information from './views/center/Information'
22 |
23 | Vue.use(VueRouter)
24 |
25 | const routerMap = [
26 | {
27 | path: '/login',
28 | name: 'login',
29 | component: Login,
30 | meta: {
31 | title: '登录',
32 | full: true
33 | }
34 | },
35 | {
36 | path: '/',
37 | name: 'home',
38 | component: Home,
39 | meta: {
40 | title: '首页',
41 | auth: true
42 | }
43 | },
44 | {
45 | path: '/apps',
46 | name: 'myApps',
47 | component: AppList,
48 | meta: {
49 | title: '我的所有应用',
50 | auth: true
51 | }
52 | },
53 | {
54 | path: "/create",
55 | name: "appCreate",
56 | component: AppCreate,
57 | meta: {
58 | title: '新建应用',
59 | auth: true
60 | }
61 | },
62 | {
63 | path: "/create_api",
64 | name: "apiCreate",
65 | component: ApiCreate,
66 | meta: {
67 | title: '新建接口',
68 | auth: true
69 | }
70 | },
71 | {
72 | path: "/app/:slug",
73 | component: AppLayout,
74 | children: [
75 | {
76 | path: "info",
77 | name: "appDetail",
78 | component: AppDetail,
79 | meta: {
80 | title: '应用详情',
81 | auth: true
82 | }
83 | },
84 | {
85 | path: "api",
86 | name: "apiList",
87 | component: ApiList,
88 | meta: {
89 | title: '应用接口列表',
90 | auth: true
91 | }
92 | },
93 | {
94 | path: "debug",
95 | name: "apiDebug",
96 | component: ApiDebug,
97 | meta: {
98 | title: '接口测试',
99 | auth: true
100 | }
101 | },
102 | {
103 | path: "log",
104 | name: "appLog",
105 | component: AppLog,
106 | meta: {
107 | title: '应用日志',
108 | auth: true
109 | }
110 | },
111 | {
112 | path: "statistics",
113 | name: "appStatistics",
114 | component: AppStatistics,
115 | meta: {
116 | title: '数据统计',
117 | auth: true
118 | }
119 | },
120 | {
121 | path: "setting",
122 | name: "basicSetting",
123 | component: BasicSetting,
124 | meta: {
125 | title: '应用设置',
126 | subKey: 'management',
127 | auth: true
128 | }
129 | },
130 | {
131 | path: "other",
132 | name: "otherSetting",
133 | component: OtherSetting,
134 | meta: {
135 | title: '高级操作',
136 | subKey: 'management',
137 | auth: true
138 | }
139 | },
140 | {
141 | path: "export",
142 | name: "exportApp",
143 | component: Export,
144 | meta: {
145 | title: '导出接口文档',
146 | subKey: 'management',
147 | auth: true
148 | }
149 | },
150 | ]
151 | },
152 | {
153 | path: "/",
154 | component: CenterLayout,
155 | children: [
156 | {
157 | path: "/profile",
158 | name: "profile",
159 | component: Personal,
160 | meta: {
161 | title: '个人资料',
162 | auth: true
163 | }
164 | },
165 | {
166 | path: "/security",
167 | name: "security",
168 | component: Security,
169 | meta: {
170 | title: '账号密码',
171 | auth: true
172 | }
173 | },
174 | {
175 | path: "/information",
176 | name: "information",
177 | component: Information,
178 | meta: {
179 | title: '我的消息',
180 | auth: true
181 | }
182 | },
183 | ]
184 | },
185 | {
186 | path: "*",
187 | name: "404",
188 | component: () => import(/* webpackChunkName: "404" */ "./views/other/404.vue"),
189 | meta: {
190 | title: '404',
191 | full: true
192 | }
193 | }
194 | ];
195 |
196 | const router = new VueRouter({
197 | routes: routerMap,
198 | scrollBehavior(to, from, savedPosition) {
199 | if (savedPosition) {
200 | return savedPosition;
201 | } else {
202 | return { x: 0, y: 0 };
203 | }
204 | },
205 | mode: "hash"
206 | });
207 |
208 | router.beforeEach((to, from, next) => {
209 | if (to.meta.auth && !store.getters.isLogin) {
210 | next({ name: 'login' }); return;
211 | }
212 |
213 | if (store.getters.isLogin && to.name == 'login') {
214 | next({ name: 'home' }); return;
215 | }
216 |
217 | // 如果设置标题,拦截后设置标题
218 | if (to.meta.title) {
219 | document.title = to.meta.title + " | VirAPI -- 虚拟数据接口系统 [开源版]";
220 | } else {
221 | document.title = "VirAPI -- 虚拟数据接口系统 [开源版]";
222 | }
223 | next();
224 | });
225 |
226 | export default router;
227 |
--------------------------------------------------------------------------------
/src/store.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Vuex from 'vuex'
3 | import app from '@/store/app'
4 | import user from '@/store/user'
5 | import getters from '@/store/getters'
6 |
7 | Vue.use(Vuex)
8 |
9 | export default new Vuex.Store({
10 | modules: {
11 | user,
12 | app
13 | },
14 | getters
15 | })
16 |
--------------------------------------------------------------------------------
/src/store/app.js:
--------------------------------------------------------------------------------
1 | import { getAppsListRequest } from "@/api/application.js";
2 |
3 | export default {
4 | state: {
5 | appList: null,
6 | maxConfig: null,
7 | },
8 | mutations: {
9 | setAppList(state, data) {
10 | if (data) {
11 | state.appList = data;
12 | } else {
13 | state.appList = [];
14 | }
15 | },
16 | setMaxConfig(state, data) {
17 | if (data) {
18 | state.maxConfig = data;
19 | } else {
20 | state.maxConfig = {};
21 | }
22 | },
23 | },
24 | actions: {
25 | handleUpdateMyApps({ commit }) {
26 | return new Promise((resolve, reject) => {
27 | getAppsListRequest()
28 | .then(res => {
29 | commit("setAppList", res.data);
30 | resolve(res.data);
31 | })
32 | .catch(err => {
33 | reject(err);
34 | });
35 | });
36 | },
37 | }
38 | };
39 |
--------------------------------------------------------------------------------
/src/store/getters.js:
--------------------------------------------------------------------------------
1 | const getters = {
2 | isLogin: state => state.user.isLogin,
3 | userName: state => state.user.nickname,
4 | avatarUrl: state => state.user.avatarUrl,
5 | virUid: state => state.user.virUid,
6 | registrationDate: state => state.user.registrationDate,
7 | myApps: state => state.app.appList,
8 | appVersion: state => state.app.version,
9 | };
10 |
11 | export default getters;
12 |
--------------------------------------------------------------------------------
/src/store/user.js:
--------------------------------------------------------------------------------
1 | import {
2 | loginRequest,
3 | logoutRequest,
4 | getMyInfoRequest
5 | } from "@/api/user.js";
6 |
7 | function getLoginCookie() {
8 | return document.cookie.split("; ").filter(v => v.indexOf('v_token=') === 0).map(v => v.split('=')[1])[0] || null;
9 | }
10 |
11 | export default {
12 | state: {
13 | isLogin: getLoginCookie(),
14 | nickname: null,
15 | avatarUrl: null,
16 | virUid: null,
17 | basicsData: null,
18 | },
19 | mutations: {
20 | setLoginStatus(state, value) {
21 | state.isLogin = value;
22 | },
23 | setUserName(state, name) {
24 | if (name) {
25 | state.nickname = name;
26 | } else {
27 | state.nickname = null;
28 | }
29 | },
30 | setUserAvatarUrl(state, avatarUrl) {
31 | if (avatarUrl) {
32 | state.avatarUrl = avatarUrl;
33 | } else {
34 | state.avatarUrl = null;
35 | }
36 | },
37 | setUserVirUid(state, virUid) {
38 | if (virUid) {
39 | state.virUid = virUid;
40 | } else {
41 | state.virUid = null;
42 | }
43 | },
44 | setUserBasicsData(state, data) {
45 | if (data) {
46 | state.basicsData = data;
47 | } else {
48 | state.basicsData = null;
49 | }
50 | },
51 | },
52 | actions: {
53 | // 设置用户主页个人信息
54 | handleUserBasicsData({ commit }, data) {
55 | commit("setUserBasicsData", data);
56 | },
57 | // 登录
58 | handleLogin({ commit }, data) {
59 | return new Promise((resolve, reject) => {
60 | loginRequest(data)
61 | .then(res => {
62 | commit("setLoginStatus", 1);
63 | commit("setUserName", res.data.nickname);
64 | commit("setUserAvatarUrl", res.data.avatar);
65 | commit("setUserVirUid", res.data.vir_uid);
66 | commit("setUserBasicsData", res.data.other_info);
67 | resolve(res.data);
68 | })
69 | .catch(err => {
70 | reject(err);
71 | });
72 | });
73 | },
74 | // 退出登录
75 | handleLogOut({ commit }) {
76 | return new Promise((resolve, reject) => {
77 | logoutRequest()
78 | .then(() => {
79 | commit("setLoginStatus", null);
80 | commit("setUserName", null);
81 | commit("setUserAvatarUrl", null);
82 | commit("setUserVirUid", null);
83 | commit("setUserBasicsData", null);
84 | resolve();
85 | })
86 | .catch(err => {
87 | commit("setLoginStatus", null);
88 | commit("setUserName", null);
89 | commit("setUserAvatarUrl", null);
90 | commit("setUserVirUid", null);
91 | commit("setUserBasicsData", null);
92 | reject(err);
93 | });
94 | });
95 | },
96 | // 获取用户相关信息
97 | handleGetUserInfo({ commit }) {
98 | return new Promise((resolve, reject) => {
99 | getMyInfoRequest()
100 | .then(res => {
101 | const data = res.data;
102 | commit("setUserName", data.nickname);
103 | commit("setUserAvatarUrl", data.avatar);
104 | commit("setUserVirUid", data.vir_uid);
105 | commit("setUserBasicsData", data.other_info);
106 | resolve(data);
107 | })
108 | .catch(err => {
109 | reject(err);
110 | });
111 | });
112 | },
113 | }
114 | };
115 |
--------------------------------------------------------------------------------
/src/utils/handle.js:
--------------------------------------------------------------------------------
1 |
2 | export function dateFormat(date, fmt) {
3 | if (null == date || undefined == date) return '';
4 | date = new Date(date);
5 | var o = {
6 | 'M+': date.getMonth() + 1, //月份
7 | 'd+': date.getDate(), //日
8 | 'h+': date.getHours(), //小时
9 | 'm+': date.getMinutes(), //分
10 | 's+': date.getSeconds(), //秒
11 | 'S': date.getMilliseconds() //毫秒
12 | };
13 | if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length));
14 | for (var k in o)
15 | if (new RegExp('(' + k + ')').test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length)));
16 | return fmt;
17 | }
18 |
19 | export function amityDateFormat(date) {
20 | //获取js 时间戳
21 | var time = new Date().getTime();
22 | //去掉 js 时间戳后三位,与php 时间戳保持一致
23 | time = parseInt((time - new Date(date)) / 1000);
24 |
25 | //存储转换值
26 | var s;
27 | if (time < 60 * 10) { //十分钟内
28 | return '刚刚';
29 | } else if ((time < 60 * 60) && (time >= 60 * 10)) {
30 | //超过十分钟少于1小时
31 | s = Math.floor(time / 60);
32 | return s + '分钟前';
33 | } else if ((time < 60 * 60 * 24) && (time >= 60 * 60)) {
34 | //超过1小时少于24小时
35 | s = Math.floor(time / 60 / 60);
36 | return s + '小时前';
37 | } else if ((time < 60 * 60 * 24 * 3) && (time >= 60 * 60 * 24)) {
38 | //超过1天少于3天内
39 | s = Math.floor(time / 60 / 60 / 24);
40 | return s + '天前';
41 | } else {
42 | //超过3天
43 | return dateFormat(date, 'yyyy-MM-dd');
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/utils/pictureProcess.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 读取图片文件获取对应base64
3 | * @param img 图片文件
4 | * @param callback 回调
5 | */
6 | export function getBase64(img, callback) {
7 | const reader = new FileReader();
8 | reader.addEventListener("load", () => callback(reader.result));
9 | reader.readAsDataURL(img);
10 | }
11 |
12 | /**
13 | * 裁剪icon logo图片
14 | */
15 | export function cropIconToBase64(img, callback, max_width = 120) {
16 | getBase64(img, res => {
17 | scaleCanvas(res, max_width, (data) => {
18 | clipSquareCanvas(data, base64 => callback(base64));
19 | });
20 | });
21 | }
22 |
23 | /**
24 | * 裁剪成正方形
25 | */
26 | function clipSquareCanvas(url, callback) {
27 | let img = new Image();
28 | img.src = url;
29 | img.onload = function () {
30 | let canvas = document.createElement("canvas");
31 | let ctx = canvas.getContext('2d');
32 | let eLength = Math.min(this.width, this.height);
33 | canvas.width = eLength;
34 | canvas.height = eLength;
35 | ctx.drawImage(this, (this.width - eLength) / 2, (this.height - eLength) / 2, eLength, eLength, 0, 0, eLength, eLength);
36 | let base64 = canvas.toDataURL('image/jpeg', 1); // 这里的“1”是指的是处理图片的清晰度(0-1)之间,当然越小图片越模糊,处理后的图片大小也就越小
37 | callback && callback(base64) // 回调base64字符串
38 | }
39 | }
40 |
41 | /**
42 | * 缩放
43 | */
44 | function scaleCanvas(url, max_width, callback) {
45 | let img = new Image();
46 | img.src = url;
47 | img.onload = function () {
48 | let canvas = document.createElement("canvas");
49 | let ctx = canvas.getContext('2d');
50 | let eLength = Math.min(this.width, this.height, max_width);
51 |
52 | if (eLength == max_width) {
53 | let ratio = this.width / this.height;
54 |
55 | if (this.width > this.height) {
56 | canvas.width = eLength * ratio;
57 | canvas.height = eLength;
58 | } else {
59 | canvas.width = eLength;
60 | canvas.height = eLength / ratio;
61 | }
62 | } else {
63 | canvas.width = this.width;
64 | canvas.height = this.height;
65 | }
66 |
67 | ctx.drawImage(this, 0, 0, canvas.width, canvas.height);
68 | let base64 = canvas.toDataURL('image/jpeg', 1); // 这里的“1”是指的是处理图片的清晰度(0-1)之间,当然越小图片越模糊,处理后的图片大小也就越小
69 | callback && callback(base64) // 回调base64字符串
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/utils/request.js:
--------------------------------------------------------------------------------
1 | import axios from "axios"
2 | import store from "../store"
3 | import router from "@/router"
4 | import { Modal, message } from 'ant-design-vue'
5 |
6 | // 创建axios实例
7 | const service = axios.create({
8 | baseURL: process.env.VUE_APP_REQUEST_URL, // api的base_url
9 | timeout: 30000, // 请求超时时间
10 | headers: {
11 | "Content-Type": "application/json; charset=UTF-8"
12 | }
13 | });
14 |
15 | // request拦截器
16 | service.interceptors.request.use(
17 | config => {
18 | return config;
19 | },
20 | error => {
21 | return Promise.reject(error);
22 | }
23 | );
24 |
25 | let loginStatusErr = false;
26 |
27 | // respone拦截器
28 | service.interceptors.response.use(
29 | response => {
30 | const res = response.data;
31 |
32 | if (res.code !== 200) {
33 | // 401 -> 未登陆或登陆验证信息失效,需要重新登录
34 | if (res.code == 401 && !loginStatusErr) {
35 | loginStatusErr = true;
36 |
37 | if (store.getters.isLogin) {
38 | store
39 | .dispatch("handleLogOut")
40 | .then(res => {
41 | Modal.confirm({
42 | title: '账户登录状态异常',
43 | content: '登录信息失效或被挤下线,是否重新登录',
44 | okText: '重新登录',
45 | onOk: () => {
46 | router.push("/login");
47 | }
48 | })
49 | })
50 | .catch(err => {
51 | Modal.confirm({
52 | title: '提示',
53 | content: '账号状态异常,请重新登录',
54 | okText: '重新登录',
55 | onOk: () => {
56 | router.push("/login");
57 | }
58 | })
59 | });
60 | } else {
61 | message.error('当前登录状态异常', 5);
62 | }
63 | }
64 |
65 | return Promise.reject({ code: res.code, message: res.message });
66 | } else {
67 | loginStatusErr = false;
68 | return res;
69 | }
70 | },
71 | error => {
72 | return Promise.reject(error);
73 | }
74 | );
75 |
76 | export default service;
77 |
--------------------------------------------------------------------------------
/src/views/Home.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
11 |
12 |
13 |
14 |
15 |
{{ $store.getters.userName }}
16 |
17 | ID:
18 | {{ $store.getters.virUid || '暂未定义' }}
19 | 前往设置
23 |
24 |
25 |
26 | 已创建
27 | {{ $store.state.user.basicsData.have_app_count || 0 }} 个应用
30 |
31 | 于{{ $store.state.user.basicsData.created.substring(0, 10) || '--' }}注册
32 |
33 |
34 |
35 |
50 |
51 |
52 |
53 |
54 |
我的应用
55 |
56 |
63 |
64 |
65 |
{{ item.name.substr(0, 1) }}
66 |
67 |
68 |
{{ item.name || '--' }}
69 |
70 |
71 | {{ item.api_count || 0 }}
72 |
{{ item.created }}
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
106 |
107 |
298 |
--------------------------------------------------------------------------------
/src/views/Login.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | 欢迎使用
10 | VirAPI 开源版
11 |
12 | 由
13 | www.virapi.com 提供
14 |
15 |
16 |
17 |
18 |
19 |
26 |
27 |
28 |
29 |
30 |
38 |
39 |
40 |
41 |
42 | 登 录
43 |
44 |
45 |
46 |
59 |
60 |
61 |
62 |
63 |
102 |
103 |
156 |
--------------------------------------------------------------------------------
/src/views/application/Detail.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
60 |
61 |
62 |
63 |
应用API网址:
64 |
{{ `${app_api_request_uri || '--/'}${$store.getters.virUid}/${appInfo.slug || '--'}` }}
65 |
71 |
72 |
73 |
74 |
75 |
验证方式:
76 |
77 | {{ verifyRuleMap[appInfo.verify_rule] || '未知验证方式' }}
78 |
79 |
80 |
81 | 请求头部Token验证: 请求头部Header中附带一个名为【app-token】的报文数据;
82 |
83 |
84 | 请求参数Token验证: 请求路由请求参数中附带一个名为【_token】的GET请求参数;
85 |
86 |
87 | 兼容模式验证: 兼容请求头部和请求参数验证规则,优先从请求头部中获取验证Token,若无则再从请求参数中获取。
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 | APP KEY
97 |
98 |
99 | :
100 | 更换
101 |
102 |
103 |
104 | 复制
105 |
106 |
107 |
108 |
109 |
请求响应模板结构:
110 |
111 |
112 | 最终结构示例(
113 | 请求成功 ):
114 |
115 |
{{ responseSucceed }}
116 |
117 | 最终结构示例(
118 | 请求失败 ):
119 |
120 |
{{ responseFailed }}
121 |
122 |
123 |
124 |
125 |
126 |
127 |
233 |
234 |
331 |
332 |
--------------------------------------------------------------------------------
/src/views/application/Layout.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
13 |
18 |
24 | {{
25 | item.name.substr(0, 1)
26 | }}
27 |
28 | {{ item.name }}
29 |
30 |
31 |
32 |
33 |
39 |
40 |
43 |
44 | 应用信息
45 |
46 |
47 |
48 |
51 |
52 | 所有接口
53 |
54 |
55 |
56 |
59 |
60 | 接口测试
61 |
62 |
63 |
64 |
67 |
68 | 请求日志
69 |
70 |
71 |
72 |
78 |
79 | 数据统计
80 |
81 |
82 |
83 |
84 |
85 | 管理
86 |
87 |
88 |
94 | 应用设置
95 |
96 |
97 |
98 |
104 | 高级操作
105 |
106 |
107 |
108 |
111 | 导出接口文档
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 | 我的应用
134 |
135 |
136 | {{
137 | nowApplication ? nowApplication.name : $route.params.slug
138 | }}
139 |
140 | {{
141 | $route.meta.title || "--"
142 | }}
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
205 |
206 |
280 |
--------------------------------------------------------------------------------
/src/views/application/List.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 |
14 |
15 |
16 |
17 |
18 | 最后更新时间降序
19 |
20 |
21 | 最后更新时间升序
22 |
23 |
24 | 创建时间降序
25 |
26 |
27 | 创建时间升序
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | {{ item.name.substr(0, 1) }}
37 |
38 |
39 |
40 |
41 | {{ item.name || '--' }}
42 |
43 |
{{ item.describe }}
44 |
45 |
46 |
47 |
48 |
49 | 对应接口数
50 |
51 |
52 | {{ item.api_count || 0 }}
53 |
54 |
55 |
56 |
57 |
58 |
59 | 最后更新时间:{{ dateFormat(item.updated) }}
60 |
61 | {{ amityDateFormat(item.updated) }}
62 |
63 |
64 |
65 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
146 |
147 |
280 |
--------------------------------------------------------------------------------
/src/views/center/Information.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | 我的消息通知
4 |
5 |
9 |
10 |
11 |
12 |
46 |
47 |
59 |
--------------------------------------------------------------------------------
/src/views/center/Layout.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
{{ $store.getters.userName }}
7 |
8 |
9 |
15 |
16 |
17 |
18 | 个人资料
19 |
20 |
21 |
22 |
23 |
24 |
25 | 账号密码
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | 消息通知
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
87 |
88 |
127 |
--------------------------------------------------------------------------------
/src/views/center/Personal.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | 编辑个人资料
4 |
5 |
6 |
7 |
19 |
20 |
21 |
22 |
34 | {{ virUidDisabled ? '自定义编辑' : '取消编辑' }}
39 |
40 |
41 |
42 |
43 |
53 |
54 |
55 |
56 |
61 | 选择图片
62 |
63 |
64 |
65 |
66 |
67 |
68 | 确定保存
69 | 取 消
70 |
71 |
72 |
73 |
74 |
75 |
226 |
227 |
245 |
--------------------------------------------------------------------------------
/src/views/center/Security.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | 重置我的账号密码
4 |
5 |
6 |
7 |
18 |
19 |
20 |
21 |
35 |
36 |
37 |
38 |
52 |
53 |
54 |
55 | 确定重置密码
56 |
57 |
58 |
59 |
60 |
61 |
141 |
142 |
144 |
--------------------------------------------------------------------------------
/src/views/data_analysis/Log.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 请求日志
5 |
12 | 今日
13 | 昨日
14 | 近7日
15 | 近30日
16 |
17 |
18 | 刷新
25 |
26 |
27 |
67 |
68 |
78 | {{ moment(created).format('YYYY-MM-DD HH:mm:ss') }}
82 |
83 |
84 | {{ uri.split($route.params.slug)[1] || '--' }}
89 |
90 |
91 | {{ method || '未知' }}
92 |
93 | {{ resultMap[result] || '未知' }}
98 |
99 |
100 |
101 |
102 | {{ params || '无' }}
103 |
104 | {{ params || '无' }}
105 |
106 | --
107 |
108 |
109 |
110 |
111 |
112 | {{ response || '无' }}
113 |
114 | {{ response || '无' }}
115 |
116 | --
117 |
118 |
119 |
120 |
121 |
122 |
304 |
305 |
358 |
--------------------------------------------------------------------------------
/src/views/data_analysis/Statistics.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 数据统计
5 |
12 | 今日
13 | 昨日
14 | 近7日
15 | 近30日
16 |
17 |
18 | 刷新
25 |
26 |
27 |
28 |
40 |
41 |
53 |
54 |
55 |
Top10请求路由
56 |
57 |
64 |
65 | {{ uri.split($route.params.slug)[1] || uri }}
70 |
71 |
72 |
73 |
74 |
75 |
76 |
Top10请求来源IP
77 |
86 |
87 |
88 |
89 |
90 |
91 |
263 |
264 |
326 |
--------------------------------------------------------------------------------
/src/views/interface/Create.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
新建接口
6 |
接口创建成功后,即可在该接口下创建您所需要的API。
7 |
当前系统默认每个应用下最多创建{{ $store.state.user.basicsData.max_api_count }}个接口,更多开放敬请期待!
8 |
9 | 若是第一次使用,或不知道如何新建接口,可点击这里查看
10 | 新手快速入门 。
14 |
15 |
16 | 注意: 请遵守本平台的规范及相关法律法规,若发现接口存在违规情况,平台将会直接冻结清除;严重的将禁封账号。
17 |
18 |
19 |
20 |
122 |
123 |
124 |
133 | 创建接口需先创建对应应用;您当前未创建任何应用,请先创建目标应用!
134 |
135 | 前往创建应用
136 | 返回上一页
142 |
143 |
144 |
145 |
146 |
147 |
148 |
326 |
327 |
355 |
--------------------------------------------------------------------------------
/src/views/interface/Debug.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
34 |
35 |
36 |
75 |
76 |
77 |
接口响应数据:
78 |
79 |
80 |
{{ responseData }}
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 | 请先选择要测试的目标接口
89 | 选择后填写测试数据即可获取虚拟响应数据了
90 | 很简单!
91 |
92 |
93 |
94 |
95 |
96 |
184 |
185 |
275 |
--------------------------------------------------------------------------------
/src/views/interface/List.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
29 |
30 |
31 |
32 |
46 |
47 |
48 |
53 |
54 | 全部
55 | GET请求接口
56 | POST请求接口
57 | PUT请求接口
58 | DELETE请求接口
59 |
60 |
61 |
62 | 创建时间降序
63 |
64 |
65 | 创建时间升序
66 |
67 |
68 | 最后更新时间降序
69 |
70 |
71 | 最后更新时间升序
72 |
73 |
74 |
75 |
76 |
93 |
94 |
95 |
105 |
106 |
107 |
108 |
109 |
324 |
325 |
398 |
--------------------------------------------------------------------------------
/src/views/management/Export.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
导出接口文档
4 |
5 | 导出当前应用基本信息及该应用下的所有接口数据,当前暂仅支持导出Markdown格式文本。
6 |
7 |
8 |
9 |
10 | 导出接口文档
11 |
12 |
13 |
14 |
15 |
16 |
42 |
43 |
54 |
--------------------------------------------------------------------------------
/src/views/management/Other.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
接口相关操作管理
5 |
6 |
10 |
11 |
拷贝指定接口到当前应用或其他应用,方便快捷减少重复创建的麻烦。
12 |
13 |
14 |
15 |
19 |
20 |
将指定应用接口转移至您的其他应用。转移成功后该接口将不在当前应用中。
21 |
22 |
23 |
24 |
25 |
应用相关操作管理
26 |
27 |
31 |
32 |
拷贝当前应用并另新建应用,执行该操作后新创建的应用将拥有与当前应用一样的基本数据配置,包括应用的所有数据接口。
33 |
34 |
35 |
43 |
44 |
45 |
49 |
50 |
清空应用所有接口,执行该操作后所有接口将永久消失无法恢复。
51 |
52 |
53 |
54 |
58 |
59 |
删除内容包括应用基本信息及其所有接口。删除后应用及接口数据将永久消失无法恢复。
60 |
61 |
62 |
63 |
72 |
73 |
请选择要{{ modalType=='copy_api'?'拷贝':'转移'}}的接口({{ checkedApi.length || 0 }}):
74 |
79 |
-- 当前应用未定义任何接口 --
83 |
84 |
85 |
86 |
请选择要{{ modalType=='copy_api'?'拷贝':'转移'}}到哪个目标应用:
87 |
94 | {{ item.label }}
100 |
101 |
102 |
103 |
104 |
112 |
113 |
114 |
127 |
128 |
129 |
130 |
141 | {{ app_api_request_uri || '--' }}{{ $store.getters.virUid }}/
142 |
143 |
144 |
145 |
146 |
153 |
154 |
155 |
156 | 确定拷贝应用
157 |
158 |
159 |
160 |
161 |
162 |
163 |
459 |
460 |
534 |
--------------------------------------------------------------------------------
/src/views/management/Setting.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
19 |
20 |
21 |
22 |
34 | {{ app_api_request_uri || '--' }}{{ $store.getters.virUid }}/
35 |
36 |
37 |
38 |
39 |
49 |
50 |
51 |
52 |
60 |
61 |
67 |
71 |
72 |
73 |
74 |
75 | 保 存
81 | 重 置
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
99 |
100 | 请求头部Token验证
101 |
102 |
103 |
104 |
105 |
106 | 请求参数Token验证
107 |
108 |
109 |
110 |
111 |
112 | 兼容模式
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 | 自定义请求响应结构 :
121 |
122 |
123 |
124 |
134 |
135 | code
136 | status
137 |
138 |
139 |
140 |
141 |
142 |
152 |
153 | 200
154 | 0
155 | succeed
156 |
157 |
158 |
159 |
160 |
161 |
171 |
172 | 1000
173 | failed
174 |
175 |
176 |
177 |
178 |
179 |
189 |
190 | message
191 | msg
192 | info
193 |
194 |
195 |
196 |
197 |
198 |
207 |
208 | Succeed
209 |
210 |
211 |
212 |
213 |
214 |
223 |
224 | Failed
225 |
226 |
227 |
228 |
229 |
230 |
240 |
241 | data
242 |
243 |
244 |
245 |
246 |
247 |
248 | 最终结构示例(
249 | 请求成功 ):
250 |
251 |
{{ responseSucceed }}
252 |
253 | 最终结构示例(
254 | 请求失败 ):
255 |
256 |
{{ responseFailed }}
257 |
258 |
259 |
260 |
261 | 保 存
267 | 重 置
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
521 |
522 |
531 |
--------------------------------------------------------------------------------
/src/views/other/404.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
😱🛸📡
4 | 404
5 |
6 | 返回首页
7 |
8 |
9 |
10 |
24 |
--------------------------------------------------------------------------------
/vue.config.js:
--------------------------------------------------------------------------------
1 | const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin')
2 | const webpack = require("webpack")
3 |
4 | module.exports = {
5 | publicPath: '/console',
6 | outputDir: 'dist',
7 | assetsDir: '',
8 | productionSourceMap: false,
9 | parallel: require('os').cpus().length > 1,
10 | runtimeCompiler: false,
11 | lintOnSave: true,
12 | css: {
13 | loaderOptions: {
14 | less: {
15 | javascriptEnabled: true,
16 | }
17 | }
18 | },
19 | configureWebpack: {
20 | plugins: [
21 | new MonacoWebpackPlugin({
22 | languages: ['json', 'markdown', 'plaintext'],
23 | // publicPath: '/'
24 | }),
25 | new webpack.ContextReplacementPlugin(/moment[/\\]locale$/, /zh-cn|zh-hk|en/),
26 | ]
27 | }
28 | }
29 |
--------------------------------------------------------------------------------