正在显示
37 个修改的文件
包含
4137 行增加
和
0 行删除
.bowerrc
0 → 100644
.env.sample
0 → 100644
.gitignore
0 → 100644
1 | +runtime |
.idea/adverh5.iml
0 → 100644
1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
2 | +<module type="WEB_MODULE" version="4"> | ||
3 | + <component name="NewModuleRootManager"> | ||
4 | + <content url="file://$MODULE_DIR$"> | ||
5 | + <excludeFolder url="file://$MODULE_DIR$/vendor/composer" /> | ||
6 | + <excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/cache" /> | ||
7 | + <excludeFolder url="file://$MODULE_DIR$/vendor/endroid/qr-code" /> | ||
8 | + <excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/guzzle" /> | ||
9 | + <excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/promises" /> | ||
10 | + <excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/psr7" /> | ||
11 | + <excludeFolder url="file://$MODULE_DIR$/vendor/karsonzhang/fastadmin-addons" /> | ||
12 | + <excludeFolder url="file://$MODULE_DIR$/vendor/markbaker/complex" /> | ||
13 | + <excludeFolder url="file://$MODULE_DIR$/vendor/markbaker/matrix" /> | ||
14 | + <excludeFolder url="file://$MODULE_DIR$/vendor/monolog/monolog" /> | ||
15 | + <excludeFolder url="file://$MODULE_DIR$/vendor/mtdowling/cron-expression" /> | ||
16 | + <excludeFolder url="file://$MODULE_DIR$/vendor/overtrue/pinyin" /> | ||
17 | + <excludeFolder url="file://$MODULE_DIR$/vendor/overtrue/socialite" /> | ||
18 | + <excludeFolder url="file://$MODULE_DIR$/vendor/overtrue/wechat" /> | ||
19 | + <excludeFolder url="file://$MODULE_DIR$/vendor/paragonie/random_compat" /> | ||
20 | + <excludeFolder url="file://$MODULE_DIR$/vendor/phpmailer/phpmailer" /> | ||
21 | + <excludeFolder url="file://$MODULE_DIR$/vendor/phpoffice/phpspreadsheet" /> | ||
22 | + <excludeFolder url="file://$MODULE_DIR$/vendor/pimple/pimple" /> | ||
23 | + <excludeFolder url="file://$MODULE_DIR$/vendor/psr/container" /> | ||
24 | + <excludeFolder url="file://$MODULE_DIR$/vendor/psr/http-message" /> | ||
25 | + <excludeFolder url="file://$MODULE_DIR$/vendor/psr/log" /> | ||
26 | + <excludeFolder url="file://$MODULE_DIR$/vendor/psr/simple-cache" /> | ||
27 | + <excludeFolder url="file://$MODULE_DIR$/vendor/ralouphie/getallheaders" /> | ||
28 | + <excludeFolder url="file://$MODULE_DIR$/vendor/symfony/http-foundation" /> | ||
29 | + <excludeFolder url="file://$MODULE_DIR$/vendor/symfony/options-resolver" /> | ||
30 | + <excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-mbstring" /> | ||
31 | + <excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-php70" /> | ||
32 | + <excludeFolder url="file://$MODULE_DIR$/vendor/symfony/psr-http-message-bridge" /> | ||
33 | + <excludeFolder url="file://$MODULE_DIR$/vendor/topthink/think-captcha" /> | ||
34 | + <excludeFolder url="file://$MODULE_DIR$/vendor/topthink/think-helper" /> | ||
35 | + <excludeFolder url="file://$MODULE_DIR$/vendor/topthink/think-installer" /> | ||
36 | + </content> | ||
37 | + <orderEntry type="inheritedJdk" /> | ||
38 | + <orderEntry type="sourceFolder" forTests="false" /> | ||
39 | + </component> | ||
40 | +</module> |
.idea/misc.xml
0 → 100644
.idea/modules.xml
0 → 100644
.idea/php.xml
0 → 100644
1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
2 | +<project version="4"> | ||
3 | + <component name="PhpIncludePathManager"> | ||
4 | + <include_path> | ||
5 | + <path value="$PROJECT_DIR$/vendor/symfony/polyfill-mbstring" /> | ||
6 | + <path value="$PROJECT_DIR$/vendor/symfony/options-resolver" /> | ||
7 | + <path value="$PROJECT_DIR$/vendor/symfony/psr-http-message-bridge" /> | ||
8 | + <path value="$PROJECT_DIR$/vendor/symfony/http-foundation" /> | ||
9 | + <path value="$PROJECT_DIR$/vendor/phpmailer/phpmailer" /> | ||
10 | + <path value="$PROJECT_DIR$/vendor/psr/container" /> | ||
11 | + <path value="$PROJECT_DIR$/vendor/symfony/polyfill-php70" /> | ||
12 | + <path value="$PROJECT_DIR$/vendor/psr/log" /> | ||
13 | + <path value="$PROJECT_DIR$/vendor/psr/http-message" /> | ||
14 | + <path value="$PROJECT_DIR$/vendor/psr/simple-cache" /> | ||
15 | + <path value="$PROJECT_DIR$/vendor/topthink/think-captcha" /> | ||
16 | + <path value="$PROJECT_DIR$/vendor/doctrine/cache" /> | ||
17 | + <path value="$PROJECT_DIR$/vendor/topthink/think-helper" /> | ||
18 | + <path value="$PROJECT_DIR$/vendor/topthink/think-installer" /> | ||
19 | + <path value="$PROJECT_DIR$/vendor/overtrue/socialite" /> | ||
20 | + <path value="$PROJECT_DIR$/vendor/ralouphie/getallheaders" /> | ||
21 | + <path value="$PROJECT_DIR$/vendor/overtrue/wechat" /> | ||
22 | + <path value="$PROJECT_DIR$/vendor/overtrue/pinyin" /> | ||
23 | + <path value="$PROJECT_DIR$/vendor/guzzlehttp/psr7" /> | ||
24 | + <path value="$PROJECT_DIR$/vendor/guzzlehttp/guzzle" /> | ||
25 | + <path value="$PROJECT_DIR$/vendor/mtdowling/cron-expression" /> | ||
26 | + <path value="$PROJECT_DIR$/vendor/guzzlehttp/promises" /> | ||
27 | + <path value="$PROJECT_DIR$/vendor/endroid/qr-code" /> | ||
28 | + <path value="$PROJECT_DIR$/vendor/monolog/monolog" /> | ||
29 | + <path value="$PROJECT_DIR$/vendor/paragonie/random_compat" /> | ||
30 | + <path value="$PROJECT_DIR$/vendor/pimple/pimple" /> | ||
31 | + <path value="$PROJECT_DIR$/vendor/composer" /> | ||
32 | + <path value="$PROJECT_DIR$/vendor/markbaker/matrix" /> | ||
33 | + <path value="$PROJECT_DIR$/vendor/markbaker/complex" /> | ||
34 | + <path value="$PROJECT_DIR$/vendor/phpoffice/phpspreadsheet" /> | ||
35 | + <path value="$PROJECT_DIR$/vendor/karsonzhang/fastadmin-addons" /> | ||
36 | + </include_path> | ||
37 | + </component> | ||
38 | +</project> |
.idea/vcs.xml
0 → 100644
.idea/workspace.xml
0 → 100644
1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
2 | +<project version="4"> | ||
3 | + <component name="ChangeListManager"> | ||
4 | + <list default="true" id="3bd62d6a-e12b-4534-8418-e8a5d7424d73" name="默认的" comment=""> | ||
5 | + <change afterPath="$PROJECT_DIR$/.gitignore" afterDir="false" /> | ||
6 | + <change afterPath="$PROJECT_DIR$/application/home/controller/Index.php" afterDir="false" /> | ||
7 | + <change afterPath="$PROJECT_DIR$/application/home/view/index/index.html" afterDir="false" /> | ||
8 | + <change afterPath="$PROJECT_DIR$/application/home/view/index/login.html" afterDir="false" /> | ||
9 | + <change afterPath="$PROJECT_DIR$/public/.gitignore" afterDir="false" /> | ||
10 | + <change afterPath="$PROJECT_DIR$/public/assets/adverh5/css/index.css" afterDir="false" /> | ||
11 | + <change afterPath="$PROJECT_DIR$/public/assets/adverh5/css/login.css" afterDir="false" /> | ||
12 | + <change afterPath="$PROJECT_DIR$/public/assets/adverh5/css/public.css" afterDir="false" /> | ||
13 | + <change afterPath="$PROJECT_DIR$/public/assets/adverh5/img/1.png" afterDir="false" /> | ||
14 | + <change afterPath="$PROJECT_DIR$/public/assets/adverh5/img/10.png" afterDir="false" /> | ||
15 | + <change afterPath="$PROJECT_DIR$/public/assets/adverh5/img/2.png" afterDir="false" /> | ||
16 | + <change afterPath="$PROJECT_DIR$/public/assets/adverh5/img/3.png" afterDir="false" /> | ||
17 | + <change afterPath="$PROJECT_DIR$/public/assets/adverh5/img/4.png" afterDir="false" /> | ||
18 | + <change afterPath="$PROJECT_DIR$/public/assets/adverh5/img/5.png" afterDir="false" /> | ||
19 | + <change afterPath="$PROJECT_DIR$/public/assets/adverh5/img/6.png" afterDir="false" /> | ||
20 | + <change afterPath="$PROJECT_DIR$/public/assets/adverh5/img/7.png" afterDir="false" /> | ||
21 | + <change afterPath="$PROJECT_DIR$/public/assets/adverh5/img/8.png" afterDir="false" /> | ||
22 | + <change afterPath="$PROJECT_DIR$/public/assets/adverh5/img/9.png" afterDir="false" /> | ||
23 | + <change afterPath="$PROJECT_DIR$/public/assets/adverh5/img/bg1.png" afterDir="false" /> | ||
24 | + <change afterPath="$PROJECT_DIR$/public/assets/adverh5/img/bg2.png" afterDir="false" /> | ||
25 | + <change afterPath="$PROJECT_DIR$/public/assets/adverh5/img/image.png" afterDir="false" /> | ||
26 | + <change afterPath="$PROJECT_DIR$/public/assets/adverh5/js/base.js" afterDir="false" /> | ||
27 | + <change afterPath="$PROJECT_DIR$/public/assets/adverh5/js/jquery.min.js" afterDir="false" /> | ||
28 | + <change afterPath="$PROJECT_DIR$/public/assets/adverh5/js/public.js" afterDir="false" /> | ||
29 | + </list> | ||
30 | + <option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" /> | ||
31 | + <option name="TRACKING_ENABLED" value="true" /> | ||
32 | + <option name="SHOW_DIALOG" value="false" /> | ||
33 | + <option name="HIGHLIGHT_CONFLICTS" value="true" /> | ||
34 | + <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" /> | ||
35 | + <option name="LAST_RESOLUTION" value="IGNORE" /> | ||
36 | + </component> | ||
37 | + <component name="ComposerSettings" synchronizationState="SYNCHRONIZE"> | ||
38 | + <pharConfigPath>$PROJECT_DIR$/composer.json</pharConfigPath> | ||
39 | + </component> | ||
40 | + <component name="FileEditorManager"> | ||
41 | + <leaf> | ||
42 | + <file leaf-file-name="Index.php" pinned="false" current-in-tab="true"> | ||
43 | + <entry file="file://$PROJECT_DIR$/application/home/controller/Index.php"> | ||
44 | + <provider selected="true" editor-type-id="text-editor"> | ||
45 | + <state relative-caret-position="442"> | ||
46 | + <caret line="31" column="5" lean-forward="true" selection-start-line="31" selection-start-column="5" selection-end-line="31" selection-end-column="5" /> | ||
47 | + <folding> | ||
48 | + <element signature="e#128#163#0#PHP" expanded="true" /> | ||
49 | + </folding> | ||
50 | + </state> | ||
51 | + </provider> | ||
52 | + </entry> | ||
53 | + </file> | ||
54 | + <file leaf-file-name="config.php" pinned="false" current-in-tab="false"> | ||
55 | + <entry file="file://$PROJECT_DIR$/application/config.php"> | ||
56 | + <provider selected="true" editor-type-id="text-editor"> | ||
57 | + <state relative-caret-position="477"> | ||
58 | + <caret line="63" column="38" selection-start-line="63" selection-start-column="38" selection-end-line="63" selection-end-column="38" /> | ||
59 | + </state> | ||
60 | + </provider> | ||
61 | + </entry> | ||
62 | + </file> | ||
63 | + <file leaf-file-name="login.html" pinned="false" current-in-tab="false"> | ||
64 | + <entry file="file://$PROJECT_DIR$/application/home/view/index/login.html"> | ||
65 | + <provider selected="true" editor-type-id="text-editor"> | ||
66 | + <state relative-caret-position="487"> | ||
67 | + <caret line="75" column="32" selection-start-line="75" selection-start-column="32" selection-end-line="75" selection-end-column="32" /> | ||
68 | + </state> | ||
69 | + </provider> | ||
70 | + </entry> | ||
71 | + </file> | ||
72 | + <file leaf-file-name="public.js" pinned="false" current-in-tab="false"> | ||
73 | + <entry file="file://$PROJECT_DIR$/public/assets/adverh5/js/public.js"> | ||
74 | + <provider selected="true" editor-type-id="text-editor"> | ||
75 | + <state relative-caret-position="102"> | ||
76 | + <caret line="6" column="1" selection-start-line="6" selection-start-column="1" selection-end-line="6" selection-end-column="1" /> | ||
77 | + </state> | ||
78 | + </provider> | ||
79 | + </entry> | ||
80 | + </file> | ||
81 | + <file leaf-file-name="login.css" pinned="false" current-in-tab="false"> | ||
82 | + <entry file="file://$PROJECT_DIR$/public/assets/adverh5/css/login.css"> | ||
83 | + <provider selected="true" editor-type-id="text-editor"> | ||
84 | + <state relative-caret-position="289"> | ||
85 | + <caret line="17" column="4" selection-start-line="17" selection-start-column="4" selection-end-line="19" selection-end-column="24" /> | ||
86 | + </state> | ||
87 | + </provider> | ||
88 | + </entry> | ||
89 | + </file> | ||
90 | + <file leaf-file-name="index.html" pinned="false" current-in-tab="false"> | ||
91 | + <entry file="file://$PROJECT_DIR$/application/home/view/index/index.html"> | ||
92 | + <provider selected="true" editor-type-id="text-editor"> | ||
93 | + <state relative-caret-position="2210"> | ||
94 | + <caret line="130" column="52" selection-start-line="130" selection-start-column="52" selection-end-line="130" selection-end-column="52" /> | ||
95 | + <folding> | ||
96 | + <element signature="n#style#0;n#div#5;n#div#2;n#div#0;n#body#0;n#html#0;n#!!top" expanded="true" /> | ||
97 | + </folding> | ||
98 | + </state> | ||
99 | + </provider> | ||
100 | + </entry> | ||
101 | + </file> | ||
102 | + <file leaf-file-name="index.css" pinned="false" current-in-tab="false"> | ||
103 | + <entry file="file://$PROJECT_DIR$/public/assets/adverh5/css/index.css"> | ||
104 | + <provider selected="true" editor-type-id="text-editor"> | ||
105 | + <state relative-caret-position="679"> | ||
106 | + <caret line="116" column="22" selection-start-line="116" selection-start-column="22" selection-end-line="116" selection-end-column="22" /> | ||
107 | + </state> | ||
108 | + </provider> | ||
109 | + </entry> | ||
110 | + </file> | ||
111 | + </leaf> | ||
112 | + </component> | ||
113 | + <component name="FileTemplateManagerImpl"> | ||
114 | + <option name="RECENT_TEMPLATES"> | ||
115 | + <list> | ||
116 | + <option value="HTML File" /> | ||
117 | + <option value="CSS File" /> | ||
118 | + <option value="JavaScript File" /> | ||
119 | + </list> | ||
120 | + </option> | ||
121 | + </component> | ||
122 | + <component name="FindInProjectRecents"> | ||
123 | + <findStrings> | ||
124 | + <find>siet</find> | ||
125 | + <find>$site</find> | ||
126 | + <find>site</find> | ||
127 | + <find>box-bottom</find> | ||
128 | + <find>dis</find> | ||
129 | + </findStrings> | ||
130 | + </component> | ||
131 | + <component name="Git.Settings"> | ||
132 | + <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" /> | ||
133 | + </component> | ||
134 | + <component name="IdeDocumentHistory"> | ||
135 | + <option name="CHANGED_PATHS"> | ||
136 | + <list> | ||
137 | + <option value="$PROJECT_DIR$/application/index/view/home/index.html" /> | ||
138 | + <option value="$PROJECT_DIR$/public/assets/adverh5/js/base.js" /> | ||
139 | + <option value="$PROJECT_DIR$/public/assets/adverh5/css/public.css" /> | ||
140 | + <option value="$PROJECT_DIR$/application/admin/view/password/index.html" /> | ||
141 | + <option value="$PROJECT_DIR$/public/assets/js/backend/password.js" /> | ||
142 | + <option value="$PROJECT_DIR$/public/assets/js/backend/goods.js" /> | ||
143 | + <option value="$PROJECT_DIR$/application/admin/view/goods/index.html" /> | ||
144 | + <option value="$PROJECT_DIR$/application/admin/view/dashboard/index.html" /> | ||
145 | + <option value="$PROJECT_DIR$/application/admin/view/index/login.html" /> | ||
146 | + <option value="$PROJECT_DIR$/application/admin/view/index/index.html" /> | ||
147 | + <option value="$PROJECT_DIR$/application/admin/view/common/header.html" /> | ||
148 | + <option value="$PROJECT_DIR$/application/admin/view/common/menu.html" /> | ||
149 | + <option value="$PROJECT_DIR$/application/extra/site.php" /> | ||
150 | + <option value="$PROJECT_DIR$/public/assets/adverh5/js/public.js" /> | ||
151 | + <option value="$PROJECT_DIR$/public/assets/adverh5/css/login.css" /> | ||
152 | + <option value="$PROJECT_DIR$/application/home/view/index/login.html" /> | ||
153 | + <option value="$PROJECT_DIR$/application/home/controller/Index.php" /> | ||
154 | + <option value="$PROJECT_DIR$/application/home/view/index/index.html" /> | ||
155 | + <option value="$PROJECT_DIR$/public/assets/adverh5/css/index.css" /> | ||
156 | + <option value="$PROJECT_DIR$/application/config.php" /> | ||
157 | + </list> | ||
158 | + </option> | ||
159 | + </component> | ||
160 | + <component name="JsBuildToolGruntFileManager" detection-done="true" sorting="DEFINITION_ORDER" /> | ||
161 | + <component name="JsBuildToolPackageJson" detection-done="true" sorting="DEFINITION_ORDER" /> | ||
162 | + <component name="JsGulpfileManager"> | ||
163 | + <detection-done>true</detection-done> | ||
164 | + <sorting>DEFINITION_ORDER</sorting> | ||
165 | + </component> | ||
166 | + <component name="NodePackageJsonFileManager"> | ||
167 | + <packageJsonPaths> | ||
168 | + <path value="$PROJECT_DIR$/public/assets/libs/Sortable/package.json" /> | ||
169 | + <path value="$PROJECT_DIR$/public/assets/libs/art-template/loader/package.json" /> | ||
170 | + <path value="$PROJECT_DIR$/public/assets/libs/art-template/package.json" /> | ||
171 | + <path value="$PROJECT_DIR$/public/assets/libs/bootstrap-daterangepicker/package.json" /> | ||
172 | + <path value="$PROJECT_DIR$/public/assets/libs/bootstrap-table/package.json" /> | ||
173 | + <path value="$PROJECT_DIR$/public/assets/libs/bootstrap/package.json" /> | ||
174 | + <path value="$PROJECT_DIR$/public/assets/libs/eonasdan-bootstrap-datetimepicker/package.json" /> | ||
175 | + <path value="$PROJECT_DIR$/public/assets/libs/fastadmin-citypicker/package.json" /> | ||
176 | + <path value="$PROJECT_DIR$/public/assets/libs/fastadmin-cxselect/package.json" /> | ||
177 | + <path value="$PROJECT_DIR$/public/assets/libs/fastadmin-layer/package.json" /> | ||
178 | + <path value="$PROJECT_DIR$/public/assets/libs/jcrop/package.json" /> | ||
179 | + <path value="$PROJECT_DIR$/public/assets/libs/jquery-slimscroll/package.json" /> | ||
180 | + <path value="$PROJECT_DIR$/public/assets/libs/require-css/package.json" /> | ||
181 | + </packageJsonPaths> | ||
182 | + </component> | ||
183 | + <component name="PhpWorkspaceProjectConfiguration"> | ||
184 | + <include_path> | ||
185 | + <path value="$PROJECT_DIR$/vendor/symfony/polyfill-mbstring" /> | ||
186 | + <path value="$PROJECT_DIR$/vendor/symfony/options-resolver" /> | ||
187 | + <path value="$PROJECT_DIR$/vendor/symfony/psr-http-message-bridge" /> | ||
188 | + <path value="$PROJECT_DIR$/vendor/symfony/http-foundation" /> | ||
189 | + <path value="$PROJECT_DIR$/vendor/phpmailer/phpmailer" /> | ||
190 | + <path value="$PROJECT_DIR$/vendor/psr/container" /> | ||
191 | + <path value="$PROJECT_DIR$/vendor/symfony/polyfill-php70" /> | ||
192 | + <path value="$PROJECT_DIR$/vendor/psr/log" /> | ||
193 | + <path value="$PROJECT_DIR$/vendor/psr/http-message" /> | ||
194 | + <path value="$PROJECT_DIR$/vendor/psr/simple-cache" /> | ||
195 | + <path value="$PROJECT_DIR$/vendor/topthink/think-captcha" /> | ||
196 | + <path value="$PROJECT_DIR$/vendor/doctrine/cache" /> | ||
197 | + <path value="$PROJECT_DIR$/vendor/topthink/think-helper" /> | ||
198 | + <path value="$PROJECT_DIR$/vendor/topthink/think-installer" /> | ||
199 | + <path value="$PROJECT_DIR$/vendor/overtrue/socialite" /> | ||
200 | + <path value="$PROJECT_DIR$/vendor/ralouphie/getallheaders" /> | ||
201 | + <path value="$PROJECT_DIR$/vendor/overtrue/wechat" /> | ||
202 | + <path value="$PROJECT_DIR$/vendor/overtrue/pinyin" /> | ||
203 | + <path value="$PROJECT_DIR$/vendor/guzzlehttp/psr7" /> | ||
204 | + <path value="$PROJECT_DIR$/vendor/guzzlehttp/guzzle" /> | ||
205 | + <path value="$PROJECT_DIR$/vendor/mtdowling/cron-expression" /> | ||
206 | + <path value="$PROJECT_DIR$/vendor/guzzlehttp/promises" /> | ||
207 | + <path value="$PROJECT_DIR$/vendor/endroid/qr-code" /> | ||
208 | + <path value="$PROJECT_DIR$/vendor/monolog/monolog" /> | ||
209 | + <path value="$PROJECT_DIR$/vendor/paragonie/random_compat" /> | ||
210 | + <path value="$PROJECT_DIR$/vendor/pimple/pimple" /> | ||
211 | + <path value="$PROJECT_DIR$/vendor/composer" /> | ||
212 | + <path value="$PROJECT_DIR$/vendor/markbaker/matrix" /> | ||
213 | + <path value="$PROJECT_DIR$/vendor/markbaker/complex" /> | ||
214 | + <path value="$PROJECT_DIR$/vendor/phpoffice/phpspreadsheet" /> | ||
215 | + <path value="$PROJECT_DIR$/vendor/karsonzhang/fastadmin-addons" /> | ||
216 | + </include_path> | ||
217 | + </component> | ||
218 | + <component name="ProjectFrameBounds" extendedState="6"> | ||
219 | + <option name="x" value="863" /> | ||
220 | + <option name="y" value="28" /> | ||
221 | + <option name="width" value="960" /> | ||
222 | + <option name="height" value="991" /> | ||
223 | + </component> | ||
224 | + <component name="ProjectView"> | ||
225 | + <navigator proportions="" version="1"> | ||
226 | + <foldersAlwaysOnTop value="true" /> | ||
227 | + </navigator> | ||
228 | + <panes> | ||
229 | + <pane id="ProjectPane"> | ||
230 | + <subPane> | ||
231 | + <expand> | ||
232 | + <path> | ||
233 | + <item name="adverh5" type="b2602c69:ProjectViewProjectNode" /> | ||
234 | + <item name="adverh5" type="462c0819:PsiDirectoryNode" /> | ||
235 | + </path> | ||
236 | + <path> | ||
237 | + <item name="adverh5" type="b2602c69:ProjectViewProjectNode" /> | ||
238 | + <item name="adverh5" type="462c0819:PsiDirectoryNode" /> | ||
239 | + <item name="application" type="462c0819:PsiDirectoryNode" /> | ||
240 | + </path> | ||
241 | + <path> | ||
242 | + <item name="adverh5" type="b2602c69:ProjectViewProjectNode" /> | ||
243 | + <item name="adverh5" type="462c0819:PsiDirectoryNode" /> | ||
244 | + <item name="application" type="462c0819:PsiDirectoryNode" /> | ||
245 | + <item name="extra" type="462c0819:PsiDirectoryNode" /> | ||
246 | + </path> | ||
247 | + <path> | ||
248 | + <item name="adverh5" type="b2602c69:ProjectViewProjectNode" /> | ||
249 | + <item name="adverh5" type="462c0819:PsiDirectoryNode" /> | ||
250 | + <item name="public" type="462c0819:PsiDirectoryNode" /> | ||
251 | + </path> | ||
252 | + <path> | ||
253 | + <item name="adverh5" type="b2602c69:ProjectViewProjectNode" /> | ||
254 | + <item name="adverh5" type="462c0819:PsiDirectoryNode" /> | ||
255 | + <item name="public" type="462c0819:PsiDirectoryNode" /> | ||
256 | + <item name="assets" type="462c0819:PsiDirectoryNode" /> | ||
257 | + </path> | ||
258 | + <path> | ||
259 | + <item name="adverh5" type="b2602c69:ProjectViewProjectNode" /> | ||
260 | + <item name="adverh5" type="462c0819:PsiDirectoryNode" /> | ||
261 | + <item name="public" type="462c0819:PsiDirectoryNode" /> | ||
262 | + <item name="assets" type="462c0819:PsiDirectoryNode" /> | ||
263 | + <item name="adverh5" type="462c0819:PsiDirectoryNode" /> | ||
264 | + </path> | ||
265 | + <path> | ||
266 | + <item name="adverh5" type="b2602c69:ProjectViewProjectNode" /> | ||
267 | + <item name="adverh5" type="462c0819:PsiDirectoryNode" /> | ||
268 | + <item name="public" type="462c0819:PsiDirectoryNode" /> | ||
269 | + <item name="assets" type="462c0819:PsiDirectoryNode" /> | ||
270 | + <item name="adverh5" type="462c0819:PsiDirectoryNode" /> | ||
271 | + <item name="js" type="462c0819:PsiDirectoryNode" /> | ||
272 | + </path> | ||
273 | + </expand> | ||
274 | + <select /> | ||
275 | + </subPane> | ||
276 | + </pane> | ||
277 | + <pane id="Scope" /> | ||
278 | + </panes> | ||
279 | + </component> | ||
280 | + <component name="PropertiesComponent"> | ||
281 | + <property name="DefaultHtmlFileTemplate" value="HTML File" /> | ||
282 | + <property name="WebServerToolWindowFactoryState" value="false" /> | ||
283 | + <property name="last_opened_file_path" value="D:/phpStudy/WWW/weight" /> | ||
284 | + <property name="list.type.of.created.stylesheet" value="CSS" /> | ||
285 | + <property name="nodejs_interpreter_path.stuck_in_default_project" value="undefined stuck path" /> | ||
286 | + <property name="nodejs_npm_path_reset_for_default_project" value="true" /> | ||
287 | + <property name="settings.editor.selected.configurable" value="terminal" /> | ||
288 | + </component> | ||
289 | + <component name="RecentsManager"> | ||
290 | + <key name="MoveFile.RECENT_KEYS"> | ||
291 | + <recent name="D:\wamp\www\adverh5\application\home\view" /> | ||
292 | + <recent name="D:\wamp\www\adverh5\application\index\view\home\index" /> | ||
293 | + </key> | ||
294 | + <key name="CopyFile.RECENT_KEYS"> | ||
295 | + <recent name="D:\wamp\www\adverh5\public\assets\adverh5\css" /> | ||
296 | + <recent name="D:\wamp\www\adverh5\public\assets\adverh5\img" /> | ||
297 | + <recent name="D:\wamp\www\adverh5\application\home\view\index" /> | ||
298 | + <recent name="D:\wamp\www\adverh5\public\assets\adverh5\js" /> | ||
299 | + <recent name="D:\wamp\www\adverh5\public" /> | ||
300 | + </key> | ||
301 | + </component> | ||
302 | + <component name="RunDashboard"> | ||
303 | + <option name="ruleStates"> | ||
304 | + <list> | ||
305 | + <RuleState> | ||
306 | + <option name="name" value="ConfigurationTypeDashboardGroupingRule" /> | ||
307 | + </RuleState> | ||
308 | + <RuleState> | ||
309 | + <option name="name" value="StatusDashboardGroupingRule" /> | ||
310 | + </RuleState> | ||
311 | + </list> | ||
312 | + </option> | ||
313 | + </component> | ||
314 | + <component name="SvnConfiguration"> | ||
315 | + <configuration /> | ||
316 | + </component> | ||
317 | + <component name="TaskManager"> | ||
318 | + <task active="true" id="Default" summary="Default task"> | ||
319 | + <changelist id="3bd62d6a-e12b-4534-8418-e8a5d7424d73" name="默认的" comment="" /> | ||
320 | + <created>1571368480027</created> | ||
321 | + <option name="number" value="Default" /> | ||
322 | + <option name="presentableId" value="Default" /> | ||
323 | + <updated>1571368480027</updated> | ||
324 | + <workItem from="1571368481262" duration="23015000" /> | ||
325 | + </task> | ||
326 | + <servers /> | ||
327 | + </component> | ||
328 | + <component name="TimeTrackingManager"> | ||
329 | + <option name="totallyTimeSpent" value="23015000" /> | ||
330 | + </component> | ||
331 | + <component name="ToolWindowManager"> | ||
332 | + <frame x="-8" y="-8" width="1936" height="1056" extended-state="6" /> | ||
333 | + <editor active="true" /> | ||
334 | + <layout> | ||
335 | + <window_info anchor="bottom" id="TODO" order="6" /> | ||
336 | + <window_info anchor="bottom" id="调试" /> | ||
337 | + <window_info anchor="bottom" id="Event Log" side_tool="true" /> | ||
338 | + <window_info anchor="bottom" id="Database Changes" show_stripe_button="false" /> | ||
339 | + <window_info anchor="bottom" id="Version Control" show_stripe_button="false" /> | ||
340 | + <window_info anchor="bottom" id="运行" /> | ||
341 | + <window_info anchor="bottom" id="Terminal" /> | ||
342 | + <window_info active="true" content_ui="combo" id="Project" order="0" visible="true" weight="0.14658849" /> | ||
343 | + <window_info anchor="bottom" id="Docker" show_stripe_button="false" /> | ||
344 | + <window_info anchor="right" id="Database" /> | ||
345 | + <window_info id="Structure" order="1" side_tool="true" weight="0.25" /> | ||
346 | + <window_info id="Favorites" side_tool="true" /> | ||
347 | + <window_info anchor="bottom" id="找到" /> | ||
348 | + <window_info anchor="bottom" id="Debug" order="3" weight="0.4" /> | ||
349 | + <window_info anchor="bottom" id="Find" order="1" /> | ||
350 | + <window_info anchor="right" id="Commander" internal_type="SLIDING" order="0" type="SLIDING" weight="0.4" /> | ||
351 | + <window_info anchor="right" content_ui="combo" id="Hierarchy" order="2" weight="0.25" /> | ||
352 | + <window_info anchor="bottom" id="Inspection" order="5" weight="0.4" /> | ||
353 | + <window_info anchor="right" id="Ant Build" order="1" weight="0.25" /> | ||
354 | + <window_info anchor="bottom" id="Run" order="2" /> | ||
355 | + <window_info anchor="bottom" id="Message" order="0" /> | ||
356 | + <window_info anchor="bottom" id="Cvs" order="4" weight="0.25" /> | ||
357 | + </layout> | ||
358 | + </component> | ||
359 | + <component name="TypeScriptGeneratedFilesManager"> | ||
360 | + <option name="version" value="1" /> | ||
361 | + </component> | ||
362 | + <component name="VcsContentAnnotationSettings"> | ||
363 | + <option name="myLimit" value="2678400000" /> | ||
364 | + </component> | ||
365 | + <component name="editorHistoryManager"> | ||
366 | + <entry file="file://$PROJECT_DIR$/.gitignore"> | ||
367 | + <provider selected="true" editor-type-id="text-editor" /> | ||
368 | + </entry> | ||
369 | + <entry file="file://$PROJECT_DIR$/application/index/controller/Index.php"> | ||
370 | + <provider selected="true" editor-type-id="text-editor" /> | ||
371 | + </entry> | ||
372 | + <entry file="file://$PROJECT_DIR$/application/common/controller/Frontend.php"> | ||
373 | + <provider selected="true" editor-type-id="text-editor"> | ||
374 | + <state relative-caret-position="153"> | ||
375 | + <caret line="14" column="6" selection-start-line="14" selection-start-column="6" selection-end-line="14" selection-end-column="6" /> | ||
376 | + </state> | ||
377 | + </provider> | ||
378 | + </entry> | ||
379 | + <entry file="file://$PROJECT_DIR$/public/index.php"> | ||
380 | + <provider selected="true" editor-type-id="text-editor" /> | ||
381 | + </entry> | ||
382 | + <entry file="file://$PROJECT_DIR$/public/.gitignore"> | ||
383 | + <provider selected="true" editor-type-id="text-editor"> | ||
384 | + <state> | ||
385 | + <caret column="7" selection-start-column="7" selection-end-column="7" /> | ||
386 | + </state> | ||
387 | + </provider> | ||
388 | + </entry> | ||
389 | + <entry file="file://$PROJECT_DIR$/public/assets/adverh5/img/10.png"> | ||
390 | + <provider selected="true" editor-type-id="images" /> | ||
391 | + </entry> | ||
392 | + <entry file="file://$PROJECT_DIR$/public/assets/adverh5/img/9.png"> | ||
393 | + <provider selected="true" editor-type-id="images" /> | ||
394 | + </entry> | ||
395 | + <entry file="file://$PROJECT_DIR$/public/assets/adverh5/js/base.js"> | ||
396 | + <provider selected="true" editor-type-id="text-editor"> | ||
397 | + <state relative-caret-position="340"> | ||
398 | + <caret line="20" column="10" selection-start-line="9" selection-end-line="20" selection-end-column="10" /> | ||
399 | + </state> | ||
400 | + </provider> | ||
401 | + </entry> | ||
402 | + <entry file="file://$PROJECT_DIR$/public/assets/adverh5/css/public.css"> | ||
403 | + <provider selected="true" editor-type-id="text-editor"> | ||
404 | + <state relative-caret-position="221"> | ||
405 | + <caret line="13" column="1" selection-start-line="13" selection-start-column="1" selection-end-line="13" selection-end-column="1" /> | ||
406 | + </state> | ||
407 | + </provider> | ||
408 | + </entry> | ||
409 | + <entry file="file://$PROJECT_DIR$/application/admin/view/password/index.html"> | ||
410 | + <provider selected="true" editor-type-id="text-editor"> | ||
411 | + <state relative-caret-position="340"> | ||
412 | + <caret line="20" column="33" selection-start-line="14" selection-start-column="24" selection-end-line="20" selection-end-column="33" /> | ||
413 | + </state> | ||
414 | + </provider> | ||
415 | + </entry> | ||
416 | + <entry file="file://$PROJECT_DIR$/application/admin/view/goods/index.html"> | ||
417 | + <provider selected="true" editor-type-id="text-editor"> | ||
418 | + <state relative-caret-position="340"> | ||
419 | + <caret line="20" column="33" selection-start-line="14" selection-start-column="24" selection-end-line="20" selection-end-column="33" /> | ||
420 | + </state> | ||
421 | + </provider> | ||
422 | + </entry> | ||
423 | + <entry file="file://$PROJECT_DIR$/public/assets/js/backend/goods.js"> | ||
424 | + <provider selected="true" editor-type-id="text-editor"> | ||
425 | + <state relative-caret-position="357"> | ||
426 | + <caret line="21" column="36" selection-start-line="21" selection-start-column="36" selection-end-line="21" selection-end-column="36" /> | ||
427 | + </state> | ||
428 | + </provider> | ||
429 | + </entry> | ||
430 | + <entry file="file://$PROJECT_DIR$/public/assets/js/backend/password.js"> | ||
431 | + <provider selected="true" editor-type-id="text-editor"> | ||
432 | + <state relative-caret-position="340"> | ||
433 | + <caret line="20" column="16" selection-start-line="20" selection-start-column="16" selection-end-line="21" selection-end-column="36" /> | ||
434 | + </state> | ||
435 | + </provider> | ||
436 | + </entry> | ||
437 | + <entry file="file://$PROJECT_DIR$/application/admin/view/index/login.html"> | ||
438 | + <provider selected="true" editor-type-id="text-editor"> | ||
439 | + <state relative-caret-position="626"> | ||
440 | + <caret line="100" column="88" lean-forward="true" selection-start-line="100" selection-start-column="88" selection-end-line="100" selection-end-column="97" /> | ||
441 | + </state> | ||
442 | + </provider> | ||
443 | + </entry> | ||
444 | + <entry file="file://$PROJECT_DIR$/application/admin/view/index/index.html"> | ||
445 | + <provider selected="true" editor-type-id="text-editor"> | ||
446 | + <state relative-caret-position="504"> | ||
447 | + <caret line="34" column="43" selection-start-line="34" selection-start-column="39" selection-end-line="34" selection-end-column="43" /> | ||
448 | + </state> | ||
449 | + </provider> | ||
450 | + </entry> | ||
451 | + <entry file="file://$PROJECT_DIR$/application/admin/view/common/script.html"> | ||
452 | + <provider selected="true" editor-type-id="text-editor" /> | ||
453 | + </entry> | ||
454 | + <entry file="file://$PROJECT_DIR$/application/admin/view/common/control.html"> | ||
455 | + <provider selected="true" editor-type-id="text-editor" /> | ||
456 | + </entry> | ||
457 | + <entry file="file://$PROJECT_DIR$/application/admin/view/common/meta.html"> | ||
458 | + <provider selected="true" editor-type-id="text-editor" /> | ||
459 | + </entry> | ||
460 | + <entry file="file://$PROJECT_DIR$/application/admin/view/dashboard/index.html"> | ||
461 | + <provider selected="true" editor-type-id="text-editor"> | ||
462 | + <state relative-caret-position="2325"> | ||
463 | + <caret line="438" column="44" selection-start-line="435" selection-start-column="36" selection-end-line="438" selection-end-column="44" /> | ||
464 | + </state> | ||
465 | + </provider> | ||
466 | + </entry> | ||
467 | + <entry file="file://$PROJECT_DIR$/application/admin/view/common/menu.html"> | ||
468 | + <provider selected="true" editor-type-id="text-editor"> | ||
469 | + <state relative-caret-position="136"> | ||
470 | + <caret line="8" column="32" selection-start-line="8" selection-start-column="32" selection-end-line="8" selection-end-column="32" /> | ||
471 | + </state> | ||
472 | + </provider> | ||
473 | + </entry> | ||
474 | + <entry file="file://$PROJECT_DIR$/application/admin/view/common/header.html"> | ||
475 | + <provider selected="true" editor-type-id="text-editor"> | ||
476 | + <state relative-caret-position="51"> | ||
477 | + <caret line="3" column="34" selection-start-line="3" selection-start-column="29" selection-end-line="3" selection-end-column="34" /> | ||
478 | + </state> | ||
479 | + </provider> | ||
480 | + </entry> | ||
481 | + <entry file="file://$PROJECT_DIR$/application/common.php"> | ||
482 | + <provider selected="true" editor-type-id="text-editor"> | ||
483 | + <state relative-caret-position="-306" /> | ||
484 | + </provider> | ||
485 | + </entry> | ||
486 | + <entry file="file://$PROJECT_DIR$/application/admin/config.php"> | ||
487 | + <provider selected="true" editor-type-id="text-editor" /> | ||
488 | + </entry> | ||
489 | + <entry file="file://$PROJECT_DIR$/application/common/controller/Backend.php"> | ||
490 | + <provider selected="true" editor-type-id="text-editor"> | ||
491 | + <state relative-caret-position="271"> | ||
492 | + <caret line="186" column="13" selection-start-line="186" selection-start-column="8" selection-end-line="186" selection-end-column="13" /> | ||
493 | + </state> | ||
494 | + </provider> | ||
495 | + </entry> | ||
496 | + <entry file="file://$PROJECT_DIR$/application/extra/site.php"> | ||
497 | + <provider selected="true" editor-type-id="text-editor"> | ||
498 | + <state relative-caret-position="51"> | ||
499 | + <caret line="3" column="17" selection-start-line="3" selection-start-column="17" selection-end-line="3" selection-end-column="17" /> | ||
500 | + </state> | ||
501 | + </provider> | ||
502 | + </entry> | ||
503 | + <entry file="file://$PROJECT_DIR$/public/assets/adverh5/js/public.js"> | ||
504 | + <provider selected="true" editor-type-id="text-editor"> | ||
505 | + <state relative-caret-position="102"> | ||
506 | + <caret line="6" column="1" selection-start-line="6" selection-start-column="1" selection-end-line="6" selection-end-column="1" /> | ||
507 | + </state> | ||
508 | + </provider> | ||
509 | + </entry> | ||
510 | + <entry file="file://$PROJECT_DIR$/public/assets/adverh5/css/index.css"> | ||
511 | + <provider selected="true" editor-type-id="text-editor"> | ||
512 | + <state relative-caret-position="679"> | ||
513 | + <caret line="116" column="22" selection-start-line="116" selection-start-column="22" selection-end-line="116" selection-end-column="22" /> | ||
514 | + </state> | ||
515 | + </provider> | ||
516 | + </entry> | ||
517 | + <entry file="file://$PROJECT_DIR$/application/home/view/index/index.html"> | ||
518 | + <provider selected="true" editor-type-id="text-editor"> | ||
519 | + <state relative-caret-position="2210"> | ||
520 | + <caret line="130" column="52" selection-start-line="130" selection-start-column="52" selection-end-line="130" selection-end-column="52" /> | ||
521 | + <folding> | ||
522 | + <element signature="n#style#0;n#div#5;n#div#2;n#div#0;n#body#0;n#html#0;n#!!top" expanded="true" /> | ||
523 | + </folding> | ||
524 | + </state> | ||
525 | + </provider> | ||
526 | + </entry> | ||
527 | + <entry file="file://$PROJECT_DIR$/public/assets/adverh5/css/login.css"> | ||
528 | + <provider selected="true" editor-type-id="text-editor"> | ||
529 | + <state relative-caret-position="289"> | ||
530 | + <caret line="17" column="4" selection-start-line="17" selection-start-column="4" selection-end-line="19" selection-end-column="24" /> | ||
531 | + </state> | ||
532 | + </provider> | ||
533 | + </entry> | ||
534 | + <entry file="file://$PROJECT_DIR$/application/config.php"> | ||
535 | + <provider selected="true" editor-type-id="text-editor"> | ||
536 | + <state relative-caret-position="477"> | ||
537 | + <caret line="63" column="38" selection-start-line="63" selection-start-column="38" selection-end-line="63" selection-end-column="38" /> | ||
538 | + </state> | ||
539 | + </provider> | ||
540 | + </entry> | ||
541 | + <entry file="file://$PROJECT_DIR$/application/home/view/index/login.html"> | ||
542 | + <provider selected="true" editor-type-id="text-editor"> | ||
543 | + <state relative-caret-position="487"> | ||
544 | + <caret line="75" column="32" selection-start-line="75" selection-start-column="32" selection-end-line="75" selection-end-column="32" /> | ||
545 | + </state> | ||
546 | + </provider> | ||
547 | + </entry> | ||
548 | + <entry file="file://$PROJECT_DIR$/application/home/controller/Index.php"> | ||
549 | + <provider selected="true" editor-type-id="text-editor"> | ||
550 | + <state relative-caret-position="442"> | ||
551 | + <caret line="31" column="5" lean-forward="true" selection-start-line="31" selection-start-column="5" selection-end-line="31" selection-end-column="5" /> | ||
552 | + <folding> | ||
553 | + <element signature="e#128#163#0#PHP" expanded="true" /> | ||
554 | + </folding> | ||
555 | + </state> | ||
556 | + </provider> | ||
557 | + </entry> | ||
558 | + </component> | ||
559 | + <component name="masterDetails"> | ||
560 | + <states> | ||
561 | + <state key="ScopeChooserConfigurable.UI"> | ||
562 | + <settings> | ||
563 | + <splitter-proportions> | ||
564 | + <option name="proportions"> | ||
565 | + <list> | ||
566 | + <option value="0.2" /> | ||
567 | + </list> | ||
568 | + </option> | ||
569 | + </splitter-proportions> | ||
570 | + </settings> | ||
571 | + </state> | ||
572 | + </states> | ||
573 | + </component> | ||
574 | +</project> |
LICENSE
0 → 100644
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, and | ||
10 | +distribution as defined by Sections 1 through 9 of this document. | ||
11 | + | ||
12 | +"Licensor" shall mean the copyright owner or entity authorized by the copyright | ||
13 | +owner that is granting the License. | ||
14 | + | ||
15 | +"Legal Entity" shall mean the union of the acting entity and all other entities | ||
16 | +that control, are controlled by, or are under common control with that entity. | ||
17 | +For the purposes of this definition, "control" means (i) the power, direct or | ||
18 | +indirect, to cause the direction or management of such entity, whether by | ||
19 | +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the | ||
20 | +outstanding shares, or (iii) beneficial ownership of such entity. | ||
21 | + | ||
22 | +"You" (or "Your") shall mean an individual or Legal Entity exercising | ||
23 | +permissions granted by this License. | ||
24 | + | ||
25 | +"Source" form shall mean the preferred form for making modifications, including | ||
26 | +but not limited to software source code, documentation source, and configuration | ||
27 | +files. | ||
28 | + | ||
29 | +"Object" form shall mean any form resulting from mechanical transformation or | ||
30 | +translation of a Source form, including but not limited to compiled object code, | ||
31 | +generated documentation, and conversions to other media types. | ||
32 | + | ||
33 | +"Work" shall mean the work of authorship, whether in Source or Object form, made | ||
34 | +available under the License, as indicated by a copyright notice that is included | ||
35 | +in or attached to the work (an example is provided in the Appendix below). | ||
36 | + | ||
37 | +"Derivative Works" shall mean any work, whether in Source or Object form, that | ||
38 | +is based on (or derived from) the Work and for which the editorial revisions, | ||
39 | +annotations, elaborations, or other modifications represent, as a whole, an | ||
40 | +original work of authorship. For the purposes of this License, Derivative Works | ||
41 | +shall not include works that remain separable from, or merely link (or bind by | ||
42 | +name) to the interfaces of, the Work and Derivative Works thereof. | ||
43 | + | ||
44 | +"Contribution" shall mean any work of authorship, including the original version | ||
45 | +of the Work and any modifications or additions to that Work or Derivative Works | ||
46 | +thereof, that is intentionally submitted to Licensor for inclusion in the Work | ||
47 | +by the copyright owner or by an individual or Legal Entity authorized to submit | ||
48 | +on behalf of the copyright owner. For the purposes of this definition, | ||
49 | +"submitted" means any form of electronic, verbal, or written communication sent | ||
50 | +to the Licensor or its representatives, including but not limited to | ||
51 | +communication on electronic mailing lists, source code control systems, and | ||
52 | +issue tracking systems that are managed by, or on behalf of, the Licensor for | ||
53 | +the purpose of discussing and improving the Work, but excluding communication | ||
54 | +that is conspicuously marked or otherwise designated in writing by the copyright | ||
55 | +owner as "Not a Contribution." | ||
56 | + | ||
57 | +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf | ||
58 | +of whom a Contribution has been received by Licensor and subsequently | ||
59 | +incorporated within the Work. | ||
60 | + | ||
61 | +2. Grant of Copyright License. | ||
62 | + | ||
63 | +Subject to the terms and conditions of this License, each Contributor hereby | ||
64 | +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, | ||
65 | +irrevocable copyright license to reproduce, prepare Derivative Works of, | ||
66 | +publicly display, publicly perform, sublicense, and distribute the Work and such | ||
67 | +Derivative Works in Source or Object form. | ||
68 | + | ||
69 | +3. Grant of Patent License. | ||
70 | + | ||
71 | +Subject to the terms and conditions of this License, each Contributor hereby | ||
72 | +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, | ||
73 | +irrevocable (except as stated in this section) patent license to make, have | ||
74 | +made, use, offer to sell, sell, import, and otherwise transfer the Work, where | ||
75 | +such license applies only to those patent claims licensable by such Contributor | ||
76 | +that are necessarily infringed by their Contribution(s) alone or by combination | ||
77 | +of their Contribution(s) with the Work to which such Contribution(s) was | ||
78 | +submitted. If You institute patent litigation against any entity (including a | ||
79 | +cross-claim or counterclaim in a lawsuit) alleging that the Work or a | ||
80 | +Contribution incorporated within the Work constitutes direct or contributory | ||
81 | +patent infringement, then any patent licenses granted to You under this License | ||
82 | +for that Work shall terminate as of the date such litigation is filed. | ||
83 | + | ||
84 | +4. Redistribution. | ||
85 | + | ||
86 | +You may reproduce and distribute copies of the Work or Derivative Works thereof | ||
87 | +in any medium, with or without modifications, and in Source or Object form, | ||
88 | +provided that You meet the following conditions: | ||
89 | + | ||
90 | +You must give any other recipients of the Work or Derivative Works a copy of | ||
91 | +this License; and | ||
92 | +You must cause any modified files to carry prominent notices stating that You | ||
93 | +changed the files; and | ||
94 | +You must retain, in the Source form of any Derivative Works that You distribute, | ||
95 | +all copyright, patent, trademark, and attribution notices from the Source form | ||
96 | +of the Work, excluding those notices that do not pertain to any part of the | ||
97 | +Derivative Works; and | ||
98 | +If the Work includes a "NOTICE" text file as part of its distribution, then any | ||
99 | +Derivative Works that You distribute must include a readable copy of the | ||
100 | +attribution notices contained within such NOTICE file, excluding those notices | ||
101 | +that do not pertain to any part of the Derivative Works, in at least one of the | ||
102 | +following places: within a NOTICE text file distributed as part of the | ||
103 | +Derivative Works; within the Source form or documentation, if provided along | ||
104 | +with the Derivative Works; or, within a display generated by the Derivative | ||
105 | +Works, if and wherever such third-party notices normally appear. The contents of | ||
106 | +the NOTICE file are for informational purposes only and do not modify the | ||
107 | +License. You may add Your own attribution notices within Derivative Works that | ||
108 | +You distribute, alongside or as an addendum to the NOTICE text from the Work, | ||
109 | +provided that such additional attribution notices cannot be construed as | ||
110 | +modifying the License. | ||
111 | +You may add Your own copyright statement to Your modifications and may provide | ||
112 | +additional or different license terms and conditions for use, reproduction, or | ||
113 | +distribution of Your modifications, or for any such Derivative Works as a whole, | ||
114 | +provided Your use, reproduction, and distribution of the Work otherwise complies | ||
115 | +with the conditions stated in this License. | ||
116 | + | ||
117 | +5. Submission of Contributions. | ||
118 | + | ||
119 | +Unless You explicitly state otherwise, any Contribution intentionally submitted | ||
120 | +for inclusion in the Work by You to the Licensor shall be under the terms and | ||
121 | +conditions of this License, without any additional terms or conditions. | ||
122 | +Notwithstanding the above, nothing herein shall supersede or modify the terms of | ||
123 | +any separate license agreement you may have executed with Licensor regarding | ||
124 | +such Contributions. | ||
125 | + | ||
126 | +6. Trademarks. | ||
127 | + | ||
128 | +This License does not grant permission to use the trade names, trademarks, | ||
129 | +service marks, or product names of the Licensor, except as required for | ||
130 | +reasonable and customary use in describing the origin of the Work and | ||
131 | +reproducing the content of the NOTICE file. | ||
132 | + | ||
133 | +7. Disclaimer of Warranty. | ||
134 | + | ||
135 | +Unless required by applicable law or agreed to in writing, Licensor provides the | ||
136 | +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, | ||
137 | +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, | ||
138 | +including, without limitation, any warranties or conditions of TITLE, | ||
139 | +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are | ||
140 | +solely responsible for determining the appropriateness of using or | ||
141 | +redistributing the Work and assume any risks associated with Your exercise of | ||
142 | +permissions under this License. | ||
143 | + | ||
144 | +8. Limitation of Liability. | ||
145 | + | ||
146 | +In no event and under no legal theory, whether in tort (including negligence), | ||
147 | +contract, or otherwise, unless required by applicable law (such as deliberate | ||
148 | +and grossly negligent acts) or agreed to in writing, shall any Contributor be | ||
149 | +liable to You for damages, including any direct, indirect, special, incidental, | ||
150 | +or consequential damages of any character arising as a result of this License or | ||
151 | +out of the use or inability to use the Work (including but not limited to | ||
152 | +damages for loss of goodwill, work stoppage, computer failure or malfunction, or | ||
153 | +any and all other commercial damages or losses), even if such Contributor has | ||
154 | +been advised of the possibility of such damages. | ||
155 | + | ||
156 | +9. Accepting Warranty or Additional Liability. | ||
157 | + | ||
158 | +While redistributing the Work or Derivative Works thereof, You may choose to | ||
159 | +offer, and charge a fee for, acceptance of support, warranty, indemnity, or | ||
160 | +other liability obligations and/or rights consistent with this License. However, | ||
161 | +in accepting such obligations, You may act only on Your own behalf and on Your | ||
162 | +sole responsibility, not on behalf of any other Contributor, and only if You | ||
163 | +agree to indemnify, defend, and hold each Contributor harmless for any liability | ||
164 | +incurred by, or claims asserted against, such Contributor by reason of your | ||
165 | +accepting any such warranty or additional liability. | ||
166 | + | ||
167 | +END OF TERMS AND CONDITIONS | ||
168 | + | ||
169 | +APPENDIX: How to apply the Apache License to your work | ||
170 | + | ||
171 | +To apply the Apache License to your work, attach the following boilerplate | ||
172 | +notice, with the fields enclosed by brackets "{}" replaced with your own | ||
173 | +identifying information. (Don't include the brackets!) The text should be | ||
174 | +enclosed in the appropriate comment syntax for the file format. We also | ||
175 | +recommend that a file or class name and description of purpose be included on | ||
176 | +the same "printed page" as the copyright notice for easier identification within | ||
177 | +third-party archives. | ||
178 | + | ||
179 | + Copyright 2017 Karson | ||
180 | + | ||
181 | + Licensed under the Apache License, Version 2.0 (the "License"); | ||
182 | + you may not use this file except in compliance with the License. | ||
183 | + You may obtain a copy of the License at | ||
184 | + | ||
185 | + http://www.apache.org/licenses/LICENSE-2.0 | ||
186 | + | ||
187 | + Unless required by applicable law or agreed to in writing, software | ||
188 | + distributed under the License is distributed on an "AS IS" BASIS, | ||
189 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
190 | + See the License for the specific language governing permissions and | ||
191 | + limitations under the License. |
README.md
0 → 100644
1 | +FastAdmin是一款基于ThinkPHP5+Bootstrap的极速后台开发框架。 | ||
2 | + | ||
3 | + | ||
4 | +## **主要特性** | ||
5 | + | ||
6 | +* 基于`Auth`验证的权限管理系统 | ||
7 | + * 支持无限级父子级权限继承,父级的管理员可任意增删改子级管理员及权限设置 | ||
8 | + * 支持单管理员多角色 | ||
9 | + * 支持管理子级数据或个人数据 | ||
10 | +* 强大的一键生成功能 | ||
11 | + * 一键生成CRUD,包括控制器、模型、视图、JS、语言包、菜单、回收站等 | ||
12 | + * 一键压缩打包JS和CSS文件,一键CDN静态资源部署 | ||
13 | + * 一键生成控制器菜单和规则 | ||
14 | + * 一键生成API接口文档 | ||
15 | +* 完善的前端功能组件开发 | ||
16 | + * 基于`AdminLTE`二次开发 | ||
17 | + * 基于`Bootstrap`开发,自适应手机、平板、PC | ||
18 | + * 基于`RequireJS`进行JS模块管理,按需加载 | ||
19 | + * 基于`Less`进行样式开发 | ||
20 | + * 基于`Bower`进行前端组件包管理 | ||
21 | +* 强大的插件扩展功能,在线安装卸载升级插件 | ||
22 | +* 通用的会员模块和API模块 | ||
23 | +* 共用同一账号体系的Web端会员中心权限验证和API接口会员权限验证 | ||
24 | +* 二级域名部署支持,同时域名支持绑定到插件 | ||
25 | +* 多语言支持,服务端及客户端支持 | ||
26 | +* 强大的第三方模块支持([CMS](https://www.fastadmin.net/store/cms.html)、[博客](https://www.fastadmin.net/store/blog.html)、[知识付费问答](https://www.fastadmin.net/store/ask.html)) | ||
27 | +* 整合第三方短信接口(阿里云、腾讯云短信) | ||
28 | +* 无缝整合第三方云存储(七牛、阿里云OSS、又拍云)功能 | ||
29 | +* 第三方富文本编辑器支持(Summernote、Kindeditor、百度编辑器) | ||
30 | +* 第三方登录(QQ、微信、微博)整合 | ||
31 | +* 第三方支付(微信、支付宝)无缝整合,微信支持PC端扫码支付 | ||
32 | +* 丰富的插件应用市场 | ||
33 | + | ||
34 | +## **安装使用** | ||
35 | + | ||
36 | +https://doc.fastadmin.net | ||
37 | + | ||
38 | +## **在线演示** | ||
39 | + | ||
40 | +https://demo.fastadmin.net | ||
41 | + | ||
42 | +用户名:admin | ||
43 | + | ||
44 | +密 码:123456 | ||
45 | + | ||
46 | +提 示:演示站数据无法进行修改,请下载源码安装体验全部功能 | ||
47 | + | ||
48 | +## **界面截图** | ||
49 | +![控制台](https://gitee.com/uploads/images/2017/0411/113717_e99ff3e7_10933.png "控制台") | ||
50 | + | ||
51 | +## **问题反馈** | ||
52 | + | ||
53 | +在使用中有任何问题,请使用以下联系方式联系我们 | ||
54 | + | ||
55 | +交流社区: https://forum.fastadmin.net | ||
56 | + | ||
57 | +QQ群: [636393962](https://jq.qq.com/?_wv=1027&k=487PNBb)(满) [708784003](https://jq.qq.com/?_wv=1027&k=5ObjtwM)(满) [964776039](https://jq.qq.com/?_wv=1027&k=59qjU2P)(3群) | ||
58 | + | ||
59 | +Email: (karsonzhang#163.com, 把#换成@) | ||
60 | + | ||
61 | +Github: https://github.com/karsonzhang/fastadmin | ||
62 | + | ||
63 | +Gitee: https://gitee.com/karson/fastadmin | ||
64 | + | ||
65 | +## **特别鸣谢** | ||
66 | + | ||
67 | +感谢以下的项目,排名不分先后 | ||
68 | + | ||
69 | +ThinkPHP:http://www.thinkphp.cn | ||
70 | + | ||
71 | +AdminLTE:https://adminlte.io | ||
72 | + | ||
73 | +Bootstrap:http://getbootstrap.com | ||
74 | + | ||
75 | +jQuery:http://jquery.com | ||
76 | + | ||
77 | +Bootstrap-table:https://github.com/wenzhixin/bootstrap-table | ||
78 | + | ||
79 | +Nice-validator: https://validator.niceue.com | ||
80 | + | ||
81 | +SelectPage: https://github.com/TerryZ/SelectPage | ||
82 | + | ||
83 | + | ||
84 | +## **版权信息** | ||
85 | + | ||
86 | +FastAdmin遵循Apache2开源协议发布,并提供免费使用。 | ||
87 | + | ||
88 | +本项目包含的第三方源码和二进制文件之版权信息另行标注。 | ||
89 | + | ||
90 | +版权所有Copyright © 2017-2019 by FastAdmin (https://www.fastadmin.net) | ||
91 | + | ||
92 | +All rights reserved。 |
addons/command/Command.php
0 → 100644
1 | +<?php | ||
2 | + | ||
3 | +namespace addons\command; | ||
4 | + | ||
5 | +use app\common\library\Menu; | ||
6 | +use think\Addons; | ||
7 | + | ||
8 | +/** | ||
9 | + * 在线命令插件 | ||
10 | + */ | ||
11 | +class Command extends Addons | ||
12 | +{ | ||
13 | + | ||
14 | + /** | ||
15 | + * 插件安装方法 | ||
16 | + * @return bool | ||
17 | + */ | ||
18 | + public function install() | ||
19 | + { | ||
20 | + $menu = [ | ||
21 | + [ | ||
22 | + 'name' => 'command', | ||
23 | + 'title' => '在线命令管理', | ||
24 | + 'icon' => 'fa fa-terminal', | ||
25 | + 'sublist' => [ | ||
26 | + ['name' => 'command/index', 'title' => '查看'], | ||
27 | + ['name' => 'command/add', 'title' => '添加'], | ||
28 | + ['name' => 'command/detail', 'title' => '详情'], | ||
29 | + ['name' => 'command/execute', 'title' => '运行'], | ||
30 | + ['name' => 'command/del', 'title' => '删除'], | ||
31 | + ['name' => 'command/multi', 'title' => '批量更新'], | ||
32 | + ] | ||
33 | + ] | ||
34 | + ]; | ||
35 | + Menu::create($menu); | ||
36 | + return true; | ||
37 | + } | ||
38 | + | ||
39 | + /** | ||
40 | + * 插件卸载方法 | ||
41 | + * @return bool | ||
42 | + */ | ||
43 | + public function uninstall() | ||
44 | + { | ||
45 | + Menu::delete('command'); | ||
46 | + return true; | ||
47 | + } | ||
48 | + | ||
49 | + /** | ||
50 | + * 插件启用方法 | ||
51 | + * @return bool | ||
52 | + */ | ||
53 | + public function enable() | ||
54 | + { | ||
55 | + Menu::enable('command'); | ||
56 | + return true; | ||
57 | + } | ||
58 | + | ||
59 | + /** | ||
60 | + * 插件禁用方法 | ||
61 | + * @return bool | ||
62 | + */ | ||
63 | + public function disable() | ||
64 | + { | ||
65 | + Menu::disable('command'); | ||
66 | + return true; | ||
67 | + } | ||
68 | + | ||
69 | +} |
1 | +<?php | ||
2 | + | ||
3 | +namespace app\admin\controller; | ||
4 | + | ||
5 | +use app\common\controller\Backend; | ||
6 | +use think\Config; | ||
7 | +use think\console\Input; | ||
8 | +use think\Db; | ||
9 | +use think\Exception; | ||
10 | + | ||
11 | +/** | ||
12 | + * 在线命令管理 | ||
13 | + * | ||
14 | + * @icon fa fa-circle-o | ||
15 | + */ | ||
16 | +class Command extends Backend | ||
17 | +{ | ||
18 | + | ||
19 | + /** | ||
20 | + * Command模型对象 | ||
21 | + */ | ||
22 | + protected $model = null; | ||
23 | + protected $noNeedRight = ['get_controller_list', 'get_field_list']; | ||
24 | + | ||
25 | + public function _initialize() | ||
26 | + { | ||
27 | + parent::_initialize(); | ||
28 | + $this->model = model('Command'); | ||
29 | + $this->view->assign("statusList", $this->model->getStatusList()); | ||
30 | + } | ||
31 | + | ||
32 | + /** | ||
33 | + * 添加 | ||
34 | + */ | ||
35 | + public function add() | ||
36 | + { | ||
37 | + | ||
38 | + $tableList = []; | ||
39 | + $list = \think\Db::query("SHOW TABLES"); | ||
40 | + foreach ($list as $key => $row) { | ||
41 | + $tableList[reset($row)] = reset($row); | ||
42 | + } | ||
43 | + | ||
44 | + $this->view->assign("tableList", $tableList); | ||
45 | + return $this->view->fetch(); | ||
46 | + } | ||
47 | + | ||
48 | + /** | ||
49 | + * 获取字段列表 | ||
50 | + * @internal | ||
51 | + */ | ||
52 | + public function get_field_list() | ||
53 | + { | ||
54 | + $dbname = Config::get('database.database'); | ||
55 | + $prefix = Config::get('database.prefix'); | ||
56 | + $table = $this->request->request('table'); | ||
57 | + //从数据库中获取表字段信息 | ||
58 | + $sql = "SELECT * FROM `information_schema`.`columns` " | ||
59 | + . "WHERE TABLE_SCHEMA = ? AND table_name = ? " | ||
60 | + . "ORDER BY ORDINAL_POSITION"; | ||
61 | + //加载主表的列 | ||
62 | + $columnList = Db::query($sql, [$dbname, $table]); | ||
63 | + $fieldlist = []; | ||
64 | + foreach ($columnList as $index => $item) { | ||
65 | + $fieldlist[] = $item['COLUMN_NAME']; | ||
66 | + } | ||
67 | + $this->success("", null, ['fieldlist' => $fieldlist]); | ||
68 | + } | ||
69 | + | ||
70 | + /** | ||
71 | + * 获取控制器列表 | ||
72 | + * @internal | ||
73 | + */ | ||
74 | + public function get_controller_list() | ||
75 | + { | ||
76 | + $adminPath = dirname(__DIR__) . DS; | ||
77 | + $controllerDir = $adminPath . 'controller' . DS; | ||
78 | + $files = new \RecursiveIteratorIterator( | ||
79 | + new \RecursiveDirectoryIterator($controllerDir), \RecursiveIteratorIterator::LEAVES_ONLY | ||
80 | + ); | ||
81 | + $list = []; | ||
82 | + foreach ($files as $name => $file) { | ||
83 | + if (!$file->isDir()) { | ||
84 | + $filePath = $file->getRealPath(); | ||
85 | + $name = str_replace($controllerDir, '', $filePath); | ||
86 | + $name = str_replace(DS, "/", $name); | ||
87 | + $list[] = ['id' => $name, 'name' => $name]; | ||
88 | + } | ||
89 | + } | ||
90 | + $pageNumber = $this->request->request("pageNumber"); | ||
91 | + $pageSize = $this->request->request("pageSize"); | ||
92 | + return json(['list' => array_slice($list, ($pageNumber - 1) * $pageSize, $pageSize), 'total' => count($list)]); | ||
93 | + } | ||
94 | + | ||
95 | + /** | ||
96 | + * 详情 | ||
97 | + */ | ||
98 | + public function detail($ids) | ||
99 | + { | ||
100 | + $row = $this->model->get($ids); | ||
101 | + if (!$row) | ||
102 | + $this->error(__('No Results were found')); | ||
103 | + $this->view->assign("row", $row); | ||
104 | + return $this->view->fetch(); | ||
105 | + } | ||
106 | + | ||
107 | + /** | ||
108 | + * 执行 | ||
109 | + */ | ||
110 | + public function execute($ids) | ||
111 | + { | ||
112 | + $row = $this->model->get($ids); | ||
113 | + if (!$row) | ||
114 | + $this->error(__('No Results were found')); | ||
115 | + $result = $this->doexecute($row['type'], json_decode($row['params'], true)); | ||
116 | + $this->success("", null, ['result' => $result]); | ||
117 | + } | ||
118 | + | ||
119 | + /** | ||
120 | + * 执行命令 | ||
121 | + */ | ||
122 | + public function command($action = '') | ||
123 | + { | ||
124 | + $commandtype = $this->request->request("commandtype"); | ||
125 | + $params = $this->request->request(); | ||
126 | + $allowfields = [ | ||
127 | + 'crud' => 'table,controller,model,fields,force,local,delete,menu', | ||
128 | + 'menu' => 'controller,delete', | ||
129 | + 'min' => 'module,resource,optimize', | ||
130 | + 'api' => 'url,module,output,template,force,title,author,class,language', | ||
131 | + ]; | ||
132 | + $argv = []; | ||
133 | + $allowfields = isset($allowfields[$commandtype]) ? explode(',', $allowfields[$commandtype]) : []; | ||
134 | + $allowfields = array_filter(array_intersect_key($params, array_flip($allowfields))); | ||
135 | + if (isset($params['local']) && !$params['local']) { | ||
136 | + $allowfields['local'] = $params['local']; | ||
137 | + } else { | ||
138 | + unset($allowfields['local']); | ||
139 | + } | ||
140 | + foreach ($allowfields as $key => $param) { | ||
141 | + $argv[] = "--{$key}=" . (is_array($param) ? implode(',', $param) : $param); | ||
142 | + } | ||
143 | + if ($commandtype == 'crud') { | ||
144 | + $extend = 'setcheckboxsuffix,enumradiosuffix,imagefield,filefield,intdatesuffix,switchsuffix,citysuffix,selectpagesuffix,selectpagessuffix,ignorefields,sortfield,editorsuffix,headingfilterfield'; | ||
145 | + $extendArr = explode(',', $extend); | ||
146 | + foreach ($params as $index => $item) { | ||
147 | + if (in_array($index, $extendArr)) { | ||
148 | + foreach (explode(',', $item) as $key => $value) { | ||
149 | + if ($value) { | ||
150 | + $argv[] = "--{$index}={$value}"; | ||
151 | + } | ||
152 | + } | ||
153 | + } | ||
154 | + } | ||
155 | + $isrelation = (int)$this->request->request('isrelation'); | ||
156 | + if ($isrelation && isset($params['relation'])) { | ||
157 | + foreach ($params['relation'] as $index => $relation) { | ||
158 | + foreach ($relation as $key => $value) { | ||
159 | + $argv[] = "--{$key}=" . (is_array($value) ? implode(',', $value) : $value); | ||
160 | + } | ||
161 | + } | ||
162 | + } | ||
163 | + } else if ($commandtype == 'menu') { | ||
164 | + if (isset($params['allcontroller']) && $params['allcontroller']) { | ||
165 | + $argv[] = "--controller=all-controller"; | ||
166 | + } else { | ||
167 | + foreach (explode(',', $params['controllerfile']) as $index => $param) { | ||
168 | + if ($param) { | ||
169 | + $argv[] = "--controller=" . substr($param, 0, -4); | ||
170 | + } | ||
171 | + } | ||
172 | + } | ||
173 | + } else if ($commandtype == 'min') { | ||
174 | + | ||
175 | + } else if ($commandtype == 'api') { | ||
176 | + | ||
177 | + } else { | ||
178 | + | ||
179 | + } | ||
180 | + if ($action == 'execute') { | ||
181 | + $result = $this->doexecute($commandtype, $argv); | ||
182 | + $this->success("", null, ['result' => $result]); | ||
183 | + } else { | ||
184 | + $this->success("", null, ['command' => "php think {$commandtype} " . implode(' ', $argv)]); | ||
185 | + } | ||
186 | + | ||
187 | + return; | ||
188 | + } | ||
189 | + | ||
190 | + protected function doexecute($commandtype, $argv) | ||
191 | + { | ||
192 | + $commandName = "\\app\\admin\\command\\" . ucfirst($commandtype); | ||
193 | + $input = new Input($argv); | ||
194 | + $output = new \addons\command\library\Output(); | ||
195 | + $command = new $commandName($commandtype); | ||
196 | + $data = [ | ||
197 | + 'type' => $commandtype, | ||
198 | + 'params' => json_encode($argv), | ||
199 | + 'command' => "php think {$commandtype} " . implode(' ', $argv), | ||
200 | + 'executetime' => time(), | ||
201 | + ]; | ||
202 | + $this->model->save($data); | ||
203 | + try { | ||
204 | + $command->run($input, $output); | ||
205 | + $result = implode("\n", $output->getMessage()); | ||
206 | + $this->model->status = 'successed'; | ||
207 | + } catch (Exception $e) { | ||
208 | + $result = implode("\n", $output->getMessage()) . "\n"; | ||
209 | + $result .= $e->getMessage(); | ||
210 | + $this->model->status = 'failured'; | ||
211 | + } | ||
212 | + $result = trim($result); | ||
213 | + $this->model->content = $result; | ||
214 | + $this->model->save(); | ||
215 | + return $result; | ||
216 | + } | ||
217 | + | ||
218 | + | ||
219 | +} |
1 | +<?php | ||
2 | + | ||
3 | +return [ | ||
4 | + 'Id' => 'ID', | ||
5 | + 'Type' => '类型', | ||
6 | + 'Params' => '参数', | ||
7 | + 'Command' => '命令', | ||
8 | + 'Content' => '返回结果', | ||
9 | + 'Executetime' => '执行时间', | ||
10 | + 'Createtime' => '创建时间', | ||
11 | + 'Updatetime' => '更新时间', | ||
12 | + 'Execute again' => '再次执行', | ||
13 | + 'Successed' => '成功', | ||
14 | + 'Failured' => '失败', | ||
15 | + 'Status' => '状态' | ||
16 | +]; |
1 | +<?php | ||
2 | + | ||
3 | +namespace app\admin\model; | ||
4 | + | ||
5 | +use think\Model; | ||
6 | + | ||
7 | +class Command extends Model | ||
8 | +{ | ||
9 | + // 表名 | ||
10 | + protected $name = 'command'; | ||
11 | + | ||
12 | + // 自动写入时间戳字段 | ||
13 | + protected $autoWriteTimestamp = 'int'; | ||
14 | + | ||
15 | + // 定义时间戳字段名 | ||
16 | + protected $createTime = 'createtime'; | ||
17 | + protected $updateTime = 'updatetime'; | ||
18 | + | ||
19 | + // 追加属性 | ||
20 | + protected $append = [ | ||
21 | + 'executetime_text', | ||
22 | + 'type_text', | ||
23 | + 'status_text' | ||
24 | + ]; | ||
25 | + | ||
26 | + | ||
27 | + public function getStatusList() | ||
28 | + { | ||
29 | + return ['successed' => __('Successed'), 'failured' => __('Failured')]; | ||
30 | + } | ||
31 | + | ||
32 | + | ||
33 | + public function getExecutetimeTextAttr($value, $data) | ||
34 | + { | ||
35 | + $value = $value ? $value : $data['executetime']; | ||
36 | + return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value; | ||
37 | + } | ||
38 | + | ||
39 | + public function getTypeTextAttr($value, $data) | ||
40 | + { | ||
41 | + $value = $value ? $value : $data['type']; | ||
42 | + $list = ['crud' => '一键生成CRUD', 'menu' => '一键生成菜单', 'min' => '一键压缩打包', 'api' => '一键生成文档']; | ||
43 | + return isset($list[$value]) ? $list[$value] : ''; | ||
44 | + } | ||
45 | + | ||
46 | + public function getStatusTextAttr($value, $data) | ||
47 | + { | ||
48 | + $value = $value ? $value : $data['status']; | ||
49 | + $list = $this->getStatusList(); | ||
50 | + return isset($list[$value]) ? $list[$value] : ''; | ||
51 | + } | ||
52 | + | ||
53 | + protected function setExecutetimeAttr($value) | ||
54 | + { | ||
55 | + return $value && !is_numeric($value) ? strtotime($value) : $value; | ||
56 | + } | ||
57 | + | ||
58 | + | ||
59 | +} |
1 | +<?php | ||
2 | + | ||
3 | +namespace app\admin\validate; | ||
4 | + | ||
5 | +use think\Validate; | ||
6 | + | ||
7 | +class Command extends Validate | ||
8 | +{ | ||
9 | + /** | ||
10 | + * 验证规则 | ||
11 | + */ | ||
12 | + protected $rule = [ | ||
13 | + ]; | ||
14 | + /** | ||
15 | + * 提示消息 | ||
16 | + */ | ||
17 | + protected $message = [ | ||
18 | + ]; | ||
19 | + /** | ||
20 | + * 验证场景 | ||
21 | + */ | ||
22 | + protected $scene = [ | ||
23 | + 'add' => [], | ||
24 | + 'edit' => [], | ||
25 | + ]; | ||
26 | + | ||
27 | +} |
1 | +<style> | ||
2 | + .relation-item {margin-top:10px;} | ||
3 | + legend {padding-bottom:5px;font-size:14px;font-weight:600;} | ||
4 | + label {font-weight:normal;} | ||
5 | + .form-control{padding:6px 8px;} | ||
6 | + #extend-zone .col-xs-2 {margin-top:10px;padding-right:0;} | ||
7 | + #extend-zone .col-xs-2:nth-child(6n+0) {padding-right:15px;} | ||
8 | +</style> | ||
9 | +<div class="panel panel-default panel-intro"> | ||
10 | + <div class="panel-heading"> | ||
11 | + <ul class="nav nav-tabs"> | ||
12 | + <li class="active"><a href="#crud" data-toggle="tab">{:__('一键生成CRUD')}</a></li> | ||
13 | + <li><a href="#menu" data-toggle="tab">{:__('一键生成菜单')}</a></li> | ||
14 | + <li><a href="#min" data-toggle="tab">{:__('一键压缩打包')}</a></li> | ||
15 | + <li><a href="#api" data-toggle="tab">{:__('一键生成API文档')}</a></li> | ||
16 | + </ul> | ||
17 | + </div> | ||
18 | + <div class="panel-body"> | ||
19 | + <div id="myTabContent" class="tab-content"> | ||
20 | + <div class="tab-pane fade active in" id="crud"> | ||
21 | + <div class="row"> | ||
22 | + <div class="col-xs-12"> | ||
23 | + <form role="form"> | ||
24 | + <input type="hidden" name="commandtype" value="crud" /> | ||
25 | + <div class="form-group"> | ||
26 | + <div class="row"> | ||
27 | + <div class="col-xs-3"> | ||
28 | + <input checked="" name="isrelation" type="hidden" value="0"> | ||
29 | + <label class="control-label" data-toggle="tooltip" title="当前只支持生成1对1关联模型,选中后请配置关联表和字段"> | ||
30 | + <input name="isrelation" type="checkbox" value="1"> | ||
31 | + 关联模型 | ||
32 | + </label> | ||
33 | + </div> | ||
34 | + <div class="col-xs-3"> | ||
35 | + <input checked="" name="local" type="hidden" value="1"> | ||
36 | + <label class="control-label" data-toggle="tooltip" title="默认模型生成在application/admin/model目录下,选中后将生成在application/common/model目录下"> | ||
37 | + <input name="local" type="checkbox" value="0"> 全局模型类 | ||
38 | + </label> | ||
39 | + </div> | ||
40 | + <div class="col-xs-3"> | ||
41 | + <input checked="" name="delete" type="hidden" value="0"> | ||
42 | + <label class="control-label" data-toggle="tooltip" title="删除CRUD生成的相关文件"> | ||
43 | + <input name="delete" type="checkbox" value="1"> 删除模式 | ||
44 | + </label> | ||
45 | + </div> | ||
46 | + <div class="col-xs-3"> | ||
47 | + <input checked="" name="force" type="hidden" value="0"> | ||
48 | + <label class="control-label" data-toggle="tooltip" title="选中后,如果已经存在同名文件将被覆盖。如果是删除将不再提醒"> | ||
49 | + <input name="force" type="checkbox" value="1"> | ||
50 | + 强制覆盖模式 | ||
51 | + </label> | ||
52 | + </div> | ||
53 | + <!-- | ||
54 | + <div class="col-xs-3"> | ||
55 | + <input checked="" name="menu" type="hidden" value="0"> | ||
56 | + <label class="control-label" data-toggle="tooltip" title="选中后,将同时生成后台菜单规则"> | ||
57 | + <input name="menu" type="checkbox" value="1"> | ||
58 | + 生成菜单 | ||
59 | + </label> | ||
60 | + </div> | ||
61 | + --> | ||
62 | + </div> | ||
63 | + </div> | ||
64 | + <div class="form-group"> | ||
65 | + <legend>主表设置</legend> | ||
66 | + <div class="row"> | ||
67 | + <div class="col-xs-3"> | ||
68 | + <label>请选择主表</label> | ||
69 | + {:build_select('table',$tableList,null,['class'=>'form-control selectpicker']);} | ||
70 | + </div> | ||
71 | + <div class="col-xs-3"> | ||
72 | + <label>自定义控制器名</label> | ||
73 | + <input type="text" class="form-control" name="controller" data-toggle="tooltip" title="默认根据表名自动生成,如果需要放在二级目录请手动填写" placeholder="支持目录层级,以/分隔"> | ||
74 | + </div> | ||
75 | + <div class="col-xs-3"> | ||
76 | + <label>自定义模型名</label> | ||
77 | + <input type="text" class="form-control" name="model" data-toggle="tooltip" title="默认根据表名自动生成" placeholder="不支持目录层级"> | ||
78 | + </div> | ||
79 | + <div class="col-xs-3"> | ||
80 | + <label>请选择显示字段(默认全部)</label> | ||
81 | + <select name="fields[]" id="fields" multiple style="height:30px;" class="form-control selectpicker"></select> | ||
82 | + </div> | ||
83 | + | ||
84 | + </div> | ||
85 | + | ||
86 | + </div> | ||
87 | + | ||
88 | + <div class="form-group hide" id="relation-zone"> | ||
89 | + <legend>关联表设置</legend> | ||
90 | + | ||
91 | + <div class="row" style="margin-top:15px;"> | ||
92 | + <div class="col-xs-12"> | ||
93 | + <a href="javascript:;" class="btn btn-primary btn-sm btn-newrelation" data-index="1">追加关联模型</a> | ||
94 | + </div> | ||
95 | + </div> | ||
96 | + </div> | ||
97 | + | ||
98 | + <hr> | ||
99 | + <div class="form-group" id="extend-zone"> | ||
100 | + <legend>字段识别设置 <span style="font-size:12px;font-weight: normal;">(与之匹配的字段都将生成相应组件)</span></legend> | ||
101 | + <div class="row"> | ||
102 | + <div class="col-xs-2"> | ||
103 | + <label>复选框后缀</label> | ||
104 | + <input type="text" class="form-control" name="setcheckboxsuffix" placeholder="默认为set类型" /> | ||
105 | + </div> | ||
106 | + <div class="col-xs-2"> | ||
107 | + <label>单选框后缀</label> | ||
108 | + <input type="text" class="form-control" name="enumradiosuffix" placeholder="默认为enum类型" /> | ||
109 | + </div> | ||
110 | + <div class="col-xs-2"> | ||
111 | + <label>图片类型后缀</label> | ||
112 | + <input type="text" class="form-control" name="imagefield" placeholder="默认为image,images,avatar,avatars" /> | ||
113 | + </div> | ||
114 | + <div class="col-xs-2"> | ||
115 | + <label>文件类型后缀</label> | ||
116 | + <input type="text" class="form-control" name="filefield" placeholder="默认为file,files" /> | ||
117 | + </div> | ||
118 | + <div class="col-xs-2"> | ||
119 | + <label>日期时间后缀</label> | ||
120 | + <input type="text" class="form-control" name="intdatesuffix" placeholder="默认为time" /> | ||
121 | + </div> | ||
122 | + <div class="col-xs-2"> | ||
123 | + <label>开关后缀</label> | ||
124 | + <input type="text" class="form-control" name="switchsuffix" placeholder="默认为switch" /> | ||
125 | + </div> | ||
126 | + <div class="col-xs-2"> | ||
127 | + <label>城市选择后缀</label> | ||
128 | + <input type="text" class="form-control" name="citysuffix" placeholder="默认为city" /> | ||
129 | + </div> | ||
130 | + <div class="col-xs-2"> | ||
131 | + <label>动态下拉后缀(单)</label> | ||
132 | + <input type="text" class="form-control" name="selectpagesuffix" placeholder="默认为_id" /> | ||
133 | + </div> | ||
134 | + <div class="col-xs-2"> | ||
135 | + <label>动态下拉后缀(多)</label> | ||
136 | + <input type="text" class="form-control" name="selectpagessuffix" placeholder="默认为_ids" /> | ||
137 | + </div> | ||
138 | + <div class="col-xs-2"> | ||
139 | + <label>忽略的字段</label> | ||
140 | + <input type="text" class="form-control" name="ignorefields" placeholder="默认无" /> | ||
141 | + </div> | ||
142 | + <div class="col-xs-2"> | ||
143 | + <label>排序字段</label> | ||
144 | + <input type="text" class="form-control" name="sortfield" placeholder="默认为weigh" /> | ||
145 | + </div> | ||
146 | + <div class="col-xs-2"> | ||
147 | + <label>富文本编辑器</label> | ||
148 | + <input type="text" class="form-control" name="editorsuffix" placeholder="默认为content" /> | ||
149 | + </div> | ||
150 | + <div class="col-xs-2"> | ||
151 | + <label>选项卡过滤字段</label> | ||
152 | + <input type="text" class="form-control" name="headingfilterfield" placeholder="默认为status" /> | ||
153 | + </div> | ||
154 | + | ||
155 | + </div> | ||
156 | + | ||
157 | + </div> | ||
158 | + | ||
159 | + <div class="form-group"> | ||
160 | + <legend>生成命令行</legend> | ||
161 | + <textarea class="form-control" data-toggle="tooltip" title="如果在线执行命令失败,可以将命令复制到命令行进行执行" rel="command" rows="1" placeholder="请点击生成命令行"></textarea> | ||
162 | + </div> | ||
163 | + | ||
164 | + <div class="form-group"> | ||
165 | + <legend>返回结果</legend> | ||
166 | + <textarea class="form-control" rel="result" rows="5" placeholder="请点击立即执行"></textarea> | ||
167 | + </div> | ||
168 | + | ||
169 | + <div class="form-group"> | ||
170 | + <button type="button" class="btn btn-info btn-embossed btn-command">{:__('生成命令行')}</button> | ||
171 | + <button type="button" class="btn btn-success btn-embossed btn-execute">{:__('立即执行')}</button> | ||
172 | + </div> | ||
173 | + | ||
174 | + </form> | ||
175 | + </div> | ||
176 | + </div> | ||
177 | + </div> | ||
178 | + <div class="tab-pane fade" id="menu"> | ||
179 | + <div class="row"> | ||
180 | + <div class="col-xs-12"> | ||
181 | + <form role="form"> | ||
182 | + <input type="hidden" name="commandtype" value="menu" /> | ||
183 | + <div class="form-group"> | ||
184 | + <div class="row"> | ||
185 | + <div class="col-xs-3"> | ||
186 | + <input checked="" name="allcontroller" type="hidden" value="0"> | ||
187 | + <label class="control-label"> | ||
188 | + <input name="allcontroller" data-toggle="collapse" data-target="#controller" type="checkbox" value="1"> 一键生成全部控制器 | ||
189 | + </label> | ||
190 | + </div> | ||
191 | + <div class="col-xs-3"> | ||
192 | + <input checked="" name="delete" type="hidden" value="0"> | ||
193 | + <label class="control-label"> | ||
194 | + <input name="delete" type="checkbox" value="1"> 删除模式 | ||
195 | + </label> | ||
196 | + </div> | ||
197 | + <div class="col-xs-3"> | ||
198 | + <input checked="" name="force" type="hidden" value="0"> | ||
199 | + <label class="control-label"> | ||
200 | + <input name="force" type="checkbox" value="1"> 强制覆盖模式 | ||
201 | + </label> | ||
202 | + </div> | ||
203 | + </div> | ||
204 | + </div> | ||
205 | + | ||
206 | + <div class="form-group in" id="controller"> | ||
207 | + <legend>控制器设置</legend> | ||
208 | + | ||
209 | + <div class="row" style="margin-top:15px;"> | ||
210 | + <div class="col-xs-12"> | ||
211 | + <input type="text" name="controllerfile" class="form-control selectpage" style="width:720px;" data-source="command/get_controller_list" data-multiple="true" name="controller" placeholder="请选择控制器" /> | ||
212 | + </div> | ||
213 | + </div> | ||
214 | + </div> | ||
215 | + | ||
216 | + <div class="form-group"> | ||
217 | + <legend>生成命令行</legend> | ||
218 | + <textarea class="form-control" rel="command" rows="1" placeholder="请点击生成命令行"></textarea> | ||
219 | + </div> | ||
220 | + | ||
221 | + <div class="form-group"> | ||
222 | + <legend>返回结果</legend> | ||
223 | + <textarea class="form-control" rel="result" rows="5" placeholder="请点击立即执行"></textarea> | ||
224 | + </div> | ||
225 | + | ||
226 | + <div class="form-group"> | ||
227 | + <button type="button" class="btn btn-info btn-embossed btn-command">{:__('生成命令行')}</button> | ||
228 | + <button type="button" class="btn btn-success btn-embossed btn-execute">{:__('立即执行')}</button> | ||
229 | + </div> | ||
230 | + | ||
231 | + </form> | ||
232 | + </div> | ||
233 | + </div> | ||
234 | + </div> | ||
235 | + <div class="tab-pane fade" id="min"> | ||
236 | + <div class="row"> | ||
237 | + <div class="col-xs-12"> | ||
238 | + <form role="form"> | ||
239 | + <input type="hidden" name="commandtype" value="min" /> | ||
240 | + <div class="form-group"> | ||
241 | + <legend>基础设置</legend> | ||
242 | + <div class="row"> | ||
243 | + <div class="col-xs-3"> | ||
244 | + <label>请选择压缩模块</label> | ||
245 | + <select name="module" class="form-control selectpicker"> | ||
246 | + <option value="all" selected>全部</option> | ||
247 | + <option value="backend">后台Backend</option> | ||
248 | + <option value="frontend">前台Frontend</option> | ||
249 | + </select> | ||
250 | + </div> | ||
251 | + <div class="col-xs-3"> | ||
252 | + <label>请选择压缩资源</label> | ||
253 | + <select name="resource" class="form-control selectpicker"> | ||
254 | + <option value="all" selected>全部</option> | ||
255 | + <option value="js">JS</option> | ||
256 | + <option value="css">CSS</option> | ||
257 | + </select> | ||
258 | + </div> | ||
259 | + <div class="col-xs-3"> | ||
260 | + <label>请选择压缩模式</label> | ||
261 | + <select name="optimize" class="form-control selectpicker"> | ||
262 | + <option value="">无</option> | ||
263 | + <option value="uglify">uglify</option> | ||
264 | + <option value="closure">closure</option> | ||
265 | + </select> | ||
266 | + </div> | ||
267 | + </div> | ||
268 | + </div> | ||
269 | + | ||
270 | + <div class="form-group in"> | ||
271 | + <legend>控制器设置</legend> | ||
272 | + | ||
273 | + <div class="row" style="margin-top:15px;"> | ||
274 | + <div class="col-xs-12"> | ||
275 | + | ||
276 | + </div> | ||
277 | + </div> | ||
278 | + </div> | ||
279 | + | ||
280 | + <div class="form-group"> | ||
281 | + <legend>生成命令行</legend> | ||
282 | + <textarea class="form-control" rel="command" rows="1" placeholder="请点击生成命令行"></textarea> | ||
283 | + </div> | ||
284 | + | ||
285 | + <div class="form-group"> | ||
286 | + <legend>返回结果</legend> | ||
287 | + <textarea class="form-control" rel="result" rows="5" placeholder="请点击立即执行"></textarea> | ||
288 | + </div> | ||
289 | + | ||
290 | + <div class="form-group"> | ||
291 | + <button type="button" class="btn btn-info btn-embossed btn-command">{:__('生成命令行')}</button> | ||
292 | + <button type="button" class="btn btn-success btn-embossed btn-execute">{:__('立即执行')}</button> | ||
293 | + </div> | ||
294 | + | ||
295 | + </form> | ||
296 | + </div> | ||
297 | + </div> | ||
298 | + </div> | ||
299 | + <div class="tab-pane fade" id="api"> | ||
300 | + <div class="row"> | ||
301 | + <div class="col-xs-12"> | ||
302 | + <form role="form"> | ||
303 | + <input type="hidden" name="commandtype" value="api" /> | ||
304 | + <div class="form-group"> | ||
305 | + <div class="row"> | ||
306 | + <div class="col-xs-3"> | ||
307 | + <input checked="" name="force" type="hidden" value="0"> | ||
308 | + <label class="control-label"> | ||
309 | + <input name="force" type="checkbox" value="1"> | ||
310 | + 覆盖模式 | ||
311 | + </label> | ||
312 | + </div> | ||
313 | + </div> | ||
314 | + </div> | ||
315 | + <div class="form-group"> | ||
316 | + <legend>文档设置</legend> | ||
317 | + <div class="row"> | ||
318 | + <div class="col-xs-3"> | ||
319 | + <label>请输入接口URL</label> | ||
320 | + <input type="text" name="url" class="form-control" placeholder="API URL,可留空" /> | ||
321 | + </div> | ||
322 | + <div class="col-xs-3"> | ||
323 | + <label>接口生成文件</label> | ||
324 | + <input type="text" name="output" class="form-control" placeholder="留空则使用api.html" /> | ||
325 | + </div> | ||
326 | + <div class="col-xs-3"> | ||
327 | + <label>模板文件</label> | ||
328 | + <input type="text" name="template" class="form-control" placeholder="如果不清楚请留空" /> | ||
329 | + </div> | ||
330 | + </div> | ||
331 | + <div class="row" style="margin-top:10px;"> | ||
332 | + <div class="col-xs-3"> | ||
333 | + <label>文档标题</label> | ||
334 | + <input type="text" name="title" class="form-control" placeholder="默认为FastAdmin" /> | ||
335 | + </div> | ||
336 | + <div class="col-xs-3"> | ||
337 | + <label>文档作者</label> | ||
338 | + <input type="text" name="author" class="form-control" placeholder="默认为FastAdmin" /> | ||
339 | + </div> | ||
340 | + <div class="col-xs-3"> | ||
341 | + <label>文档语言</label> | ||
342 | + <select name="language" class="form-control"> | ||
343 | + <option value="" selected>请选择语言</option> | ||
344 | + <option value="zh-cn">中文</option> | ||
345 | + <option value="en">英文</option> | ||
346 | + </select> | ||
347 | + </div> | ||
348 | + </div> | ||
349 | + </div> | ||
350 | + | ||
351 | + <div class="form-group"> | ||
352 | + <legend>生成命令行</legend> | ||
353 | + <textarea class="form-control" rel="command" rows="1" placeholder="请点击生成命令行"></textarea> | ||
354 | + </div> | ||
355 | + | ||
356 | + <div class="form-group"> | ||
357 | + <legend>返回结果</legend> | ||
358 | + <textarea class="form-control" rel="result" rows="5" placeholder="请点击立即执行"></textarea> | ||
359 | + </div> | ||
360 | + | ||
361 | + <div class="form-group"> | ||
362 | + <button type="button" class="btn btn-info btn-embossed btn-command">{:__('生成命令行')}</button> | ||
363 | + <button type="button" class="btn btn-success btn-embossed btn-execute">{:__('立即执行')}</button> | ||
364 | + </div> | ||
365 | + | ||
366 | + </form> | ||
367 | + </div> | ||
368 | + </div> | ||
369 | + </div> | ||
370 | + </div> | ||
371 | + </div> | ||
372 | +</div> | ||
373 | +<script id="relationtpl" type="text/html"> | ||
374 | + <div class="row relation-item"> | ||
375 | + <div class="col-xs-2"> | ||
376 | + <label>请选择关联表</label> | ||
377 | + <select name="relation[<%=index%>][relation]" class="form-control relationtable"></select> | ||
378 | + </div> | ||
379 | + <div class="col-xs-2"> | ||
380 | + <label>请选择关联类型</label> | ||
381 | + <select name="relation[<%=index%>][relationmode]" class="form-control relationmode"></select> | ||
382 | + </div> | ||
383 | + <div class="col-xs-2"> | ||
384 | + <label>关联外键</label> | ||
385 | + <select name="relation[<%=index%>][relationforeignkey]" class="form-control relationforeignkey"></select> | ||
386 | + </div> | ||
387 | + <div class="col-xs-2"> | ||
388 | + <label>关联主键</label> | ||
389 | + <select name="relation[<%=index%>][relationprimarykey]" class="form-control relationprimarykey"></select> | ||
390 | + </div> | ||
391 | + <div class="col-xs-2"> | ||
392 | + <label>请选择显示字段</label> | ||
393 | + <select name="relation[<%=index%>][relationfields][]" multiple class="form-control relationfields"></select> | ||
394 | + </div> | ||
395 | + <div class="col-xs-2"> | ||
396 | + <label> </label> | ||
397 | + <a href="javascript:;" class="btn btn-danger btn-block btn-removerelation">移除</a> | ||
398 | + </div> | ||
399 | + </div> | ||
400 | +</script> |
1 | +<table class="table table-striped"> | ||
2 | + <thead> | ||
3 | + <tr> | ||
4 | + <th>{:__('Title')}</th> | ||
5 | + <th>{:__('Content')}</th> | ||
6 | + </tr> | ||
7 | + </thead> | ||
8 | + <tbody> | ||
9 | + <tr> | ||
10 | + <td>{:__('Type')}</td> | ||
11 | + <td>{$row.type}({$row.type_text})</td> | ||
12 | + </tr> | ||
13 | + <tr> | ||
14 | + <td>{:__('Params')}</td> | ||
15 | + <td>{$row.params}</td> | ||
16 | + </tr> | ||
17 | + <tr> | ||
18 | + <td>{:__('Command')}</td> | ||
19 | + <td>{$row.command}</td> | ||
20 | + </tr> | ||
21 | + <tr> | ||
22 | + <td>{:__('Content')}</td> | ||
23 | + <td> | ||
24 | + <textarea class="form-control" name="" id="" cols="60" rows="10">{$row.content}</textarea> | ||
25 | + </td> | ||
26 | + </tr> | ||
27 | + <tr> | ||
28 | + <td>{:__('Executetime')}</td> | ||
29 | + <td>{$row.executetime|datetime}</td> | ||
30 | + </tr> | ||
31 | + <tr> | ||
32 | + <td>{:__('Status')}</td> | ||
33 | + <td>{$row.status_text}</td> | ||
34 | + </tr> | ||
35 | + </tbody> | ||
36 | +</table> | ||
37 | +<div class="hide layer-footer"> | ||
38 | + <label class="control-label col-xs-12 col-sm-2"></label> | ||
39 | + <div class="col-xs-12 col-sm-8"> | ||
40 | + <button type="reset" class="btn btn-primary btn-embossed btn-close" onclick="Layer.closeAll();">{:__('Close')}</button> | ||
41 | + </div> | ||
42 | +</div> |
1 | +<div class="panel panel-default panel-intro"> | ||
2 | + {:build_heading()} | ||
3 | + | ||
4 | + <div class="panel-body"> | ||
5 | + <div id="myTabContent" class="tab-content"> | ||
6 | + <div class="tab-pane fade active in" id="one"> | ||
7 | + <div class="widget-body no-padding"> | ||
8 | + <div id="toolbar" class="toolbar"> | ||
9 | + <a href="javascript:;" class="btn btn-primary btn-refresh" title="{:__('Refresh')}" ><i class="fa fa-refresh"></i> </a> | ||
10 | + <a href="javascript:;" class="btn btn-success btn-add {:$auth->check('command/add')?'':'hide'}" title="{:__('Add')}" ><i class="fa fa-plus"></i> {:__('Add')}</a> | ||
11 | + <a href="javascript:;" class="btn btn-danger btn-del btn-disabled disabled {:$auth->check('command/del')?'':'hide'}" title="{:__('Delete')}" ><i class="fa fa-trash"></i> {:__('Delete')}</a> | ||
12 | + | ||
13 | + </div> | ||
14 | + <table id="table" class="table table-striped table-bordered table-hover" | ||
15 | + data-operate-detail="{:$auth->check('command/detail')}" | ||
16 | + data-operate-execute="{:$auth->check('command/execute')}" | ||
17 | + data-operate-del="{:$auth->check('command/del')}" | ||
18 | + width="100%"> | ||
19 | + </table> | ||
20 | + </div> | ||
21 | + </div> | ||
22 | + | ||
23 | + </div> | ||
24 | + </div> | ||
25 | +</div> |
addons/command/config.php
0 → 100644
addons/command/controller/Index.php
0 → 100644
addons/command/info.ini
0 → 100644
addons/command/install.sql
0 → 100644
1 | +CREATE TABLE IF NOT EXISTS `__PREFIX__command` ( | ||
2 | + `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID', | ||
3 | + `type` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '类型', | ||
4 | + `params` varchar(1500) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '参数', | ||
5 | + `command` varchar(1500) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '命令', | ||
6 | + `content` text COMMENT '返回结果', | ||
7 | + `executetime` int(10) UNSIGNED NOT NULL DEFAULT 0 COMMENT '执行时间', | ||
8 | + `createtime` int(10) UNSIGNED NOT NULL DEFAULT 0 COMMENT '创建时间', | ||
9 | + `updatetime` int(10) UNSIGNED NOT NULL DEFAULT 0 COMMENT '更新时间', | ||
10 | + `status` enum('successed','failured') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'failured' COMMENT '状态', | ||
11 | + PRIMARY KEY (`id`) USING BTREE | ||
12 | +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '在线命令表' ROW_FORMAT = Compact; |
addons/command/library/Output.php
0 → 100644
1 | +<?php | ||
2 | + | ||
3 | +namespace addons\command\library; | ||
4 | + | ||
5 | +/** | ||
6 | + * Class Output | ||
7 | + */ | ||
8 | +class Output extends \think\console\Output | ||
9 | +{ | ||
10 | + | ||
11 | + protected $message = []; | ||
12 | + | ||
13 | + public function __construct($driver = 'console') | ||
14 | + { | ||
15 | + parent::__construct($driver); | ||
16 | + } | ||
17 | + | ||
18 | + protected function block($style, $message) | ||
19 | + { | ||
20 | + $this->message[] = $message; | ||
21 | + } | ||
22 | + | ||
23 | + public function getMessage() | ||
24 | + { | ||
25 | + return $this->message; | ||
26 | + } | ||
27 | + | ||
28 | +} |
1 | +define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'template'], function ($, undefined, Backend, Table, Form, Template) { | ||
2 | + | ||
3 | + var Controller = { | ||
4 | + index: function () { | ||
5 | + // 初始化表格参数配置 | ||
6 | + Table.api.init({ | ||
7 | + extend: { | ||
8 | + index_url: 'command/index', | ||
9 | + add_url: 'command/add', | ||
10 | + edit_url: '', | ||
11 | + del_url: 'command/del', | ||
12 | + multi_url: 'command/multi', | ||
13 | + table: 'command', | ||
14 | + } | ||
15 | + }); | ||
16 | + | ||
17 | + var table = $("#table"); | ||
18 | + | ||
19 | + // 初始化表格 | ||
20 | + table.bootstrapTable({ | ||
21 | + url: $.fn.bootstrapTable.defaults.extend.index_url, | ||
22 | + pk: 'id', | ||
23 | + sortName: 'id', | ||
24 | + columns: [ | ||
25 | + [ | ||
26 | + {checkbox: true}, | ||
27 | + {field: 'id', title: __('Id')}, | ||
28 | + {field: 'type', title: __('Type'), formatter: Table.api.formatter.search}, | ||
29 | + {field: 'type_text', title: __('Type')}, | ||
30 | + { | ||
31 | + field: 'command', title: __('Command'), formatter: function (value, row, index) { | ||
32 | + return '<input type="text" class="form-control" value="' + value + '">'; | ||
33 | + } | ||
34 | + }, | ||
35 | + { | ||
36 | + field: 'executetime', | ||
37 | + title: __('Executetime'), | ||
38 | + operate: 'RANGE', | ||
39 | + addclass: 'datetimerange', | ||
40 | + formatter: Table.api.formatter.datetime | ||
41 | + }, | ||
42 | + { | ||
43 | + field: 'createtime', | ||
44 | + title: __('Createtime'), | ||
45 | + operate: 'RANGE', | ||
46 | + addclass: 'datetimerange', | ||
47 | + formatter: Table.api.formatter.datetime | ||
48 | + }, | ||
49 | + { | ||
50 | + field: 'updatetime', | ||
51 | + title: __('Updatetime'), | ||
52 | + operate: 'RANGE', | ||
53 | + addclass: 'datetimerange', | ||
54 | + formatter: Table.api.formatter.datetime | ||
55 | + }, | ||
56 | + { | ||
57 | + field: 'status', | ||
58 | + title: __('Status'), | ||
59 | + table: table, | ||
60 | + custom: {"successed": 'success', "failured": 'danger'}, | ||
61 | + searchList: {"successed": __('Successed'), "failured": __('Failured')}, | ||
62 | + formatter: Table.api.formatter.status | ||
63 | + }, | ||
64 | + { | ||
65 | + field: 'operate', | ||
66 | + title: __('Operate'), | ||
67 | + buttons: [ | ||
68 | + { | ||
69 | + name: 'execute', | ||
70 | + title: __('Execute again'), | ||
71 | + text: __('Execute again'), | ||
72 | + url: 'command/execute', | ||
73 | + icon: 'fa fa-repeat', | ||
74 | + classname: 'btn btn-success btn-xs btn-execute btn-ajax', | ||
75 | + success: function (data) { | ||
76 | + Layer.alert("<textarea class='form-control' cols='60' rows='5'>" + data.result + "</textarea>", { | ||
77 | + title: __("执行结果"), | ||
78 | + shadeClose: true | ||
79 | + }); | ||
80 | + table.bootstrapTable('refresh'); | ||
81 | + return false; | ||
82 | + } | ||
83 | + }, | ||
84 | + { | ||
85 | + name: 'execute', | ||
86 | + title: __('Detail'), | ||
87 | + text: __('Detail'), | ||
88 | + url: 'command/detail', | ||
89 | + icon: 'fa fa-list', | ||
90 | + classname: 'btn btn-info btn-xs btn-execute btn-dialog' | ||
91 | + } | ||
92 | + ], | ||
93 | + table: table, | ||
94 | + events: Table.api.events.operate, | ||
95 | + formatter: Table.api.formatter.operate | ||
96 | + } | ||
97 | + ] | ||
98 | + ] | ||
99 | + }); | ||
100 | + | ||
101 | + // 为表格绑定事件 | ||
102 | + Table.api.bindevent(table); | ||
103 | + }, | ||
104 | + add: function () { | ||
105 | + require(['bootstrap-select', 'bootstrap-select-lang']); | ||
106 | + var mainfields = []; | ||
107 | + var relationfields = {}; | ||
108 | + var maintable = []; | ||
109 | + var relationtable = []; | ||
110 | + var relationmode = ["belongsto", "hasone"]; | ||
111 | + | ||
112 | + var renderselect = function (select, data) { | ||
113 | + var html = []; | ||
114 | + for (var i = 0; i < data.length; i++) { | ||
115 | + html.push("<option value='" + data[i] + "'>" + data[i] + "</option>"); | ||
116 | + } | ||
117 | + $(select).html(html.join("")); | ||
118 | + select.trigger("change"); | ||
119 | + if (select.data("selectpicker")) { | ||
120 | + select.selectpicker('refresh'); | ||
121 | + } | ||
122 | + return select; | ||
123 | + }; | ||
124 | + | ||
125 | + $("select[name=table] option").each(function () { | ||
126 | + maintable.push($(this).val()); | ||
127 | + }); | ||
128 | + $(document).on('change', "input[name='isrelation']", function () { | ||
129 | + $("#relation-zone").toggleClass("hide", !$(this).prop("checked")); | ||
130 | + }); | ||
131 | + $(document).on('change', "select[name='table']", function () { | ||
132 | + var that = this; | ||
133 | + Fast.api.ajax({ | ||
134 | + url: "command/get_field_list", | ||
135 | + data: {table: $(that).val()}, | ||
136 | + }, function (data, ret) { | ||
137 | + mainfields = data.fieldlist; | ||
138 | + $("#relation-zone .relation-item").remove(); | ||
139 | + renderselect($("#fields"), mainfields); | ||
140 | + return false; | ||
141 | + }); | ||
142 | + return false; | ||
143 | + }); | ||
144 | + $(document).on('click', "a.btn-newrelation", function () { | ||
145 | + var that = this; | ||
146 | + var index = parseInt($(that).data("index")) + 1; | ||
147 | + var content = Template("relationtpl", {index: index}); | ||
148 | + content = $(content.replace(/\[index\]/, index)); | ||
149 | + $(this).data("index", index); | ||
150 | + $(content).insertBefore($(that).closest(".row")); | ||
151 | + $('select', content).selectpicker(); | ||
152 | + var exists = [$("select[name='table']").val()]; | ||
153 | + $("select.relationtable").each(function () { | ||
154 | + exists.push($(this).val()); | ||
155 | + }); | ||
156 | + relationtable = []; | ||
157 | + $.each(maintable, function (i, j) { | ||
158 | + if ($.inArray(j, exists) < 0) { | ||
159 | + relationtable.push(j); | ||
160 | + } | ||
161 | + }); | ||
162 | + renderselect($("select.relationtable", content), relationtable); | ||
163 | + $("select.relationtable", content).trigger("change"); | ||
164 | + }); | ||
165 | + $(document).on('click', "a.btn-removerelation", function () { | ||
166 | + $(this).closest(".row").remove(); | ||
167 | + }); | ||
168 | + $(document).on('change', "#relation-zone select.relationmode", function () { | ||
169 | + var table = $("select.relationtable", $(this).closest(".row")).val(); | ||
170 | + var that = this; | ||
171 | + Fast.api.ajax({ | ||
172 | + url: "command/get_field_list", | ||
173 | + data: {table: table}, | ||
174 | + }, function (data, ret) { | ||
175 | + renderselect($(that).closest(".row").find("select.relationprimarykey"), $(that).val() == 'belongsto' ? data.fieldlist : mainfields); | ||
176 | + renderselect($(that).closest(".row").find("select.relationforeignkey"), $(that).val() == 'hasone' ? data.fieldlist : mainfields); | ||
177 | + return false; | ||
178 | + }); | ||
179 | + }); | ||
180 | + $(document).on('change', "#relation-zone select.relationtable", function () { | ||
181 | + var that = this; | ||
182 | + Fast.api.ajax({ | ||
183 | + url: "command/get_field_list", | ||
184 | + data: {table: $(that).val()}, | ||
185 | + }, function (data, ret) { | ||
186 | + renderselect($(that).closest(".row").find("select.relationmode"), relationmode); | ||
187 | + renderselect($(that).closest(".row").find("select.relationfields"), mainfields) | ||
188 | + renderselect($(that).closest(".row").find("select.relationforeignkey"), data.fieldlist) | ||
189 | + renderselect($(that).closest(".row").find("select.relationfields"), data.fieldlist) | ||
190 | + return false; | ||
191 | + }); | ||
192 | + }); | ||
193 | + $(document).on('click', ".btn-command", function () { | ||
194 | + var form = $(this).closest("form"); | ||
195 | + var textarea = $("textarea[rel=command]", form); | ||
196 | + textarea.val(''); | ||
197 | + Fast.api.ajax({ | ||
198 | + url: "command/command/action/command", | ||
199 | + data: form.serialize(), | ||
200 | + }, function (data, ret) { | ||
201 | + textarea.val(data.command); | ||
202 | + return false; | ||
203 | + }); | ||
204 | + }); | ||
205 | + $(document).on('click', ".btn-execute", function () { | ||
206 | + var form = $(this).closest("form"); | ||
207 | + var textarea = $("textarea[rel=result]", form); | ||
208 | + textarea.val(''); | ||
209 | + Fast.api.ajax({ | ||
210 | + url: "command/command/action/execute", | ||
211 | + data: form.serialize(), | ||
212 | + }, function (data, ret) { | ||
213 | + textarea.val(data.result); | ||
214 | + window.parent.$(".toolbar .btn-refresh").trigger('click'); | ||
215 | + top.window.Fast.api.refreshmenu(); | ||
216 | + return false; | ||
217 | + }, function () { | ||
218 | + window.parent.$(".toolbar .btn-refresh").trigger('click'); | ||
219 | + }); | ||
220 | + }); | ||
221 | + $("select[name='table']").trigger("change"); | ||
222 | + Controller.api.bindevent(); | ||
223 | + }, | ||
224 | + edit: function () { | ||
225 | + Controller.api.bindevent(); | ||
226 | + }, | ||
227 | + api: { | ||
228 | + bindevent: function () { | ||
229 | + Form.api.bindevent($("form[role=form]")); | ||
230 | + } | ||
231 | + } | ||
232 | + }; | ||
233 | + return Controller; | ||
234 | +}); |
application/.htaccess
0 → 100644
1 | +deny from all |
application/admin/behavior/AdminLog.php
0 → 100644
application/admin/command/Addon.php
0 → 100644
1 | +<?php | ||
2 | + | ||
3 | +namespace app\admin\command; | ||
4 | + | ||
5 | +use think\addons\AddonException; | ||
6 | +use think\addons\Service; | ||
7 | +use think\Config; | ||
8 | +use think\console\Command; | ||
9 | +use think\console\Input; | ||
10 | +use think\console\input\Option; | ||
11 | +use think\console\Output; | ||
12 | +use think\Db; | ||
13 | +use think\Exception; | ||
14 | +use think\exception\PDOException; | ||
15 | + | ||
16 | +class Addon extends Command | ||
17 | +{ | ||
18 | + | ||
19 | + protected function configure() | ||
20 | + { | ||
21 | + $this | ||
22 | + ->setName('addon') | ||
23 | + ->addOption('name', 'a', Option::VALUE_REQUIRED, 'addon name', null) | ||
24 | + ->addOption('action', 'c', Option::VALUE_REQUIRED, 'action(create/enable/disable/install/uninstall/refresh/upgrade/package)', 'create') | ||
25 | + ->addOption('force', 'f', Option::VALUE_OPTIONAL, 'force override', null) | ||
26 | + ->addOption('release', 'r', Option::VALUE_OPTIONAL, 'addon release version', null) | ||
27 | + ->addOption('uid', 'u', Option::VALUE_OPTIONAL, 'fastadmin uid', null) | ||
28 | + ->addOption('token', 't', Option::VALUE_OPTIONAL, 'fastadmin token', null) | ||
29 | + ->addOption('local', 'l', Option::VALUE_OPTIONAL, 'local package', null) | ||
30 | + ->setDescription('Addon manager'); | ||
31 | + } | ||
32 | + | ||
33 | + protected function execute(Input $input, Output $output) | ||
34 | + { | ||
35 | + $name = $input->getOption('name') ?: ''; | ||
36 | + $action = $input->getOption('action') ?: ''; | ||
37 | + if (stripos($name, 'addons/') !== false) { | ||
38 | + $name = explode('/', $name)[1]; | ||
39 | + } | ||
40 | + //强制覆盖 | ||
41 | + $force = $input->getOption('force'); | ||
42 | + //版本 | ||
43 | + $release = $input->getOption('release') ?: ''; | ||
44 | + //uid | ||
45 | + $uid = $input->getOption('uid') ?: ''; | ||
46 | + //token | ||
47 | + $token = $input->getOption('token') ?: ''; | ||
48 | + | ||
49 | + include dirname(__DIR__) . DS . 'common.php'; | ||
50 | + | ||
51 | + if (!$name) { | ||
52 | + throw new Exception('Addon name could not be empty'); | ||
53 | + } | ||
54 | + if (!$action || !in_array($action, ['create', 'disable', 'enable', 'install', 'uninstall', 'refresh', 'upgrade', 'package'])) { | ||
55 | + throw new Exception('Please input correct action name'); | ||
56 | + } | ||
57 | + | ||
58 | + // 查询一次SQL,判断连接是否正常 | ||
59 | + Db::execute("SELECT 1"); | ||
60 | + | ||
61 | + $addonDir = ADDON_PATH . $name . DS; | ||
62 | + switch ($action) { | ||
63 | + case 'create': | ||
64 | + //非覆盖模式时如果存在则报错 | ||
65 | + if (is_dir($addonDir) && !$force) { | ||
66 | + throw new Exception("addon already exists!\nIf you need to create again, use the parameter --force=true "); | ||
67 | + } | ||
68 | + //如果存在先移除 | ||
69 | + if (is_dir($addonDir)) { | ||
70 | + rmdirs($addonDir); | ||
71 | + } | ||
72 | + mkdir($addonDir, 0755, true); | ||
73 | + mkdir($addonDir . DS . 'controller', 0755, true); | ||
74 | + $menuList = \app\common\library\Menu::export($name); | ||
75 | + $createMenu = $this->getCreateMenu($menuList); | ||
76 | + $prefix = Config::get('database.prefix'); | ||
77 | + $createTableSql = ''; | ||
78 | + try { | ||
79 | + $result = Db::query("SHOW CREATE TABLE `" . $prefix . $name . "`;"); | ||
80 | + if (isset($result[0]) && isset($result[0]['Create Table'])) { | ||
81 | + $createTableSql = $result[0]['Create Table']; | ||
82 | + } | ||
83 | + } catch (PDOException $e) { | ||
84 | + | ||
85 | + } | ||
86 | + | ||
87 | + $data = [ | ||
88 | + 'name' => $name, | ||
89 | + 'addon' => $name, | ||
90 | + 'addonClassName' => ucfirst($name), | ||
91 | + 'addonInstallMenu' => $createMenu ? "\$menu = " . var_export_short($createMenu, "\t") . ";\n\tMenu::create(\$menu);" : '', | ||
92 | + 'addonUninstallMenu' => $menuList ? 'Menu::delete("' . $name . '");' : '', | ||
93 | + 'addonEnableMenu' => $menuList ? 'Menu::enable("' . $name . '");' : '', | ||
94 | + 'addonDisableMenu' => $menuList ? 'Menu::disable("' . $name . '");' : '', | ||
95 | + ]; | ||
96 | + $this->writeToFile("addon", $data, $addonDir . ucfirst($name) . '.php'); | ||
97 | + $this->writeToFile("config", $data, $addonDir . 'config.php'); | ||
98 | + $this->writeToFile("info", $data, $addonDir . 'info.ini'); | ||
99 | + $this->writeToFile("controller", $data, $addonDir . 'controller' . DS . 'Index.php'); | ||
100 | + if ($createTableSql) { | ||
101 | + $createTableSql = str_replace("`" . $prefix, '`__PREFIX__', $createTableSql); | ||
102 | + file_put_contents($addonDir . 'install.sql', $createTableSql); | ||
103 | + } | ||
104 | + | ||
105 | + $output->info("Create Successed!"); | ||
106 | + break; | ||
107 | + case 'disable': | ||
108 | + case 'enable': | ||
109 | + try { | ||
110 | + //调用启用、禁用的方法 | ||
111 | + Service::$action($name, 0); | ||
112 | + } catch (AddonException $e) { | ||
113 | + if ($e->getCode() != -3) { | ||
114 | + throw new Exception($e->getMessage()); | ||
115 | + } | ||
116 | + if (!$force) { | ||
117 | + //如果有冲突文件则提醒 | ||
118 | + $data = $e->getData(); | ||
119 | + foreach ($data['conflictlist'] as $k => $v) { | ||
120 | + $output->warning($v); | ||
121 | + } | ||
122 | + $output->info("Are you sure you want to " . ($action == 'enable' ? 'override' : 'delete') . " all those files? Type 'yes' to continue: "); | ||
123 | + $line = fgets(defined('STDIN') ? STDIN : fopen('php://stdin', 'r')); | ||
124 | + if (trim($line) != 'yes') { | ||
125 | + throw new Exception("Operation is aborted!"); | ||
126 | + } | ||
127 | + } | ||
128 | + //调用启用、禁用的方法 | ||
129 | + Service::$action($name, 1); | ||
130 | + } catch (Exception $e) { | ||
131 | + throw new Exception($e->getMessage()); | ||
132 | + } | ||
133 | + $output->info(ucfirst($action) . " Successed!"); | ||
134 | + break; | ||
135 | + case 'install': | ||
136 | + //非覆盖模式时如果存在则报错 | ||
137 | + if (is_dir($addonDir) && !$force) { | ||
138 | + throw new Exception("addon already exists!\nIf you need to install again, use the parameter --force=true "); | ||
139 | + } | ||
140 | + //如果存在先移除 | ||
141 | + if (is_dir($addonDir)) { | ||
142 | + rmdirs($addonDir); | ||
143 | + } | ||
144 | + // 获取本地路径 | ||
145 | + $local = $input->getOption('local'); | ||
146 | + try { | ||
147 | + Service::install($name, 0, ['version' => $release], $local); | ||
148 | + } catch (AddonException $e) { | ||
149 | + if ($e->getCode() != -3) { | ||
150 | + throw new Exception($e->getMessage()); | ||
151 | + } | ||
152 | + if (!$force) { | ||
153 | + //如果有冲突文件则提醒 | ||
154 | + $data = $e->getData(); | ||
155 | + foreach ($data['conflictlist'] as $k => $v) { | ||
156 | + $output->warning($v); | ||
157 | + } | ||
158 | + $output->info("Are you sure you want to override all those files? Type 'yes' to continue: "); | ||
159 | + $line = fgets(defined('STDIN') ? STDIN : fopen('php://stdin', 'r')); | ||
160 | + if (trim($line) != 'yes') { | ||
161 | + throw new Exception("Operation is aborted!"); | ||
162 | + } | ||
163 | + } | ||
164 | + Service::install($name, 1, ['version' => $release, 'uid' => $uid, 'token' => $token], $local); | ||
165 | + } catch (Exception $e) { | ||
166 | + throw new Exception($e->getMessage()); | ||
167 | + } | ||
168 | + | ||
169 | + $output->info("Install Successed!"); | ||
170 | + break; | ||
171 | + case 'uninstall': | ||
172 | + //非覆盖模式时如果存在则报错 | ||
173 | + if (!$force) { | ||
174 | + throw new Exception("If you need to uninstall addon, use the parameter --force=true "); | ||
175 | + } | ||
176 | + try { | ||
177 | + Service::uninstall($name, 0); | ||
178 | + } catch (AddonException $e) { | ||
179 | + if ($e->getCode() != -3) { | ||
180 | + throw new Exception($e->getMessage()); | ||
181 | + } | ||
182 | + if (!$force) { | ||
183 | + //如果有冲突文件则提醒 | ||
184 | + $data = $e->getData(); | ||
185 | + foreach ($data['conflictlist'] as $k => $v) { | ||
186 | + $output->warning($v); | ||
187 | + } | ||
188 | + $output->info("Are you sure you want to delete all those files? Type 'yes' to continue: "); | ||
189 | + $line = fgets(defined('STDIN') ? STDIN : fopen('php://stdin', 'r')); | ||
190 | + if (trim($line) != 'yes') { | ||
191 | + throw new Exception("Operation is aborted!"); | ||
192 | + } | ||
193 | + } | ||
194 | + Service::uninstall($name, 1); | ||
195 | + } catch (Exception $e) { | ||
196 | + throw new Exception($e->getMessage()); | ||
197 | + } | ||
198 | + | ||
199 | + $output->info("Uninstall Successed!"); | ||
200 | + break; | ||
201 | + case 'refresh': | ||
202 | + Service::refresh(); | ||
203 | + $output->info("Refresh Successed!"); | ||
204 | + break; | ||
205 | + case 'upgrade': | ||
206 | + Service::upgrade($name, ['version' => $release, 'uid' => $uid, 'token' => $token]); | ||
207 | + $output->info("Upgrade Successed!"); | ||
208 | + break; | ||
209 | + case 'package': | ||
210 | + $infoFile = $addonDir . 'info.ini'; | ||
211 | + if (!is_file($infoFile)) { | ||
212 | + throw new Exception(__('Addon info file was not found')); | ||
213 | + } | ||
214 | + | ||
215 | + $info = get_addon_info($name); | ||
216 | + if (!$info) { | ||
217 | + throw new Exception(__('Addon info file data incorrect')); | ||
218 | + } | ||
219 | + $infoname = isset($info['name']) ? $info['name'] : ''; | ||
220 | + if (!$infoname || !preg_match("/^[a-z]+$/i", $infoname) || $infoname != $name) { | ||
221 | + throw new Exception(__('Addon info name incorrect')); | ||
222 | + } | ||
223 | + | ||
224 | + $infoversion = isset($info['version']) ? $info['version'] : ''; | ||
225 | + if (!$infoversion || !preg_match("/^\d+\.\d+\.\d+$/i", $infoversion)) { | ||
226 | + throw new Exception(__('Addon info version incorrect')); | ||
227 | + } | ||
228 | + | ||
229 | + $addonTmpDir = RUNTIME_PATH . 'addons' . DS; | ||
230 | + if (!is_dir($addonTmpDir)) { | ||
231 | + @mkdir($addonTmpDir, 0755, true); | ||
232 | + } | ||
233 | + $addonFile = $addonTmpDir . $infoname . '-' . $infoversion . '.zip'; | ||
234 | + if (!class_exists('ZipArchive')) { | ||
235 | + throw new Exception(__('ZinArchive not install')); | ||
236 | + } | ||
237 | + $zip = new \ZipArchive; | ||
238 | + $zip->open($addonFile, \ZipArchive::CREATE | \ZipArchive::OVERWRITE); | ||
239 | + | ||
240 | + $files = new \RecursiveIteratorIterator( | ||
241 | + new \RecursiveDirectoryIterator($addonDir), \RecursiveIteratorIterator::LEAVES_ONLY | ||
242 | + ); | ||
243 | + | ||
244 | + foreach ($files as $name => $file) { | ||
245 | + if (!$file->isDir()) { | ||
246 | + $filePath = $file->getRealPath(); | ||
247 | + $relativePath = str_replace(DS, '/', substr($filePath, strlen($addonDir))); | ||
248 | + if (!in_array($file->getFilename(), ['.git', '.DS_Store', 'Thumbs.db'])) { | ||
249 | + $zip->addFile($filePath, $relativePath); | ||
250 | + } | ||
251 | + } | ||
252 | + } | ||
253 | + $zip->close(); | ||
254 | + $output->info("Package Successed!"); | ||
255 | + break; | ||
256 | + | ||
257 | + default : | ||
258 | + break; | ||
259 | + } | ||
260 | + } | ||
261 | + | ||
262 | + /** | ||
263 | + * 获取创建菜单的数组 | ||
264 | + * @param array $menu | ||
265 | + * @return array | ||
266 | + */ | ||
267 | + protected function getCreateMenu($menu) | ||
268 | + { | ||
269 | + $result = []; | ||
270 | + foreach ($menu as $k => & $v) { | ||
271 | + $arr = [ | ||
272 | + 'name' => $v['name'], | ||
273 | + 'title' => $v['title'], | ||
274 | + ]; | ||
275 | + if ($v['icon'] != 'fa fa-circle-o') { | ||
276 | + $arr['icon'] = $v['icon']; | ||
277 | + } | ||
278 | + if ($v['ismenu']) { | ||
279 | + $arr['ismenu'] = $v['ismenu']; | ||
280 | + } | ||
281 | + if (isset($v['childlist']) && $v['childlist']) { | ||
282 | + $arr['sublist'] = $this->getCreateMenu($v['childlist']); | ||
283 | + } | ||
284 | + $result[] = $arr; | ||
285 | + } | ||
286 | + return $result; | ||
287 | + } | ||
288 | + | ||
289 | + /** | ||
290 | + * 写入到文件 | ||
291 | + * @param string $name | ||
292 | + * @param array $data | ||
293 | + * @param string $pathname | ||
294 | + * @return mixed | ||
295 | + */ | ||
296 | + protected function writeToFile($name, $data, $pathname) | ||
297 | + { | ||
298 | + $search = $replace = []; | ||
299 | + foreach ($data as $k => $v) { | ||
300 | + $search[] = "{%{$k}%}"; | ||
301 | + $replace[] = $v; | ||
302 | + } | ||
303 | + $stub = file_get_contents($this->getStub($name)); | ||
304 | + $content = str_replace($search, $replace, $stub); | ||
305 | + | ||
306 | + if (!is_dir(dirname($pathname))) { | ||
307 | + mkdir(strtolower(dirname($pathname)), 0755, true); | ||
308 | + } | ||
309 | + return file_put_contents($pathname, $content); | ||
310 | + } | ||
311 | + | ||
312 | + /** | ||
313 | + * 获取基础模板 | ||
314 | + * @param string $name | ||
315 | + * @return string | ||
316 | + */ | ||
317 | + protected function getStub($name) | ||
318 | + { | ||
319 | + return __DIR__ . '/Addon/stubs/' . $name . '.stub'; | ||
320 | + } | ||
321 | + | ||
322 | +} |
1 | +<?php | ||
2 | + | ||
3 | +namespace addons\{%name%}; | ||
4 | + | ||
5 | +use app\common\library\Menu; | ||
6 | +use think\Addons; | ||
7 | + | ||
8 | +/** | ||
9 | + * 插件 | ||
10 | + */ | ||
11 | +class {%addonClassName%} extends Addons | ||
12 | +{ | ||
13 | + | ||
14 | + /** | ||
15 | + * 插件安装方法 | ||
16 | + * @return bool | ||
17 | + */ | ||
18 | + public function install() | ||
19 | + { | ||
20 | + {%addonInstallMenu%} | ||
21 | + return true; | ||
22 | + } | ||
23 | + | ||
24 | + /** | ||
25 | + * 插件卸载方法 | ||
26 | + * @return bool | ||
27 | + */ | ||
28 | + public function uninstall() | ||
29 | + { | ||
30 | + {%addonUninstallMenu%} | ||
31 | + return true; | ||
32 | + } | ||
33 | + | ||
34 | + /** | ||
35 | + * 插件启用方法 | ||
36 | + * @return bool | ||
37 | + */ | ||
38 | + public function enable() | ||
39 | + { | ||
40 | + {%addonEnableMenu%} | ||
41 | + return true; | ||
42 | + } | ||
43 | + | ||
44 | + /** | ||
45 | + * 插件禁用方法 | ||
46 | + * @return bool | ||
47 | + */ | ||
48 | + public function disable() | ||
49 | + { | ||
50 | + {%addonDisableMenu%} | ||
51 | + return true; | ||
52 | + } | ||
53 | + | ||
54 | + /** | ||
55 | + * 实现钩子方法 | ||
56 | + * @return mixed | ||
57 | + */ | ||
58 | + public function testhook($param) | ||
59 | + { | ||
60 | + // 调用钩子时候的参数信息 | ||
61 | + print_r($param); | ||
62 | + // 当前插件的配置信息,配置信息存在当前目录的config.php文件中,见下方 | ||
63 | + print_r($this->getConfig()); | ||
64 | + // 可以返回模板,模板文件默认读取的为插件目录中的文件。模板名不能为空! | ||
65 | + //return $this->fetch('view/info'); | ||
66 | + } | ||
67 | + | ||
68 | +} |
1 | +<?php | ||
2 | + | ||
3 | +return [ | ||
4 | + [ | ||
5 | + //配置唯一标识 | ||
6 | + 'name' => 'usernmae', | ||
7 | + //显示的标题 | ||
8 | + 'title' => '用户名', | ||
9 | + //类型 | ||
10 | + 'type' => 'string', | ||
11 | + //数据字典 | ||
12 | + 'content' => [ | ||
13 | + ], | ||
14 | + //值 | ||
15 | + 'value' => '', | ||
16 | + //验证规则 | ||
17 | + 'rule' => 'required', | ||
18 | + //错误消息 | ||
19 | + 'msg' => '', | ||
20 | + //提示消息 | ||
21 | + 'tip' => '', | ||
22 | + //成功消息 | ||
23 | + 'ok' => '', | ||
24 | + //扩展信息 | ||
25 | + 'extend' => '' | ||
26 | + ], | ||
27 | + [ | ||
28 | + 'name' => 'password', | ||
29 | + 'title' => '密码', | ||
30 | + 'type' => 'string', | ||
31 | + 'content' => [ | ||
32 | + ], | ||
33 | + 'value' => '', | ||
34 | + 'rule' => 'required', | ||
35 | + 'msg' => '', | ||
36 | + 'tip' => '', | ||
37 | + 'ok' => '', | ||
38 | + 'extend' => '' | ||
39 | + ], | ||
40 | +]; |
application/admin/command/Api.php
0 → 100644
1 | +<?php | ||
2 | + | ||
3 | +namespace app\admin\command; | ||
4 | + | ||
5 | +use app\admin\command\Api\library\Builder; | ||
6 | +use think\Config; | ||
7 | +use think\console\Command; | ||
8 | +use think\console\Input; | ||
9 | +use think\console\input\Option; | ||
10 | +use think\console\Output; | ||
11 | +use think\Exception; | ||
12 | + | ||
13 | +class Api extends Command | ||
14 | +{ | ||
15 | + protected function configure() | ||
16 | + { | ||
17 | + $site = Config::get('site'); | ||
18 | + $this | ||
19 | + ->setName('api') | ||
20 | + ->addOption('url', 'u', Option::VALUE_OPTIONAL, 'default api url', '') | ||
21 | + ->addOption('module', 'm', Option::VALUE_OPTIONAL, 'module name(admin/index/api)', 'api') | ||
22 | + ->addOption('output', 'o', Option::VALUE_OPTIONAL, 'output index file name', 'api.html') | ||
23 | + ->addOption('template', 'e', Option::VALUE_OPTIONAL, '', 'index.html') | ||
24 | + ->addOption('force', 'f', Option::VALUE_OPTIONAL, 'force override general file', false) | ||
25 | + ->addOption('title', 't', Option::VALUE_OPTIONAL, 'document title', $site['name']) | ||
26 | + ->addOption('author', 'a', Option::VALUE_OPTIONAL, 'document author', $site['name']) | ||
27 | + ->addOption('class', 'c', Option::VALUE_OPTIONAL | Option::VALUE_IS_ARRAY, 'extend class', null) | ||
28 | + ->addOption('language', 'l', Option::VALUE_OPTIONAL, 'language', 'zh-cn') | ||
29 | + ->setDescription('Build Api document from controller'); | ||
30 | + } | ||
31 | + | ||
32 | + protected function execute(Input $input, Output $output) | ||
33 | + { | ||
34 | + $apiDir = __DIR__ . DS . 'Api' . DS; | ||
35 | + | ||
36 | + $force = $input->getOption('force'); | ||
37 | + $url = $input->getOption('url'); | ||
38 | + $language = $input->getOption('language'); | ||
39 | + $language = $language ? $language : 'zh-cn'; | ||
40 | + $langFile = $apiDir . 'lang' . DS . $language . '.php'; | ||
41 | + if (!is_file($langFile)) { | ||
42 | + throw new Exception('language file not found'); | ||
43 | + } | ||
44 | + $lang = include_once $langFile; | ||
45 | + // 目标目录 | ||
46 | + $output_dir = ROOT_PATH . 'public' . DS; | ||
47 | + $output_file = $output_dir . $input->getOption('output'); | ||
48 | + if (is_file($output_file) && !$force) { | ||
49 | + throw new Exception("api index file already exists!\nIf you need to rebuild again, use the parameter --force=true "); | ||
50 | + } | ||
51 | + // 模板文件 | ||
52 | + $template_dir = $apiDir . 'template' . DS; | ||
53 | + $template_file = $template_dir . $input->getOption('template'); | ||
54 | + if (!is_file($template_file)) { | ||
55 | + throw new Exception('template file not found'); | ||
56 | + } | ||
57 | + // 额外的类 | ||
58 | + $classes = $input->getOption('class'); | ||
59 | + // 标题 | ||
60 | + $title = $input->getOption('title'); | ||
61 | + // 作者 | ||
62 | + $author = $input->getOption('author'); | ||
63 | + // 模块 | ||
64 | + $module = $input->getOption('module'); | ||
65 | + | ||
66 | + $moduleDir = APP_PATH . $module . DS; | ||
67 | + if (!is_dir($moduleDir)) { | ||
68 | + throw new Exception('module not found'); | ||
69 | + } | ||
70 | + | ||
71 | + if (version_compare(PHP_VERSION, '7.0.0', '<')) { | ||
72 | + if (extension_loaded('Zend OPcache')) { | ||
73 | + $configuration = opcache_get_configuration(); | ||
74 | + $directives = $configuration['directives']; | ||
75 | + $configName = request()->isCli() ? 'opcache.enable_cli' : 'opcache.enable'; | ||
76 | + if (!$directives[$configName]) { | ||
77 | + throw new Exception("Please make sure {$configName} is turned on, Get help:https://forum.fastadmin.net/d/1321"); | ||
78 | + } | ||
79 | + } else { | ||
80 | + throw new Exception("Please make sure opcache already enabled, Get help:https://forum.fastadmin.net/d/1321"); | ||
81 | + } | ||
82 | + } | ||
83 | + | ||
84 | + $controllerDir = $moduleDir . Config::get('url_controller_layer') . DS; | ||
85 | + $files = new \RecursiveIteratorIterator( | ||
86 | + new \RecursiveDirectoryIterator($controllerDir), | ||
87 | + \RecursiveIteratorIterator::LEAVES_ONLY | ||
88 | + ); | ||
89 | + | ||
90 | + foreach ($files as $name => $file) { | ||
91 | + if (!$file->isDir() && $file->getExtension() == 'php') { | ||
92 | + $filePath = $file->getRealPath(); | ||
93 | + $classes[] = $this->get_class_from_file($filePath); | ||
94 | + } | ||
95 | + } | ||
96 | + $classes = array_unique(array_filter($classes)); | ||
97 | + | ||
98 | + $config = [ | ||
99 | + 'title' => $title, | ||
100 | + 'author' => $author, | ||
101 | + 'description' => '', | ||
102 | + 'apiurl' => $url, | ||
103 | + 'language' => $language, | ||
104 | + ]; | ||
105 | + $builder = new Builder($classes); | ||
106 | + $content = $builder->render($template_file, ['config' => $config, 'lang' => $lang]); | ||
107 | + | ||
108 | + if (!file_put_contents($output_file, $content)) { | ||
109 | + throw new Exception('Cannot save the content to ' . $output_file); | ||
110 | + } | ||
111 | + $output->info("Build Successed!"); | ||
112 | + } | ||
113 | + | ||
114 | + /** | ||
115 | + * get full qualified class name | ||
116 | + * | ||
117 | + * @param string $path_to_file | ||
118 | + * @author JBYRNE http://jarretbyrne.com/2015/06/197/ | ||
119 | + * @return string | ||
120 | + */ | ||
121 | + protected function get_class_from_file($path_to_file) | ||
122 | + { | ||
123 | + //Grab the contents of the file | ||
124 | + $contents = file_get_contents($path_to_file); | ||
125 | + | ||
126 | + //Start with a blank namespace and class | ||
127 | + $namespace = $class = ""; | ||
128 | + | ||
129 | + //Set helper values to know that we have found the namespace/class token and need to collect the string values after them | ||
130 | + $getting_namespace = $getting_class = false; | ||
131 | + | ||
132 | + //Go through each token and evaluate it as necessary | ||
133 | + foreach (token_get_all($contents) as $token) { | ||
134 | + | ||
135 | + //If this token is the namespace declaring, then flag that the next tokens will be the namespace name | ||
136 | + if (is_array($token) && $token[0] == T_NAMESPACE) { | ||
137 | + $getting_namespace = true; | ||
138 | + } | ||
139 | + | ||
140 | + //If this token is the class declaring, then flag that the next tokens will be the class name | ||
141 | + if (is_array($token) && $token[0] == T_CLASS) { | ||
142 | + $getting_class = true; | ||
143 | + } | ||
144 | + | ||
145 | + //While we're grabbing the namespace name... | ||
146 | + if ($getting_namespace === true) { | ||
147 | + | ||
148 | + //If the token is a string or the namespace separator... | ||
149 | + if (is_array($token) && in_array($token[0], [T_STRING, T_NS_SEPARATOR])) { | ||
150 | + | ||
151 | + //Append the token's value to the name of the namespace | ||
152 | + $namespace .= $token[1]; | ||
153 | + } elseif ($token === ';') { | ||
154 | + | ||
155 | + //If the token is the semicolon, then we're done with the namespace declaration | ||
156 | + $getting_namespace = false; | ||
157 | + } | ||
158 | + } | ||
159 | + | ||
160 | + //While we're grabbing the class name... | ||
161 | + if ($getting_class === true) { | ||
162 | + | ||
163 | + //If the token is a string, it's the name of the class | ||
164 | + if (is_array($token) && $token[0] == T_STRING) { | ||
165 | + | ||
166 | + //Store the token's value as the class name | ||
167 | + $class = $token[1]; | ||
168 | + | ||
169 | + //Got what we need, stope here | ||
170 | + break; | ||
171 | + } | ||
172 | + } | ||
173 | + } | ||
174 | + | ||
175 | + //Build the fully-qualified class name and return it | ||
176 | + return $namespace ? $namespace . '\\' . $class : $class; | ||
177 | + } | ||
178 | +} |
application/admin/command/Api/lang/zh-cn.php
0 → 100644
1 | +<?php | ||
2 | + | ||
3 | +return [ | ||
4 | + 'Info' => '基础信息', | ||
5 | + 'Sandbox' => '在线测试', | ||
6 | + 'Sampleoutput' => '返回示例', | ||
7 | + 'Headers' => 'Headers', | ||
8 | + 'Parameters' => '参数', | ||
9 | + 'Body' => '正文', | ||
10 | + 'Name' => '名称', | ||
11 | + 'Type' => '类型', | ||
12 | + 'Required' => '必选', | ||
13 | + 'Description' => '描述', | ||
14 | + 'Send' => '提交', | ||
15 | + 'Reset' => '重置', | ||
16 | + 'Tokentips' => 'Token在会员注册或登录后都会返回,WEB端同时存在于Cookie中', | ||
17 | + 'Apiurltips' => 'API接口URL', | ||
18 | + 'Savetips' => '点击保存后Token和Api url都将保存在本地Localstorage中', | ||
19 | + 'ReturnHeaders' => '响应头', | ||
20 | + 'ReturnParameters' => '返回参数', | ||
21 | + 'Response' => '响应输出', | ||
22 | +]; |
1 | +<?php | ||
2 | + | ||
3 | +namespace app\admin\command\Api\library; | ||
4 | + | ||
5 | +use think\Config; | ||
6 | + | ||
7 | +/** | ||
8 | + * @website https://github.com/calinrada/php-apidoc | ||
9 | + * @author Calin Rada <rada.calin@gmail.com> | ||
10 | + * @author Karson <karsonzhang@163.com> | ||
11 | + */ | ||
12 | +class Builder | ||
13 | +{ | ||
14 | + | ||
15 | + /** | ||
16 | + * | ||
17 | + * @var \think\View | ||
18 | + */ | ||
19 | + public $view = null; | ||
20 | + | ||
21 | + /** | ||
22 | + * parse classes | ||
23 | + * @var array | ||
24 | + */ | ||
25 | + protected $classes = []; | ||
26 | + | ||
27 | + /** | ||
28 | + * | ||
29 | + * @param array $classes | ||
30 | + */ | ||
31 | + public function __construct($classes = []) | ||
32 | + { | ||
33 | + $this->classes = array_merge($this->classes, $classes); | ||
34 | + $this->view = new \think\View(Config::get('template'), Config::get('view_replace_str')); | ||
35 | + } | ||
36 | + | ||
37 | + protected function extractAnnotations() | ||
38 | + { | ||
39 | + foreach ($this->classes as $class) { | ||
40 | + $classAnnotation = Extractor::getClassAnnotations($class); | ||
41 | + // 如果忽略 | ||
42 | + if (isset($classAnnotation['ApiInternal'])) { | ||
43 | + continue; | ||
44 | + } | ||
45 | + Extractor::getClassMethodAnnotations($class); | ||
46 | + } | ||
47 | + $allClassAnnotation = Extractor::getAllClassAnnotations(); | ||
48 | + $allClassMethodAnnotation = Extractor::getAllClassMethodAnnotations(); | ||
49 | + | ||
50 | +// foreach ($allClassMethodAnnotation as $className => &$methods) { | ||
51 | +// foreach ($methods as &$method) { | ||
52 | +// //权重判断 | ||
53 | +// if ($method && !isset($method['ApiWeigh']) && isset($allClassAnnotation[$className]['ApiWeigh'])) { | ||
54 | +// $method['ApiWeigh'] = $allClassAnnotation[$className]['ApiWeigh']; | ||
55 | +// } | ||
56 | +// } | ||
57 | +// } | ||
58 | +// unset($methods); | ||
59 | + return [$allClassAnnotation, $allClassMethodAnnotation]; | ||
60 | + } | ||
61 | + | ||
62 | + protected function generateHeadersTemplate($docs) | ||
63 | + { | ||
64 | + if (!isset($docs['ApiHeaders'])) { | ||
65 | + return []; | ||
66 | + } | ||
67 | + | ||
68 | + $headerslist = array(); | ||
69 | + foreach ($docs['ApiHeaders'] as $params) { | ||
70 | + $tr = array( | ||
71 | + 'name' => $params['name'], | ||
72 | + 'type' => $params['type'], | ||
73 | + 'sample' => isset($params['sample']) ? $params['sample'] : '', | ||
74 | + 'required' => isset($params['required']) ? $params['required'] : false, | ||
75 | + 'description' => isset($params['description']) ? $params['description'] : '', | ||
76 | + ); | ||
77 | + $headerslist[] = $tr; | ||
78 | + } | ||
79 | + | ||
80 | + return $headerslist; | ||
81 | + } | ||
82 | + | ||
83 | + protected function generateParamsTemplate($docs) | ||
84 | + { | ||
85 | + if (!isset($docs['ApiParams'])) { | ||
86 | + return []; | ||
87 | + } | ||
88 | + | ||
89 | + $paramslist = array(); | ||
90 | + foreach ($docs['ApiParams'] as $params) { | ||
91 | + $tr = array( | ||
92 | + 'name' => $params['name'], | ||
93 | + 'type' => isset($params['type']) ? $params['type'] : 'string', | ||
94 | + 'sample' => isset($params['sample']) ? $params['sample'] : '', | ||
95 | + 'required' => isset($params['required']) ? $params['required'] : true, | ||
96 | + 'description' => isset($params['description']) ? $params['description'] : '', | ||
97 | + ); | ||
98 | + $paramslist[] = $tr; | ||
99 | + } | ||
100 | + | ||
101 | + return $paramslist; | ||
102 | + } | ||
103 | + | ||
104 | + protected function generateReturnHeadersTemplate($docs) | ||
105 | + { | ||
106 | + if (!isset($docs['ApiReturnHeaders'])) { | ||
107 | + return []; | ||
108 | + } | ||
109 | + | ||
110 | + $headerslist = array(); | ||
111 | + foreach ($docs['ApiReturnHeaders'] as $params) { | ||
112 | + $tr = array( | ||
113 | + 'name' => $params['name'], | ||
114 | + 'type' => 'string', | ||
115 | + 'sample' => isset($params['sample']) ? $params['sample'] : '', | ||
116 | + 'required' => isset($params['required']) && $params['required'] ? 'Yes' : 'No', | ||
117 | + 'description' => isset($params['description']) ? $params['description'] : '', | ||
118 | + ); | ||
119 | + $headerslist[] = $tr; | ||
120 | + } | ||
121 | + | ||
122 | + return $headerslist; | ||
123 | + } | ||
124 | + | ||
125 | + protected function generateReturnParamsTemplate($st_params) | ||
126 | + { | ||
127 | + if (!isset($st_params['ApiReturnParams'])) { | ||
128 | + return []; | ||
129 | + } | ||
130 | + | ||
131 | + $paramslist = array(); | ||
132 | + foreach ($st_params['ApiReturnParams'] as $params) { | ||
133 | + $tr = array( | ||
134 | + 'name' => $params['name'], | ||
135 | + 'type' => isset($params['type']) ? $params['type'] : 'string', | ||
136 | + 'sample' => isset($params['sample']) ? $params['sample'] : '', | ||
137 | + 'description' => isset($params['description']) ? $params['description'] : '', | ||
138 | + ); | ||
139 | + $paramslist[] = $tr; | ||
140 | + } | ||
141 | + | ||
142 | + return $paramslist; | ||
143 | + } | ||
144 | + | ||
145 | + protected function generateBadgeForMethod($data) | ||
146 | + { | ||
147 | + $method = strtoupper(is_array($data['ApiMethod'][0]) ? $data['ApiMethod'][0]['data'] : $data['ApiMethod'][0]); | ||
148 | + $labes = array( | ||
149 | + 'POST' => 'label-primary', | ||
150 | + 'GET' => 'label-success', | ||
151 | + 'PUT' => 'label-warning', | ||
152 | + 'DELETE' => 'label-danger', | ||
153 | + 'PATCH' => 'label-default', | ||
154 | + 'OPTIONS' => 'label-info' | ||
155 | + ); | ||
156 | + | ||
157 | + return isset($labes[$method]) ? $labes[$method] : $labes['GET']; | ||
158 | + } | ||
159 | + | ||
160 | + public function parse() | ||
161 | + { | ||
162 | + list($allClassAnnotations, $allClassMethodAnnotations) = $this->extractAnnotations(); | ||
163 | + | ||
164 | + $sectorArr = []; | ||
165 | + foreach ($allClassAnnotations as $index => $allClassAnnotation) { | ||
166 | + $sector = isset($allClassAnnotation['ApiSector']) ? $allClassAnnotation['ApiSector'][0] : $allClassAnnotation['ApiTitle'][0]; | ||
167 | + $sectorArr[$sector] = isset($allClassAnnotation['ApiWeigh']) ? $allClassAnnotation['ApiWeigh'][0] : 0; | ||
168 | + } | ||
169 | + arsort($sectorArr); | ||
170 | + $routes = include_once CONF_PATH . 'route.php'; | ||
171 | + $subdomain = false; | ||
172 | + if (config('url_domain_deploy') && isset($routes['__domain__']) && isset($routes['__domain__']['api']) && $routes['__domain__']['api']) { | ||
173 | + $subdomain = true; | ||
174 | + } | ||
175 | + $counter = 0; | ||
176 | + $section = null; | ||
177 | + $weigh = 0; | ||
178 | + $docslist = []; | ||
179 | + foreach ($allClassMethodAnnotations as $class => $methods) { | ||
180 | + foreach ($methods as $name => $docs) { | ||
181 | + if (isset($docs['ApiSector'][0])) { | ||
182 | + $section = is_array($docs['ApiSector'][0]) ? $docs['ApiSector'][0]['data'] : $docs['ApiSector'][0]; | ||
183 | + } else { | ||
184 | + $section = $class; | ||
185 | + } | ||
186 | + if (0 === count($docs)) { | ||
187 | + continue; | ||
188 | + } | ||
189 | + $route = is_array($docs['ApiRoute'][0]) ? $docs['ApiRoute'][0]['data'] : $docs['ApiRoute'][0]; | ||
190 | + if ($subdomain) { | ||
191 | + $route = substr($route, 4); | ||
192 | + } | ||
193 | + $docslist[$section][$name] = [ | ||
194 | + 'id' => $counter, | ||
195 | + 'method' => is_array($docs['ApiMethod'][0]) ? $docs['ApiMethod'][0]['data'] : $docs['ApiMethod'][0], | ||
196 | + 'method_label' => $this->generateBadgeForMethod($docs), | ||
197 | + 'section' => $section, | ||
198 | + 'route' => $route, | ||
199 | + 'title' => is_array($docs['ApiTitle'][0]) ? $docs['ApiTitle'][0]['data'] : $docs['ApiTitle'][0], | ||
200 | + 'summary' => is_array($docs['ApiSummary'][0]) ? $docs['ApiSummary'][0]['data'] : $docs['ApiSummary'][0], | ||
201 | + 'body' => isset($docs['ApiBody'][0]) ? is_array($docs['ApiBody'][0]) ? $docs['ApiBody'][0]['data'] : $docs['ApiBody'][0] : '', | ||
202 | + 'headerslist' => $this->generateHeadersTemplate($docs), | ||
203 | + 'paramslist' => $this->generateParamsTemplate($docs), | ||
204 | + 'returnheaderslist' => $this->generateReturnHeadersTemplate($docs), | ||
205 | + 'returnparamslist' => $this->generateReturnParamsTemplate($docs), | ||
206 | + 'weigh' => is_array($docs['ApiWeigh'][0]) ? $docs['ApiWeigh'][0]['data'] : $docs['ApiWeigh'][0], | ||
207 | + 'return' => isset($docs['ApiReturn']) ? is_array($docs['ApiReturn'][0]) ? $docs['ApiReturn'][0]['data'] : $docs['ApiReturn'][0] : '', | ||
208 | + ]; | ||
209 | + $counter++; | ||
210 | + } | ||
211 | + } | ||
212 | + | ||
213 | + //重建排序 | ||
214 | + foreach ($docslist as $index => &$methods) { | ||
215 | + $methodSectorArr = []; | ||
216 | + foreach ($methods as $name => $method) { | ||
217 | + $methodSectorArr[$name] = isset($method['weigh']) ? $method['weigh'] : 0; | ||
218 | + } | ||
219 | + arsort($methodSectorArr); | ||
220 | + $methods = array_merge(array_flip(array_keys($methodSectorArr)), $methods); | ||
221 | + } | ||
222 | + $docslist = array_merge(array_flip(array_keys($sectorArr)), $docslist); | ||
223 | + | ||
224 | + return $docslist; | ||
225 | + } | ||
226 | + | ||
227 | + public function getView() | ||
228 | + { | ||
229 | + return $this->view; | ||
230 | + } | ||
231 | + | ||
232 | + /** | ||
233 | + * 渲染 | ||
234 | + * @param string $template | ||
235 | + * @param array $vars | ||
236 | + * @return string | ||
237 | + */ | ||
238 | + public function render($template, $vars = []) | ||
239 | + { | ||
240 | + $docslist = $this->parse(); | ||
241 | + | ||
242 | + return $this->view->display(file_get_contents($template), array_merge($vars, ['docslist' => $docslist])); | ||
243 | + } | ||
244 | +} |
1 | +<?php | ||
2 | + | ||
3 | +namespace app\admin\command\Api\library; | ||
4 | + | ||
5 | +use Exception; | ||
6 | + | ||
7 | +/** | ||
8 | + * Class imported from https://github.com/eriknyk/Annotations | ||
9 | + * @author Erik Amaru Ortiz https://github.com/eriknyk | ||
10 | + * | ||
11 | + * @license http://opensource.org/licenses/bsd-license.php The BSD License | ||
12 | + * @author Calin Rada <rada.calin@gmail.com> | ||
13 | + */ | ||
14 | +class Extractor | ||
15 | +{ | ||
16 | + | ||
17 | + /** | ||
18 | + * Static array to store already parsed annotations | ||
19 | + * @var array | ||
20 | + */ | ||
21 | + private static $annotationCache; | ||
22 | + | ||
23 | + private static $classAnnotationCache; | ||
24 | + | ||
25 | + private static $classMethodAnnotationCache; | ||
26 | + | ||
27 | + /** | ||
28 | + * Indicates that annotations should has strict behavior, 'false' by default | ||
29 | + * @var boolean | ||
30 | + */ | ||
31 | + private $strict = false; | ||
32 | + | ||
33 | + /** | ||
34 | + * Stores the default namespace for Objects instance, usually used on methods like getMethodAnnotationsObjects() | ||
35 | + * @var string | ||
36 | + */ | ||
37 | + public $defaultNamespace = ''; | ||
38 | + | ||
39 | + /** | ||
40 | + * Sets strict variable to true/false | ||
41 | + * @param bool $value boolean value to indicate that annotations to has strict behavior | ||
42 | + */ | ||
43 | + public function setStrict($value) | ||
44 | + { | ||
45 | + $this->strict = (bool)$value; | ||
46 | + } | ||
47 | + | ||
48 | + /** | ||
49 | + * Sets default namespace to use in object instantiation | ||
50 | + * @param string $namespace default namespace | ||
51 | + */ | ||
52 | + public function setDefaultNamespace($namespace) | ||
53 | + { | ||
54 | + $this->defaultNamespace = $namespace; | ||
55 | + } | ||
56 | + | ||
57 | + /** | ||
58 | + * Gets default namespace used in object instantiation | ||
59 | + * @return string $namespace default namespace | ||
60 | + */ | ||
61 | + public function getDefaultAnnotationNamespace() | ||
62 | + { | ||
63 | + return $this->defaultNamespace; | ||
64 | + } | ||
65 | + | ||
66 | + /** | ||
67 | + * Gets all anotations with pattern @SomeAnnotation() from a given class | ||
68 | + * | ||
69 | + * @param string $className class name to get annotations | ||
70 | + * @return array self::$classAnnotationCache all annotated elements | ||
71 | + */ | ||
72 | + public static function getClassAnnotations($className) | ||
73 | + { | ||
74 | + if (!isset(self::$classAnnotationCache[$className])) { | ||
75 | + $class = new \ReflectionClass($className); | ||
76 | + self::$classAnnotationCache[$className] = self::parseAnnotations($class->getDocComment()); | ||
77 | + } | ||
78 | + | ||
79 | + return self::$classAnnotationCache[$className]; | ||
80 | + } | ||
81 | + | ||
82 | + /** | ||
83 | + * 获取类所有方法的属性配置 | ||
84 | + * @param $className | ||
85 | + * @return mixed | ||
86 | + * @throws \ReflectionException | ||
87 | + */ | ||
88 | + public static function getClassMethodAnnotations($className) | ||
89 | + { | ||
90 | + $class = new \ReflectionClass($className); | ||
91 | + | ||
92 | + foreach ($class->getMethods() as $object) { | ||
93 | + self::$classMethodAnnotationCache[$className][$object->name] = self::getMethodAnnotations($className, $object->name); | ||
94 | + } | ||
95 | + | ||
96 | + return self::$classMethodAnnotationCache[$className]; | ||
97 | + } | ||
98 | + | ||
99 | + public static function getAllClassAnnotations() | ||
100 | + { | ||
101 | + return self::$classAnnotationCache; | ||
102 | + } | ||
103 | + | ||
104 | + public static function getAllClassMethodAnnotations() | ||
105 | + { | ||
106 | + return self::$classMethodAnnotationCache; | ||
107 | + } | ||
108 | + | ||
109 | + /** | ||
110 | + * Gets all anotations with pattern @SomeAnnotation() from a determinated method of a given class | ||
111 | + * | ||
112 | + * @param string $className class name | ||
113 | + * @param string $methodName method name to get annotations | ||
114 | + * @return array self::$annotationCache all annotated elements of a method given | ||
115 | + */ | ||
116 | + public static function getMethodAnnotations($className, $methodName) | ||
117 | + { | ||
118 | + if (!isset(self::$annotationCache[$className . '::' . $methodName])) { | ||
119 | + try { | ||
120 | + $method = new \ReflectionMethod($className, $methodName); | ||
121 | + $class = new \ReflectionClass($className); | ||
122 | + if (!$method->isPublic() || $method->isConstructor()) { | ||
123 | + $annotations = array(); | ||
124 | + } else { | ||
125 | + $annotations = self::consolidateAnnotations($method, $class); | ||
126 | + } | ||
127 | + } catch (\ReflectionException $e) { | ||
128 | + $annotations = array(); | ||
129 | + } | ||
130 | + | ||
131 | + self::$annotationCache[$className . '::' . $methodName] = $annotations; | ||
132 | + } | ||
133 | + | ||
134 | + return self::$annotationCache[$className . '::' . $methodName]; | ||
135 | + } | ||
136 | + | ||
137 | + /** | ||
138 | + * Gets all anotations with pattern @SomeAnnotation() from a determinated method of a given class | ||
139 | + * and instance its abcAnnotation class | ||
140 | + * | ||
141 | + * @param string $className class name | ||
142 | + * @param string $methodName method name to get annotations | ||
143 | + * @return array self::$annotationCache all annotated objects of a method given | ||
144 | + */ | ||
145 | + public function getMethodAnnotationsObjects($className, $methodName) | ||
146 | + { | ||
147 | + $annotations = $this->getMethodAnnotations($className, $methodName); | ||
148 | + $objects = array(); | ||
149 | + | ||
150 | + $i = 0; | ||
151 | + | ||
152 | + foreach ($annotations as $annotationClass => $listParams) { | ||
153 | + $annotationClass = ucfirst($annotationClass); | ||
154 | + $class = $this->defaultNamespace . $annotationClass . 'Annotation'; | ||
155 | + | ||
156 | + // verify is the annotation class exists, depending if Annotations::strict is true | ||
157 | + // if not, just skip the annotation instance creation. | ||
158 | + if (!class_exists($class)) { | ||
159 | + if ($this->strict) { | ||
160 | + throw new Exception(sprintf('Runtime Error: Annotation Class Not Found: %s', $class)); | ||
161 | + } else { | ||
162 | + // silent skip & continue | ||
163 | + continue; | ||
164 | + } | ||
165 | + } | ||
166 | + | ||
167 | + if (empty($objects[$annotationClass])) { | ||
168 | + $objects[$annotationClass] = new $class(); | ||
169 | + } | ||
170 | + | ||
171 | + foreach ($listParams as $params) { | ||
172 | + if (is_array($params)) { | ||
173 | + foreach ($params as $key => $value) { | ||
174 | + $objects[$annotationClass]->set($key, $value); | ||
175 | + } | ||
176 | + } else { | ||
177 | + $objects[$annotationClass]->set($i++, $params); | ||
178 | + } | ||
179 | + } | ||
180 | + } | ||
181 | + | ||
182 | + return $objects; | ||
183 | + } | ||
184 | + | ||
185 | + private static function consolidateAnnotations($method, $class) | ||
186 | + { | ||
187 | + $dockblockClass = $class->getDocComment(); | ||
188 | + $docblockMethod = $method->getDocComment(); | ||
189 | + $methodName = $method->getName(); | ||
190 | + | ||
191 | + $methodAnnotations = self::parseAnnotations($docblockMethod); | ||
192 | + $classAnnotations = self::parseAnnotations($dockblockClass); | ||
193 | + if (isset($methodAnnotations['ApiInternal']) || $methodName == '_initialize' || $methodName == '_empty') { | ||
194 | + return []; | ||
195 | + } | ||
196 | + | ||
197 | + $properties = $class->getDefaultProperties(); | ||
198 | + $noNeedLogin = isset($properties['noNeedLogin']) ? is_array($properties['noNeedLogin']) ? $properties['noNeedLogin'] : [$properties['noNeedLogin']] : []; | ||
199 | + $noNeedRight = isset($properties['noNeedRight']) ? is_array($properties['noNeedRight']) ? $properties['noNeedRight'] : [$properties['noNeedRight']] : []; | ||
200 | + | ||
201 | + preg_match_all("/\*[\s]+(.*)(\\r\\n|\\r|\\n)/U", str_replace('/**', '', $docblockMethod), $methodArr); | ||
202 | + preg_match_all("/\*[\s]+(.*)(\\r\\n|\\r|\\n)/U", str_replace('/**', '', $dockblockClass), $classArr); | ||
203 | + | ||
204 | + if (!isset($methodAnnotations['ApiMethod'])) { | ||
205 | + $methodAnnotations['ApiMethod'] = ['get']; | ||
206 | + } | ||
207 | + if (!isset($methodAnnotations['ApiWeigh'])) { | ||
208 | + $methodAnnotations['ApiWeigh'] = [0]; | ||
209 | + } | ||
210 | + if (!isset($methodAnnotations['ApiSummary'])) { | ||
211 | + $methodAnnotations['ApiSummary'] = $methodAnnotations['ApiTitle']; | ||
212 | + } | ||
213 | + | ||
214 | + if ($methodAnnotations) { | ||
215 | + foreach ($classAnnotations as $name => $valueClass) { | ||
216 | + if (count($valueClass) !== 1) { | ||
217 | + continue; | ||
218 | + } | ||
219 | + | ||
220 | + if ($name === 'ApiRoute') { | ||
221 | + if (isset($methodAnnotations[$name])) { | ||
222 | + $methodAnnotations[$name] = [rtrim($valueClass[0], '/') . $methodAnnotations[$name][0]]; | ||
223 | + } else { | ||
224 | + $methodAnnotations[$name] = [rtrim($valueClass[0], '/') . '/' . $method->getName()]; | ||
225 | + } | ||
226 | + } | ||
227 | + | ||
228 | + if ($name === 'ApiSector') { | ||
229 | + $methodAnnotations[$name] = $valueClass; | ||
230 | + } | ||
231 | + } | ||
232 | + } | ||
233 | + if (!isset($methodAnnotations['ApiRoute'])) { | ||
234 | + $urlArr = []; | ||
235 | + $className = $class->getName(); | ||
236 | + | ||
237 | + list($prefix, $suffix) = explode('\\' . \think\Config::get('url_controller_layer') . '\\', $className); | ||
238 | + $prefixArr = explode('\\', $prefix); | ||
239 | + $suffixArr = explode('\\', $suffix); | ||
240 | + if ($prefixArr[0] == \think\Config::get('app_namespace')) { | ||
241 | + $prefixArr[0] = ''; | ||
242 | + } | ||
243 | + $urlArr = array_merge($urlArr, $prefixArr); | ||
244 | + $urlArr[] = implode('.', array_map(function ($item) { | ||
245 | + return \think\Loader::parseName($item); | ||
246 | + }, $suffixArr)); | ||
247 | + $urlArr[] = $method->getName(); | ||
248 | + | ||
249 | + $methodAnnotations['ApiRoute'] = [implode('/', $urlArr)]; | ||
250 | + } | ||
251 | + if (!isset($methodAnnotations['ApiSector'])) { | ||
252 | + $methodAnnotations['ApiSector'] = isset($classAnnotations['ApiSector']) ? $classAnnotations['ApiSector'] : $classAnnotations['ApiTitle']; | ||
253 | + } | ||
254 | + if (!isset($methodAnnotations['ApiParams'])) { | ||
255 | + $params = self::parseCustomAnnotations($docblockMethod, 'param'); | ||
256 | + foreach ($params as $k => $v) { | ||
257 | + $arr = explode(' ', preg_replace("/[\s]+/", " ", $v)); | ||
258 | + $methodAnnotations['ApiParams'][] = [ | ||
259 | + 'name' => isset($arr[1]) ? str_replace('$', '', $arr[1]) : '', | ||
260 | + 'nullable' => false, | ||
261 | + 'type' => isset($arr[0]) ? $arr[0] : 'string', | ||
262 | + 'description' => isset($arr[2]) ? $arr[2] : '' | ||
263 | + ]; | ||
264 | + } | ||
265 | + } | ||
266 | + $methodAnnotations['ApiPermissionLogin'] = [!in_array('*', $noNeedLogin) && !in_array($methodName, $noNeedLogin)]; | ||
267 | + $methodAnnotations['ApiPermissionRight'] = [!in_array('*', $noNeedRight) && !in_array($methodName, $noNeedRight)]; | ||
268 | + return $methodAnnotations; | ||
269 | + } | ||
270 | + | ||
271 | + /** | ||
272 | + * Parse annotations | ||
273 | + * | ||
274 | + * @param string $docblock | ||
275 | + * @param string $name | ||
276 | + * @return array parsed annotations params | ||
277 | + */ | ||
278 | + private static function parseCustomAnnotations($docblock, $name = 'param') | ||
279 | + { | ||
280 | + $annotations = array(); | ||
281 | + | ||
282 | + $docblock = substr($docblock, 3, -2); | ||
283 | + if (preg_match_all('/@' . $name . '(?:\s*(?:\(\s*)?(.*?)(?:\s*\))?)??\s*(?:\n|\*\/)/', $docblock, $matches)) { | ||
284 | + foreach ($matches[1] as $k => $v) { | ||
285 | + $annotations[] = $v; | ||
286 | + } | ||
287 | + } | ||
288 | + return $annotations; | ||
289 | + } | ||
290 | + | ||
291 | + /** | ||
292 | + * Parse annotations | ||
293 | + * | ||
294 | + * @param string $docblock | ||
295 | + * @return array parsed annotations params | ||
296 | + */ | ||
297 | + private static function parseAnnotations($docblock) | ||
298 | + { | ||
299 | + $annotations = array(); | ||
300 | + | ||
301 | + // Strip away the docblock header and footer to ease parsing of one line annotations | ||
302 | + $docblock = substr($docblock, 3, -2); | ||
303 | + if (preg_match_all('/@(?<name>[A-Za-z_-]+)[\s\t]*\((?<args>(?:(?!\)).)*)\)\r?/s', $docblock, $matches)) { | ||
304 | + $numMatches = count($matches[0]); | ||
305 | + for ($i = 0; $i < $numMatches; ++$i) { | ||
306 | + $name = $matches['name'][$i]; | ||
307 | + $value = ''; | ||
308 | + // annotations has arguments | ||
309 | + if (isset($matches['args'][$i])) { | ||
310 | + $argsParts = trim($matches['args'][$i]); | ||
311 | + if ($name == 'ApiReturn') { | ||
312 | + $value = $argsParts; | ||
313 | + } elseif ($matches['args'][$i] != '') { | ||
314 | + $argsParts = preg_replace("/\{(\w+)\}/", '#$1#', $argsParts); | ||
315 | + $value = self::parseArgs($argsParts); | ||
316 | + if (is_string($value)) { | ||
317 | + $value = preg_replace("/\#(\w+)\#/", '{$1}', $argsParts); | ||
318 | + } | ||
319 | + } | ||
320 | + } | ||
321 | + | ||
322 | + $annotations[$name][] = $value; | ||
323 | + } | ||
324 | + } | ||
325 | + if (stripos($docblock, '@ApiInternal') !== false) { | ||
326 | + $annotations['ApiInternal'] = [true]; | ||
327 | + } | ||
328 | + if (!isset($annotations['ApiTitle'])) { | ||
329 | + preg_match_all("/\*[\s]+(.*)(\\r\\n|\\r|\\n)/U", str_replace('/**', '', $docblock), $matchArr); | ||
330 | + $title = isset($matchArr[1]) && isset($matchArr[1][0]) ? $matchArr[1][0] : ''; | ||
331 | + $annotations['ApiTitle'] = [$title]; | ||
332 | + } | ||
333 | + | ||
334 | + return $annotations; | ||
335 | + } | ||
336 | + | ||
337 | + /** | ||
338 | + * Parse individual annotation arguments | ||
339 | + * | ||
340 | + * @param string $content arguments string | ||
341 | + * @return array annotated arguments | ||
342 | + */ | ||
343 | + private static function parseArgs($content) | ||
344 | + { | ||
345 | + // Replace initial stars | ||
346 | + $content = preg_replace('/^\s*\*/m', '', $content); | ||
347 | + | ||
348 | + $data = array(); | ||
349 | + $len = strlen($content); | ||
350 | + $i = 0; | ||
351 | + $var = ''; | ||
352 | + $val = ''; | ||
353 | + $level = 1; | ||
354 | + | ||
355 | + $prevDelimiter = ''; | ||
356 | + $nextDelimiter = ''; | ||
357 | + $nextToken = ''; | ||
358 | + $composing = false; | ||
359 | + $type = 'plain'; | ||
360 | + $delimiter = null; | ||
361 | + $quoted = false; | ||
362 | + $tokens = array('"', '"', '{', '}', ',', '='); | ||
363 | + | ||
364 | + while ($i <= $len) { | ||
365 | + $prev_c = substr($content, $i - 1, 1); | ||
366 | + $c = substr($content, $i++, 1); | ||
367 | + | ||
368 | + if ($c === '"' && $prev_c !== "\\") { | ||
369 | + $delimiter = $c; | ||
370 | + //open delimiter | ||
371 | + if (!$composing && empty($prevDelimiter) && empty($nextDelimiter)) { | ||
372 | + $prevDelimiter = $nextDelimiter = $delimiter; | ||
373 | + $val = ''; | ||
374 | + $composing = true; | ||
375 | + $quoted = true; | ||
376 | + } else { | ||
377 | + // close delimiter | ||
378 | + if ($c !== $nextDelimiter) { | ||
379 | + throw new Exception(sprintf( | ||
380 | + "Parse Error: enclosing error -> expected: [%s], given: [%s]", | ||
381 | + $nextDelimiter, | ||
382 | + $c | ||
383 | + )); | ||
384 | + } | ||
385 | + | ||
386 | + // validating syntax | ||
387 | + if ($i < $len) { | ||
388 | + if (',' !== substr($content, $i, 1) && '\\' !== $prev_c) { | ||
389 | + throw new Exception(sprintf( | ||
390 | + "Parse Error: missing comma separator near: ...%s<--", | ||
391 | + substr($content, ($i - 10), $i) | ||
392 | + )); | ||
393 | + } | ||
394 | + } | ||
395 | + | ||
396 | + $prevDelimiter = $nextDelimiter = ''; | ||
397 | + $composing = false; | ||
398 | + $delimiter = null; | ||
399 | + } | ||
400 | + } elseif (!$composing && in_array($c, $tokens)) { | ||
401 | + switch ($c) { | ||
402 | + case '=': | ||
403 | + $prevDelimiter = $nextDelimiter = ''; | ||
404 | + $level = 2; | ||
405 | + $composing = false; | ||
406 | + $type = 'assoc'; | ||
407 | + $quoted = false; | ||
408 | + break; | ||
409 | + case ',': | ||
410 | + $level = 3; | ||
411 | + | ||
412 | + // If composing flag is true yet, | ||
413 | + // it means that the string was not enclosed, so it is parsing error. | ||
414 | + if ($composing === true && !empty($prevDelimiter) && !empty($nextDelimiter)) { | ||
415 | + throw new Exception(sprintf( | ||
416 | + "Parse Error: enclosing error -> expected: [%s], given: [%s]", | ||
417 | + $nextDelimiter, | ||
418 | + $c | ||
419 | + )); | ||
420 | + } | ||
421 | + | ||
422 | + $prevDelimiter = $nextDelimiter = ''; | ||
423 | + break; | ||
424 | + case '{': | ||
425 | + $subc = ''; | ||
426 | + $subComposing = true; | ||
427 | + | ||
428 | + while ($i <= $len) { | ||
429 | + $c = substr($content, $i++, 1); | ||
430 | + | ||
431 | + if (isset($delimiter) && $c === $delimiter) { | ||
432 | + throw new Exception(sprintf( | ||
433 | + "Parse Error: Composite variable is not enclosed correctly." | ||
434 | + )); | ||
435 | + } | ||
436 | + | ||
437 | + if ($c === '}') { | ||
438 | + $subComposing = false; | ||
439 | + break; | ||
440 | + } | ||
441 | + $subc .= $c; | ||
442 | + } | ||
443 | + | ||
444 | + // if the string is composing yet means that the structure of var. never was enclosed with '}' | ||
445 | + if ($subComposing) { | ||
446 | + throw new Exception(sprintf( | ||
447 | + "Parse Error: Composite variable is not enclosed correctly. near: ...%s'", | ||
448 | + $subc | ||
449 | + )); | ||
450 | + } | ||
451 | + | ||
452 | + $val = self::parseArgs($subc); | ||
453 | + break; | ||
454 | + } | ||
455 | + } else { | ||
456 | + if ($level == 1) { | ||
457 | + $var .= $c; | ||
458 | + } elseif ($level == 2) { | ||
459 | + $val .= $c; | ||
460 | + } | ||
461 | + } | ||
462 | + | ||
463 | + if ($level === 3 || $i === $len) { | ||
464 | + if ($type == 'plain' && $i === $len) { | ||
465 | + $data = self::castValue($var); | ||
466 | + } else { | ||
467 | + $data[trim($var)] = self::castValue($val, !$quoted); | ||
468 | + } | ||
469 | + | ||
470 | + $level = 1; | ||
471 | + $var = $val = ''; | ||
472 | + $composing = false; | ||
473 | + $quoted = false; | ||
474 | + } | ||
475 | + } | ||
476 | + | ||
477 | + return $data; | ||
478 | + } | ||
479 | + | ||
480 | + /** | ||
481 | + * Try determinate the original type variable of a string | ||
482 | + * | ||
483 | + * @param string $val string containing possibles variables that can be cast to bool or int | ||
484 | + * @param boolean $trim indicate if the value passed should be trimmed after to try cast | ||
485 | + * @return mixed returns the value converted to original type if was possible | ||
486 | + */ | ||
487 | + private static function castValue($val, $trim = false) | ||
488 | + { | ||
489 | + if (is_array($val)) { | ||
490 | + foreach ($val as $key => $value) { | ||
491 | + $val[$key] = self::castValue($value); | ||
492 | + } | ||
493 | + } elseif (is_string($val)) { | ||
494 | + if ($trim) { | ||
495 | + $val = trim($val); | ||
496 | + } | ||
497 | + $val = stripslashes($val); | ||
498 | + $tmp = strtolower($val); | ||
499 | + | ||
500 | + if ($tmp === 'false' || $tmp === 'true') { | ||
501 | + $val = $tmp === 'true'; | ||
502 | + } elseif (is_numeric($val)) { | ||
503 | + return $val + 0; | ||
504 | + } | ||
505 | + | ||
506 | + unset($tmp); | ||
507 | + } | ||
508 | + | ||
509 | + return $val; | ||
510 | + } | ||
511 | +} |
1 | +<!DOCTYPE html> | ||
2 | +<html lang="{$config.language}"> | ||
3 | + <head> | ||
4 | + <meta charset="utf-8"> | ||
5 | + <meta http-equiv="X-UA-Compatible" content="IE=edge"> | ||
6 | + <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
7 | + <meta name="description" content=""> | ||
8 | + <meta name="author" content="{$config.author}"> | ||
9 | + <title>{$config.title}</title> | ||
10 | + | ||
11 | + <!-- Bootstrap Core CSS --> | ||
12 | + <link href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"> | ||
13 | + | ||
14 | + <!-- Plugin CSS --> | ||
15 | + <link href="https://cdn.staticfile.org/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"> | ||
16 | + | ||
17 | + <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries --> | ||
18 | + <!--[if lt IE 9]> | ||
19 | + <script src="https://cdn.staticfile.org/html5shiv/3.7.3/html5shiv.min.js"></script> | ||
20 | + <script src="https://cdn.staticfile.org/respond.js/1.4.2/respond.min.js"></script> | ||
21 | + <![endif]--> | ||
22 | + | ||
23 | + <style type="text/css"> | ||
24 | + body { | ||
25 | + padding-top: 70px; margin-bottom: 15px; | ||
26 | + -webkit-font-smoothing: antialiased; | ||
27 | + -moz-osx-font-smoothing: grayscale; | ||
28 | + font-family: "Roboto", "SF Pro SC", "SF Pro Display", "SF Pro Icons", "PingFang SC", BlinkMacSystemFont, -apple-system, "Segoe UI", "Microsoft Yahei", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", "Helvetica", "Arial", sans-serif; | ||
29 | + font-weight: 400; | ||
30 | + } | ||
31 | + h2 { font-size: 1.6em; } | ||
32 | + hr { margin-top: 10px; } | ||
33 | + .tab-pane { padding-top: 10px; } | ||
34 | + .mt0 { margin-top: 0px; } | ||
35 | + .footer { font-size: 12px; color: #666; } | ||
36 | + .label { display: inline-block; min-width: 65px; padding: 0.3em 0.6em 0.3em; } | ||
37 | + .string { color: green; } | ||
38 | + .number { color: darkorange; } | ||
39 | + .boolean { color: blue; } | ||
40 | + .null { color: magenta; } | ||
41 | + .key { color: red; } | ||
42 | + .popover { max-width: 400px; max-height: 400px; overflow-y: auto;} | ||
43 | + .list-group.panel > .list-group-item { | ||
44 | + } | ||
45 | + .list-group-item:last-child { | ||
46 | + border-radius:0; | ||
47 | + } | ||
48 | + h4.panel-title a { | ||
49 | + font-weight:normal; | ||
50 | + font-size:14px; | ||
51 | + } | ||
52 | + h4.panel-title a .text-muted { | ||
53 | + font-size:12px; | ||
54 | + font-weight:normal; | ||
55 | + font-family: 'Verdana'; | ||
56 | + } | ||
57 | + #sidebar { | ||
58 | + width: 220px; | ||
59 | + position: fixed; | ||
60 | + margin-left: -240px; | ||
61 | + overflow-y:auto; | ||
62 | + } | ||
63 | + #sidebar > .list-group { | ||
64 | + margin-bottom:0; | ||
65 | + } | ||
66 | + #sidebar > .list-group > a{ | ||
67 | + text-indent:0; | ||
68 | + } | ||
69 | + #sidebar .child { | ||
70 | + border:1px solid #ddd; | ||
71 | + border-bottom:none; | ||
72 | + } | ||
73 | + #sidebar .child > a { | ||
74 | + border:0; | ||
75 | + } | ||
76 | + #sidebar .list-group a.current { | ||
77 | + background:#f5f5f5; | ||
78 | + } | ||
79 | + @media (max-width: 1620px){ | ||
80 | + #sidebar { | ||
81 | + margin:0; | ||
82 | + } | ||
83 | + #accordion { | ||
84 | + padding-left:235px; | ||
85 | + } | ||
86 | + } | ||
87 | + @media (max-width: 768px){ | ||
88 | + #sidebar { | ||
89 | + display: none; | ||
90 | + } | ||
91 | + #accordion { | ||
92 | + padding-left:0px; | ||
93 | + } | ||
94 | + } | ||
95 | + .label-primary { | ||
96 | + background-color: #248aff; | ||
97 | + } | ||
98 | + | ||
99 | + </style> | ||
100 | + </head> | ||
101 | + <body> | ||
102 | + <!-- Fixed navbar --> | ||
103 | + <div class="navbar navbar-default navbar-fixed-top" role="navigation"> | ||
104 | + <div class="container"> | ||
105 | + <div class="navbar-header"> | ||
106 | + <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> | ||
107 | + <span class="sr-only">Toggle navigation</span> | ||
108 | + <span class="icon-bar"></span> | ||
109 | + <span class="icon-bar"></span> | ||
110 | + <span class="icon-bar"></span> | ||
111 | + </button> | ||
112 | + <a class="navbar-brand" href="https://www.fastadmin.net" target="_blank">{$config.title}</a> | ||
113 | + </div> | ||
114 | + <div class="navbar-collapse collapse"> | ||
115 | + <form class="navbar-form navbar-right"> | ||
116 | + <div class="form-group"> | ||
117 | + Token: | ||
118 | + </div> | ||
119 | + <div class="form-group"> | ||
120 | + <input type="text" class="form-control input-sm" data-toggle="tooltip" title="{$lang.Tokentips}" placeholder="token" id="token" /> | ||
121 | + </div> | ||
122 | + <div class="form-group"> | ||
123 | + Apiurl: | ||
124 | + </div> | ||
125 | + <div class="form-group"> | ||
126 | + <input id="apiUrl" type="text" class="form-control input-sm" data-toggle="tooltip" title="{$lang.Apiurltips}" placeholder="https://api.mydomain.com" value="{$config.apiurl}" /> | ||
127 | + </div> | ||
128 | + <div class="form-group"> | ||
129 | + <button type="button" class="btn btn-success btn-sm" data-toggle="tooltip" title="{$lang.Savetips}" id="save_data"> | ||
130 | + <span class="glyphicon glyphicon-floppy-disk" aria-hidden="true"></span> | ||
131 | + </button> | ||
132 | + </div> | ||
133 | + </form> | ||
134 | + </div><!--/.nav-collapse --> | ||
135 | + </div> | ||
136 | + </div> | ||
137 | + | ||
138 | + <div class="container"> | ||
139 | + <!-- menu --> | ||
140 | + <div id="sidebar"> | ||
141 | + <div class="list-group panel"> | ||
142 | + {foreach name="docslist" id="docs"} | ||
143 | + <a href="#{$key}" class="list-group-item" data-toggle="collapse" data-parent="#sidebar">{$key} <i class="fa fa-caret-down"></i></a> | ||
144 | + <div class="child collapse" id="{$key}"> | ||
145 | + {foreach name="docs" id="api" } | ||
146 | + <a href="javascript:;" data-id="{$api.id}" class="list-group-item">{$api.title}</a> | ||
147 | + {/foreach} | ||
148 | + </div> | ||
149 | + {/foreach} | ||
150 | + </div> | ||
151 | + </div> | ||
152 | + <div class="panel-group" id="accordion"> | ||
153 | + {foreach name="docslist" id="docs"} | ||
154 | + <h2>{$key}</h2> | ||
155 | + <hr> | ||
156 | + {foreach name="docs" id="api" } | ||
157 | + <div class="panel panel-default"> | ||
158 | + <div class="panel-heading" id="heading-{$api.id}"> | ||
159 | + <h4 class="panel-title"> | ||
160 | + <span class="label {$api.method_label}">{$api.method|strtoupper}</span> | ||
161 | + <a data-toggle="collapse" data-parent="#accordion{$api.id}" href="#collapseOne{$api.id}"> {$api.title} <span class="text-muted">{$api.route}</span></a> | ||
162 | + </h4> | ||
163 | + </div> | ||
164 | + <div id="collapseOne{$api.id}" class="panel-collapse collapse"> | ||
165 | + <div class="panel-body"> | ||
166 | + | ||
167 | + <!-- Nav tabs --> | ||
168 | + <ul class="nav nav-tabs" id="doctab{$api.id}"> | ||
169 | + <li class="active"><a href="#info{$api.id}" data-toggle="tab">{$lang.Info}</a></li> | ||
170 | + <li><a href="#sandbox{$api.id}" data-toggle="tab">{$lang.Sandbox}</a></li> | ||
171 | + <li><a href="#sample{$api.id}" data-toggle="tab">{$lang.Sampleoutput}</a></li> | ||
172 | + </ul> | ||
173 | + | ||
174 | + <!-- Tab panes --> | ||
175 | + <div class="tab-content"> | ||
176 | + | ||
177 | + <div class="tab-pane active" id="info{$api.id}"> | ||
178 | + <div class="well"> | ||
179 | + {$api.summary} | ||
180 | + </div> | ||
181 | + <div class="panel panel-default"> | ||
182 | + <div class="panel-heading"><strong>{$lang.Headers}</strong></div> | ||
183 | + <div class="panel-body"> | ||
184 | + {if $api.headerslist} | ||
185 | + <table class="table table-hover"> | ||
186 | + <thead> | ||
187 | + <tr> | ||
188 | + <th>{$lang.Name}</th> | ||
189 | + <th>{$lang.Type}</th> | ||
190 | + <th>{$lang.Required}</th> | ||
191 | + <th>{$lang.Description}</th> | ||
192 | + </tr> | ||
193 | + </thead> | ||
194 | + <tbody> | ||
195 | + {foreach name="api['headerslist']" id="header"} | ||
196 | + <tr> | ||
197 | + <td>{$header.name}</td> | ||
198 | + <td>{$header.type}</td> | ||
199 | + <td>{$header.required?'是':'否'}</td> | ||
200 | + <td>{$header.description}</td> | ||
201 | + </tr> | ||
202 | + {/foreach} | ||
203 | + </tbody> | ||
204 | + </table> | ||
205 | + {else /} | ||
206 | + 无 | ||
207 | + {/if} | ||
208 | + </div> | ||
209 | + </div> | ||
210 | + <div class="panel panel-default"> | ||
211 | + <div class="panel-heading"><strong>{$lang.Parameters}</strong></div> | ||
212 | + <div class="panel-body"> | ||
213 | + {if $api.paramslist} | ||
214 | + <table class="table table-hover"> | ||
215 | + <thead> | ||
216 | + <tr> | ||
217 | + <th>{$lang.Name}</th> | ||
218 | + <th>{$lang.Type}</th> | ||
219 | + <th>{$lang.Required}</th> | ||
220 | + <th>{$lang.Description}</th> | ||
221 | + </tr> | ||
222 | + </thead> | ||
223 | + <tbody> | ||
224 | + {foreach name="api['paramslist']" id="param"} | ||
225 | + <tr> | ||
226 | + <td>{$param.name}</td> | ||
227 | + <td>{$param.type}</td> | ||
228 | + <td>{:$param.required?'是':'否'}</td> | ||
229 | + <td>{$param.description}</td> | ||
230 | + </tr> | ||
231 | + {/foreach} | ||
232 | + </tbody> | ||
233 | + </table> | ||
234 | + {else /} | ||
235 | + 无 | ||
236 | + {/if} | ||
237 | + </div> | ||
238 | + </div> | ||
239 | + <div class="panel panel-default"> | ||
240 | + <div class="panel-heading"><strong>{$lang.Body}</strong></div> | ||
241 | + <div class="panel-body"> | ||
242 | + {$api.body|default='无'} | ||
243 | + </div> | ||
244 | + </div> | ||
245 | + </div><!-- #info --> | ||
246 | + | ||
247 | + <div class="tab-pane" id="sandbox{$api.id}"> | ||
248 | + <div class="row"> | ||
249 | + <div class="col-md-12"> | ||
250 | + {if $api.headerslist} | ||
251 | + <div class="panel panel-default"> | ||
252 | + <div class="panel-heading"><strong>{$lang.Headers}</strong></div> | ||
253 | + <div class="panel-body"> | ||
254 | + <div class="headers"> | ||
255 | + {foreach name="api['headerslist']" id="param"} | ||
256 | + <div class="form-group"> | ||
257 | + <label class="control-label" for="{$param.name}">{$param.name}</label> | ||
258 | + <input type="{$param.type}" class="form-control input-sm" id="{$param.name}" {if $param.required}required{/if} placeholder="{$param.description} - Ex: {$param.sample}" name="{$param.name}"> | ||
259 | + </div> | ||
260 | + {/foreach} | ||
261 | + </div> | ||
262 | + </div> | ||
263 | + </div> | ||
264 | + {/if} | ||
265 | + <div class="panel panel-default"> | ||
266 | + <div class="panel-heading"><strong>{$lang.Parameters}</strong></div> | ||
267 | + <div class="panel-body"> | ||
268 | + <form enctype="application/x-www-form-urlencoded" role="form" action="{$api.route}" method="{$api.method}" name="form{$api.id}" id="form{$api.id}"> | ||
269 | + {if $api.paramslist} | ||
270 | + {foreach name="api['paramslist']" id="param"} | ||
271 | + <div class="form-group"> | ||
272 | + <label class="control-label" for="{$param.name}">{$param.name}</label> | ||
273 | + <input type="{$param.type}" class="form-control input-sm" id="{$param.name}" {if $param.required}required{/if} placeholder="{$param.description}{if $param.sample} - 例: {$param.sample}{/if}" name="{$param.name}"> | ||
274 | + </div> | ||
275 | + {/foreach} | ||
276 | + {else /} | ||
277 | + <div class="form-group"> | ||
278 | + 无 | ||
279 | + </div> | ||
280 | + {/if} | ||
281 | + <div class="form-group"> | ||
282 | + <button type="submit" class="btn btn-success send" rel="{$api.id}">{$lang.Send}</button> | ||
283 | + <button type="reset" class="btn btn-info" rel="{$api.id}">{$lang.Reset}</button> | ||
284 | + </div> | ||
285 | + </form> | ||
286 | + </div> | ||
287 | + </div> | ||
288 | + <div class="panel panel-default"> | ||
289 | + <div class="panel-heading"><strong>{$lang.Response}</strong></div> | ||
290 | + <div class="panel-body"> | ||
291 | + <div class="row"> | ||
292 | + <div class="col-md-12" style="overflow-x:auto"> | ||
293 | + <pre id="response_headers{$api.id}"></pre> | ||
294 | + <pre id="response{$api.id}"></pre> | ||
295 | + </div> | ||
296 | + </div> | ||
297 | + </div> | ||
298 | + </div> | ||
299 | + <div class="panel panel-default"> | ||
300 | + <div class="panel-heading"><strong>{$lang.ReturnParameters}</strong></div> | ||
301 | + <div class="panel-body"> | ||
302 | + {if $api.returnparamslist} | ||
303 | + <table class="table table-hover"> | ||
304 | + <thead> | ||
305 | + <tr> | ||
306 | + <th>{$lang.Name}</th> | ||
307 | + <th>{$lang.Type}</th> | ||
308 | + <th>{$lang.Description}</th> | ||
309 | + </tr> | ||
310 | + </thead> | ||
311 | + <tbody> | ||
312 | + {foreach name="api['returnparamslist']" id="param"} | ||
313 | + <tr> | ||
314 | + <td>{$param.name}</td> | ||
315 | + <td>{$param.type}</td> | ||
316 | + <td>{$param.description}</td> | ||
317 | + </tr> | ||
318 | + {/foreach} | ||
319 | + </tbody> | ||
320 | + </table> | ||
321 | + {else /} | ||
322 | + 无 | ||
323 | + {/if} | ||
324 | + </div> | ||
325 | + </div> | ||
326 | + </div> | ||
327 | + </div> | ||
328 | + </div><!-- #sandbox --> | ||
329 | + | ||
330 | + <div class="tab-pane" id="sample{$api.id}"> | ||
331 | + <div class="row"> | ||
332 | + <div class="col-md-12"> | ||
333 | + <pre id="sample_response{$api.id}">{$api.return|default='无'}</pre> | ||
334 | + </div> | ||
335 | + </div> | ||
336 | + </div><!-- #sample --> | ||
337 | + | ||
338 | + </div><!-- .tab-content --> | ||
339 | + </div> | ||
340 | + </div> | ||
341 | + </div> | ||
342 | + {/foreach} | ||
343 | + {/foreach} | ||
344 | + </div> | ||
345 | + | ||
346 | + <hr> | ||
347 | + | ||
348 | + <div class="row mt0 footer"> | ||
349 | + <div class="col-md-6" align="left"> | ||
350 | + Generated on {:date('Y-m-d H:i:s')} | ||
351 | + </div> | ||
352 | + <div class="col-md-6" align="right"> | ||
353 | + <a href="https://www.fastadmin.net" target="_blank">FastAdmin</a> | ||
354 | + </div> | ||
355 | + </div> | ||
356 | + | ||
357 | + </div> <!-- /container --> | ||
358 | + | ||
359 | + <!-- jQuery --> | ||
360 | + <script src="https://cdn.staticfile.org/jquery/2.1.4/jquery.min.js"></script> | ||
361 | + | ||
362 | + <!-- Bootstrap Core JavaScript --> | ||
363 | + <script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script> | ||
364 | + | ||
365 | + <script type="text/javascript"> | ||
366 | + function syntaxHighlight(json) { | ||
367 | + if (typeof json != 'string') { | ||
368 | + json = JSON.stringify(json, undefined, 2); | ||
369 | + } | ||
370 | + json = json.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'); | ||
371 | + return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) { | ||
372 | + var cls = 'number'; | ||
373 | + if (/^"/.test(match)) { | ||
374 | + if (/:$/.test(match)) { | ||
375 | + cls = 'key'; | ||
376 | + } else { | ||
377 | + cls = 'string'; | ||
378 | + } | ||
379 | + } else if (/true|false/.test(match)) { | ||
380 | + cls = 'boolean'; | ||
381 | + } else if (/null/.test(match)) { | ||
382 | + cls = 'null'; | ||
383 | + } | ||
384 | + return '<span class="' + cls + '">' + match + '</span>'; | ||
385 | + }); | ||
386 | + } | ||
387 | + | ||
388 | + function prepareStr(str) { | ||
389 | + try { | ||
390 | + return syntaxHighlight(JSON.stringify(JSON.parse(str.replace(/'/g, '"')), null, 2)); | ||
391 | + } catch (e) { | ||
392 | + return str; | ||
393 | + } | ||
394 | + } | ||
395 | + var storage = (function () { | ||
396 | + var uid = new Date; | ||
397 | + var storage; | ||
398 | + var result; | ||
399 | + try { | ||
400 | + (storage = window.localStorage).setItem(uid, uid); | ||
401 | + result = storage.getItem(uid) == uid; | ||
402 | + storage.removeItem(uid); | ||
403 | + return result && storage; | ||
404 | + } catch (exception) { | ||
405 | + } | ||
406 | + }()); | ||
407 | + | ||
408 | + $.fn.serializeObject = function () | ||
409 | + { | ||
410 | + var o = {}; | ||
411 | + var a = this.serializeArray(); | ||
412 | + $.each(a, function () { | ||
413 | + if (!this.value) { | ||
414 | + return; | ||
415 | + } | ||
416 | + if (o[this.name] !== undefined) { | ||
417 | + if (!o[this.name].push) { | ||
418 | + o[this.name] = [o[this.name]]; | ||
419 | + } | ||
420 | + o[this.name].push(this.value || ''); | ||
421 | + } else { | ||
422 | + o[this.name] = this.value || ''; | ||
423 | + } | ||
424 | + }); | ||
425 | + return o; | ||
426 | + }; | ||
427 | + | ||
428 | + $(document).ready(function () { | ||
429 | + | ||
430 | + if (storage) { | ||
431 | + storage.getItem('token') && $('#token').val(storage.getItem('token')); | ||
432 | + storage.getItem('apiUrl') && $('#apiUrl').val(storage.getItem('apiUrl')); | ||
433 | + } | ||
434 | + | ||
435 | + $('[data-toggle="tooltip"]').tooltip({ | ||
436 | + placement: 'bottom' | ||
437 | + }); | ||
438 | + | ||
439 | + $(window).on("resize", function(){ | ||
440 | + $("#sidebar").css("max-height", $(window).height()-80); | ||
441 | + }); | ||
442 | + | ||
443 | + $(window).trigger("resize"); | ||
444 | + | ||
445 | + $(document).on("click", "#sidebar .list-group > .list-group-item", function(){ | ||
446 | + $("#sidebar .list-group > .list-group-item").removeClass("current"); | ||
447 | + $(this).addClass("current"); | ||
448 | + }); | ||
449 | + $(document).on("click", "#sidebar .child a", function(){ | ||
450 | + var heading = $("#heading-"+$(this).data("id")); | ||
451 | + if(!heading.next().hasClass("in")){ | ||
452 | + $("a", heading).trigger("click"); | ||
453 | + } | ||
454 | + $("html,body").animate({scrollTop:heading.offset().top-70}); | ||
455 | + }); | ||
456 | + | ||
457 | + $('code[id^=response]').hide(); | ||
458 | + | ||
459 | + $.each($('pre[id^=sample_response],pre[id^=sample_post_body]'), function () { | ||
460 | + if ($(this).html() == 'NA') { | ||
461 | + return; | ||
462 | + } | ||
463 | + var str = prepareStr($(this).html()); | ||
464 | + $(this).html(str); | ||
465 | + }); | ||
466 | + | ||
467 | + $("[data-toggle=popover]").popover({placement: 'right'}); | ||
468 | + | ||
469 | + $('[data-toggle=popover]').on('shown.bs.popover', function () { | ||
470 | + var $sample = $(this).parent().find(".popover-content"), | ||
471 | + str = $(this).data('content'); | ||
472 | + if (typeof str == "undefined" || str === "") { | ||
473 | + return; | ||
474 | + } | ||
475 | + var str = prepareStr(str); | ||
476 | + $sample.html('<pre>' + str + '</pre>'); | ||
477 | + }); | ||
478 | + | ||
479 | + $('body').on('click', '#save_data', function (e) { | ||
480 | + if (storage) { | ||
481 | + storage.setItem('token', $('#token').val()); | ||
482 | + storage.setItem('apiUrl', $('#apiUrl').val()); | ||
483 | + } else { | ||
484 | + alert('Your browser does not support local storage'); | ||
485 | + } | ||
486 | + }); | ||
487 | + | ||
488 | + $('body').on('click', '.send', function (e) { | ||
489 | + e.preventDefault(); | ||
490 | + var form = $(this).closest('form'); | ||
491 | + //added /g to get all the matched params instead of only first | ||
492 | + var matchedParamsInRoute = $(form).attr('action').match(/[^{]+(?=\})/g); | ||
493 | + var theId = $(this).attr('rel'); | ||
494 | + //keep a copy of action attribute in order to modify the copy | ||
495 | + //instead of the initial attribute | ||
496 | + var url = $(form).attr('action'); | ||
497 | + var method = $(form).prop('method').toLowerCase() || 'get'; | ||
498 | + | ||
499 | + var formData = new FormData(); | ||
500 | + | ||
501 | + $(form).find('input').each(function (i, input) { | ||
502 | + if ($(input).attr('type').toLowerCase() == 'file') { | ||
503 | + formData.append($(input).attr('name'), $(input)[0].files[0]); | ||
504 | + method = 'post'; | ||
505 | + } else { | ||
506 | + formData.append($(input).attr('name'), $(input).val()) | ||
507 | + } | ||
508 | + }); | ||
509 | + | ||
510 | + var index, key, value; | ||
511 | + | ||
512 | + if (matchedParamsInRoute) { | ||
513 | + var params = {}; | ||
514 | + formData.forEach(function(value, key){ | ||
515 | + params[key] = value; | ||
516 | + }); | ||
517 | + for (index = 0; index < matchedParamsInRoute.length; ++index) { | ||
518 | + try { | ||
519 | + key = matchedParamsInRoute[index]; | ||
520 | + value = params[key]; | ||
521 | + if (typeof value == "undefined") | ||
522 | + value = ""; | ||
523 | + url = url.replace("\{" + key + "\}", value); | ||
524 | + formData.delete(key); | ||
525 | + } catch (err) { | ||
526 | + console.log(err); | ||
527 | + } | ||
528 | + } | ||
529 | + } | ||
530 | + | ||
531 | + var headers = {}; | ||
532 | + | ||
533 | + var token = $('#token').val(); | ||
534 | + if (token.length > 0) { | ||
535 | + headers['token'] = token; | ||
536 | + } | ||
537 | + | ||
538 | + $("#sandbox" + theId + " .headers input[type=text]").each(function () { | ||
539 | + val = $(this).val(); | ||
540 | + if (val.length > 0) { | ||
541 | + headers[$(this).prop('name')] = val; | ||
542 | + } | ||
543 | + }); | ||
544 | + | ||
545 | + $.ajax({ | ||
546 | + url: $('#apiUrl').val() + url, | ||
547 | + data: method == 'get' ? $(form).serialize() : formData, | ||
548 | + type: method, | ||
549 | + dataType: 'json', | ||
550 | + contentType: false, | ||
551 | + processData: false, | ||
552 | + headers: headers, | ||
553 | + xhrFields: { | ||
554 | + withCredentials: true | ||
555 | + }, | ||
556 | + success: function (data, textStatus, xhr) { | ||
557 | + if (typeof data === 'object') { | ||
558 | + var str = JSON.stringify(data, null, 2); | ||
559 | + $('#response' + theId).html(syntaxHighlight(str)); | ||
560 | + } else { | ||
561 | + $('#response' + theId).html(data || ''); | ||
562 | + } | ||
563 | + $('#response_headers' + theId).html('HTTP ' + xhr.status + ' ' + xhr.statusText + '<br/><br/>' + xhr.getAllResponseHeaders()); | ||
564 | + $('#response' + theId).show(); | ||
565 | + }, | ||
566 | + error: function (xhr, textStatus, error) { | ||
567 | + try { | ||
568 | + var str = JSON.stringify($.parseJSON(xhr.responseText), null, 2); | ||
569 | + } catch (e) { | ||
570 | + var str = xhr.responseText; | ||
571 | + } | ||
572 | + $('#response_headers' + theId).html('HTTP ' + xhr.status + ' ' + xhr.statusText + '<br/><br/>' + xhr.getAllResponseHeaders()); | ||
573 | + $('#response' + theId).html(syntaxHighlight(str)); | ||
574 | + $('#response' + theId).show(); | ||
575 | + } | ||
576 | + }); | ||
577 | + return false; | ||
578 | + }); | ||
579 | + }); | ||
580 | + </script> | ||
581 | + </body> | ||
582 | +</html> |
-
请 注册 或 登录 后发表评论