commit 836bdc9409b19132a13de6ef589355be21280fb9 Author: molong Date: Sun Jan 18 09:52:48 2026 +0800 first commit diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..f904d4b --- /dev/null +++ b/.editorconfig @@ -0,0 +1,18 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = tab +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[*.{yml,yaml}] +indent_size = 2 + +[docker-compose.yml] +indent_size = 4 diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..db23f7a --- /dev/null +++ b/.env.example @@ -0,0 +1,70 @@ +APP_NAME=sentos +VERSION=1.0.0 +APP_ENV=local +APP_KEY= +APP_DEBUG=true +APP_TIMEZONE=Asia/Shanghai +APP_URL=http://localhost + +APP_LOCALE=en +APP_FALLBACK_LOCALE=en +APP_FAKER_LOCALE=en_US + +APP_MAINTENANCE_DRIVER=file +APP_MAINTENANCE_STORE=database + +BCRYPT_ROUNDS=12 + +LOG_CHANNEL=stack +LOG_STACK=single +LOG_DEPRECATIONS_CHANNEL=null +LOG_LEVEL=debug + +DB_CONNECTION=mysql +DB_HOST=127.0.0.1 +DB_PORT=3306 +DB_DATABASE=laravel +DB_USERNAME=root +DB_PASSWORD= + +SESSION_DRIVER=file +SESSION_LIFETIME=120 +SESSION_ENCRYPT=false +SESSION_PATH=/ +SESSION_DOMAIN=null + +BROADCAST_CONNECTION=log +FILESYSTEM_DISK=local +QUEUE_CONNECTION=database + +CACHE_STORE=file +CACHE_PREFIX= + +MEMCACHED_HOST=127.0.0.1 + +REDIS_CLIENT=phpredis +REDIS_HOST=127.0.0.1 +REDIS_PASSWORD=null +REDIS_PORT=6379 + +MAIL_MAILER=log +MAIL_HOST=127.0.0.1 +MAIL_PORT=2525 +MAIL_USERNAME=null +MAIL_PASSWORD=null +MAIL_ENCRYPTION=null +MAIL_FROM_ADDRESS="hello@example.com" +MAIL_FROM_NAME="${APP_NAME}" + +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_DEFAULT_REGION=us-east-1 +AWS_BUCKET= +AWS_USE_PATH_STYLE_ENDPOINT=false + +OSS_ACCESS_KEY_ID= +OSS_ACCESS_KEY_SECRET= +OSS_BUCKET=nowart +OSS_ENDPOINT=oss-cn-hangzhou.aliyuncs.com + +VITE_APP_NAME="${APP_NAME}" diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..fcb21d3 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,11 @@ +* text=auto eol=lf + +*.blade.php diff=html +*.css diff=css +*.html diff=html +*.md diff=markdown +*.php diff=php + +/.github export-ignore +CHANGELOG.md export-ignore +.styleci.yml export-ignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8254a67 --- /dev/null +++ b/.gitignore @@ -0,0 +1,30 @@ +/.phpunit.cache +/node_modules +/public/build +/public/hot +/public/storage +/public/static +/public/assets +/public/*.js +/public/.user.ini +/storage/*.key +/vendor +/resources/admin-bak +.env +.env.backup +.env.production +.phpactor.json +.phpunit.result.cache +Homestead.json +Homestead.yaml +auth.json +npm-debug.log +yarn-error.log +/.fleet +/.idea +/.vscode +composer.lock +bootstrap/cache/* +package-lock.json +yarn.lock +/resources/antdvadmin diff --git a/.htaccess b/.htaccess new file mode 100644 index 0000000..0519ecb --- /dev/null +++ b/.htaccess @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f63f5a9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,194 @@ +木兰宽松许可证,第2版 + +木兰宽松许可证,第2版 + +2020年1月 http://license.coscl.org.cn/MulanPSL2 + +您对“软件”的复制、使用、修改及分发受木兰宽松许可证,第2版(“本许可证”)的如下条款的约束: + +0. 定义 + +“软件” 是指由“贡献”构成的许可在“本许可证”下的程序和相关文档的集合。 + +“贡献” 是指由任一“贡献者”许可在“本许可证”下的受版权法保护的作品。 + +“贡献者” 是指将受版权法保护的作品许可在“本许可证”下的自然人或“法人实体”。 + +“法人实体” 是指提交贡献的机构及其“关联实体”。 + +“关联实体” 是指,对“本许可证”下的行为方而言,控制、受控制或与其共同受控制的机构,此处的控制是 +指有受控方或共同受控方至少50%直接或间接的投票权、资金或其他有价证券。 + +1. 授予版权许可 + +每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的版权许可,您可 +以复制、使用、修改、分发其“贡献”,不论修改与否。 + +2. 授予专利许可 + +每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的(根据本条规定 +撤销除外)专利许可,供您制造、委托制造、使用、许诺销售、销售、进口其“贡献”或以其他方式转移其“贡 +献”。前述专利许可仅限于“贡献者”现在或将来拥有或控制的其“贡献”本身或其“贡献”与许可“贡献”时的“软 +件”结合而将必然会侵犯的专利权利要求,不包括对“贡献”的修改或包含“贡献”的其他结合。如果您或您的“ +关联实体”直接或间接地,就“软件”或其中的“贡献”对任何人发起专利侵权诉讼(包括反诉或交叉诉讼)或 +其他专利维权行动,指控其侵犯专利权,则“本许可证”授予您对“软件”的专利许可自您提起诉讼或发起维权 +行动之日终止。 + +3. 无商标许可 + +“本许可证”不提供对“贡献者”的商品名称、商标、服务标志或产品名称的商标许可,但您为满足第4条规定 +的声明义务而必须使用除外。 + +4. 分发限制 + +您可以在任何媒介中将“软件”以源程序形式或可执行形式重新分发,不论修改与否,但您必须向接收者提供“ +本许可证”的副本,并保留“软件”中的版权、商标、专利及免责声明。 + +5. 免责声明与责任限制 + +“软件”及其中的“贡献”在提供时不带任何明示或默示的担保。在任何情况下,“贡献者”或版权所有者不对 +任何人因使用“软件”或其中的“贡献”而引发的任何直接或间接损失承担责任,不论因何种原因导致或者基于 +何种法律理论,即使其曾被建议有此种损失的可能性。 + +6. 语言 + +“本许可证”以中英文双语表述,中英文版本具有同等法律效力。如果中英文版本存在任何冲突不一致,以中文 +版为准。 + +条款结束 + +如何将木兰宽松许可证,第2版,应用到您的软件 + +如果您希望将木兰宽松许可证,第2版,应用到您的新软件,为了方便接收者查阅,建议您完成如下三步: + +1, 请您补充如下声明中的空白,包括软件名、软件的首次发表年份以及您作为版权人的名字; + +2, 请您在软件包的一级目录下创建以“LICENSE”为名的文件,将整个许可证文本放入该文件中; + +3, 请将如下声明文本放入每个源文件的头部注释中。 + +Copyright (c) [Year] [name of copyright holder] +[Software Name] is licensed under Mulan PSL v2. +You can use this software according to the terms and conditions of the Mulan +PSL v2. +You may obtain a copy of Mulan PSL v2 at: + http://license.coscl.org.cn/MulanPSL2 +THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +See the Mulan PSL v2 for more details. + +Mulan Permissive Software License,Version 2 + +Mulan Permissive Software License,Version 2 (Mulan PSL v2) + +January 2020 http://license.coscl.org.cn/MulanPSL2 + +Your reproduction, use, modification and distribution of the Software shall +be subject to Mulan PSL v2 (this License) with the following terms and +conditions: + +0. Definition + +Software means the program and related documents which are licensed under +this License and comprise all Contribution(s). + +Contribution means the copyrightable work licensed by a particular +Contributor under this License. + +Contributor means the Individual or Legal Entity who licenses its +copyrightable work under this License. + +Legal Entity means the entity making a Contribution and all its +Affiliates. + +Affiliates means entities that control, are controlled by, or are under +common control with the acting entity under this License, ‘control’ means +direct or indirect ownership of at least fifty percent (50%) of the voting +power, capital or other securities of controlled or commonly controlled +entity. + +1. Grant of Copyright License + +Subject to the terms and conditions of this License, each Contributor hereby +grants to you a perpetual, worldwide, royalty-free, non-exclusive, +irrevocable copyright license to reproduce, use, modify, or distribute its +Contribution, with modification or not. + +2. Grant of Patent License + +Subject to the terms and conditions of this License, each Contributor hereby +grants to you a perpetual, worldwide, royalty-free, non-exclusive, +irrevocable (except for revocation under this Section) patent license to +make, have made, use, offer for sale, sell, import or otherwise transfer its +Contribution, where such patent license is only limited to the patent claims +owned or controlled by such Contributor now or in future which will be +necessarily infringed by its Contribution alone, or by combination of the +Contribution with the Software to which the Contribution was contributed. +The patent license shall not apply to any modification of the Contribution, +and any other combination which includes the Contribution. If you or your +Affiliates directly or indirectly institute patent litigation (including a +cross claim or counterclaim in a litigation) or other patent enforcement +activities against any individual or entity by alleging that the Software or +any Contribution in it infringes patents, then any patent license granted to +you under this License for the Software shall terminate as of the date such +litigation or activity is filed or taken. + +3. No Trademark License + +No trademark license is granted to use the trade names, trademarks, service +marks, or product names of Contributor, except as required to fulfill notice +requirements in section 4. + +4. Distribution Restriction + +You may distribute the Software in any medium with or without modification, +whether in source or executable forms, provided that you provide recipients +with a copy of this License and retain copyright, patent, trademark and +disclaimer statements in the Software. + +5. Disclaimer of Warranty and Limitation of Liability + +THE SOFTWARE AND CONTRIBUTION IN IT ARE PROVIDED WITHOUT WARRANTIES OF ANY +KIND, EITHER EXPRESS OR IMPLIED. IN NO EVENT SHALL ANY CONTRIBUTOR OR +COPYRIGHT HOLDER BE LIABLE TO YOU FOR ANY DAMAGES, INCLUDING, BUT NOT +LIMITED TO ANY DIRECT, OR INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING +FROM YOUR USE OR INABILITY TO USE THE SOFTWARE OR THE CONTRIBUTION IN IT, NO +MATTER HOW IT’S CAUSED OR BASED ON WHICH LEGAL THEORY, EVEN IF ADVISED OF +THE POSSIBILITY OF SUCH DAMAGES. + +6. Language + +THIS LICENSE IS WRITTEN IN BOTH CHINESE AND ENGLISH, AND THE CHINESE VERSION +AND ENGLISH VERSION SHALL HAVE THE SAME LEGAL EFFECT. IN THE CASE OF +DIVERGENCE BETWEEN THE CHINESE AND ENGLISH VERSIONS, THE CHINESE VERSION +SHALL PREVAIL. + +END OF THE TERMS AND CONDITIONS + +How to Apply the Mulan Permissive Software License,Version 2 +(Mulan PSL v2) to Your Software + +To apply the Mulan PSL v2 to your work, for easy identification by +recipients, you are suggested to complete following three steps: + +i. Fill in the blanks in following statement, including insert your software +name, the year of the first publication of your software, and your name +identified as the copyright owner; + +ii. Create a file named "LICENSE" which contains the whole context of this +License in the first directory of your software package; + +iii. Attach the statement to the appropriate annotated syntax at the +beginning of each source file. + +Copyright (c) [Year] [name of copyright holder] +[Software Name] is licensed under Mulan PSL v2. +You can use this software according to the terms and conditions of the Mulan +PSL v2. +You may obtain a copy of Mulan PSL v2 at: + http://license.coscl.org.cn/MulanPSL2 +THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY +KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +See the Mulan PSL v2 for more details. diff --git a/README.md b/README.md new file mode 100644 index 0000000..813308d --- /dev/null +++ b/README.md @@ -0,0 +1,56 @@ +### Laradmin +Laradmin是一个基于Laravel + Vue 开发的后台管理系统,基础功能包括:用户管理、角色管理、菜单管理、部门管理、日志管理、字典管理、参数管理、定时任务、城市管理、客户端管理、图片上传、富文本编辑器 + +### 演示地址 +http://www.sentcms.com +账号:admin +密码:admin888 +(请勿修改密码,数据定期清理) + +### 运行环境 + +1. PHP >= 7.2.0 +2. MySQL >= 5.7.0 +3. Redis >= 4.0.0 +4. Laravel >= 11.0.0 +5. Vue >= 3.0.0 +6. Node >= 16.0.0 +7. Vite >= 4.0.0 + +### 服务器端安装 +1. 运行composer install +2. 运行composer install-project +3. 运行php artisan migrate +4. 运行php artisan module:seed +5. 运行php artisan serve +6. 访问http://localhost:8000 + +### 前端安装 +1. cd resources/admin +2. yarn install +3. yarn run serve +4. 访问http://localhost:8080 + +### 线上部署 +1. 运行composer install +2. 运行composer install-project +3. 运行php artisan migrate +4. 运行php artisan module:seed +5. 运行cd resources/admin && yarn install && yarn run build +6. 访问http://域名/admin + + +### 功能 +- [x] 用户管理 后台用户管理 +- [x] 部门管理 配置公司的部门结构,支持树形结构 +- [x] 菜单管理 配置系统菜单,按钮等等 +- [x] 角色管理 配置用户担当的角色,分配权限 +- [x] 配置管理 参数的管理 +- [x] 字典管理 字典数据管理 +- [x] 城市数据 城市数据管理 +- [x] 客户端管理 客户端管理 +- [x] 定时任务 定时任务管理 +- [x] 操作日志 后台用户操作记录 +- [x] 图片上传 图片上传管理 +- [x] 富文本编辑器 富文本编辑器管理 +- [ ] 应用管理 应用管理 diff --git a/app/Console/Commands/Workerman.php b/app/Console/Commands/Workerman.php new file mode 100644 index 0000000..87cf1dd --- /dev/null +++ b/app/Console/Commands/Workerman.php @@ -0,0 +1,87 @@ + +// +---------------------------------------------------------------------- +namespace App\Console\Commands; + +use Illuminate\Console\Command; +use GatewayWorker\BusinessWorker; +use GatewayWorker\Gateway; +use GatewayWorker\Register; +use Workerman\Worker; +use App\Events\WorkermanEvent; + +class Workerman extends Command { + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'work {action} {--d}'; + + /** + * The console command description. + * + * @var string + */ + protected $description = 'Start a Workerman server.'; + + /** + * Execute the console command. + */ + public function handle() { + global $argv; + $action = $this->argument('action'); + + $argv[0] = 'work'; + $argv[1] = $action; + $argv[2] = $this->option('d') ? '-d' : ''; + + $this->start(); + } + + public function start() { + $this->startGateWay(); + $this->startBusinessWorker(); + $this->startRegister(); + + $workerPath = storage_path('workerman/'); + if (!is_dir($workerPath)) + mkdir($workerPath, 0755, true); + Worker::$pidFile = $workerPath . config('app.name') . '_workman.pid'; + $logPath = $workerPath . date('Ym') . '/'; + if (!is_dir($logPath)) + mkdir($logPath, 0755, true); + Worker::$logFile = $logPath . date('d') . '.log'; + + Worker::runAll(); + } + + public function startGateWay() { + $gateway = new Gateway("websocket://0.0.0.0:2346"); + $gateway->name = 'Gateway'; + $gateway->count = 4; + $gateway->lanIp = '127.0.0.1'; + $gateway->startPort = 2900; + $gateway->pingInterval = 10; + $gateway->pingNotResponseLimit = 1; + $gateway->pingData = '{"type":"pong"}'; + $gateway->registerAddress = '127.0.0.1:1236'; + } + + public function startBusinessWorker() { + $worker = new BusinessWorker(); + $worker->name = 'BusinessWorker'; + $worker->count = 3; + $worker->registerAddress = '127.0.0.1:1236'; + $worker->eventHandler = WorkermanEvent::class; + } + + public function startRegister() { + $register = new Register('text://0.0.0.0:1236'); + } +} diff --git a/app/Console/Commands/WorkermanWin.php b/app/Console/Commands/WorkermanWin.php new file mode 100644 index 0000000..a77f268 --- /dev/null +++ b/app/Console/Commands/WorkermanWin.php @@ -0,0 +1,81 @@ + +// +---------------------------------------------------------------------- +namespace App\Console\Commands; + +use Illuminate\Console\Command; +use GatewayWorker\BusinessWorker; +use GatewayWorker\Gateway; +use GatewayWorker\Register; +use Workerman\Worker; +use App\Events\WorkermanEvent; + +class WorkermanWin extends Command { + + // 兼容windows + protected $signature = 'wk {action : action} {--start=all : start} {--d : daemon mode}'; + + protected $description = 'Start a Workerman server.'; + + public function handle() { + global $argv; + $action = $this->argument('action'); + + + //针对 Windows 一次执行,无法注册多个协议的特殊处理 + if ($action === 'single') { + $start = $this->option('start'); + if ($start === 'register') { + $this->startRegister(); + } elseif ($start === 'gateway') { + $this->startGateWay(); + } elseif ($start === 'worker') { + $this->startBusinessWorker(); + } + Worker::runAll(); + + return; + } + + $argv[1] = $action; + $argv[2] = $this->option('d') ? '-d' : ''; + + $this->start(); + } + + public function start() { + $this->startGateWay(); + $this->startBusinessWorker(); + $this->startRegister(); + Worker::runAll(); + } + + public function startGateWay() { + $gateway = new Gateway("websocket://0.0.0.0:2346"); + $gateway->name = 'Gateway'; + $gateway->count = 4; + $gateway->lanIp = '127.0.0.1'; + $gateway->startPort = 2900; + $gateway->pingInterval = 10; + $gateway->pingNotResponseLimit = 1; + $gateway->pingData = '{"type":"pong"}'; + $gateway->registerAddress = '127.0.0.1:1236'; + } + + public function startBusinessWorker() { + $worker = new BusinessWorker(); + $worker->name = 'BusinessWorker'; + $worker->count = 3; + $worker->registerAddress = '127.0.0.1:1236'; + $worker->eventHandler = WorkermanEvent::class; + } + + public function startRegister() { + $register = new Register('text://0.0.0.0:1236'); + } +} diff --git a/app/Events/WorkermanEvent.php b/app/Events/WorkermanEvent.php new file mode 100644 index 0000000..96bfe73 --- /dev/null +++ b/app/Events/WorkermanEvent.php @@ -0,0 +1,103 @@ + +// +---------------------------------------------------------------------- +namespace App\Events; + +use GatewayWorker\BusinessWorker; +use GatewayWorker\Lib\Gateway; +use Illuminate\Support\Facades\Log; +use Workerman\Lib\Timer; + +class WorkermanEvent { + + /** + * @title worker 启动时触发 + * + * @param BusinessWorker $businessWorker + * @return void + */ + public static function onWorkerStart(BusinessWorker $businessWorker) { + + self::log(__FUNCTION__, $businessWorker->workerId); + // 向所有客户端连接发送数据 + // Gateway::sendToAll("worker started"); + // 定时向所有客户端连接发送数据 + Timer::add(1, function() use ($businessWorker) { + $time_now = time(); + foreach ($businessWorker->connections as $connection) { + if (empty($connection->lastMessageTime)){ + $connection->lastMessageTime = $time_now; + continue; + } + if ($time_now - $connection->lastMessageTime > 10) { + $connection->lastMessageTime = $time_now; + Gateway::sendToClient($connection->id, 'pong'); + } + } + }); + } + + /** + * @title 当客户端连接时触发 + * + * @param int $client_id 连接id + * @return void + */ + public static function onConnect($client_id) { + self::log(__FUNCTION__, $client_id); + } + + /** + * @title 客户端连接时触发 + * + * @param int $client_id 连接id + * @param mixed $message 具体消息 + * @return void + */ + public static function onWebSocketConnect($client_id, $data) { + self::log(__FUNCTION__, $client_id, $data); + } + + /** + * @title 当客户端发来消息时触发 + * + * @param int $client_id 连接id + * @param mixed $message 具体消息 + * @return void + */ + public static function onMessage(string $client_id, string $message) { + self::log(__FUNCTION__, $client_id, $message); + + if (empty($message)) { + return; + } + + Gateway::sendToAll($message); + if ($message == 'ping') { + Gateway::sendToClient($client_id, 'pong'); + return; + } + } + + /** + * @title 当用户断开连接时触发 + * + * @param int $client_id 连接id + * @return void + */ + public static function onClose(string $client_id) { + self::log(__FUNCTION__, $client_id); + Gateway::destoryClient($client_id); + } + + protected static function log(string $title, ...$data): void{ + if (config('app.debug')) { + Log::info("{$title} | " . json_encode($data, 256)); + } + } +} diff --git a/app/Http/Controllers/Admin/Auth/Department.php b/app/Http/Controllers/Admin/Auth/Department.php new file mode 100644 index 0000000..053d4ca --- /dev/null +++ b/app/Http/Controllers/Admin/Auth/Department.php @@ -0,0 +1,87 @@ + +// +---------------------------------------------------------------------- +namespace App\Http\Controllers\Admin\Auth; + +use Illuminate\Http\Request; +use App\Http\Controllers\BaseController; +use App\Services\Auth\DepartmentService; + +class Department extends BaseController { + + + /** + * @title 部门列表 + * + * @return \Illuminate\Http\JsonResponse + */ + public function index(Request $request, DepartmentService $service){ + try { + $this->data['data'] = $service->getDataList($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 添加部门 + * + * @param Request $request + * @param Department $service + * @return void + */ + public function add(Request $request, DepartmentService $service){ + try { + $this->data['data'] = $service->create($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 修改部门 + * + * @param Request $request + * @param DepartmentService $service + * @return void + */ + public function edit(Request $request, DepartmentService $service){ + try { + $this->data['data'] = $service->update($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 删除部门 + * + * @param Request $request + * @param DepartmentService $service + * @return void + */ + public function delete(Request $request, DepartmentService $service){ + try { + $this->data['data'] = $service->delete($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } +} diff --git a/app/Http/Controllers/Admin/Auth/Index.php b/app/Http/Controllers/Admin/Auth/Index.php new file mode 100644 index 0000000..83f286d --- /dev/null +++ b/app/Http/Controllers/Admin/Auth/Index.php @@ -0,0 +1,88 @@ + +// +---------------------------------------------------------------------- +namespace App\Http\Controllers\Admin\Auth; + +use App\Http\Controllers\BaseController; +use Illuminate\Support\Facades\Auth; +use Illuminate\Http\Request; +use Illuminate\Auth\Events\Login; + +class Index extends BaseController { + + /** + * @title 用户登录 + * + * @return \Illuminate\Http\JsonResponse + */ + public function login() { + $credentials = request(['username', 'password']); + + $token = auth('admin')->attempt($credentials); + + if (!$token) { + $this->data['code'] = 0; + $this->data['message'] = '登录失败!'; + }else{ + $this->data['code'] = 1; + $this->data['message'] = '登录成功!'; + $this->data['data'] = [ + 'access_token' => $token, + 'token_type' => 'bearer', + 'expires_in' => auth('admin')->factory()->getTTL() * 60 + ]; + event(new Login('admin', auth('admin')->user(), false)); + } + return response()->json($this->data); + } + + /** + * @title 获取用户信息 + * + * @return \Illuminate\Http\JsonResponse + */ + public function user() { + try { + $user = auth('admin')->userOrFail(); + $user->roles = $user->roles()->get(); + $this->data['data'] = $user; + } catch (\Tymon\JWTAuth\Exceptions\UserNotDefinedException $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 退出 + * @desc 退出 + * @method POST + * @param \Illuminate\Http\Request $request + * + * @return \Illuminate\Http\JsonResponse + */ + public function logout() { + auth('admin')->logout(); + $this->data['message'] = '退出成功'; + return response()->json($this->data); + } + + /** + * @title 刷新token + * @desc 刷新token + * @method POST + * @param \Illuminate\Http\Request $request + * + * @return \Illuminate\Http\JsonResponse + */ + public function refresh() { + $this->data['data'] = auth('admin')->refresh(); + return response()->json($this->data); + } +} diff --git a/app/Http/Controllers/Admin/Auth/Menu.php b/app/Http/Controllers/Admin/Auth/Menu.php new file mode 100644 index 0000000..09e12c6 --- /dev/null +++ b/app/Http/Controllers/Admin/Auth/Menu.php @@ -0,0 +1,103 @@ + +// +---------------------------------------------------------------------- +namespace App\Http\Controllers\Admin\Auth; + +use Illuminate\Http\Request; +use App\Http\Controllers\BaseController; +use App\Services\Auth\MenuService; + +class Menu extends BaseController { + + + /** + * @title 权限节点列表 + * + * @return \Illuminate\Http\JsonResponse + */ + public function index(Request $request, MenuService $service){ + try { + $this->data['data'] = $service->getDataList($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 获取我的权限节点 + * + * @return \Illuminate\Http\JsonResponse + */ + public function my(Request $request, MenuService $service){ + try { + $this->data['data'] = $service->getMyMenuList($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 添加权限节点 + * + * @param Request $request + * @param Department $service + * @return void + */ + public function add(Request $request, MenuService $service){ + try { + $this->data['data'] = $service->create($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 修改权限节点 + * + * @param Request $request + * @param MenuService $service + * @return void + */ + public function edit(Request $request, MenuService $service){ + try { + $this->data['data'] = $service->update($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 删除权限节点 + * + * @param Request $request + * @param MenuService $service + * @return void + */ + public function delete(Request $request, MenuService $service){ + try { + $this->data['data'] = $service->delete($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } +} diff --git a/app/Http/Controllers/Admin/Auth/Role.php b/app/Http/Controllers/Admin/Auth/Role.php new file mode 100644 index 0000000..85ee6d0 --- /dev/null +++ b/app/Http/Controllers/Admin/Auth/Role.php @@ -0,0 +1,105 @@ + +// +---------------------------------------------------------------------- +namespace App\Http\Controllers\Admin\Auth; + +use Illuminate\Http\Request; +use App\Http\Controllers\BaseController; +use App\Services\Auth\RoleService; + +class Role extends BaseController { + + + /** + * @title 角色列表 + * + * @return \Illuminate\Http\JsonResponse + */ + public function index(Request $request, RoleService $service){ + try { + $this->data['data'] = $service->getDataList($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 添加角色 + * + * @param Request $request + * @param Department $service + * @return void + */ + public function add(Request $request, RoleService $service){ + try { + $this->data['data'] = $service->create($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 修改角色 + * + * @param Request $request + * @param RoleService $service + * @return void + */ + public function edit(Request $request, RoleService $service){ + try { + $this->data['data'] = $service->update($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 删除角色 + * + * @param Request $request + * @param RoleService $service + * @return void + */ + public function delete(Request $request, RoleService $service){ + try { + $this->data['data'] = $service->delete($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 角色权限 + * + * @param Request $request + * @param RoleService $service + * @return void + */ + public function auth(Request $request, RoleService $service){ + try { + $this->data['data'] = $service->auth($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } +} diff --git a/app/Http/Controllers/Admin/Auth/Users.php b/app/Http/Controllers/Admin/Auth/Users.php new file mode 100644 index 0000000..ad3bc68 --- /dev/null +++ b/app/Http/Controllers/Admin/Auth/Users.php @@ -0,0 +1,123 @@ + +// +---------------------------------------------------------------------- +namespace App\Http\Controllers\Admin\Auth; + +use Illuminate\Http\Request; +use App\Http\Controllers\BaseController; +use App\Services\Auth\UsersService; + +class Users extends BaseController { + + + /** + * @title 用户列表 + * + * @return \Illuminate\Http\JsonResponse + */ + public function index(Request $request, UsersService $service){ + try { + $this->data['data'] = $service->getDataList($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 添加用户 + * + * @param Request $request + * @param UsersService $service + * @return void + */ + public function add(Request $request, UsersService $service){ + try { + $this->data['data'] = $service->create($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 修改用户 + * + * @param Request $request + * @param UsersService $service + * @return void + */ + public function edit(Request $request, UsersService $service){ + try { + $this->data['data'] = $service->update($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 删除用户 + * + * @param Request $request + * @param UsersService $service + * @return void + */ + public function delete(Request $request, UsersService $service){ + try { + $this->data['data'] = $service->delete($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 修改密码 + * + * @param Request $request + * @param UsersService $service + * @return void + */ + public function passwd(Request $request, UsersService $service){ + try { + $this->data['data'] = $service->upPassword($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 角色分配 + * + * @param Request $request + * @param UsersService $service + * @return void + */ + public function uprole(Request $request, UsersService $service){ + try { + $this->data['data'] = $service->uprole($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } +} diff --git a/app/Http/Controllers/Admin/System/Area.php b/app/Http/Controllers/Admin/System/Area.php new file mode 100644 index 0000000..4e129a4 --- /dev/null +++ b/app/Http/Controllers/Admin/System/Area.php @@ -0,0 +1,67 @@ + +// +---------------------------------------------------------------------- +namespace App\Http\Controllers\Admin\System; + +use Illuminate\Http\Request; +use App\Http\Controllers\BaseController; +use App\Services\System\AreaService; + +class Area extends BaseController { + + /** + * @title 地区列表 + * + * @param Request $request + * @param AreaService $service + * @return void + */ + public function index(Request $request, AreaService $service){ + try { + $this->data['data'] = $service->getAreaList($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + return response()->json($this->data); + } + + /** + * @title 添加地区 + * + * @param Request $request + * @param AreaService $service + * @return void + */ + public function add(Request $request, AreaService $service){ + try { + $this->data['data'] = $service->create($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + return response()->json($this->data); + } + + /** + * @title 编辑地区 + * + * @param Request $request + * @param AreaService $service + * @return void + */ + public function edit(Request $request, AreaService $service){ + try { + $this->data['data'] = $service->update($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + return response()->json($this->data); + } +} diff --git a/app/Http/Controllers/Admin/System/Client.php b/app/Http/Controllers/Admin/System/Client.php new file mode 100644 index 0000000..a0ba758 --- /dev/null +++ b/app/Http/Controllers/Admin/System/Client.php @@ -0,0 +1,24 @@ + +// +---------------------------------------------------------------------- +namespace App\Http\Controllers\Admin\System; + +use Illuminate\Http\Request; +use App\Http\Controllers\BaseController; +use App\Traits\AdminController; +use App\Services\System\ClientService; + +class Client extends BaseController { + + use AdminController; + protected $service = null; + + public function __construct(ClientService $service) { + $this->service = $service; + } +} diff --git a/app/Http/Controllers/Admin/System/Crontab.php b/app/Http/Controllers/Admin/System/Crontab.php new file mode 100644 index 0000000..5555344 --- /dev/null +++ b/app/Http/Controllers/Admin/System/Crontab.php @@ -0,0 +1,34 @@ + +// +---------------------------------------------------------------------- +namespace App\Http\Controllers\Admin\System; + +use Illuminate\Http\Request; +use App\Http\Controllers\BaseController; +use App\Traits\AdminController; +use App\Services\System\CrontabService; + +class Crontab extends BaseController { + + use AdminController; + protected $service = null; + + public function __construct(CrontabService $service) { + $this->service = $service; + } + + public function reload(Request $request){ + try { + $this->data['data'] = $this->service->reload($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + return $this->data; + } +} diff --git a/app/Http/Controllers/Admin/System/Dict.php b/app/Http/Controllers/Admin/System/Dict.php new file mode 100644 index 0000000..aac77c2 --- /dev/null +++ b/app/Http/Controllers/Admin/System/Dict.php @@ -0,0 +1,175 @@ + +// +---------------------------------------------------------------------- +namespace App\Http\Controllers\Admin\System; + +use Illuminate\Http\Request; +use App\Http\Controllers\BaseController; +use App\Services\System\DictService; + +class Dict extends BaseController { + + /** + * @title 字典列表 + * + * @return void + */ + public function all(Request $request, DictService $service){ + try { + $this->data['data'] = $service->getDictionaryAll($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + return $this->data; + } + + /** + * @title 字典列表 + * + * @param Request $request + * @param DictService $service + * @return void + */ + public function lists(Request $request, DictService $service){ + try { + $this->data['data'] = $service->getDataList($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 字段分类 + * + * @param Request $request + * @param DictService $service + * @return void + */ + public function category(Request $request, DictService $service){ + try { + $this->data['data'] = $service->getCategoryList($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 添加字典 + * + * @param Request $request + * @param DictService $service + * @return void + */ + public function add(Request $request, DictService $service){ + try { + $this->data['data'] = $service->create($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 修改字典 + * + * @param Request $request + * @param DictService $service + * @return void + */ + public function edit(Request $request, DictService $service){ + try { + $this->data['data'] = $service->update($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 删除字典 + * + * @param Request $request + * @param DictService $service + * @return void + */ + public function delete(Request $request, DictService $service){ + try { + $this->data['data'] = $service->delete($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 添加字典分类 + * + * @param Request $request + * @param DictService $service + * @return void + */ + public function addcate(Request $request, DictService $service){ + try { + $this->data['data'] = $service->createCategory($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 修改字典分类 + * + * @param Request $request + * @param DictService $service + * @return void + */ + public function editcate(Request $request, DictService $service){ + try { + $this->data['data'] = $service->updateCategory($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 删除字典分类 + * + * @param Request $request + * @param DictService $service + * @return void + */ + public function deleteCate(Request $request, DictService $service){ + try { + $this->data['data'] = $service->deleteCategory($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } +} diff --git a/app/Http/Controllers/Admin/System/File.php b/app/Http/Controllers/Admin/System/File.php new file mode 100644 index 0000000..70039cb --- /dev/null +++ b/app/Http/Controllers/Admin/System/File.php @@ -0,0 +1,179 @@ + +// +---------------------------------------------------------------------- +namespace App\Http\Controllers\Admin\System; + +use Illuminate\Http\Request; +use Illuminate\Support\Facades\Storage; +use App\Http\Controllers\BaseController; + +class File extends BaseController { + + /** + * @title 上传文件 + * + * @param Request $request + * @return void + */ + public function upload(Request $request){ + $type = $request->input('type', 'images'); + + try { + switch ($type) { + case 'avatar': + $this->data['data'] = $this->avatar($request); + break; + case 'images': + $this->data['data'] = $this->images($request); + break; + case 'files': + $this->data['data'] = $this->files($request); + break; + default: + $this->data['data'] = $this->images($request); + break; + } + } catch (\Throwable $th) { + $this->data['code'] = 0; + $this->data['message'] = $th->getMessage(); + } + return response()->json($this->data); + } + + /** + * @title 文件列表 + * + * @param Request $request + * @return void + */ + public function lists(Request $request){ + $path = $request->input('groupId', 'images'); + + $files = Storage::allFiles($path); + + $data = []; + foreach ($files as $key => $value) { + $data[] = [ + 'path' => $value, + 'url' => Storage::disk()->url($path), + 'name'=> explode('/', $value)[2], + ]; + } + $this->data['data'] = ['data' => $data]; + return response()->json($this->data); + } + + + /** + * @title 列表菜单 + * + * @return void + */ + public function menu(Request $request){ + $file = [ + ['id'=> 'images','title'=>'图片', 'children' => []], + ['id'=> 'files','title'=>'文件', 'children' => []], + ['id'=> 'videos','title'=>'视频', 'children' => []], + ['id'=> 'audios','title'=>'音频', 'children' => []], + ]; + + foreach ($file as $key => $value) { + $paths = Storage::disk()->allDirectories($value['id']); + foreach ($paths as $key => $value) { + $file[$key]['children'][] = ['id'=> $value, 'title'=>explode('/', $value)[1]]; + } + } + + $this->data['data'] = $file; + return response()->json($this->data); + } + + /** + * @title 删除文件 + * + * @return void + */ + public function delete(Request $request){ + return response()->json($this->data); + } + + /** + * @title 上传头像 + * + * @param Request $request + * @return void + */ + protected function avatar(Request $request){ + $file = $request->file('file'); + + if ($file->isValid()) { + $ext = $file->extension(); + $name = $file->hashName(); + + $path = $file->store('avatar/'.date('Ymd')); + + $data = [ + 'path' => $path, + 'url' => Storage::disk()->url($path), + 'src' => Storage::disk()->url($path), + 'name'=> $name, + 'size'=> $file->getSize(), + 'ext' => $ext, + ]; + return $data; + }else{ + throw new \Exception('上传失败'); + } + } + + protected function images(Request $request){ + $file = $request->file('file'); + + if ($file->isValid()) { + $ext = $file->extension(); + $name = $file->hashName(); + + $path = $file->store('images/'.date('Ymd')); + + $data = [ + 'path' => $path, + 'url' => Storage::disk()->url($path), + 'src' => Storage::disk()->url($path), + 'name'=> $name, + 'size'=> $file->getSize(), + 'ext' => $ext, + ]; + return $data; + }else{ + throw new \Exception('上传失败'); + } + } + + protected function files(Request $request){ + $file = $request->file('file'); + + if ($file->isValid()) { + $ext = $file->extension(); + $name = $file->hashName(); + + $path = $file->store('files/'.date('Ymd')); + + $data = [ + 'path' => $path, + 'url' => Storage::disk()->url($path), + 'src' => Storage::disk()->url($path), + 'name'=> $name, + 'size'=> $file->getSize(), + 'ext' => $ext, + ]; + return $data; + }else{ + throw new \Exception('上传失败'); + } + } +} diff --git a/app/Http/Controllers/Admin/System/Index.php b/app/Http/Controllers/Admin/System/Index.php new file mode 100644 index 0000000..f4b1d25 --- /dev/null +++ b/app/Http/Controllers/Admin/System/Index.php @@ -0,0 +1,59 @@ + +// +---------------------------------------------------------------------- +namespace App\Http\Controllers\Admin\System; + +use Illuminate\Http\Request; +use App\Http\Controllers\BaseController; + +class Index extends BaseController { + + /** + * @title 系统信息 + * + * @return void + */ + public function version(Request $request){ + $system_info_mysql = \Illuminate\Support\Facades\DB::select("select version() as v;"); + + $this->data['data'] = [ + ['label' => '系统核心版本', 'values' => env('VERSION', '0.0.1')], + ['label' => '服务器操作系统', 'values' => PHP_OS], + ['label' => '运行环境', 'values' => $_SERVER['SERVER_SOFTWARE']], + ['label' => 'MYSQL版本', 'values' => $system_info_mysql[0]->v], + ['label' => '上传限制', 'values' => '10'] + ]; + return response()->json($this->data); + } + + /** + * @title 系统信息 + * + * @return void + */ + public function info(Request $request){ + $this->data['data'] = [ + ['label' => '系统名称', 'values' => env('APP_NAME', 'LarAdmin')], + ['label' => '后端框架', 'values' => 'PHP8 + Laravel ' . \Illuminate\Foundation\Application::VERSION], + ['label' => '前端框架', 'values' => 'VITE + VUE3 + Element Plus'], + ['label' => '系统后台UI', 'values' => 'SentUI 1.0.0'], + ['label' => 'Development', 'values' => 'molong'] + ]; + return response()->json($this->data); + } + + /** + * @title 清理缓存 + * + * @return void + */ + public function clearcache(Request $request){ + $this->data['data'] = cache()->flush(); + return response()->json($this->data); + } +} diff --git a/app/Http/Controllers/Admin/System/Log.php b/app/Http/Controllers/Admin/System/Log.php new file mode 100644 index 0000000..8ab338c --- /dev/null +++ b/app/Http/Controllers/Admin/System/Log.php @@ -0,0 +1,68 @@ + +// +---------------------------------------------------------------------- +namespace App\Http\Controllers\Admin\System; + +use Illuminate\Http\Request; +use App\Http\Controllers\BaseController; +use App\Services\System\LogService; + +class Log extends BaseController { + + /** + * @title 日志列表 + * @param Request $request + * @param LogService $service + * @return \Illuminate\Http\JsonResponse + */ + public function index(Request $request, LogService $service){ + try { + $this->data['data'] = $service->getDataList($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 我的日志列表 + * @param Request $request + * @param LogService $service + * @return \Illuminate\Http\JsonResponse + */ + public function my(Request $request, LogService $service){ + try { + $request->merge(['is_my' => 1]); + $this->data['data'] = $service->getDataList($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 删除日志 + * @param Request $request + * @param LogService $service + * @return \Illuminate\Http\JsonResponse + */ + public function delete(Request $request, LogService $service){ + try { + $this->data['data'] = $service->delete($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } +} diff --git a/app/Http/Controllers/Admin/System/Menu.php b/app/Http/Controllers/Admin/System/Menu.php new file mode 100644 index 0000000..fb3f816 --- /dev/null +++ b/app/Http/Controllers/Admin/System/Menu.php @@ -0,0 +1,24 @@ + +// +---------------------------------------------------------------------- +namespace App\Http\Controllers\Admin\System; + +use Illuminate\Http\Request; +use App\Http\Controllers\BaseController; +use App\Traits\AdminController; +use App\Services\System\ClientMenuService; + +class Menu extends BaseController { + + use AdminController; + protected $service = null; + + public function __construct(ClientMenuService $service) { + $this->service = $service; + } +} diff --git a/app/Http/Controllers/Admin/System/Modules.php b/app/Http/Controllers/Admin/System/Modules.php new file mode 100644 index 0000000..81ca5b0 --- /dev/null +++ b/app/Http/Controllers/Admin/System/Modules.php @@ -0,0 +1,34 @@ + +// +---------------------------------------------------------------------- +namespace App\Http\Controllers\Admin\System; + +use Illuminate\Http\Request; +use App\Http\Controllers\BaseController; +use App\Traits\AdminController; +use App\Services\System\ModulesService; + +class Modules extends BaseController { + + use AdminController; + protected $service = null; + + public function __construct(ModulesService $service) { + $this->service = $service; + } + + public function update(Request $request){ + try { + $this->data['data'] = $this->service->update($request); + } catch (\Throwable $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + return response()->json($this->data); + } +} diff --git a/app/Http/Controllers/Admin/System/Setting.php b/app/Http/Controllers/Admin/System/Setting.php new file mode 100644 index 0000000..8cba304 --- /dev/null +++ b/app/Http/Controllers/Admin/System/Setting.php @@ -0,0 +1,99 @@ + +// +---------------------------------------------------------------------- +namespace App\Http\Controllers\Admin\System; + +use Illuminate\Http\Request; +use App\Http\Controllers\BaseController; +use App\Services\System\SettingService; + +class Setting extends BaseController { + + /** + * @title 获取配置列表 + * + * @param Request $request + * @param SettingService $service + * @return void + */ + public function index(Request $request, SettingService $service){ + try { + $this->data['data'] = $service->getDataList($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 创建配置 + * + * @param Request $request + * @param SettingService $service + * @return void + */ + public function add(Request $request, SettingService $service){ + try { + $this->data['data'] = $service->create($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } + + public function edit(Request $request, SettingService $service){ + try { + $this->data['data'] = $service->update($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 更新配置 + * + * @param Request $request + * @param SettingService $service + * @return void + */ + public function save(Request $request, SettingService $service){ + try { + $this->data['data'] = $service->saveSetting($request); + $this->data['message'] = "保存成功!"; + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 获取配置字段 + * + * @param Request $request + * @param SettingService $service + * @return void + */ + public function fields(Request $request, SettingService $service){ + try { + $this->data['data'] = $service->getFields($request); + } catch (\Throwable $th) { + $this->data['code'] = 0; + $this->data['message'] = $th->getMessage(); + } + return response()->json($this->data); + } +} diff --git a/app/Http/Controllers/Admin/System/Tasks.php b/app/Http/Controllers/Admin/System/Tasks.php new file mode 100644 index 0000000..364566a --- /dev/null +++ b/app/Http/Controllers/Admin/System/Tasks.php @@ -0,0 +1,88 @@ + +// +---------------------------------------------------------------------- +namespace App\Http\Controllers\Admin\System; + +use Illuminate\Http\Request; +use App\Http\Controllers\BaseController; +use App\Services\System\TasksService; + +class Tasks extends BaseController { + + /** + * @title 任务列表 + * + * @param Request $request + * @param TasksService $service + * @return void + */ + public function index(Request $request, TasksService $service){ + try { + $this->data['data'] = $service->getDataList($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 添加任务 + * + * @param Request $request + * @param TasksService $service + * @return void + */ + public function add(Request $request, TasksService $service){ + try { + $this->data['data'] = $service->create($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 编辑任务 + * + * @param Request $request + * @param TasksService $service + * @return void + */ + public function edit(Request $request, TasksService $service){ + try { + $this->data['data'] = $service->update($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 删除任务 + * + * @param Request $request + * @param TasksService $service + * @return void + */ + public function delete(Request $request, TasksService $service){ + try { + $this->data['data'] = $service->delete($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } +} diff --git a/app/Http/Controllers/Api/System/Area.php b/app/Http/Controllers/Api/System/Area.php new file mode 100644 index 0000000..ffda325 --- /dev/null +++ b/app/Http/Controllers/Api/System/Area.php @@ -0,0 +1,34 @@ + +// +---------------------------------------------------------------------- +namespace App\Http\Controllers\Api\System; + +use Illuminate\Http\Request; +use App\Http\Controllers\BaseController; +use App\Services\System\AreaService; + +class Area extends BaseController { + + /** + * @title 获取地区数据 + * + * @param Request $request + * @return void + */ + public function index(Request $request, AreaService $service){ + try { + $this->data['data'] = $service->getDataList($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } + +} diff --git a/app/Http/Controllers/Api/System/Index.php b/app/Http/Controllers/Api/System/Index.php new file mode 100644 index 0000000..19acfd2 --- /dev/null +++ b/app/Http/Controllers/Api/System/Index.php @@ -0,0 +1,34 @@ + +// +---------------------------------------------------------------------- +namespace App\Http\Controllers\Api\System; + +use Illuminate\Http\Request; +use App\Http\Controllers\BaseController; +use App\Services\System\QrcodeService; + +class Index extends BaseController { + + /** + * @title 生成二维码 + * + * @param Request $request + * @return void + */ + public function qrcode(Request $request, QrcodeService $service){ + try { + $this->data['data'] = $service->getQrcode($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } + +} diff --git a/app/Http/Controllers/BaseController.php b/app/Http/Controllers/BaseController.php new file mode 100644 index 0000000..7dc700b --- /dev/null +++ b/app/Http/Controllers/BaseController.php @@ -0,0 +1,25 @@ + +// +---------------------------------------------------------------------- +namespace App\Http\Controllers; + +use Illuminate\Http\Request; +use Illuminate\Support\Facades\Http; +use Illuminate\Support\Facades\Process; + +class BaseController { + + protected $data = ['code' => 1, 'message' => 'success', 'data' => []]; + + public function __construct(Request $request){ + if ($request->filled('page')) { + $offset = ($request->input('page', 1) - 1) * $request->input('limit', 30); + $request->mergeIfMissing(['offset' => $offset]); + } + } +} diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php new file mode 100644 index 0000000..8677cd5 --- /dev/null +++ b/app/Http/Controllers/Controller.php @@ -0,0 +1,8 @@ + +// +---------------------------------------------------------------------- +namespace App\Http\Middleware; + +use Closure; +use Illuminate\Http\Request; +use Symfony\Component\HttpFoundation\Response; +use App\Services\System\LogService; +use Illuminate\Support\Facades\Log; + +class AuthCheckMiddleware { + + public function handle(Request $request, Closure $next, string $guard = 'admin'): Response { + $response = $next($request); + if (auth($guard)->check()) { + if($guard == 'admin'){ + app(LogService::class)->createLog($request); + } + return $response; + }else{ + return response()->json(['code' => 2000, 'message' => '未登录,请重新登录!']); + } + } +} diff --git a/app/Listeners/AdminLogin.php b/app/Listeners/AdminLogin.php new file mode 100644 index 0000000..8de5934 --- /dev/null +++ b/app/Listeners/AdminLogin.php @@ -0,0 +1,33 @@ + +// +---------------------------------------------------------------------- +namespace App\Listeners; + +use Illuminate\Auth\Events\Login; +use App\Models\Auth\Admin; + +class AdminLogin { + + /** + * Create the event listener. + */ + public function __construct() { + // + } + + /** + * Handle the event. + */ + public function handle(Login $event): void { + $admin = $event->user; + + $admin->last_login_ip = request()->ip(); + $admin->last_login_at = date('Y-m-d H:i:s'); + $admin->save(); + } +} diff --git a/app/Models/Auth/Admin.php b/app/Models/Auth/Admin.php new file mode 100644 index 0000000..347418b --- /dev/null +++ b/app/Models/Auth/Admin.php @@ -0,0 +1,60 @@ + +// +---------------------------------------------------------------------- +namespace App\Models\Auth; + +use Tymon\JWTAuth\Contracts\JWTSubject; +use Illuminate\Notifications\Notifiable; +use Illuminate\Foundation\Auth\User as Authenticatable; +use Illuminate\Database\Eloquent\Casts\Attribute; +use Illuminate\Support\Facades\Hash; +use App\Traits\ModelTrait; + +class Admin extends Authenticatable implements JWTSubject { + use Notifiable, ModelTrait; + + protected $table = 'auth_admins'; + protected $primaryKey = 'uid'; + protected $fillable = ['username', 'nickname', 'email', 'mobile', 'password', 'department_id', 'status', 'last_login_at', 'last_login_ip']; + protected $hidden = ['password', 'deleted_at']; + protected $with = ['department', 'roles']; + + // Rest omitted for brevity + + /** + * Get the identifier that will be stored in the subject claim of the JWT. + * + * @return mixed + */ + public function getJWTIdentifier() { + return $this->getKey(); + } + + /** + * Return a key value array, containing any custom claims to be added to the JWT. + * + * @return array + */ + public function getJWTCustomClaims() { + return []; + } + + public function password() : Attribute { + return new Attribute( + set: fn ($value) => Hash::make($value), + ); + } + + public function department(){ + return $this->belongsTo(Department::class, 'department_id', 'id'); + } + + public function roles(){ + return $this->belongsToMany(Role::class, 'auth_admins_roles', 'uid', 'role_id'); + } +} diff --git a/app/Models/Auth/Department.php b/app/Models/Auth/Department.php new file mode 100644 index 0000000..3bc756f --- /dev/null +++ b/app/Models/Auth/Department.php @@ -0,0 +1,27 @@ + +// +---------------------------------------------------------------------- +namespace App\Models\Auth; + +class Department extends \App\Models\BaseModel { + + protected $table = 'auth_departments'; + protected $fillable = ['title', 'name', 'parent_id', 'status', 'sort']; + protected $hidden = ['deleted_at']; + + protected function casts(): array { + return [ + 'parent_id' => 'integer', + 'sort' => 'integer', + ]; + } + + public function members(){ + return $this->hasMany(Admin::class, 'department_id', 'uid'); + } +} diff --git a/app/Models/Auth/Permission.php b/app/Models/Auth/Permission.php new file mode 100644 index 0000000..5b167c2 --- /dev/null +++ b/app/Models/Auth/Permission.php @@ -0,0 +1,39 @@ + +// +---------------------------------------------------------------------- +namespace App\Models\Auth; + +use Illuminate\Database\Eloquent\Casts\Attribute; + +class Permission extends \App\Models\BaseModel { + + protected $table = 'auth_permissions'; + protected $fillable = ['title', 'name', 'parent_id', 'hiddenBreadcrumb', 'hidden', 'affix', 'color', 'fullpage', 'icon', 'status', 'sort']; + protected $hidden = ['deleted_at']; + + public function meta() : Attribute { + return Attribute::make( + get: fn (mixed $value, array $data) => [ + 'id' => $data['id'], + 'parent_id' => $data['parent_id'], + 'title' => $data['title'], + 'hiddenBreadcrumb' => $data['hiddenBreadcrumb'], + 'hidden' => $data['hidden'], + 'affix' => $data['affix'], + 'color' => $data['color'], + 'fullpage' => $data['fullpage'], + 'icon' => isset($data['icon']) && $data['icon'] ? $data['icon'] : 'el-icon-document', + // 'icon' => isset($data['icon']) && $data['icon'] ? $data['icon'] : 'AIconFileTextOutlined', + ], + ); + } + + public function roles(){ + return $this->belongsToMany(Role::class, 'auth_roles_permissions', 'permission_id', 'role_id'); + } +} diff --git a/app/Models/Auth/Role.php b/app/Models/Auth/Role.php new file mode 100644 index 0000000..744751c --- /dev/null +++ b/app/Models/Auth/Role.php @@ -0,0 +1,23 @@ + +// +---------------------------------------------------------------------- +namespace App\Models\Auth; + +class Role extends \App\Models\BaseModel { + + protected $table = 'auth_roles'; + protected $fillable = ['title', 'name', 'data_range', 'sort', 'status', 'description']; + protected $hidden = ['deleted_at']; + + public function admins(){ + return $this->belongsToMany(Admin::class, 'auth_admins_roles', 'role_id', 'uid'); + } + public function permissions(){ + return $this->belongsToMany(Permission::class, 'auth_roles_permissions', 'role_id', 'permission_id'); + } +} diff --git a/app/Models/BaseModel.php b/app/Models/BaseModel.php new file mode 100644 index 0000000..697a4ad --- /dev/null +++ b/app/Models/BaseModel.php @@ -0,0 +1,46 @@ + +// +---------------------------------------------------------------------- +namespace App\Models; + +use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Facades\Schema; + +class BaseModel extends Model { + + protected $dateFormat = 'Y-m-d H:i:s'; + + protected function casts(): array { + return [ + 'created_at' => 'datetime:Y-m-d H:i:s', + 'updated_at' => 'datetime:Y-m-d H:i:s', + 'deleted_at' => 'datetime:Y-m-d H:i:s', + ]; + } + + protected function serializeDate($data){ + return $data->timezone('Asia/Shanghai')->format('Y-m-d H:i:s'); + } + /** + * 过滤移除非当前表的字段参数 + * + * @param array $params + * + * @return array + */ + public function setFilterFields(array $params) : array { + $fields = Schema::getColumnListing($this->getTable()); + foreach ($params as $key => $param) { + if ( !in_array($key, $fields) ) unset($params[$key]); + } + // 同时过滤时间戳字段【时间戳只允许自动更改,不允许手动设置】 + if ( $this->timestamps === true && isset($params[self::CREATED_AT]) ) unset($params[self::CREATED_AT]); + if ( $this->timestamps === true && isset($params[self::UPDATED_AT]) ) unset($params[self::UPDATED_AT]); + return $params; + } +} diff --git a/app/Models/System/Area.php b/app/Models/System/Area.php new file mode 100644 index 0000000..55b1cf0 --- /dev/null +++ b/app/Models/System/Area.php @@ -0,0 +1,16 @@ + +// +---------------------------------------------------------------------- +namespace App\Models\System; + +class Area extends \App\Models\BaseModel { + + protected $table = 'system_areas'; + protected $fillable = ['title', 'parent_id', 'status']; + protected $hidden = ['deleted_at']; +} diff --git a/app/Models/System/Client.php b/app/Models/System/Client.php new file mode 100644 index 0000000..08c3487 --- /dev/null +++ b/app/Models/System/Client.php @@ -0,0 +1,39 @@ + +// +---------------------------------------------------------------------- +namespace App\Models\System; + +use Illuminate\Database\Eloquent\Casts\Attribute; + +class Client extends \App\Models\BaseModel { + + protected $table = 'system_client'; + protected $fillable = ['title', 'app_id', 'secret', 'redirect', 'status']; + + protected function casts(): array { + return [ + 'status' => 'integer', + ]; + } + + public function appId() : Attribute { + return Attribute::make( + set: function (mixed $value, array $data){ + return uniqid(); + } + ); + } + + public function secret() : Attribute { + return Attribute::make( + set: function (mixed $value, array $data){ + return md5(uniqid()); + } + ); + } +} diff --git a/app/Models/System/ClientMenu.php b/app/Models/System/ClientMenu.php new file mode 100644 index 0000000..770a64d --- /dev/null +++ b/app/Models/System/ClientMenu.php @@ -0,0 +1,27 @@ + +// +---------------------------------------------------------------------- +namespace App\Models\System; + +use Illuminate\Database\Eloquent\Casts\Attribute; + +class ClientMenu extends \App\Models\BaseModel { + + protected $table = 'system_client_menu'; + protected $with = ['client']; + + protected function casts(): array { + return [ + 'status' => 'integer', + 'sort' => 'integer', + ]; + } + public function client(){ + return $this->belongsTo(Client::class, 'client_id', 'id'); + } +} diff --git a/app/Models/System/Config.php b/app/Models/System/Config.php new file mode 100644 index 0000000..0994f97 --- /dev/null +++ b/app/Models/System/Config.php @@ -0,0 +1,45 @@ + +// +---------------------------------------------------------------------- +namespace App\Models\System; + +use Illuminate\Database\Eloquent\Casts\Attribute; + +class Config extends \App\Models\BaseModel { + + protected $table = 'system_configs'; + protected $fillable = ['values', 'name', 'title', 'group', 'remark', 'type', 'status', 'sort']; + protected $hidden = ['deleted_at']; + + protected function casts(): array { + return [ + 'option' => 'json', + ]; + } + + public function options(): Attribute { + return Attribute::make( + get: fn (mixed $value, array $data) => [ + 'placeholder' => $data['title'], + 'options' => $data['option'] ? json_decode($data['option'], true) : [], + ], + ); + } + + public function label() : Attribute { + return Attribute::make( + get: fn (mixed $value, array $data) => $data['title'], + ); + } + + public function prop() : Attribute { + return Attribute::make( + get: fn (mixed $value, array $data) => $data['title'], + ); + } +} diff --git a/app/Models/System/Crontab.php b/app/Models/System/Crontab.php new file mode 100644 index 0000000..1fa00b5 --- /dev/null +++ b/app/Models/System/Crontab.php @@ -0,0 +1,24 @@ + +// +---------------------------------------------------------------------- +namespace App\Models\System; + +use Illuminate\Database\Eloquent\Casts\Attribute; + +class Crontab extends \App\Models\BaseModel { + + protected $table = 'system_crontabs'; + protected $fillable = []; + protected $hidden = ['deleted_at']; + + protected function casts(): array { + return [ + 'status' => 'integer', + ]; + } +} diff --git a/app/Models/System/Dict.php b/app/Models/System/Dict.php new file mode 100644 index 0000000..8846302 --- /dev/null +++ b/app/Models/System/Dict.php @@ -0,0 +1,22 @@ + +// +---------------------------------------------------------------------- +namespace App\Models\System; + +use Illuminate\Database\Eloquent\Casts\Attribute; + +class Dict extends \App\Models\BaseModel { + + protected $table = 'system_dicts'; + protected $fillable = ['title', 'values', 'group_id', 'group_name', 'sort', 'status']; + protected $hidden = ['deleted_at']; + + public function group(){ + return $this->belongsTo(DictGroup::class, 'group_id', 'id'); + } +} diff --git a/app/Models/System/DictGroup.php b/app/Models/System/DictGroup.php new file mode 100644 index 0000000..f9a99e1 --- /dev/null +++ b/app/Models/System/DictGroup.php @@ -0,0 +1,25 @@ + +// +---------------------------------------------------------------------- +namespace App\Models\System; + +class DictGroup extends \App\Models\BaseModel { + + protected $table = 'system_dict_groups'; + protected $fillable = ['title', 'name', 'parent_id', 'sort', 'status']; + protected $hidden = ['deleted_at']; + + protected function casts(): array { + return [ + 'parent_id' => 'integer', + 'status' => 'integer', + 'sort' => 'integer', + ]; + } + +} diff --git a/app/Models/System/Log.php b/app/Models/System/Log.php new file mode 100644 index 0000000..68926f1 --- /dev/null +++ b/app/Models/System/Log.php @@ -0,0 +1,22 @@ + +// +---------------------------------------------------------------------- +namespace App\Models\System; + +use App\Models\Auth\Admin; + +class Log extends \App\Models\BaseModel { + + protected $table = 'system_logs'; + protected $fillable = ['title', 'name', 'url', 'method', 'client_ip', 'browser', 'user_id', 'data', 'remark', 'status']; + protected $hidden = ['deleted_at']; + + public function user() { + return $this->belongsTo(Admin::class, 'user_id', 'uid'); + } +} diff --git a/app/Models/System/Modules.php b/app/Models/System/Modules.php new file mode 100644 index 0000000..32a6a2c --- /dev/null +++ b/app/Models/System/Modules.php @@ -0,0 +1,13 @@ + +// +---------------------------------------------------------------------- +namespace App\Models\System; + +class Modules extends \App\Models\BaseModel { + // +} diff --git a/app/Models/System/Tasks.php b/app/Models/System/Tasks.php new file mode 100644 index 0000000..eb91f8b --- /dev/null +++ b/app/Models/System/Tasks.php @@ -0,0 +1,31 @@ + +// +---------------------------------------------------------------------- +namespace App\Models\System; + +use Illuminate\Database\Eloquent\Casts\Attribute; +use Illuminate\Support\Facades\Storage; +use App\Models\Auth\Admin; + +class Tasks extends \App\Models\BaseModel { + + protected $table = 'system_tasks'; + protected $fillable = []; + protected $hidden = ['deleted_at']; + protected $append = ['url']; + + public function url(): Attribute{ + return Attribute::make( + get: fn (mixed $value, array $data) => Storage::disk('public')->url($data['file']), + ); + } + + public function user() { + return $this->belongsTo(Admin::class, 'user_id', 'uid'); + } +} diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php new file mode 100644 index 0000000..9d453bb --- /dev/null +++ b/app/Providers/AppServiceProvider.php @@ -0,0 +1,26 @@ +alias('Debugbar', \Barryvdh\Debugbar\Facades\Debugbar::class); + } + + /** + * Bootstrap any application services. + */ + public function boot(): void + { + // + } +} diff --git a/app/Services/Auth/DepartmentService.php b/app/Services/Auth/DepartmentService.php new file mode 100644 index 0000000..71050c5 --- /dev/null +++ b/app/Services/Auth/DepartmentService.php @@ -0,0 +1,127 @@ + +// +---------------------------------------------------------------------- +namespace App\Services\Auth; + +use App\Models\Auth\Department; +use App\Support\Tree; + +class DepartmentService { + + /** + * @title 获取部门列表 + * + * @param [type] $request + * @return void + */ + public function getDataList($request) { + $map = []; + + if ($request->filled('title')) { + $map[] = ['title', 'like', '%' . $request->input('title') . '%']; + } + if ($request->filled('name')) { + $map[] = ['name', 'like', '%' . $request->input('name') . '%']; + } + if ($request->filled('pid')) { + $map[] = ['parent_id', '=', $request->input('pid')]; + } + + $query = Department::where($map)->orderBy('id', 'desc'); + + if($request->filled('is_tree') && $request->filled('is_tree') == 1){ + $query = $query->get()->toArray(); + $tree = new Tree(); + $data = $tree->list_to_tree($query, 0, 'id', 'parent_id'); + }else{ + $data = [ + 'total' => $query->count(), + 'page' => $request->input('page', 1), + 'data' => $query->offset($request->input('offset', 0))->limit($request->input('limit', 10))->get(), + ]; + } + return $data; + } + + /** + * @title 添加部门 + * + * @param [type] $request + * @return void + */ + public function create($request) { + $request->validate([ + 'title' => 'required|unique:auth_departments', + 'name' => 'required|unique:auth_departments', + ]); + $data = $request->all(); + $data['status'] = 1; + + return Department::create($data); + } + + /** + * @title 修改部门 + * + * @param [type] $request + * @return void + */ + public function update($request) { + try { + $department = Department::findOrFail($request->input('id')); + } catch (\Throwable $th) { + throw new \Exception("部门不存在!", 1); + } + + if ($request->filled('title')) { + $department->title = $request->input('title'); + } + if ($request->filled('name')) { + $department->name = $request->input('name'); + } + if ($request->filled('parent_id')) { + $department->parent_id = $request->input('parent_id'); + } + if ($request->filled('status')) { + $department->status = $request->input('status'); + } + if ($request->filled('sort')) { + $department->sort = $request->input('sort'); + } + + $department->save(); + return $department; + } + + /** + * @title 删除部门 + * + * @param [type] $request + * @return void + */ + public function delete($request) { + if($request->filled('id')){ + try { + $department = Department::findOrFail($request->input('id')); + } catch (\Throwable $th) { + throw new \Exception("角色不存在!", 1); + } + $department->delete(); + } + if($request->filled('ids')){ + try { + $ids = $request->input('ids'); + $department = Department::whereIn('id', $ids)->delete(); + } catch (\Throwable $th) { + throw new \Exception($th->getMessage(), 1); + } + } + + return $department; + } +} diff --git a/app/Services/Auth/MenuService.php b/app/Services/Auth/MenuService.php new file mode 100644 index 0000000..85bc98c --- /dev/null +++ b/app/Services/Auth/MenuService.php @@ -0,0 +1,193 @@ + +// +---------------------------------------------------------------------- +namespace App\Services\Auth; + +use App\Models\Auth\Permission; +use App\Models\Auth\Admin; +use App\Support\Tree; + +class MenuService { + + /** + * @title 获取权限节点列表 + * + * @param [type] $request + * @return void + */ + public function getDataList($request) { + $map = []; + + if ($request->filled('title')) { + $map[] = ['title', 'like', '%' . $request->input('title') . '%']; + } + if ($request->filled('name')) { + $map[] = ['name', 'like', '%' . $request->input('name') . '%']; + } + if ($request->filled('pid')) { + $map[] = ['parent_id', '=', $request->input('pid')]; + } + + $query = Permission::where($map)->orderBy('sort', 'asc')->orderBy('id', 'desc'); + + + if($request->filled('is_tree') && $request->filled('is_tree') == 1){ + $query = $query->get()->toArray(); + $tree = new Tree(); + $data = $tree->list_to_tree($query, 0, 'id', 'parent_id'); + }else{ + $data = [ + 'total' => $query->count(), + 'page' => $request->input('page', 1), + 'data' => $query->offset($request->input('offset', 0))->limit($request->input('limit', 10))->get(), + ]; + } + return $data; + } + + /** + * @title 获取我的权限节点 + * + * @param [type] $request + * @return void + */ + public function getMyMenuList($request){ + $map = []; + $data = []; + $uid = auth('admin')->user()['uid']; + + $tree = new Tree(); + if($uid != env('ADMIN_ID', 1)){ + $map[] = ['uid', '=', $uid]; + + $admin = Admin::with(['roles'])->where($map)->first(); + $permission = []; + $menu = []; + foreach ($admin->roles as $key => $role) { + $menu = array_merge($menu, $role->permissions->append(['meta'])->toArray()); + $permission = array_merge($permission, $role->permissions->pluck('name')->toArray()); + } + $data = [ + 'menu' => $tree->list_to_tree($menu, 0, 'id', 'parent_id'), + 'permissions' => $permission + ]; + }else{ + $query = Permission::where($map)->orderBy('sort', 'asc'); + + $data = [ + 'menu' => $tree->list_to_tree($query->where('type', '=', 'menu')->get()->append(['meta'])->toArray(), 0, 'id', 'parent_id'), + 'permissions' => $query->pluck('name') + ]; + } + + return $data; + } + + /** + * @title 添加权限节点 + * + * @param [type] $request + * @return void + */ + public function create($request) { + $request->validate([ + 'title' => 'required|unique:auth_permissions', + 'name' => 'required|unique:auth_permissions', + ]); + $data = $request->all(); + $data['status'] = 1; + + return Permission::create($data); + } + + /** + * @title 修改权限节点 + * + * @param [type] $request + * @return void + */ + public function update($request) { + try { + $permission = Permission::findOrFail($request->input('id')); + } catch (\Throwable $th) { + throw new \Exception("权限节点不存在!", 1); + } + + $data = $request->all(); + foreach ($data as $key => $value) { + if (in_array($key, ['affix', 'color', 'component', 'fullpage', 'hidden', 'hiddenBreadcrumb', 'icon', 'name', 'parent_id', 'redirect', 'sort', 'title', 'type', 'path', 'status'])) { + $permission->$key = $value; + } + } + + $permission->save(); + return $permission; + } + + /** + * @title 删除权限节点 + * + * @param [type] $request + * @return void + */ + public function delete($request) { + if($request->filled('id')){ + try { + $permission = Permission::findOrFail($request->input('id')); + } catch (\Throwable $th) { + throw new \Exception("权限节点不存在!", 1); + } + + $permission->roles()->detach(); + $permission->delete(); + } + if($request->filled('ids')){ + try { + $permission = Permission::whereIn('id', $request->input('ids')); + foreach ($permission->get() as $item) { + $item->roles()->detach(); + } + $permission->delete(); + } catch (\Throwable $th) { + throw new \Exception($th->getMessage(), 1); + } + } + + return $permission; + } + + /** + * @title 批量导入 + * + * @param [array] $data + * @return void + */ + public function importMenu($data, $parent_id = 0){ + foreach ($data as $key => $value) { + $save = [ + 'title' => $value['title'], + 'name' => $value['name'], + 'path' => $value['path'], + 'component' => $value['component'], + 'type' => $value['type'], + 'affix' => isset($value['affix']) ? $value['affix'] : 0, + 'parent_id' => $parent_id, + 'status' => 1, + 'sort' => isset($value['sort']) ? $value['sort'] : $key, + 'icon' => isset($value['icon']) ? $value['icon'] : '', + 'hidden' => isset($value['hidden']) ? $value['hidden'] : 0, + 'created_at' => date('Y-m-d H:i:s'), + 'updated_at' => date('Y-m-d H:i:s'), + ]; + $res = Permission::insertGetId($save); + if (isset($value['children']) && !empty($value['children'])) { + $this->importMenu($value['children'], $res); + } + } + } +} diff --git a/app/Services/Auth/RoleService.php b/app/Services/Auth/RoleService.php new file mode 100644 index 0000000..dce32dd --- /dev/null +++ b/app/Services/Auth/RoleService.php @@ -0,0 +1,159 @@ + +// +---------------------------------------------------------------------- +namespace App\Services\Auth; + +use App\Models\Auth\Role; +use App\Support\Tree; + +class RoleService { + + /** + * @title 获取角色列表 + * + * @param [type] $request + * @return void + */ + public function getDataList($request) { + $map = []; + + if ($request->filled('title')) { + $map[] = ['title', 'like', '%' . $request->input('title') . '%']; + } + if ($request->filled('name')) { + $map[] = ['name', 'like', '%' . $request->input('name') . '%']; + } + + $query = Role::with(['permissions'])->where($map)->orderBy('id', 'desc'); + + if($request->filled('is_tree') && $request->filled('is_tree') == 1){ + $query = $query->get()->toArray(); + $tree = new Tree(); + $data = $tree->list_to_tree($query, 0, 'id', 'parent_id'); + }else{ + if ($request->filled('page')){ + $data = [ + 'total' => $query->count(), + 'page' => $request->input('page', 1), + 'data' => $query->offset($request->input('offset', 0))->limit($request->input('limit', 10))->get(), + ]; + }else{ + $data = $query->get(); + } + } + return $data; + } + + /** + * @title 添加角色 + * + * @param [type] $request + * @return void + */ + public function create($request) { + $request->validate([ + 'title' => 'required|unique:auth_roles', + 'name' => 'required|alpha_dash:ascii|unique:auth_roles', + ]); + $data = $request->all(); + $data['status'] = 1; + + return Role::create($data); + } + + /** + * @title 修改角色 + * + * @param [type] $request + * @return void + */ + public function update($request) { + $request->validate([ + 'title' => 'required|unique:auth_roles,title,' . $request->input('id'), + 'name' => 'required|alpha_dash:ascii|unique:auth_roles,name,' . $request->input('id'), + ]); + try { + $role = Role::findOrFail($request->input('id')); + } catch (\Throwable $th) { + throw new \Exception("角色不存在!", 1); + } + + if ($request->filled('title')) { + $role->title = $request->input('title'); + } + if ($request->filled('name')) { + $role->name = $request->input('name'); + } + if ($request->filled('status')) { + $role->status = $request->input('status'); + } + if ($request->filled('sort')) { + $role->sort = $request->input('sort'); + } + + $role->save(); + return $role; + } + + /** + * @title 删除角色 + * + * @param [type] $request + * @return void + */ + public function delete($request) { + if($request->filled('id')){ + try { + $role = Role::findOrFail($request->input('id')); + } catch (\Throwable $th) { + throw new \Exception("角色不存在!", 1); + } + $role->permissions()->detach(); + $role->admins()->detach(); + $role->delete(); + } + if($request->filled('ids')){ + try { + $role = Role::whereIn('id', $request->input('ids')); + foreach ($role->get() as $item) { + $item->permissions()->detach(); //删除关联 + $item->admins()->detach(); //删除关联 + } + + $role->delete(); + } catch (\Throwable $th) { + throw new \Exception($th->getMessage(), 1); + } + } + + return $role; + } + + /** + * @title 角色授权 + * + * @param [type] $request + * @return void + */ + public function auth($request){ + try { + $role = Role::with(['permissions'])->findOrFail($request->input('id')); + } catch (\Throwable $th) { + throw new \Exception("角色不存在!", 1); + } + + + if($request->filled('data_range')){ + $role->data_range = $request->input('data_range'); + } + + $role->permissions()->sync($request->input('permissions')); + $role->save(); + return $role; + } +} diff --git a/app/Services/Auth/UsersService.php b/app/Services/Auth/UsersService.php new file mode 100644 index 0000000..a79c935 --- /dev/null +++ b/app/Services/Auth/UsersService.php @@ -0,0 +1,194 @@ + +// +---------------------------------------------------------------------- +namespace App\Services\Auth; + +use App\Models\Auth\Admin; + +class UsersService { + + /** + * @title 获取用户列表 + * + * @param [type] $request + * @return void + */ + public function getDataList($request) { + $map = []; + + if ($request->filled('username')) { + $map[] = ['username', 'like', '%' . $request->input('username') . '%']; + } + if ($request->filled('mobile')) { + $map[] = ['mobile', 'like', '%' . $request->input('mobile') . '%']; + } + if ($request->filled('email')) { + $map[] = ['email', 'like', '%' . $request->input('email') . '%']; + } + if ($request->filled('department_id')) { + $map[] = ['department_id', '=', $request->input('department_id')]; + } + + $query = Admin::where($map)->orderBy('uid', 'desc'); + + $data = [ + 'total' => $query->count(), + 'page' => $request->input('page', 1), + 'data' => $query->offset($request->input('offset', 0))->limit($request->input('limit', 10))->get(), + ]; + return $data; + } + + /** + * @title 添加用户 + * + * @param [type] $request + * @return void + */ + public function create($request) { + $request->validate([ + 'username' => 'required|unique:auth_admins,username', + // 'email' => 'required|unique:auth_admins', + // 'mobile' => 'required|unique:auth_admins', + 'password' => 'required|min:8', + ]); + + $data = $request->all(); + $data['status'] = 1; + $data['last_login_at'] = date('Y-m-d H:i:s'); + $data['last_login_ip'] = $request->ip(); + + $admins = Admin::create($data); + $request->whenFilled('roles_id', function ($value) use ($admins) { + $admins->roles()->sync($value); + }); + return $admins; + } + + /** + * @title 修改用户 + * + * @param [type] $request + * @return void + */ + public function update($request) { + $request->validate([ + 'username' => 'required|unique:auth_admins,username,' . $request->input('uid').',uid', + ]); + try { + $admins = Admin::findOrFail($request->input('uid')); + } catch (\Throwable $th) { + throw new \Exception("用户不存在!", 1); + } + + if ($request->filled('username')) { + $admins->username = $request->input('username'); + } + if ($request->filled('email')) { + $admins->email = $request->input('email'); + } + if ($request->filled('mobile')) { + $admins->mobile = $request->input('mobile'); + } + if ($request->filled('nickname')) { + $admins->nickname = $request->input('nickname'); + } + if ($request->filled('department_id')) { + $admins->department_id = $request->input('department_id'); + } + if ($request->filled('avatar')) { + $admins->avatar = $request->input('avatar'); + } + if ($request->filled('remark')) { + $admins->remark = $request->input('remark'); + } + if ($request->filled('status')) { + $admins->status = $request->input('status'); + } + if ($request->filled('password')) { + $admins->password = $request->input('password'); + } + $request->whenFilled('roles_id', function ($value) use ($admins) { + $admins->roles()->sync($value); + }); + + $admins->save(); + return $admins; + } + + /** + * @title 删除用户 + * + * @param [type] $request + * @return void + */ + public function delete($request) { + if($request->filled('id')){ + if($request->input('id') == env('ADMIN_ID', 1)){ + throw new \Exception("超级管理员不能删除!", 1); + } + try { + $admins = Admin::findOrFail($request->input('id')); + } catch (\Throwable $th) { + throw new \Exception("角色不存在!", 1); + } + $admins->roles()->detach(); + $admins->delete(); + } + if($request->filled('ids')){ + if(in_array(env('ADMIN_ID', 1), $request->input('ids'))){ + throw new \Exception("超级管理员不能删除!", 1); + } + try { + $admins = Admin::whereIn('uid', $request->input('ids')); + foreach ($admins->get() as $item) { + $item->roles()->detach(); //删除关联 + } + $admins = $admins->delete(); + } catch (\Throwable $th) { + throw new \Exception($th->getMessage(), 1); + } + } + return $admins; + } + + /** + * @title 修改密码 + * + * @param [type] $request + * @return void + */ + public function upPassword($request) { + try { + $admins = Admin::findOrFail($request->input('uid')); + } catch (\Throwable $th) { + throw new \Exception("用户不存在!", 1); + } + + $admins->password = $request->input('password'); + $admins->save(); + return $admins; + } + + /** + * @title 设置角色 + * + * @param [type] $request + * @return void + */ + public function uprole($request){ + try { + $admins = Admin::with(['roles'])->findOrFail($request->input('uid')); + } catch (\Illuminate\Database\Eloquent\ModelNotFoundException $th) { + throw new \Exception($th->getMessage(), 1); + } + + $admins->roles()->sync($request->input('roles')); + return $admins; + } +} diff --git a/app/Services/System/AreaService.php b/app/Services/System/AreaService.php new file mode 100644 index 0000000..d11499c --- /dev/null +++ b/app/Services/System/AreaService.php @@ -0,0 +1,118 @@ + +// +---------------------------------------------------------------------- +namespace App\Services\System; + +use App\Models\System\Area; +use App\Support\Tree; + +class AreaService { + + /** + * @title 区域列表 + * + * @param [type] $request + * @return void + */ + public function getAreaList($request){ + $map = []; + if($request->filled('title')){ + $map[] = ['title', 'like', '%' . $request->input('title') . '%']; + } + if($request->filled('id')){ + $map[] = ['id', '=', $request->input('id')]; + } + if($request->filled('parent_code')){ + $map[] = ['parent_code', '=', $request->input('parent_code')]; + } + + $query = Area::where($map)->orderBy('id', 'asc'); + + if($request->filled('page')){ + $data = [ + 'total' => $query->count(), + 'page' => $request->input('page', 1), + 'data' => $query->offset($request->input('offset', 0))->limit($request->input('limit', 10))->get(), + ]; + }else{ + if($request->filled('is_tree') && $request->filled('is_tree') == 1){ + $query = $query->get()->toArray(); + $tree = new Tree(); + $data = $tree->list_to_tree($query, 0, 'code', 'parent_code'); + }else{ + $data = $query->get(); + } + } + return $data; + } + + /** + * @title 添加区域 + * + * @param [type] $request + * @return void + */ + public function create($request){ + $request->validate([ + 'title' => 'required', + 'code' => 'required|unique:system_areas', + ], [ + 'code.unique' => '编码已存在', + 'title.required' => '请输入标题', + 'code.required' => '请输入编码', + ]); + + $area = new Area; + + foreach ($area->setFilterFields($request->all()) as $key => $value) { + $area->$key = $value; + } + + $area->save(); + return $area; + } + + /** + * @title 编辑区域 + * + * @param [type] $request + * @return void + */ + public function update($request){ + $request->validate([ + 'id' => 'required', + 'title' => 'required', + 'code' => 'required', + ], [ + 'id.required' => '参数错误', + 'title.required' => '请输入标题', + 'code.required' => '请输入编码', + ]); + + $area = Area::where('id', '=', $request->input('id'))->first(); + + foreach ($area->setFilterFields($request->all()) as $key => $value) { + $area->$key = $value; + } + + $area->save(); + return $area; + } + + public function delete($request){ + $request->validate([ + 'id' => 'required', + ], [ + 'id.required' => '参数错误', + ]); + + $area = Area::where('id', '=', $request->input('id'))->first(); + $area->delete(); + return $area; + } +} diff --git a/app/Services/System/ClientMenuService.php b/app/Services/System/ClientMenuService.php new file mode 100644 index 0000000..ab72352 --- /dev/null +++ b/app/Services/System/ClientMenuService.php @@ -0,0 +1,88 @@ + +// +---------------------------------------------------------------------- +namespace App\Services\System; + +use App\Models\System\ClientMenu; +use App\Support\Tree; + +class ClientMenuService { + + public function getDataList($request){ + $map = []; + + if($request->filled('title')){ + $map[] = ['title', 'like', '%' . $request->input('title') . '%']; + } + + $query = ClientMenu::where($map)->orderBy('id', 'asc'); + + if($request->filled('page')){ + $data = [ + 'total' => $query->count(), + 'page' => $request->input('page', 1), + 'data' => $query->offset($request->input('offset', 0))->limit($request->input('limit', 10))->get(), + ]; + }else{ + if($request->filled('is_tree') && $request->filled('is_tree') == 1){ + $query = $query->get()->toArray(); + $tree = new Tree(); + $data = $tree->list_to_tree($query, 0, 'id', 'parent_id'); + }else{ + $data = $query->get(); + } + } + return $data; + } + + public function create($request){ + $request->validate([ + 'title' => 'required', + ]); + $menu = new ClientMenu(); + + foreach ($menu->setFilterFields($request->all()) as $key => $value) { + $menu->$key = $value; + } + $menu->save(); + + return $menu; + } + + public function update($request){ + $request->validate([ + 'id' => 'required', + 'title' => 'required', + ], [ + 'id.required' => '参数错误', + 'title.required' => '请输入标题', + ]); + + $menu = ClientMenu::where('id', $request->input('id'))->first(); + + foreach ($menu->setFilterFields($request->all()) as $key => $value) { + $menu->$key = $value; + } + + $menu->save(); + return $menu; + } + + public function delete($request){ + $request->validate([ + 'id' => 'required', + ], [ + 'id.required' => '参数错误', + ]); + + $menu = ClientMenu::where('id', $request->input('id'))->first(); + + $menu->delete(); + return $menu; + } +} diff --git a/app/Services/System/ClientService.php b/app/Services/System/ClientService.php new file mode 100644 index 0000000..07cf7f0 --- /dev/null +++ b/app/Services/System/ClientService.php @@ -0,0 +1,84 @@ + +// +---------------------------------------------------------------------- +namespace App\Services\System; + +use App\Models\System\Client; + +class ClientService { + + public function getDataList($request){ + $map = []; + + if($request->filled('title')){ + $map[] = ['title', 'like', '%' . $request->input('title') . '%']; + } + + $query = Client::where($map)->orderBy('id', 'asc'); + + if($request->filled('page')){ + $data = [ + 'total' => $query->count(), + 'page' => $request->input('page', 1), + 'data' => $query->offset($request->input('offset', 0))->limit($request->input('limit', 10))->get(), + ]; + }else{ + $data = $query->get(); + } + return $data; + } + + public function create($request){ + $request->validate([ + 'title' => 'required', + ]); + $client = new Client(); + + foreach ($client->setFilterFields($request->all()) as $key => $value) { + $client->$key = $value; + } + + $client->app_id = ''; + $client->secret = ''; + $client->redirect = ''; + $client->save(); + + return $client; + } + + public function update($request){ + $request->validate([ + 'id' => 'required', + 'title' => 'required', + ], [ + 'id.required' => '参数错误', + 'title.required' => '请输入标题', + ]); + + $client = Client::where('id', $request->input('id'))->first(); + + $client->title = $request->input('title'); + $client->status = $request->input('status', 0); + + $client->save(); + return $client; + } + + public function delete($request){ + $request->validate([ + 'id' => 'required', + ], [ + 'id.required' => '参数错误', + ]); + + $client = Client::where('id', $request->input('id'))->first(); + + $client->delete(); + return $client; + } +} diff --git a/app/Services/System/CrontabService.php b/app/Services/System/CrontabService.php new file mode 100644 index 0000000..cd20aa1 --- /dev/null +++ b/app/Services/System/CrontabService.php @@ -0,0 +1,119 @@ + +// +---------------------------------------------------------------------- +namespace App\Services\System; + +use App\Models\System\Crontab; + +class CrontabService { + + public function getDataList($request){ + $map = []; + + if($request->filled('title')){ + $map[] = ['title', 'like', '%' . $request->input('title') . '%']; + } + if($request->filled('type')){ + $map[] = ['type', '=', $request->input('type')]; + } + + $query = Crontab::where($map)->orderBy('id', 'asc'); + + if($request->filled('page')){ + $data = [ + 'total' => $query->count(), + 'page' => $request->input('page', 1), + 'data' => $query->offset($request->input('offset', 0))->limit($request->input('limit', 10))->get(), + ]; + }else{ + $data = $query->get(); + } + + return $data; + } + + public function create($request){ + $request->validate([ + 'title' => 'required', + 'type' => 'required', + 'expression' => 'required', + 'command' => 'required', + ], [ + 'title.required' => '请输入标题', + 'type.required' => '请选择类型', + 'expression.required' => '请输入表达式', + 'command.required' => '请输入命令', + ]); + + $crontab = new Crontab; + + foreach ($crontab->setFilterFields($request->all()) as $key => $value) { + $crontab->$key = $value; + } + + $crontab->save(); + + return $crontab; + } + + public function update($request){ + $request->validate([ + 'id' => 'required', + 'title' => 'required', + 'type' => 'required', + 'expression' => 'required', + 'command' => 'required', + ], [ + 'id.required' => '参数错误', + 'title.required' => '请输入标题', + 'type.required' => '请选择类型', + 'expression.required' => '请输入表达式', + 'command.required' => '请输入命令', + ]); + + $crontab = Crontab::where('id', $request->input('id'))->first(); + + foreach ($crontab->setFilterFields($request->all()) as $key => $value) { + $crontab->$key = $value; + } + + $crontab->save(); + + return $crontab; + } + + public function delete($request){ + $request->validate([ + 'id' => 'required', + ], [ + 'id.required' => '参数错误', + ]); + + $crontab = Crontab::where('id', $request->input('id'))->first(); + + $crontab->delete(); + return $crontab; + } + + public function reload($request){ + $request->validate([ + 'id' => 'required', + 'status' => 'required', + ], [ + 'id.required' => '参数错误', + 'status.required' => '参数错误', + ]); + + $crontab = Crontab::where('id', $request->input('id'))->first(); + + $crontab->status = $request->input('status'); + + $crontab->save(); + return $crontab; + } +} diff --git a/app/Services/System/DictService.php b/app/Services/System/DictService.php new file mode 100644 index 0000000..243de39 --- /dev/null +++ b/app/Services/System/DictService.php @@ -0,0 +1,254 @@ + +// +---------------------------------------------------------------------- +namespace App\Services\System; + +use App\Models\System\Dict; +use App\Models\System\DictGroup; +use App\Support\Tree; + +class DictService { + + /** + * @title 获取列表数据 + * + * @param [type] $request + * @return void + */ + public function getDataList($request) { + $map = []; + + if ($request->filled('title')) { + $map[] = ['title', 'like', '%' . $request->input('title') . '%']; + } + if ($request->filled('name')) { + $map[] = ['group_name', '=', $request->input('name')]; + } + if ($request->filled('group_id')) { + $map[] = ['group_id', '=', $request->input('group_id')]; + } + + $query = Dict::where($map)->orderBy('sort', 'asc')->orderBy('id', 'desc'); + + if($request->filled('page')){ + $data = [ + 'total' => $query->count(), + 'page' => $request->input('page', 1), + 'data' => $query->offset($request->input('offset', 0))->limit($request->input('limit', 10))->get(), + ]; + }else{ + $data = $query->get(); + } + return $data; + } + + /** + * @title 字段分类数据 + * + * @param [type] $request + * @return void + */ + public function getCategoryList($request){ + $map = []; + + if ($request->filled('title')) { + $map[] = ['title', 'like', '%' . $request->input('title') . '%']; + } + if ($request->filled('name')) { + $map[] = ['name', 'like', '%' . $request->input('name') . '%']; + } + + $query = DictGroup::where($map)->orderBy('id', 'desc'); + + + if($request->filled('is_tree') && $request->filled('is_tree') == 1){ + $query = $query->get()->toArray(); + $tree = new Tree(); + $data = $tree->list_to_tree($query, 0, 'id', 'parent_id'); + }else{ + $data = [ + 'total' => $query->count(), + 'page' => $request->input('page', 1), + 'data' => $query->limit($request->input('limit', 10))->offset($request->input('page', 1) - 1)->get(), + ]; + } + return $data; + } + + /** + * @title 创建字典 + * + * @param [type] $request + * @return void + */ + public function create($request){ + $request->validate([ + 'title' => 'required', + 'values' => 'required', + ]); + + $data = $request->all(); + + $data['group_name'] = DictGroup::where('id', '=', $data['group_id'])->value('name'); + + $dict = Dict::create($data); + + if($dict->values == ''){ + $dict->values = $dict->id; + $dict->save(); + } + $this->setDictCache(true); + return $dict; + } + + /** + * @title 更新字典 + * + * @param [type] $request + * @return void + */ + public function update($request){ + try { + $dict = Dict::findOrFail($request->input('id')); + } catch (\Throwable $th) { + throw new \Exception("改字典不存在", 1); + } + + if ($request->filled('title')) { + $dict->title = $request->input('title'); + } + if ($request->filled('values')) { + $dict->values = $request->input('values'); + } + if ($request->filled('sort')) { + $dict->sort = $request->input('sort'); + } + if ($request->filled('status')) { + $dict->status = $request->input('status'); + } + if ($request->filled('group_id')) { + $dict->group_id = $request->input('group_id'); + $dict->group_name = DictGroup::where('id', '=', $request->input('group_id'))->value('name'); + } + + $dict->save(); + $this->setDictCache(true); + return $dict; + } + + /** + * @title 删除字典 + * + * @param [type] $request + * @return void + */ + public function delete($request){ + try { + $dict = Dict::findOrFail($request->input('id')); + } catch (\Throwable $th) { + throw new \Exception("改字典不存在", 1); + } + + $dict->delete(); + $this->setDictCache(true); + return $dict; + } + + /** + * @title 创建字典分类 + * + * @param [type] $request + * @return void + */ + public function createCategory($request){ + $request->validate([ + 'title' => 'required|unique:system_dict_groups', + 'name' => 'required|unique:system_dict_groups', + ]); + + $data = $request->all(); + $data['status'] = 1; + $group = DictGroup::create($data); + + $this->setDictCache(true); + return $group; + } + + /** + * @title 更新字典分类 + * + * @param [type] $request + * @return void + */ + public function updateCategory($request){ + try { + $dict = DictGroup::findOrFail($request->input('id')); + } catch (\Throwable $th) { + throw new \Exception("改字典不存在", 1); + } + + if ($request->filled('parent_id')) { + $dict->parent_id = $request->input('parent_id'); + } + if ($request->filled('title')) { + $dict->title = $request->input('title'); + } + if ($request->filled('name')) { + $dict->name = $request->input('name'); + } + if ($request->filled('sort')) { + $dict->sort = $request->input('sort'); + } + if ($request->filled('status')) { + $dict->status = $request->input('status'); + } + + $dict->save(); + $this->setDictCache(true); + return $dict; + } + + /** + * @title 删除字典 + * + * @param [type] $request + * @return void + */ + public function deleteCategory($request){ + try { + $dict = DictGroup::findOrFail($request->input('id')); + } catch (\Throwable $th) { + throw new \Exception("改字典不存在", 1); + } + + $dict->delete(); + $this->setDictCache(true); + return $dict; + } + + public function getDictionaryAll(){ + $map = []; + + $map[] = ['status', '=', 1]; + + $list = Dict::where($map)->get(); + + $data = []; + foreach ($list as $key => $value) { + $data[$value['group_name']][] = $value; + } + return $data; + } + + public function setDictCache($refresh = false){ + if (!cache('dict') || $refresh){ + $data = $this->getDictionaryAll(); + cache('dict', $data); + } + } +} diff --git a/app/Services/System/LogService.php b/app/Services/System/LogService.php new file mode 100644 index 0000000..71c1077 --- /dev/null +++ b/app/Services/System/LogService.php @@ -0,0 +1,113 @@ + +// +---------------------------------------------------------------------- +namespace App\Services\System; + +use App\Models\System\Log; + +class LogService { + + /** + * @title 获取日志列表 + * + * @param [type] $request + * @return void + */ + public function getDataList($request) { + $map = []; + + if ($request->filled('title')) { + $map[] = ['title', 'like', '%' . $request->input('title') . '%']; + } + if ($request->filled('name')) { + $map[] = ['name', 'like', '%' . $request->input('name') . '%']; + } + if ($request->filled('url')) { + $map[] = ['url', 'like', '%' . $request->input('url') . '%']; + } + if ($request->filled('method')) { + $map[] = ['method', 'like', '%' . $request->input('method') . '%']; + } + if ($request->filled('ip')) { + $map[] = ['ip', 'like', '%' . $request->input('ip') . '%']; + } + if ($request->filled('user_id')) { + $map[] = ['user_id', '=', $request->input('user_id')]; + } + if ($request->filled('remark')) { + $map[] = ['remark', 'like', '%' . $request->input('remark') . '%']; + } + if ($request->filled('status')) { + $map[] = ['status', '=', $request->input('status')]; + } + if ($request->filled('is_my')) { + $user = auth('admin')->user(); + $map[] = ['user_id', '=', $user->uid]; + } + + $query = Log::with(['user:nickname,username,uid'])->where($map)->orderBy('id', 'desc'); + + if ($request->filled('date')) { + $query->whereBetween('created_at', $request->input('date')); + } + $data = [ + 'total' => $query->count(), + 'page' => $request->input('page', 1), + 'data' => $query->offset($request->input('offset', 0))->limit($request->input('limit', 10))->get(), + ]; + return $data; + } + + public function createLog($request){ + list($class, $method) = explode('@', $request->route()->getActionName()); + $reflection = new \ReflectionMethod("\\" . $class, $method); + $docComment = $reflection->getDocComment(); + $matches = []; + preg_match("/@title(.*)(\\r\\n|\\r|\\n)/U", $docComment, $matches); + + $data = [ + 'user_id' => auth('admin')->user()->uid, + 'title' => isset($matches[1]) ? trim($matches[1]) : '', + 'name' => $request->route()->getName() ?? '', + 'client_ip' => $request->ip(), + 'url' => $request->path(), + 'method' => $request->method(), + 'data' => json_encode($request->all()), + 'browser' => $request->userAgent(), + 'remark' => $request->input('remark', ''), + 'status' => 1, + ]; + $log = Log::create($data); + + return $log; + } + + /** + * @title 删除日志 + * @param Request $request + * @return bool + */ + public function delete($request){ + $map = []; + if($request->filled('id')){ + $map[] = ['id', '=', $request->input('id')]; + } + if($request->filled('ids')){ + $map[] = ['id', 'IN', $request->input('ids')]; + } + + $query = Log::where($map); + + if($request->filled('date')){ + $query->whereBetween('created_at', $request->input('date')); + } + + $query->delete(); + return true; + } +} diff --git a/app/Services/System/ModulesService.php b/app/Services/System/ModulesService.php new file mode 100644 index 0000000..e2b0ff9 --- /dev/null +++ b/app/Services/System/ModulesService.php @@ -0,0 +1,147 @@ + +// +---------------------------------------------------------------------- +namespace App\Services\System; + +use Nwidart\Modules\Contracts\ActivatorInterface; +use Nwidart\Modules\Module; +use Nwidart\Modules\Facades\Module as ModuleFacade; +use Illuminate\Container\Container; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Facades\Artisan; +use App\Models\System\Modules; + +class ModulesService implements ActivatorInterface { + + /** + * 模块管理的模型 + * @var class-string $modelClass + */ + private string $modelClass; + /** + * Laravel Filesystem instance + * + * @var Filesystem + */ + private $files; + + public function __construct(Container $app) { + $this->modelClass = $app['config']['modules.activators.database.model']; + $this->files = $app['files']; + } + + /** + * Enables a module + */ + public function enable(Module $module): void { + $this->setActiveByName($module->getName(), true); + } + + /** + * Disables a module + */ + public function disable(Module $module): void { + $this->setActiveByName($module->getName(), false); + } + + /** + * Determine whether the given status same with a module status. + */ + public function hasStatus(Module|string $module, bool $status): bool { + $name = $module instanceof Module ? $module->getName() : $module; + $moduleRecord = $this->modelClass::where('name', $name)->first(); + if ($moduleRecord) { + return boolval($moduleRecord['status']) === $status; + } else { + return $status === false; + } + } + + /** + * Set active state for a module. + */ + public function setActive(Module $module, bool $active): void { + $this->setActiveByName($module->getName(), $active); + } + + /** + * Sets a module status by its name + */ + public function setActiveByName(string $name, bool $active): void { + $module = ModuleFacade::getModulePath($name); + $json = $this->readJson($module . 'module.json'); + $this->modelClass::upsert(['name' => $name, 'status' => $active, 'title' => $json['alias']], ['name'], ['status', 'title']); + } + + /** + * Deletes a module activation status + */ + public function delete(Module $module): void { + $moduleRecord = $this->modelClass::where('name', $module->getName())->first(); + if ($moduleRecord) { + $moduleRecord->delete(); + } + } + + /** + * Deletes any module activation statuses created by this class. + */ + public function reset(): void { + $this->modelClass::truncate(); + } + + + public function getDataList($request){ + $map = []; + + if($request->filled('title')){ + $map[] = ['title', 'like', '%' . $request->input('title') . '%']; + } + + $query = Modules::where($map)->orderBy('id', 'asc'); + + if($request->filled('page')){ + $data = [ + 'total' => $query->count(), + 'page' => $request->input('page', 1), + 'data' => $query->offset($request->input('offset', 0))->limit($request->input('limit', 10))->get(), + ]; + }else{ + $data = $query->get(); + } + return $data; + } + + public function update($request){ + if($request->filled('name')){ + $name = $request->input('name'); + $module = ModuleFacade::getModulePath($name); + $json = $this->readJson($module . 'module.json'); + + $module = Modules::where('name', $name)->first(); + + $module->title = $json['alias']; + + $module->save(); + return $module; + } + } + + /** + * Reads the json file that contains the activation statuses. + * + * @throws FileNotFoundException + */ + private function readJson($file): array { + if (! $this->files->exists($file)) { + return []; + } + + return $this->files->json($file); + } +} diff --git a/app/Services/System/SettingService.php b/app/Services/System/SettingService.php new file mode 100644 index 0000000..8276c63 --- /dev/null +++ b/app/Services/System/SettingService.php @@ -0,0 +1,100 @@ + +// +---------------------------------------------------------------------- +namespace App\Services\System; + +use App\Models\System\Config; + +class SettingService { + + /** + * @title 获取配置列表 + * + * @param [type] $request + * @return void + */ + public function getDataList($request){ + $map = []; + + if ($request->filled('group_name')) { + $map[] = ['group', '=', $request->input('group_name')]; + } + + $data = Config::where($map)->orderBy('sort', 'asc')->pluck('values', 'name'); + return $data; + } + + public function create($request){ + $request->validate([ + 'title' => 'required', + 'name' => 'required', + 'values' => 'required', + ]); + + $data = $request->all(); + $config = Config::create($data); + + $this->setConfigCache(true); + return $config; + } + + public function update($request){ + $request->validate([ + 'title' => 'required', + 'name' => 'required', + ]); + $config = Config::where('id', $request->input('id'))->first(); + + + foreach ($config->setFilterFields($request->all()) as $key => $value) { + $config->$key = $value; + } + $config->save(); + + $this->setConfigCache(true); + return $config; + } + + public function saveSetting($request){ + $data = $request->all(); + + foreach ($data as $key => $value) { + Config::where('name', $key)->update(['values' => $value]); + } + + $this->setConfigCache(true); + return true; + } + + public function delete($request){ + $data = $request->all(); + $config = Config::find($data['id']); + $config->delete(); + + $this->setConfigCache(true); + return $config; + } + + public function getFields($request){ + $map = []; + + if ($request->filled('group_name')) { + $map[] = ['group', '=', $request->input('group_name')]; + } + + $config = Config::where($map)->orderBy('sort', 'asc')->get()->append(['options', 'label']); + return $config; + } + + public static function setConfigCache($refresh = false){ + if (!cache('config') || $refresh){ + $config = Config::orderBy('sort', 'asc')->pluck('values', 'name'); + cache('config', $config); + } + } +} diff --git a/app/Services/System/TasksService.php b/app/Services/System/TasksService.php new file mode 100644 index 0000000..15b90c3 --- /dev/null +++ b/app/Services/System/TasksService.php @@ -0,0 +1,111 @@ + +// +---------------------------------------------------------------------- +namespace App\Services\System; + +use App\Models\System\Tasks; +use Illuminate\Support\Facades\Storage; + +class TasksService { + + public function getDataList($request){ + $map = []; + + if ($request->filled('title')) { + $map[] = ['title', 'like', '%' . $request->input('title') . '%']; + } + if ($request->filled('name')) { + $map[] = ['name', 'like', '%' . $request->input('name') . '%']; + } + if ($request->filled('url')) { + $map[] = ['url', 'like', '%' . $request->input('url') . '%']; + } + if ($request->filled('method')) { + $map[] = ['method', 'like', '%' . $request->input('method') . '%']; + } + if ($request->filled('ip')) { + $map[] = ['ip', 'like', '%' . $request->input('ip') . '%']; + } + if ($request->filled('user_id')) { + $map[] = ['user_id', '=', $request->input('user_id')]; + } + if ($request->filled('data')) { + $map[] = ['data', 'like', '%' . $request->input('data') . '%']; + } + if ($request->filled('remark')) { + $map[] = ['remark', 'like', '%' . $request->input('remark') . '%']; + } + if ($request->filled('status')) { + $map[] = ['status', '=', $request->input('status')]; + } + if ($request->filled('is_my')) { + $user = auth('admin')->user(); + $map[] = ['user_id', '=', $user->uid]; + } + + $query = Tasks::with(['user:nickname,username,uid'])->where($map)->orderBy('id', 'desc'); + + if ($request->filled('page')) { + $data = [ + 'total' => $query->count(), + 'page' => $request->input('page', 1), + 'data' => $query->offset($request->input('offset', 0))->limit($request->input('limit', 10))->get()->append(['url']), + ]; + }else{ + $data = $query->get()->append(['url']); + } + return $data; + } + + public function create($data){ + $task = new Tasks(); + + $task->type = $data['type'] ? $data['type'] : 'export'; + $task->title = $data['title'] ? $data['title'] : $task->type . '任务'; + $task->file = $data['file'] ? $data['file'] : ''; + $task->status = $data['status'] ? $data['status'] : 0; + $task->user_id = auth('admin')->user()->uid; + + $task->save(); + return $task; + } + + public function update($data){ + $task = Tasks::where('id', $data['id'])->first(); + + if ($data['status'] != '') { + $task->status = $data['status']; + } + if ($data['file'] != '') { + $task->file = $data['file']; + } + if (in_array($data['type'], ['import', 'export'])) { + $task->type = $data['type']; + } + if ($data['title'] != '') { + $task->title = $data['title']; + } + + $task->save(); + } + + public function delete($data){ + $task = Tasks::where('id', $data['id'])->first(); + + if ($task->status == 0) { + throw new \Exception("该任务正在执行中,无法删除"); + } + + if ($task->file) { + Storage::disk('public')->delete($task->file); + } + + $task->delete(); + return $task; + } +} diff --git a/app/Support/Regex.php b/app/Support/Regex.php new file mode 100644 index 0000000..8d565e9 --- /dev/null +++ b/app/Support/Regex.php @@ -0,0 +1,230 @@ + +// +---------------------------------------------------------------------- +namespace App\Support; + +class Regex { + + + /** + * 验证用户名 + * + * @param string $value 验证的值 + * @param int $minLen 最小长度 + * @param int $maxLen 最大长度 + * @param string $type 验证类型,默认‘ALL’,EN.验证英文,CN.验证中文,ALL.验证中文和英文 + * @return bool + */ + public static function isUsername($value, $minLen = 2, $maxLen = 48, $type = 'ALL') + { + if (empty ($value)) { + return false; + } + + switch ($type) { + case 'EN' : + $match = '/^[_\w\d]{' . $minLen . ',' . $maxLen . '}$/iu'; + break; + case 'CN' : + $match = '/^[_\x{4e00}-\x{9fa5}\d]{' . $minLen . ',' . $maxLen . '}$/iu'; + break; + default : + $match = '/^[_\w\d\x{4e00}-\x{9fa5}]{' . $minLen . ',' . $maxLen . '}$/iu'; + } + + return preg_match($match, $value) !== 0; + } + + /** + * 验证密码 + * + * @param string $value 验证的值 + * @param int $minLen 最小长度 + * @param int $maxLen 最大长度 + * @return bool + */ + public static function isPassword($value, $minLen = 6, $maxLen = 16) + { + $value = trim($value); + if (empty ($value)) { + return false; + } + + $match = '/^[\\~!@#$%^&*()-_=+|{}\[\],.?\/:;\'\"\d\w]{' . $minLen . ',' . $maxLen . '}$/'; + + return preg_match($match, $value) !== 0; + } + + /** + * 验证eamil + * + * @param string $value 验证的值 + * @return bool + */ + public static function isEmail($value) + { + $value = trim($value); + if (empty ($value)) { + return false; + } + + $match = '/^[\w\d]+[\w\d-.]*@[\w\d-.]+\.[\w\d]{2,10}$/i'; + + return preg_match($match, $value) !== 0; + } + + /** + * 验证电话号码 + * + * @param string $value 验证的值 + * @return bool + */ + public static function isTelephone($value) + { + $value = trim($value); + if (empty ($value)) { + return false; + } + + $match = '/^0[0-9]{2,3}[-]?\d{7,8}$/'; + + return preg_match($match, $value) !== 0; + } + + /** + * 验证手机 + * + * @param string $value 验证的值 + * @return bool + */ + public static function isMobile($value) + { + $value = trim($value); + if (empty ($value)) { + return false; + } + + $match = '/^[(86)|0]?(1\d{10})$/'; + + return preg_match($match, $value) !== 0; + } + + /** + * 验证邮政编码 + * + * @param string $value 验证的值 + * @return bool + */ + public static function isPostCode($value) + { + $value = trim($value); + if (empty ($value)) { + return false; + } + + $match = '/\d{6}/'; + + return preg_match($match, $value) !== 0; + } + + /** + * 验证IP + * + * @param string $value 验证的值 + * @return boolean + */ + public static function isIp($value) + { + $value = trim($value); + if (empty ($value)) { + return false; + } + + $match = '/^(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9])' . + '\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)' . + '\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)' . + '\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])$/'; + + return preg_match($match, $value) !== 0; + } + + /** + * 验证身份证号码 + * + * @param string $value 验证的值 + * @return boolean + */ + public static function isIDCard($value) + { + $value = trim($value); + if (empty ($value)) { + return false; + } + + if (strlen($value) > 18) { + return false; + } + + $match = '/^\d{6}((1[89])|(2\d))\d{2}((0\d)|(1[0-2]))((3[01])|([0-2]\d))\d{3}(\d|X)$/i'; + + return preg_match($match, $value) !== 0; + } + + /** + * 验证URL + * + * @param string $value 验证的值 + * @return boolean + */ + public static function isUrl($value) + { + $value = strtolower(trim($value)); + if (empty ($value)) { + return false; + } + $match = '/^(http:\/\/)?(https:\/\/)?([\w\d-]+\.)+[\w-]+(\/[\d\w-.\/?%&=]*)?$/'; + + return preg_match($match, $value) !== 0; + } + + /** + * 是否有数字 + * 说明:如果字符串中含有非法字符返回假,没有返回真 + * + * @param string $value 验证的值 + * @return int + */ + public static function hasNumber($value) + { + return preg_match("/[0-9]/", $value) != false; + } + + /** + * 是否含有英文 + * 说明:如果字符串中含有非法字符返回假,没有返回真 + * + * @param string $value 验证的值 + * @return bool + */ + public static function hasEnglish($value) + { + return preg_match("/[a-zA-Z]/", $value) != false; + } + + /** + * 是否有中文 + * 说明:如果字符串中含有非法字符返回假,没有返回真 + * + * @param string $value 验证的值 + * @return bool + */ + public static function hasChinese($value) + { + return preg_match("/[\x7f-\xff]/", $value) != false; + } +} diff --git a/app/Support/Time.php b/app/Support/Time.php new file mode 100644 index 0000000..981113d --- /dev/null +++ b/app/Support/Time.php @@ -0,0 +1,298 @@ + +// +---------------------------------------------------------------------- +namespace App\Support; + +use Illuminate\Support\Carbon; + +class Time { + + /** + * 返回今日开始和结束的时间戳 + * + * @return array + */ + public static function today($data = '') { + [$y, $m, $d] = explode('-', $data ? $data : date('Y-m-d')); + + return [ + mktime(0, 0, 0, $m, $d, $y), + mktime(23, 59, 59, $m, $d, $y), + ]; + } + + /** + * 返回昨日开始和结束的时间戳 + * + * @return array + */ + public static function yesterday() { + $yesterday = date('d') - 1; + + return [ + mktime(0, 0, 0, date('m'), $yesterday, date('Y')), + mktime(23, 59, 59, date('m'), $yesterday, date('Y')), + ]; + } + + /** + * 返回本周开始和结束的时间戳 + * + * @return array + */ + public static function week() { + [$y, $m, $d, $w] = explode('-', date('Y-m-d-w')); + if ($w == 0) $w = 7; //修正周日的问题 + + return [ + mktime(0, 0, 0, $m, $d - $w + 1, $y), mktime(23, 59, 59, $m, $d - $w + 7, $y), + ]; + } + + /** + * 返回上周开始和结束的时间戳 + * + * @return array + */ + public static function lastWeek() { + $timestamp = time(); + + return [ + strtotime(date('Y-m-d', strtotime("last week Monday", $timestamp))), + strtotime(date('Y-m-d', strtotime("last week Sunday", $timestamp))) + 24 * 3600 - 1, + ]; + } + + /** + * 返回本月开始和结束的时间戳 + * + * @return array + */ + public static function month($data = '') { + $res = []; + $data = $data ? $data : date('Y-m-d'); + $nextMoth = date('Y-m-d', strtotime($data . ' +1 month')); + + return [ + mktime(0, 0, 0, date('m', strtotime($data)), 1, date('Y', strtotime($data))), + mktime(0, 0, 0, date('m', strtotime($nextMoth)), 1, date('Y', strtotime($nextMoth))), + ]; + } + + /** + * 返回上个月开始和结束的时间戳 + * + * @return array + */ + public static function lastMonth() { + $y = date('Y'); + $m = date('m'); + $begin = mktime(0, 0, 0, $m - 1, 1, $y); + $end = mktime(23, 59, 59, $m - 1, date('t', $begin), $y); + + return [$begin, $end]; + } + + /** + * 返回今年开始和结束的时间戳 + * + * @return array + */ + public static function year() { + $y = date('Y'); + + return [ + mktime(0, 0, 0, 1, 1, $y), + mktime(23, 59, 59, 12, 31, $y), + ]; + } + + /** + * 返回去年开始和结束的时间戳 + * + * @return array + */ + public static function lastYear() { + $year = date('Y') - 1; + + return [ + mktime(0, 0, 0, 1, 1, $year), + mktime(23, 59, 59, 12, 31, $year), + ]; + } + + /** + * 获取几天前零点到现在/昨日结束的时间戳 + * + * @param int $day 天数 + * @param bool $now 返回现在或者昨天结束时间戳 + * @return array + */ + public static function dayToNow($day = 1, $now = true) { + $end = time(); + if (!$now) { + [$foo, $end] = self::yesterday(); + } + + return [ + mktime(0, 0, 0, date('m'), date('d') - $day, date('Y')), + $end, + ]; + } + + /** + * 返回几天前的时间戳 + * + * @param int $day + * @return int + */ + public static function daysAgo($day = 1) { + $nowTime = time(); + + return $nowTime - self::daysToSecond($day); + } + + /** + * 返回几天后的时间戳 + * + * @param int $day + * @return int + */ + public static function daysAfter($day = 1) { + $nowTime = time(); + + return $nowTime + self::daysToSecond($day); + } + + /** + * 天数转换成秒数 + * + * @param int $day + * @return int + */ + public static function daysToSecond($day = 1) { + return $day * 86400; + } + + /** + * 周数转换成秒数 + * + * @param int $week + * @return int + */ + public static function weekToSecond($week = 1) { + return self::daysToSecond() * 7 * $week; + } + + /** + * 获取毫秒级别的时间戳 + */ + public static function getMillisecond() { + $time = explode(" ", microtime()); + $time = $time[1] . ($time[0] * 1000); + $time2 = explode(".", $time); + $time = $time2[0]; + + return $time; + } + + /** + * 获取相对时间 + * + * @param int $timeStamp + * @return string + */ + public static function formatRelative($timeStamp) { + $currentTime = time(); + + // 判断传入时间戳是否早于当前时间戳 + $isEarly = $timeStamp <= $currentTime; + + // 获取两个时间戳差值 + $diff = abs($currentTime - $timeStamp); + + $dirStr = $isEarly ? '前' : '后'; + + if ($diff < 60) { // 一分钟之内 + $resStr = $diff . '秒' . $dirStr; + } elseif ($diff >= 60 && $diff < 3600) { // 多于59秒,少于等于59分钟59秒 + $resStr = floor($diff / 60) . '分钟' . $dirStr; + } elseif ($diff >= 3600 && $diff < 86400) { // 多于59分钟59秒,少于等于23小时59分钟59秒 + $resStr = floor($diff / 3600) . '小时' . $dirStr; + } elseif ($diff >= 86400 && $diff < 2623860) { // 多于23小时59分钟59秒,少于等于29天59分钟59秒 + $resStr = floor($diff / 86400) . '天' . $dirStr; + } elseif ($diff >= 2623860 && $diff <= 31567860 && $isEarly) { // 多于29天59分钟59秒,少于364天23小时59分钟59秒,且传入的时间戳早于当前 + $resStr = date('m-d H:i', $timeStamp); + } else { + $resStr = date('Y-m-d', $timeStamp); + } + + return $resStr; + } + + /** + * 范围日期转换时间戳 + * + * @param string $rangeDatetime + * @param int $maxRange 最大时间间隔 + * @param string $delimiter + * @return array + */ + public static function parseRange($rangeDatetime, $maxRange = 0, $delimiter = ' - ') { + $rangeDatetime = explode($delimiter, $rangeDatetime, 2); + $rangeDatetime[0] = strtotime($rangeDatetime[0]); + $rangeDatetime[1] = isset($rangeDatetime[1]) ? strtotime($rangeDatetime[1]) : time(); + $rangeDatetime = [ + min($rangeDatetime[0], $rangeDatetime[1]), + max($rangeDatetime[0], $rangeDatetime[1]), + ]; + + // 如果结束时间小于或等于开始时间 直接返回null +// if ($rangeDatetime[1] < $rangeDatetime[0]) { +// return null; +// } + + // 如果大于最大时间间隔 则用结束时间减去最大时间间隔获得开始时间 + if ($maxRange > 0 && $rangeDatetime[1] - $rangeDatetime[0] > $maxRange) { + $rangeDatetime[0] = $rangeDatetime[1] - $maxRange; + } + + return $rangeDatetime; + } + + /** + * 获取指定时间范围内的日期数组 + * @param int $startTime + * @param int $endTime + * @return \Carbon\CarbonPeriod + */ + public static function daysUntilOfTimestamp($startTime, $endTime) { + $startTime = Carbon::createFromTimestamp($startTime); + $endTime = Carbon::createFromTimestamp($endTime); + + return $startTime->daysUntil($endTime); + } + + /** + * 时间排序 + * + * @param array $times + * @return array + */ + public static function sort($times) { + usort($times, function ($com1, $com2) { + $com1 = strtotime($com1); + $com2 = strtotime($com2); + + return $com1 < $com2 ? -1 : 1; + }); + + return $times; + } + +} diff --git a/app/Support/Tree.php b/app/Support/Tree.php new file mode 100644 index 0000000..5b74cde --- /dev/null +++ b/app/Support/Tree.php @@ -0,0 +1,88 @@ + +// +---------------------------------------------------------------------- +namespace App\Support; + +class Tree { + + public static function list_to_tree($list, $root = 0, $pk = 'id', $pid = 'parent_id', $children = 'children') { + // 创建Tree + $tree = array(); + if (is_array($list)) { + // 创建基于主键的数组引用 + $refer = array(); + foreach ($list as $key => $data) { + $refer[$data[$pk]] = &$list[$key]; + } + foreach ($list as $key => $data) { + // 判断是否存在parent + $parentId = 0; + if (isset($data[$pid])) { + $parentId = $data[$pid]; + } + if ((string)$root == $parentId) { + $tree[] = &$list[$key]; + } else { + if (isset($refer[$parentId])) { + $parent = &$refer[$parentId]; + $parent[$children][] = &$list[$key]; + } + } + } + } + return $tree; + } + + public static function tree_to_list($tree = [], $children = 'children') { + if (empty($tree) || !is_array($tree)) { + return $tree; + } + $arrRes = []; + foreach ($tree as $k => $v) { + $arrTmp = $v; + unset($arrTmp[$children]); + $arrRes[] = $arrTmp; + if (!empty($v[$children])) { + $arrTmp = self::tree_to_list($v[$children], $children); + $arrRes = array_merge($arrRes, $arrTmp); + } + } + return $arrRes; + } + + /** + * 获得所有的子 + */ + public static function get_children($data, $id = 0, $pk = 'id', $pid = 'parent_id') { + $array = []; + foreach ($data as $k => $v) { + if ($v[$pid] == $id) { + $array[] = $v[$pk]; + array_merge($array, self::get_children($data, $v[$pk], $pk, $pid)); + } + } + return $array; + } + + /** + * 获取id的所有父,包含自己 + */ + public static function get_parents($data, $id = 0, $pk = 'id', $pid = 'parent_id', $field = '') { + $temp = []; + foreach ($data as $k => $v) { + if ($v[$pk] == $id) { + $temp[] = $v; + $temp = array_merge($temp, self::get_parents($data, $v[$pid]), $pk, $pid, $field); + } + } + if($field){ + $temp = array_column($temp, $field); + } + return $temp; + } +} diff --git a/app/Traits/AdminController.php b/app/Traits/AdminController.php new file mode 100644 index 0000000..7c97ba4 --- /dev/null +++ b/app/Traits/AdminController.php @@ -0,0 +1,88 @@ + +// +---------------------------------------------------------------------- +namespace App\Traits; + +use Illuminate\Http\Request; + +trait AdminController { + + protected $service = null; + + /** + * @title 列表 + * + * @param Request $request + * @param $service + * @return void + */ + public function index(Request $request){ + try { + $this->data['data'] = $this->service->getDataList($request); + } catch (\Throwable $th) { + $this->data['code'] = 0; + $this->data['message'] = $th->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 添加 + * + * @param Request $request + * @param $service + * @return void + */ + public function add(Request $request){ + try { + $this->data['data'] = $this->service->create($request); + } catch (\Throwable $th) { + $this->data['code'] = 0; + $this->data['message'] = $th->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 编辑 + * + * @param Request $request + * @param $service + * @return void + */ + public function edit(Request $request){ + try { + $this->data['data'] = $this->service->update($request); + } catch (\Throwable $th) { + $this->data['code'] = 0; + $this->data['message'] = $th->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 删除 + * + * @param Request $request + * @param $service + * @return void + */ + public function delete(Request $request){ + try { + $this->data['data'] = $this->service->delete($request); + } catch (\Throwable $th) { + $this->data['code'] = 0; + $this->data['message'] = $th->getMessage(); + } + + return response()->json($this->data); + } +} diff --git a/app/Traits/ModelTrait.php b/app/Traits/ModelTrait.php new file mode 100644 index 0000000..0872cb3 --- /dev/null +++ b/app/Traits/ModelTrait.php @@ -0,0 +1,43 @@ + +// +---------------------------------------------------------------------- +namespace App\Traits; + +use Illuminate\Support\Facades\Schema; + +trait ModelTrait { + + protected function casts(): array { + return [ + 'created_at' => 'datetime:Y-m-d H:i:s', + 'updated_at' => 'datetime:Y-m-d H:i:s', + 'deleted_at' => 'datetime:Y-m-d H:i:s', + ]; + } + + protected function serializeDate($data){ + return $data->timezone('Asia/Shanghai')->format('Y-m-d H:i:s'); + } + /** + * 过滤移除非当前表的字段参数 + * + * @param array $params + * + * @return array + */ + public function setFilterFields(array $params) : array { + $fields = Schema::getColumnListing($this->getTable()); + foreach ($params as $key => $param) { + if ( !in_array($key, $fields) ) unset($params[$key]); + } + // 同时过滤时间戳字段【时间戳只允许自动更改,不允许手动设置】 + if ( $this->timestamps === true && isset($params[self::CREATED_AT]) ) unset($params[self::CREATED_AT]); + if ( $this->timestamps === true && isset($params[self::UPDATED_AT]) ) unset($params[self::UPDATED_AT]); + return $params; + } +} diff --git a/artisan b/artisan new file mode 100644 index 0000000..8e04b42 --- /dev/null +++ b/artisan @@ -0,0 +1,15 @@ +#!/usr/bin/env php +handleCommand(new ArgvInput); + +exit($status); diff --git a/bootstrap/app.php b/bootstrap/app.php new file mode 100644 index 0000000..d3f20ba --- /dev/null +++ b/bootstrap/app.php @@ -0,0 +1,38 @@ +withRouting( + web: __DIR__.'/../routes/web.php', + api: __DIR__.'/../routes/api.php', + commands: __DIR__.'/../routes/console.php', + then: function(){ + Route::middleware(['api']) + ->prefix('admin') + ->name('admin.') + ->group(base_path('routes/admin.php')); + }, + health: '/up', + ) + ->withMiddleware(function (Middleware $middleware) { + $middleware->alias([ + 'auth.check' => AuthCheckMiddleware::class + ]); + }) + ->withExceptions(function (Exceptions $exceptions) { + $exceptions->shouldRenderJsonWhen(function(Request $request, Throwable $e){ + // if($request->is('admin/*')){ + // return true; + // } + return $request->expectsJson(); + }); + }) + ->withEvents(discover: [ + __DIR__ . '/../app/Listeners' + ]) + ->create(); diff --git a/bootstrap/cache/.gitignore b/bootstrap/cache/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/bootstrap/cache/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/bootstrap/providers.php b/bootstrap/providers.php new file mode 100644 index 0000000..38b258d --- /dev/null +++ b/bootstrap/providers.php @@ -0,0 +1,5 @@ + env('APP_NAME', 'Laravel'), + + /* + |-------------------------------------------------------------------------- + | Application Environment + |-------------------------------------------------------------------------- + | + | This value determines the "environment" your application is currently + | running in. This may determine how you prefer to configure various + | services the application utilizes. Set this in your ".env" file. + | + */ + + 'env' => env('APP_ENV', 'production'), + + /* + |-------------------------------------------------------------------------- + | Application Debug Mode + |-------------------------------------------------------------------------- + | + | When your application is in debug mode, detailed error messages with + | stack traces will be shown on every error that occurs within your + | application. If disabled, a simple generic error page is shown. + | + */ + + 'debug' => (bool) env('APP_DEBUG', false), + + /* + |-------------------------------------------------------------------------- + | Application URL + |-------------------------------------------------------------------------- + | + | This URL is used by the console to properly generate URLs when using + | the Artisan command line tool. You should set this to the root of + | the application so that it's available within Artisan commands. + | + */ + + 'url' => env('APP_URL', 'http://localhost'), + + /* + |-------------------------------------------------------------------------- + | Application Timezone + |-------------------------------------------------------------------------- + | + | Here you may specify the default timezone for your application, which + | will be used by the PHP date and date-time functions. The timezone + | is set to "UTC" by default as it is suitable for most use cases. + | + */ + + 'timezone' => env('APP_TIMEZONE', 'UTC'), + + /* + |-------------------------------------------------------------------------- + | Application Locale Configuration + |-------------------------------------------------------------------------- + | + | The application locale determines the default locale that will be used + | by Laravel's translation / localization methods. This option can be + | set to any locale for which you plan to have translation strings. + | + */ + + 'locale' => env('APP_LOCALE', 'en'), + + 'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'), + + 'faker_locale' => env('APP_FAKER_LOCALE', 'en_US'), + + /* + |-------------------------------------------------------------------------- + | Encryption Key + |-------------------------------------------------------------------------- + | + | This key is utilized by Laravel's encryption services and should be set + | to a random, 32 character string to ensure that all encrypted values + | are secure. You should do this prior to deploying the application. + | + */ + + 'cipher' => 'AES-256-CBC', + + 'key' => env('APP_KEY'), + + 'previous_keys' => [ + ...array_filter( + explode(',', env('APP_PREVIOUS_KEYS', '')) + ), + ], + + /* + |-------------------------------------------------------------------------- + | Maintenance Mode Driver + |-------------------------------------------------------------------------- + | + | These configuration options determine the driver used to determine and + | manage Laravel's "maintenance mode" status. The "cache" driver will + | allow maintenance mode to be controlled across multiple machines. + | + | Supported drivers: "file", "cache" + | + */ + + 'maintenance' => [ + 'driver' => env('APP_MAINTENANCE_DRIVER', 'file'), + 'store' => env('APP_MAINTENANCE_STORE', 'database'), + ], + +]; diff --git a/config/auth.php b/config/auth.php new file mode 100644 index 0000000..b4bb506 --- /dev/null +++ b/config/auth.php @@ -0,0 +1,122 @@ + [ + 'guard' => env('AUTH_GUARD', 'web'), + 'passwords' => env('AUTH_PASSWORD_BROKER', 'users'), + ], + + /* + |-------------------------------------------------------------------------- + | Authentication Guards + |-------------------------------------------------------------------------- + | + | Next, you may define every authentication guard for your application. + | Of course, a great default configuration has been defined for you + | which utilizes session storage plus the Eloquent user provider. + | + | All authentication guards have a user provider, which defines how the + | users are actually retrieved out of your database or other storage + | system used by the application. Typically, Eloquent is utilized. + | + | Supported: "session" + | + */ + + 'guards' => [ + 'web' => [ + 'driver' => 'session', + 'provider' => 'members', + ], + 'admin' => [ + 'driver' => 'jwt', + 'provider' => 'admins' + ], + 'api' => [ + 'driver' => 'jwt', + 'provider' => 'members', + ] + ], + + /* + |-------------------------------------------------------------------------- + | User Providers + |-------------------------------------------------------------------------- + | + | All authentication guards have a user provider, which defines how the + | users are actually retrieved out of your database or other storage + | system used by the application. Typically, Eloquent is utilized. + | + | If you have multiple user tables or models you may configure multiple + | providers to represent the model / table. These providers may then + | be assigned to any extra authentication guards you have defined. + | + | Supported: "database", "eloquent" + | + */ + + 'providers' => [ + 'admins' => [ + 'driver' => 'eloquent', + 'model' => App\Models\Auth\Admin::class, + ], + 'members' => [ + 'driver' => 'eloquent', + 'model' => App\Member\Models\Member::class, + ], + ], + + /* + |-------------------------------------------------------------------------- + | Resetting Passwords + |-------------------------------------------------------------------------- + | + | These configuration options specify the behavior of Laravel's password + | reset functionality, including the table utilized for token storage + | and the user provider that is invoked to actually retrieve users. + | + | The expiry time is the number of minutes that each reset token will be + | considered valid. This security feature keeps tokens short-lived so + | they have less time to be guessed. You may change this as needed. + | + | The throttle setting is the number of seconds a user must wait before + | generating more password reset tokens. This prevents the user from + | quickly generating a very large amount of password reset tokens. + | + */ + + 'passwords' => [ + 'users' => [ + 'provider' => 'members', + 'table' => env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_reset_tokens'), + 'expire' => 60, + 'throttle' => 60, + ], + ], + + /* + |-------------------------------------------------------------------------- + | Password Confirmation Timeout + |-------------------------------------------------------------------------- + | + | Here you may define the amount of seconds before a password confirmation + | window expires and users are asked to re-enter their password via the + | confirmation screen. By default, the timeout lasts for three hours. + | + */ + + 'password_timeout' => env('AUTH_PASSWORD_TIMEOUT', 10800), + +]; diff --git a/config/cache.php b/config/cache.php new file mode 100644 index 0000000..6b57b18 --- /dev/null +++ b/config/cache.php @@ -0,0 +1,107 @@ + env('CACHE_STORE', 'database'), + + /* + |-------------------------------------------------------------------------- + | Cache Stores + |-------------------------------------------------------------------------- + | + | Here you may define all of the cache "stores" for your application as + | well as their drivers. You may even define multiple stores for the + | same cache driver to group types of items stored in your caches. + | + | Supported drivers: "array", "database", "file", "memcached", + | "redis", "dynamodb", "octane", "null" + | + */ + + 'stores' => [ + + 'array' => [ + 'driver' => 'array', + 'serialize' => false, + ], + + 'database' => [ + 'driver' => 'database', + 'table' => env('DB_CACHE_TABLE', 'cache'), + 'connection' => env('DB_CACHE_CONNECTION'), + 'lock_connection' => env('DB_CACHE_LOCK_CONNECTION'), + ], + + 'file' => [ + 'driver' => 'file', + 'path' => storage_path('framework/cache/data'), + 'lock_path' => storage_path('framework/cache/data'), + ], + + 'memcached' => [ + 'driver' => 'memcached', + 'persistent_id' => env('MEMCACHED_PERSISTENT_ID'), + 'sasl' => [ + env('MEMCACHED_USERNAME'), + env('MEMCACHED_PASSWORD'), + ], + 'options' => [ + // Memcached::OPT_CONNECT_TIMEOUT => 2000, + ], + 'servers' => [ + [ + 'host' => env('MEMCACHED_HOST', '127.0.0.1'), + 'port' => env('MEMCACHED_PORT', 11211), + 'weight' => 100, + ], + ], + ], + + 'redis' => [ + 'driver' => 'redis', + 'connection' => env('REDIS_CACHE_CONNECTION', 'cache'), + 'lock_connection' => env('REDIS_CACHE_LOCK_CONNECTION', 'default'), + ], + + 'dynamodb' => [ + 'driver' => 'dynamodb', + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), + 'table' => env('DYNAMODB_CACHE_TABLE', 'cache'), + 'endpoint' => env('DYNAMODB_ENDPOINT'), + ], + + 'octane' => [ + 'driver' => 'octane', + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Cache Key Prefix + |-------------------------------------------------------------------------- + | + | When utilizing the APC, database, memcached, Redis, and DynamoDB cache + | stores, there might be other applications using the same cache. For + | that reason, you may prefix every cache key to avoid collisions. + | + */ + + 'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_cache_'), + +]; diff --git a/config/cors.php b/config/cors.php new file mode 100644 index 0000000..6480dde --- /dev/null +++ b/config/cors.php @@ -0,0 +1,34 @@ + ['api/*', 'admin/*'], + + 'allowed_methods' => ['*'], + + 'allowed_origins' => ['*'], + + 'allowed_origins_patterns' => [], + + 'allowed_headers' => ['*'], + + 'exposed_headers' => [], + + 'max_age' => 0, + + 'supports_credentials' => false, + +]; diff --git a/config/database.php b/config/database.php new file mode 100644 index 0000000..9f07812 --- /dev/null +++ b/config/database.php @@ -0,0 +1,170 @@ + env('DB_CONNECTION', 'sqlite'), + + /* + |-------------------------------------------------------------------------- + | Database Connections + |-------------------------------------------------------------------------- + | + | Below are all of the database connections defined for your application. + | An example configuration is provided for each database system which + | is supported by Laravel. You're free to add / remove connections. + | + */ + + 'connections' => [ + + 'sqlite' => [ + 'driver' => 'sqlite', + 'url' => env('DB_URL'), + 'database' => env('DB_DATABASE', database_path('database.sqlite')), + 'prefix' => '', + 'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true), + ], + + 'mysql' => [ + 'driver' => 'mysql', + 'url' => env('DB_URL'), + 'host' => env('DB_HOST', '127.0.0.1'), + 'port' => env('DB_PORT', '3306'), + 'database' => env('DB_DATABASE', 'laravel'), + 'username' => env('DB_USERNAME', 'root'), + 'password' => env('DB_PASSWORD', ''), + 'unix_socket' => env('DB_SOCKET', ''), + 'charset' => env('DB_CHARSET', 'utf8mb4'), + 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'), + 'prefix' => 'sent_', + 'prefix_indexes' => true, + 'strict' => true, + 'engine' => null, + 'options' => extension_loaded('pdo_mysql') ? array_filter([ + PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), + ]) : [], + ], + + 'mariadb' => [ + 'driver' => 'mariadb', + 'url' => env('DB_URL'), + 'host' => env('DB_HOST', '127.0.0.1'), + 'port' => env('DB_PORT', '3306'), + 'database' => env('DB_DATABASE', 'laravel'), + 'username' => env('DB_USERNAME', 'root'), + 'password' => env('DB_PASSWORD', ''), + 'unix_socket' => env('DB_SOCKET', ''), + 'charset' => env('DB_CHARSET', 'utf8mb4'), + 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'), + 'prefix' => '', + 'prefix_indexes' => true, + 'strict' => true, + 'engine' => null, + 'options' => extension_loaded('pdo_mysql') ? array_filter([ + PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), + ]) : [], + ], + + 'pgsql' => [ + 'driver' => 'pgsql', + 'url' => env('DB_URL'), + 'host' => env('DB_HOST', '127.0.0.1'), + 'port' => env('DB_PORT', '5432'), + 'database' => env('DB_DATABASE', 'laravel'), + 'username' => env('DB_USERNAME', 'root'), + 'password' => env('DB_PASSWORD', ''), + 'charset' => env('DB_CHARSET', 'utf8'), + 'prefix' => '', + 'prefix_indexes' => true, + 'search_path' => 'public', + 'sslmode' => 'prefer', + ], + + 'sqlsrv' => [ + 'driver' => 'sqlsrv', + 'url' => env('DB_URL'), + 'host' => env('DB_HOST', 'localhost'), + 'port' => env('DB_PORT', '1433'), + 'database' => env('DB_DATABASE', 'laravel'), + 'username' => env('DB_USERNAME', 'root'), + 'password' => env('DB_PASSWORD', ''), + 'charset' => env('DB_CHARSET', 'utf8'), + 'prefix' => '', + 'prefix_indexes' => true, + // 'encrypt' => env('DB_ENCRYPT', 'yes'), + // 'trust_server_certificate' => env('DB_TRUST_SERVER_CERTIFICATE', 'false'), + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Migration Repository Table + |-------------------------------------------------------------------------- + | + | This table keeps track of all the migrations that have already run for + | your application. Using this information, we can determine which of + | the migrations on disk haven't actually been run on the database. + | + */ + + 'migrations' => [ + 'table' => 'migrations', + 'update_date_on_publish' => true, + ], + + /* + |-------------------------------------------------------------------------- + | Redis Databases + |-------------------------------------------------------------------------- + | + | Redis is an open source, fast, and advanced key-value store that also + | provides a richer body of commands than a typical key-value system + | such as Memcached. You may define your connection settings here. + | + */ + + 'redis' => [ + + 'client' => env('REDIS_CLIENT', 'phpredis'), + + 'options' => [ + 'cluster' => env('REDIS_CLUSTER', 'redis'), + 'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'), + ], + + 'default' => [ + 'url' => env('REDIS_URL'), + 'host' => env('REDIS_HOST', '127.0.0.1'), + 'username' => env('REDIS_USERNAME'), + 'password' => env('REDIS_PASSWORD'), + 'port' => env('REDIS_PORT', '6379'), + 'database' => env('REDIS_DB', '0'), + ], + + 'cache' => [ + 'url' => env('REDIS_URL'), + 'host' => env('REDIS_HOST', '127.0.0.1'), + 'username' => env('REDIS_USERNAME'), + 'password' => env('REDIS_PASSWORD'), + 'port' => env('REDIS_PORT', '6379'), + 'database' => env('REDIS_CACHE_DB', '1'), + ], + + ], + +]; diff --git a/config/debugbar.php b/config/debugbar.php new file mode 100644 index 0000000..44aff57 --- /dev/null +++ b/config/debugbar.php @@ -0,0 +1,325 @@ + env('DEBUGBAR_ENABLED', null), + 'except' => [ + 'telescope*', + 'horizon*', + ], + + /* + |-------------------------------------------------------------------------- + | Storage settings + |-------------------------------------------------------------------------- + | + | DebugBar stores data for session/ajax requests. + | You can disable this, so the debugbar stores data in headers/session, + | but this can cause problems with large data collectors. + | By default, file storage (in the storage folder) is used. Redis and PDO + | can also be used. For PDO, run the package migrations first. + | + | Warning: Enabling storage.open will allow everyone to access previous + | request, do not enable open storage in publicly available environments! + | Specify a callback if you want to limit based on IP or authentication. + | Leaving it to null will allow localhost only. + */ + 'storage' => [ + 'enabled' => true, + 'open' => env('DEBUGBAR_OPEN_STORAGE'), // bool/callback. + 'driver' => 'file', // redis, file, pdo, socket, custom + 'path' => storage_path('debugbar'), // For file driver + 'connection' => null, // Leave null for default connection (Redis/PDO) + 'provider' => '', // Instance of StorageInterface for custom driver + 'hostname' => '127.0.0.1', // Hostname to use with the "socket" driver + 'port' => 2304, // Port to use with the "socket" driver + ], + + /* + |-------------------------------------------------------------------------- + | Editor + |-------------------------------------------------------------------------- + | + | Choose your preferred editor to use when clicking file name. + | + | Supported: "phpstorm", "vscode", "vscode-insiders", "vscode-remote", + | "vscode-insiders-remote", "vscodium", "textmate", "emacs", + | "sublime", "atom", "nova", "macvim", "idea", "netbeans", + | "xdebug", "espresso" + | + */ + + 'editor' => env('DEBUGBAR_EDITOR') ?: env('IGNITION_EDITOR', 'phpstorm'), + + /* + |-------------------------------------------------------------------------- + | Remote Path Mapping + |-------------------------------------------------------------------------- + | + | If you are using a remote dev server, like Laravel Homestead, Docker, or + | even a remote VPS, it will be necessary to specify your path mapping. + | + | Leaving one, or both of these, empty or null will not trigger the remote + | URL changes and Debugbar will treat your editor links as local files. + | + | "remote_sites_path" is an absolute base path for your sites or projects + | in Homestead, Vagrant, Docker, or another remote development server. + | + | Example value: "/home/vagrant/Code" + | + | "local_sites_path" is an absolute base path for your sites or projects + | on your local computer where your IDE or code editor is running on. + | + | Example values: "/Users//Code", "C:\Users\\Documents\Code" + | + */ + + 'remote_sites_path' => env('DEBUGBAR_REMOTE_SITES_PATH'), + 'local_sites_path' => env('DEBUGBAR_LOCAL_SITES_PATH', env('IGNITION_LOCAL_SITES_PATH')), + + /* + |-------------------------------------------------------------------------- + | Vendors + |-------------------------------------------------------------------------- + | + | Vendor files are included by default, but can be set to false. + | This can also be set to 'js' or 'css', to only include javascript or css vendor files. + | Vendor files are for css: font-awesome (including fonts) and highlight.js (css files) + | and for js: jquery and highlight.js + | So if you want syntax highlighting, set it to true. + | jQuery is set to not conflict with existing jQuery scripts. + | + */ + + 'include_vendors' => true, + + /* + |-------------------------------------------------------------------------- + | Capture Ajax Requests + |-------------------------------------------------------------------------- + | + | The Debugbar can capture Ajax requests and display them. If you don't want this (ie. because of errors), + | you can use this option to disable sending the data through the headers. + | + | Optionally, you can also send ServerTiming headers on ajax requests for the Chrome DevTools. + | + | Note for your request to be identified as ajax requests they must either send the header + | X-Requested-With with the value XMLHttpRequest (most JS libraries send this), or have application/json as a Accept header. + | + | By default `ajax_handler_auto_show` is set to true allowing ajax requests to be shown automatically in the Debugbar. + | Changing `ajax_handler_auto_show` to false will prevent the Debugbar from reloading. + */ + + 'capture_ajax' => true, + 'add_ajax_timing' => false, + 'ajax_handler_auto_show' => true, + 'ajax_handler_enable_tab' => true, + + /* + |-------------------------------------------------------------------------- + | Custom Error Handler for Deprecated warnings + |-------------------------------------------------------------------------- + | + | When enabled, the Debugbar shows deprecated warnings for Symfony components + | in the Messages tab. + | + */ + 'error_handler' => false, + + /* + |-------------------------------------------------------------------------- + | Clockwork integration + |-------------------------------------------------------------------------- + | + | The Debugbar can emulate the Clockwork headers, so you can use the Chrome + | Extension, without the server-side code. It uses Debugbar collectors instead. + | + */ + 'clockwork' => false, + + /* + |-------------------------------------------------------------------------- + | DataCollectors + |-------------------------------------------------------------------------- + | + | Enable/disable DataCollectors + | + */ + + 'collectors' => [ + 'phpinfo' => true, // Php version + 'messages' => true, // Messages + 'time' => true, // Time Datalogger + 'memory' => true, // Memory usage + 'exceptions' => true, // Exception displayer + 'log' => true, // Logs from Monolog (merged in messages if enabled) + 'db' => true, // Show database (PDO) queries and bindings + 'views' => true, // Views with their data + 'route' => true, // Current route information + 'auth' => false, // Display Laravel authentication status + 'gate' => true, // Display Laravel Gate checks + 'session' => true, // Display session data + 'symfony_request' => true, // Only one can be enabled.. + 'mail' => true, // Catch mail messages + 'laravel' => false, // Laravel version and environment + 'events' => false, // All events fired + 'default_request' => false, // Regular or special Symfony request logger + 'logs' => false, // Add the latest log messages + 'files' => false, // Show the included files + 'config' => false, // Display config settings + 'cache' => false, // Display cache events + 'models' => true, // Display models + 'livewire' => true, // Display Livewire (when available) + 'jobs' => false, // Display dispatched jobs + ], + + /* + |-------------------------------------------------------------------------- + | Extra options + |-------------------------------------------------------------------------- + | + | Configure some DataCollectors + | + */ + + 'options' => [ + 'time' => [ + 'memory_usage' => false, // Calculated by subtracting memory start and end, it may be inaccurate + ], + 'messages' => [ + 'trace' => true, // Trace the origin of the debug message + ], + 'memory' => [ + 'reset_peak' => false, // run memory_reset_peak_usage before collecting + 'with_baseline' => false, // Set boot memory usage as memory peak baseline + 'precision' => 0, // Memory rounding precision + ], + 'auth' => [ + 'show_name' => true, // Also show the users name/email in the debugbar + 'show_guards' => true, // Show the guards that are used + ], + 'db' => [ + 'with_params' => true, // Render SQL with the parameters substituted + 'backtrace' => true, // Use a backtrace to find the origin of the query in your files. + 'backtrace_exclude_paths' => [], // Paths to exclude from backtrace. (in addition to defaults) + 'timeline' => false, // Add the queries to the timeline + 'duration_background' => true, // Show shaded background on each query relative to how long it took to execute. + 'explain' => [ // Show EXPLAIN output on queries + 'enabled' => false, + 'types' => ['SELECT'], // Deprecated setting, is always only SELECT + ], + 'hints' => false, // Show hints for common mistakes + 'show_copy' => false, // Show copy button next to the query, + 'slow_threshold' => false, // Only track queries that last longer than this time in ms + 'memory_usage' => false, // Show queries memory usage + 'soft_limit' => 100, // After the soft limit, no parameters/backtrace are captured + 'hard_limit' => 500, // After the hard limit, queries are ignored + ], + 'mail' => [ + 'timeline' => false, // Add mails to the timeline + 'show_body' => true, + ], + 'views' => [ + 'timeline' => false, // Add the views to the timeline (Experimental) + 'data' => false, //true for all data, 'keys' for only names, false for no parameters. + 'group' => 50, // Group duplicate views. Pass value to auto-group, or true/false to force + 'exclude_paths' => [ // Add the paths which you don't want to appear in the views + 'vendor/filament' // Exclude Filament components by default + ], + ], + 'route' => [ + 'label' => true, // show complete route on bar + ], + 'session' => [ + 'hiddens' => [], // hides sensitive values using array paths + ], + 'symfony_request' => [ + 'hiddens' => [], // hides sensitive values using array paths, example: request_request.password + ], + 'events' => [ + 'data' => false, // collect events data, listeners + ], + 'logs' => [ + 'file' => null, + ], + 'cache' => [ + 'values' => true, // collect cache values + ], + ], + + /* + |-------------------------------------------------------------------------- + | Inject Debugbar in Response + |-------------------------------------------------------------------------- + | + | Usually, the debugbar is added just before , by listening to the + | Response after the App is done. If you disable this, you have to add them + | in your template yourself. See http://phpdebugbar.com/docs/rendering.html + | + */ + + 'inject' => true, + + /* + |-------------------------------------------------------------------------- + | DebugBar route prefix + |-------------------------------------------------------------------------- + | + | Sometimes you want to set route prefix to be used by DebugBar to load + | its resources from. Usually the need comes from misconfigured web server or + | from trying to overcome bugs like this: http://trac.nginx.org/nginx/ticket/97 + | + */ + 'route_prefix' => '_debugbar', + + /* + |-------------------------------------------------------------------------- + | DebugBar route middleware + |-------------------------------------------------------------------------- + | + | Additional middleware to run on the Debugbar routes + */ + 'route_middleware' => [], + + /* + |-------------------------------------------------------------------------- + | DebugBar route domain + |-------------------------------------------------------------------------- + | + | By default DebugBar route served from the same domain that request served. + | To override default domain, specify it as a non-empty value. + */ + 'route_domain' => null, + + /* + |-------------------------------------------------------------------------- + | DebugBar theme + |-------------------------------------------------------------------------- + | + | Switches between light and dark theme. If set to auto it will respect system preferences + | Possible values: auto, light, dark + */ + 'theme' => env('DEBUGBAR_THEME', 'auto'), + + /* + |-------------------------------------------------------------------------- + | Backtrace stack limit + |-------------------------------------------------------------------------- + | + | By default, the DebugBar limits the number of frames returned by the 'debug_backtrace()' function. + | If you need larger stacktraces, you can increase this number. Setting it to 0 will result in no limit. + */ + 'debug_backtrace_limit' => 50, +]; diff --git a/config/filesystems.php b/config/filesystems.php new file mode 100644 index 0000000..46e4e9a --- /dev/null +++ b/config/filesystems.php @@ -0,0 +1,94 @@ + env('FILESYSTEM_DISK', 'local'), + + /* + |-------------------------------------------------------------------------- + | Filesystem Disks + |-------------------------------------------------------------------------- + | + | Below you may configure as many filesystem disks as necessary, and you + | may even configure multiple disks for the same driver. Examples for + | most supported storage drivers are configured here for reference. + | + | Supported Drivers: "local", "ftp", "sftp", "s3" + | + */ + + 'disks' => [ + + 'local' => [ + 'driver' => 'local', + 'root' => storage_path('app'), + 'throw' => false, + ], + + 'public' => [ + 'driver' => 'local', + 'root' => storage_path('app/public'), + 'url' => env('APP_URL').'/storage', + 'visibility' => 'public', + 'throw' => false, + ], + + 's3' => [ + 'driver' => 's3', + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_DEFAULT_REGION'), + 'bucket' => env('AWS_BUCKET'), + 'url' => env('AWS_URL'), + 'endpoint' => env('AWS_ENDPOINT'), + 'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false), + 'throw' => false, + ], + + 'oss' => [ + "driver" => "oss", + "access_key_id" => env("OSS_ACCESS_KEY_ID"), // Required, yourAccessKeyId + "access_key_secret" => env("OSS_ACCESS_KEY_SECRET"), // Required, yourAccessKeySecret + "bucket" => env("OSS_BUCKET"), // Required, for example: my-bucket + "endpoint" => env("OSS_ENDPOINT"), // Required, for example: oss-cn-shanghai.aliyuncs.com + "internal" => env("OSS_INTERNAL", null), // Optional, for example: oss-cn-shanghai-internal.aliyuncs.com + "domain" => env("OSS_DOMAIN", null), // Optional, for example: oss.my-domain.com + "is_cname" => env("OSS_CNAME", false), // Optional, if the Endpoint is a custom domain name, this must be true, see: https://github.com/aliyun/aliyun-oss-php-sdk/blob/572d0f8e099e8630ae7139ed3fdedb926c7a760f/src/OSS/OssClient.php#L113C1-L122C78 + "prefix" => env("OSS_PREFIX", ""), // Optional, the prefix of the store path + "use_ssl" => env("OSS_SSL", false), // Optional, whether to use HTTPS + "throw" => env("OSS_THROW", false), // Optional, whether to throw an exception that causes an error + "signatureVersion" => env("OSS_SIGNATURE_VERSION", "v1"), // Optional, select v1 or v4 as the signature version + "region" => env("OSS_REGION", ""), // Optional, for example: cn-shanghai, used only when v4 signature version is selected + "options" => [], // Optional, add global configuration parameters, For example: [\OSS\OssClient::OSS_CHECK_MD5 => false] + "macros" => [] // Optional, add custom Macro, For example: [\App\Macros\ListBuckets::class, \App\Macros\CreateBucket::class] + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Symbolic Links + |-------------------------------------------------------------------------- + | + | Here you may configure the symbolic links that will be created when the + | `storage:link` Artisan command is executed. The array keys should be + | the locations of the links and the values should be their targets. + | + */ + + 'links' => [ + public_path('storage') => storage_path('app/public'), + ], + +]; diff --git a/config/gateway.php b/config/gateway.php new file mode 100644 index 0000000..046495f --- /dev/null +++ b/config/gateway.php @@ -0,0 +1,40 @@ + +// +---------------------------------------------------------------------- + +return[ + // 扩展自身需要的配置 + 'protocol' => 'websocket', // 协议 支持 tcp udp unix http websocket text + 'host' => '0.0.0.0', // 监听地址 + 'port' => 2348, // 监听端口 + 'socket' => '', // 完整监听地址 + 'context' => [], // socket 上下文选项 + 'register_deploy' => true, // 是否需要部署register + 'businessWorker_deploy' => true, // 是否需要部署businessWorker + 'gateway_deploy' => true, // 是否需要部署gateway + + // Register配置 + 'registerAddress' => '127.0.0.1:1236', + + // Gateway配置 + 'name' => 'workerman', + 'count' => 1, + 'lanIp' => '127.0.0.1', + 'startPort' => 2000, + 'daemonize' => false, + 'pingInterval' => 30, + 'pingNotResponseLimit' => 0, + 'pingData' => '{"type":"ping"}', + + // BusinsessWorker配置 + 'businessWorker' => [ + 'name' => 'BusinessWorker', + 'count' => 1, + 'eventHandler' => '\Modules\Workerman\Events\WorkermanEvent', + ], +]; diff --git a/config/jwt.php b/config/jwt.php new file mode 100644 index 0000000..4d23414 --- /dev/null +++ b/config/jwt.php @@ -0,0 +1,301 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return [ + + /* + |-------------------------------------------------------------------------- + | JWT Authentication Secret + |-------------------------------------------------------------------------- + | + | Don't forget to set this in your .env file, as it will be used to sign + | your tokens. A helper command is provided for this: + | `php artisan jwt:secret` + | + | Note: This will be used for Symmetric algorithms only (HMAC), + | since RSA and ECDSA use a private/public key combo (See below). + | + */ + + 'secret' => env('JWT_SECRET'), + + /* + |-------------------------------------------------------------------------- + | JWT Authentication Keys + |-------------------------------------------------------------------------- + | + | The algorithm you are using, will determine whether your tokens are + | signed with a random string (defined in `JWT_SECRET`) or using the + | following public & private keys. + | + | Symmetric Algorithms: + | HS256, HS384 & HS512 will use `JWT_SECRET`. + | + | Asymmetric Algorithms: + | RS256, RS384 & RS512 / ES256, ES384 & ES512 will use the keys below. + | + */ + + 'keys' => [ + + /* + |-------------------------------------------------------------------------- + | Public Key + |-------------------------------------------------------------------------- + | + | A path or resource to your public key. + | + | E.g. 'file://path/to/public/key' + | + */ + + 'public' => env('JWT_PUBLIC_KEY'), + + /* + |-------------------------------------------------------------------------- + | Private Key + |-------------------------------------------------------------------------- + | + | A path or resource to your private key. + | + | E.g. 'file://path/to/private/key' + | + */ + + 'private' => env('JWT_PRIVATE_KEY'), + + /* + |-------------------------------------------------------------------------- + | Passphrase + |-------------------------------------------------------------------------- + | + | The passphrase for your private key. Can be null if none set. + | + */ + + 'passphrase' => env('JWT_PASSPHRASE'), + + ], + + /* + |-------------------------------------------------------------------------- + | JWT time to live + |-------------------------------------------------------------------------- + | + | Specify the length of time (in minutes) that the token will be valid for. + | Defaults to 1 hour. + | + | You can also set this to null, to yield a never expiring token. + | Some people may want this behaviour for e.g. a mobile app. + | This is not particularly recommended, so make sure you have appropriate + | systems in place to revoke the token if necessary. + | Notice: If you set this to null you should remove 'exp' element from 'required_claims' list. + | + */ + + 'ttl' => env('JWT_TTL', 60 * 60 * 24 * 7 ), + + /* + |-------------------------------------------------------------------------- + | Refresh time to live + |-------------------------------------------------------------------------- + | + | Specify the length of time (in minutes) that the token can be refreshed + | within. I.E. The user can refresh their token within a 2 week window of + | the original token being created until they must re-authenticate. + | Defaults to 2 weeks. + | + | You can also set this to null, to yield an infinite refresh time. + | Some may want this instead of never expiring tokens for e.g. a mobile app. + | This is not particularly recommended, so make sure you have appropriate + | systems in place to revoke the token if necessary. + | + */ + + 'refresh_ttl' => env('JWT_REFRESH_TTL', 20160), + + /* + |-------------------------------------------------------------------------- + | JWT hashing algorithm + |-------------------------------------------------------------------------- + | + | Specify the hashing algorithm that will be used to sign the token. + | + */ + + 'algo' => env('JWT_ALGO', Tymon\JWTAuth\Providers\JWT\Provider::ALGO_HS256), + + /* + |-------------------------------------------------------------------------- + | Required Claims + |-------------------------------------------------------------------------- + | + | Specify the required claims that must exist in any token. + | A TokenInvalidException will be thrown if any of these claims are not + | present in the payload. + | + */ + + 'required_claims' => [ + 'iss', + 'iat', + 'exp', + 'nbf', + 'sub', + 'jti', + ], + + /* + |-------------------------------------------------------------------------- + | Persistent Claims + |-------------------------------------------------------------------------- + | + | Specify the claim keys to be persisted when refreshing a token. + | `sub` and `iat` will automatically be persisted, in + | addition to the these claims. + | + | Note: If a claim does not exist then it will be ignored. + | + */ + + 'persistent_claims' => [ + // 'foo', + // 'bar', + ], + + /* + |-------------------------------------------------------------------------- + | Lock Subject + |-------------------------------------------------------------------------- + | + | This will determine whether a `prv` claim is automatically added to + | the token. The purpose of this is to ensure that if you have multiple + | authentication models e.g. `App\User` & `App\OtherPerson`, then we + | should prevent one authentication request from impersonating another, + | if 2 tokens happen to have the same id across the 2 different models. + | + | Under specific circumstances, you may want to disable this behaviour + | e.g. if you only have one authentication model, then you would save + | a little on token size. + | + */ + + 'lock_subject' => true, + + /* + |-------------------------------------------------------------------------- + | Leeway + |-------------------------------------------------------------------------- + | + | This property gives the jwt timestamp claims some "leeway". + | Meaning that if you have any unavoidable slight clock skew on + | any of your servers then this will afford you some level of cushioning. + | + | This applies to the claims `iat`, `nbf` and `exp`. + | + | Specify in seconds - only if you know you need it. + | + */ + + 'leeway' => env('JWT_LEEWAY', 0), + + /* + |-------------------------------------------------------------------------- + | Blacklist Enabled + |-------------------------------------------------------------------------- + | + | In order to invalidate tokens, you must have the blacklist enabled. + | If you do not want or need this functionality, then set this to false. + | + */ + + 'blacklist_enabled' => env('JWT_BLACKLIST_ENABLED', true), + + /* + | ------------------------------------------------------------------------- + | Blacklist Grace Period + | ------------------------------------------------------------------------- + | + | When multiple concurrent requests are made with the same JWT, + | it is possible that some of them fail, due to token regeneration + | on every request. + | + | Set grace period in seconds to prevent parallel request failure. + | + */ + + 'blacklist_grace_period' => env('JWT_BLACKLIST_GRACE_PERIOD', 0), + + /* + |-------------------------------------------------------------------------- + | Cookies encryption + |-------------------------------------------------------------------------- + | + | By default Laravel encrypt cookies for security reason. + | If you decide to not decrypt cookies, you will have to configure Laravel + | to not encrypt your cookie token by adding its name into the $except + | array available in the middleware "EncryptCookies" provided by Laravel. + | see https://laravel.com/docs/master/responses#cookies-and-encryption + | for details. + | + | Set it to true if you want to decrypt cookies. + | + */ + + 'decrypt_cookies' => false, + + /* + |-------------------------------------------------------------------------- + | Providers + |-------------------------------------------------------------------------- + | + | Specify the various providers used throughout the package. + | + */ + + 'providers' => [ + + /* + |-------------------------------------------------------------------------- + | JWT Provider + |-------------------------------------------------------------------------- + | + | Specify the provider that is used to create and decode the tokens. + | + */ + + 'jwt' => Tymon\JWTAuth\Providers\JWT\Lcobucci::class, + + /* + |-------------------------------------------------------------------------- + | Authentication Provider + |-------------------------------------------------------------------------- + | + | Specify the provider that is used to authenticate users. + | + */ + + 'auth' => Tymon\JWTAuth\Providers\Auth\Illuminate::class, + + /* + |-------------------------------------------------------------------------- + | Storage Provider + |-------------------------------------------------------------------------- + | + | Specify the provider that is used to store tokens in the blacklist. + | + */ + + 'storage' => Tymon\JWTAuth\Providers\Storage\Illuminate::class, + + ], + +]; diff --git a/config/logging.php b/config/logging.php new file mode 100644 index 0000000..d526b64 --- /dev/null +++ b/config/logging.php @@ -0,0 +1,132 @@ + env('LOG_CHANNEL', 'stack'), + + /* + |-------------------------------------------------------------------------- + | Deprecations Log Channel + |-------------------------------------------------------------------------- + | + | This option controls the log channel that should be used to log warnings + | regarding deprecated PHP and library features. This allows you to get + | your application ready for upcoming major versions of dependencies. + | + */ + + 'deprecations' => [ + 'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'), + 'trace' => env('LOG_DEPRECATIONS_TRACE', false), + ], + + /* + |-------------------------------------------------------------------------- + | Log Channels + |-------------------------------------------------------------------------- + | + | Here you may configure the log channels for your application. Laravel + | utilizes the Monolog PHP logging library, which includes a variety + | of powerful log handlers and formatters that you're free to use. + | + | Available Drivers: "single", "daily", "slack", "syslog", + | "errorlog", "monolog", "custom", "stack" + | + */ + + 'channels' => [ + + 'stack' => [ + 'driver' => 'stack', + 'channels' => explode(',', env('LOG_STACK', 'single')), + 'ignore_exceptions' => false, + ], + + 'single' => [ + 'driver' => 'single', + 'path' => storage_path('logs/laravel.log'), + 'level' => env('LOG_LEVEL', 'debug'), + 'replace_placeholders' => true, + ], + + 'daily' => [ + 'driver' => 'daily', + 'path' => storage_path('logs/laravel.log'), + 'level' => env('LOG_LEVEL', 'debug'), + 'days' => env('LOG_DAILY_DAYS', 14), + 'replace_placeholders' => true, + ], + + 'slack' => [ + 'driver' => 'slack', + 'url' => env('LOG_SLACK_WEBHOOK_URL'), + 'username' => env('LOG_SLACK_USERNAME', 'Laravel Log'), + 'emoji' => env('LOG_SLACK_EMOJI', ':boom:'), + 'level' => env('LOG_LEVEL', 'critical'), + 'replace_placeholders' => true, + ], + + 'papertrail' => [ + 'driver' => 'monolog', + 'level' => env('LOG_LEVEL', 'debug'), + 'handler' => env('LOG_PAPERTRAIL_HANDLER', SyslogUdpHandler::class), + 'handler_with' => [ + 'host' => env('PAPERTRAIL_URL'), + 'port' => env('PAPERTRAIL_PORT'), + 'connectionString' => 'tls://'.env('PAPERTRAIL_URL').':'.env('PAPERTRAIL_PORT'), + ], + 'processors' => [PsrLogMessageProcessor::class], + ], + + 'stderr' => [ + 'driver' => 'monolog', + 'level' => env('LOG_LEVEL', 'debug'), + 'handler' => StreamHandler::class, + 'formatter' => env('LOG_STDERR_FORMATTER'), + 'with' => [ + 'stream' => 'php://stderr', + ], + 'processors' => [PsrLogMessageProcessor::class], + ], + + 'syslog' => [ + 'driver' => 'syslog', + 'level' => env('LOG_LEVEL', 'debug'), + 'facility' => env('LOG_SYSLOG_FACILITY', LOG_USER), + 'replace_placeholders' => true, + ], + + 'errorlog' => [ + 'driver' => 'errorlog', + 'level' => env('LOG_LEVEL', 'debug'), + 'replace_placeholders' => true, + ], + + 'null' => [ + 'driver' => 'monolog', + 'handler' => NullHandler::class, + ], + + 'emergency' => [ + 'path' => storage_path('logs/laravel.log'), + ], + + ], + +]; diff --git a/config/mail.php b/config/mail.php new file mode 100644 index 0000000..07342fc --- /dev/null +++ b/config/mail.php @@ -0,0 +1,116 @@ + env('MAIL_MAILER', 'log'), + + /* + |-------------------------------------------------------------------------- + | Mailer Configurations + |-------------------------------------------------------------------------- + | + | Here you may configure all of the mailers used by your application plus + | their respective settings. Several examples have been configured for + | you and you are free to add your own as your application requires. + | + | Laravel supports a variety of mail "transport" drivers that can be used + | when delivering an email. You may specify which one you're using for + | your mailers below. You may also add additional mailers if needed. + | + | Supported: "smtp", "sendmail", "mailgun", "ses", "ses-v2", + | "postmark", "resend", "log", "array", + | "failover", "roundrobin" + | + */ + + 'mailers' => [ + + 'smtp' => [ + 'transport' => 'smtp', + 'url' => env('MAIL_URL'), + 'host' => env('MAIL_HOST', '127.0.0.1'), + 'port' => env('MAIL_PORT', 2525), + 'encryption' => env('MAIL_ENCRYPTION', 'tls'), + 'username' => env('MAIL_USERNAME'), + 'password' => env('MAIL_PASSWORD'), + 'timeout' => null, + 'local_domain' => env('MAIL_EHLO_DOMAIN'), + ], + + 'ses' => [ + 'transport' => 'ses', + ], + + 'postmark' => [ + 'transport' => 'postmark', + // 'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'), + // 'client' => [ + // 'timeout' => 5, + // ], + ], + + 'resend' => [ + 'transport' => 'resend', + ], + + 'sendmail' => [ + 'transport' => 'sendmail', + 'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -bs -i'), + ], + + 'log' => [ + 'transport' => 'log', + 'channel' => env('MAIL_LOG_CHANNEL'), + ], + + 'array' => [ + 'transport' => 'array', + ], + + 'failover' => [ + 'transport' => 'failover', + 'mailers' => [ + 'smtp', + 'log', + ], + ], + + 'roundrobin' => [ + 'transport' => 'roundrobin', + 'mailers' => [ + 'ses', + 'postmark', + ], + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Global "From" Address + |-------------------------------------------------------------------------- + | + | You may wish for all emails sent by your application to be sent from + | the same address. Here you may specify a name and address that is + | used globally for all emails that are sent by your application. + | + */ + + 'from' => [ + 'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'), + 'name' => env('MAIL_FROM_NAME', 'Example'), + ], + +]; diff --git a/config/modules.php b/config/modules.php new file mode 100644 index 0000000..9d09af2 --- /dev/null +++ b/config/modules.php @@ -0,0 +1,273 @@ + 'Modules', + + /* + |-------------------------------------------------------------------------- + | Module Stubs + |-------------------------------------------------------------------------- + | + | Default module stubs. + | + */ + 'stubs' => [ + 'enabled' => false, + 'path' => base_path('storage/stubs'), + 'files' => [ + 'routes/web' => 'routes/web.php', + 'routes/api' => 'routes/api.php', + 'routes/admin' => 'routes/admin.php', + // 'views/index' => 'resources/views/index.blade.php', + // 'views/master' => 'resources/views/layouts/master.blade.php', + 'scaffold/config' => 'config/config.php', + 'composer' => 'composer.json', + // 'assets/js/app' => 'resources/assets/js/app.js', + // 'assets/sass/app' => 'resources/assets/sass/app.scss', + // 'vite' => 'vite.config.js', + // 'package' => 'package.json', + ], + 'replacements' => [ + 'routes/web' => ['LOWER_NAME', 'STUDLY_NAME', 'MODULE_NAMESPACE', 'CONTROLLER_NAMESPACE'], + 'routes/api' => ['LOWER_NAME', 'STUDLY_NAME', 'MODULE_NAMESPACE', 'CONTROLLER_NAMESPACE'], + 'routes/admin' => ['LOWER_NAME', 'STUDLY_NAME', 'MODULE_NAMESPACE', 'CONTROLLER_NAMESPACE'], + 'vite' => ['LOWER_NAME', 'STUDLY_NAME'], + 'json' => ['LOWER_NAME', 'STUDLY_NAME', 'MODULE_NAMESPACE', 'PROVIDER_NAMESPACE'], + 'views/index' => ['LOWER_NAME'], + 'views/master' => ['LOWER_NAME', 'STUDLY_NAME'], + 'scaffold/config' => ['STUDLY_NAME'], + 'composer' => [ + 'LOWER_NAME', + 'STUDLY_NAME', + 'VENDOR', + 'AUTHOR_NAME', + 'AUTHOR_EMAIL', + 'MODULE_NAMESPACE', + 'PROVIDER_NAMESPACE', + 'APP_FOLDER_NAME', + ], + ], + 'gitkeep' => true, + ], + 'paths' => [ + /* + |-------------------------------------------------------------------------- + | Modules path + |-------------------------------------------------------------------------- + | + | This path is used to save the generated module. + | This path will also be added automatically to the list of scanned folders. + | + */ + 'modules' => base_path('modules'), + + /* + |-------------------------------------------------------------------------- + | Modules assets path + |-------------------------------------------------------------------------- + | + | Here you may update the modules' assets path. + | + */ + 'assets' => public_path('modules'), + + /* + |-------------------------------------------------------------------------- + | The migrations' path + |-------------------------------------------------------------------------- + | + | Where you run the 'module:publish-migration' command, where do you publish the + | the migration files? + | + */ + 'migration' => base_path('database/migrations'), + + /* + |-------------------------------------------------------------------------- + | The app path + |-------------------------------------------------------------------------- + | + | app folder name + | for example can change it to 'src' or 'App' + */ + 'app_folder' => 'app/', + + /* + |-------------------------------------------------------------------------- + | Generator path + |-------------------------------------------------------------------------- + | Customise the paths where the folders will be generated. + | Setting the generate key to false will not generate that folder + */ + 'generator' => [ + // app/ + 'actions' => ['path' => 'app/Actions', 'generate' => false], + 'casts' => ['path' => 'app/Casts', 'generate' => false], + 'channels' => ['path' => 'app/Broadcasting', 'generate' => false], + 'class' => ['path' => 'app/Classes', 'generate' => false], + 'command' => ['path' => 'app/Console', 'generate' => false], + 'component-class' => ['path' => 'app/View/Components', 'generate' => false], + 'emails' => ['path' => 'app/Emails', 'generate' => false], + 'event' => ['path' => 'app/Events', 'generate' => false], + 'enums' => ['path' => 'app/Enums', 'generate' => false], + 'exceptions' => ['path' => 'app/Exceptions', 'generate' => false], + 'jobs' => ['path' => 'app/Jobs', 'generate' => false], + 'helpers' => ['path' => 'app/Helpers', 'generate' => false], + 'interfaces' => ['path' => 'app/Interfaces', 'generate' => false], + 'listener' => ['path' => 'app/Listeners', 'generate' => false], + 'model' => ['path' => 'app/Models', 'generate' => true], + 'notifications' => ['path' => 'app/Notifications', 'generate' => false], + 'observer' => ['path' => 'app/Observers', 'generate' => false], + 'policies' => ['path' => 'app/Policies', 'generate' => false], + 'provider' => ['path' => 'app/Providers', 'generate' => true], + 'repository' => ['path' => 'app/Repositories', 'generate' => false], + 'resource' => ['path' => 'app/Transformers', 'generate' => false], + 'route-provider' => ['path' => 'app/Providers', 'generate' => true], + 'rules' => ['path' => 'app/Rules', 'generate' => false], + 'services' => ['path' => 'app/Services', 'generate' => true], + 'scopes' => ['path' => 'app/Models/Scopes', 'generate' => false], + 'traits' => ['path' => 'app/Traits', 'generate' => false], + + // app/Http/ + 'controller' => ['path' => 'app/Controllers', 'generate' => true], + 'filter' => ['path' => 'app/Http/Middleware', 'generate' => false], + 'request' => ['path' => 'app/Http/Requests', 'generate' => false], + + // config/ + 'config' => ['path' => 'config', 'generate' => true], + + // database/ + 'factory' => ['path' => 'database/factories', 'generate' => true], + 'migration' => ['path' => 'database/migrations', 'generate' => true], + 'seeder' => ['path' => 'database/seeders', 'generate' => true], + + // lang/ + 'lang' => ['path' => 'lang', 'generate' => false], + + // resource/ + 'assets' => ['path' => 'resources/assets', 'generate' => false], + 'component-view' => ['path' => 'resources/views/components', 'generate' => false], + 'views' => ['path' => 'resources/views', 'generate' => false], + + // routes/ + 'routes' => ['path' => 'routes', 'generate' => true], + + // tests/ + 'test-feature' => ['path' => 'tests/Feature', 'generate' => false], + 'test-unit' => ['path' => 'tests/Unit', 'generate' => false], + ], + ], + + /* + |-------------------------------------------------------------------------- + | Package commands + |-------------------------------------------------------------------------- + | + | Here you can define which commands will be visible and used in your + | application. You can add your own commands to merge section. + | + */ + 'commands' => ConsoleServiceProvider::defaultCommands() + ->merge([ + // New commands go here + ])->toArray(), + + /* + |-------------------------------------------------------------------------- + | Scan Path + |-------------------------------------------------------------------------- + | + | Here you define which folder will be scanned. By default will scan vendor + | directory. This is useful if you host the package in packagist website. + | + */ + 'scan' => [ + 'enabled' => false, + 'paths' => [ + base_path('vendor/*/*'), + ], + ], + + /* + |-------------------------------------------------------------------------- + | Composer File Template + |-------------------------------------------------------------------------- + | + | Here is the config for the composer.json file, generated by this package + | + */ + 'composer' => [ + 'vendor' => env('MODULE_VENDOR', 'tensent'), + 'author' => [ + 'name' => env('MODULE_AUTHOR_NAME', 'molong'), + 'email' => env('MODULE_AUTHOR_EMAIL', 'molong@tensent.cn'), + ], + 'composer-output' => false, + ], + + /* + |-------------------------------------------------------------------------- + | Caching + |-------------------------------------------------------------------------- + | + | Here is the config for setting up the caching feature. + | + */ + 'cache' => [ + 'enabled' => env('MODULES_CACHE_ENABLED', false), + 'driver' => env('MODULES_CACHE_DRIVER', 'file'), + 'key' => env('MODULES_CACHE_KEY', 'laravel-modules'), + 'lifetime' => env('MODULES_CACHE_LIFETIME', 60), + ], + + /* + |-------------------------------------------------------------------------- + | Choose what laravel-modules will register as custom namespaces. + | Setting one to false will require you to register that part + | in your own Service Provider class. + |-------------------------------------------------------------------------- + */ + 'register' => [ + 'translations' => true, + /** + * load files on boot or register method + */ + 'files' => 'register', + ], + + /* + |-------------------------------------------------------------------------- + | Activators + |-------------------------------------------------------------------------- + | + | You can define new types of activators here, file, database, etc. The only + | required parameter is 'class'. + | The file activator will store the activation status in storage/installed_modules + */ + 'activators' => [ + 'file' => [ + 'class' => FileActivator::class, + 'statuses-file' => base_path('storage/modules_statuses.json'), + 'cache-key' => 'activator.installed', + 'cache-lifetime' => 604800, + ], + 'database' => [ + 'class' => \App\Services\System\ModulesService::class, + 'model' => \App\Models\System\Modules::class + ] + ], + + 'activator' => 'file', +]; diff --git a/config/queue.php b/config/queue.php new file mode 100644 index 0000000..116bd8d --- /dev/null +++ b/config/queue.php @@ -0,0 +1,112 @@ + env('QUEUE_CONNECTION', 'database'), + + /* + |-------------------------------------------------------------------------- + | Queue Connections + |-------------------------------------------------------------------------- + | + | Here you may configure the connection options for every queue backend + | used by your application. An example configuration is provided for + | each backend supported by Laravel. You're also free to add more. + | + | Drivers: "sync", "database", "beanstalkd", "sqs", "redis", "null" + | + */ + + 'connections' => [ + + 'sync' => [ + 'driver' => 'sync', + ], + + 'database' => [ + 'driver' => 'database', + 'connection' => env('DB_QUEUE_CONNECTION'), + 'table' => env('DB_QUEUE_TABLE', 'jobs'), + 'queue' => env('DB_QUEUE', 'default'), + 'retry_after' => (int) env('DB_QUEUE_RETRY_AFTER', 90), + 'after_commit' => false, + ], + + 'beanstalkd' => [ + 'driver' => 'beanstalkd', + 'host' => env('BEANSTALKD_QUEUE_HOST', 'localhost'), + 'queue' => env('BEANSTALKD_QUEUE', 'default'), + 'retry_after' => (int) env('BEANSTALKD_QUEUE_RETRY_AFTER', 90), + 'block_for' => 0, + 'after_commit' => false, + ], + + 'sqs' => [ + 'driver' => 'sqs', + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'), + 'queue' => env('SQS_QUEUE', 'default'), + 'suffix' => env('SQS_SUFFIX'), + 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), + 'after_commit' => false, + ], + + 'redis' => [ + 'driver' => 'redis', + 'connection' => env('REDIS_QUEUE_CONNECTION', 'default'), + 'queue' => env('REDIS_QUEUE', 'default'), + 'retry_after' => (int) env('REDIS_QUEUE_RETRY_AFTER', 90), + 'block_for' => null, + 'after_commit' => false, + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Job Batching + |-------------------------------------------------------------------------- + | + | The following options configure the database and table that store job + | batching information. These options can be updated to any database + | connection and table which has been defined by your application. + | + */ + + 'batching' => [ + 'database' => env('DB_CONNECTION', 'sqlite'), + 'table' => 'job_batches', + ], + + /* + |-------------------------------------------------------------------------- + | Failed Queue Jobs + |-------------------------------------------------------------------------- + | + | These options configure the behavior of failed queue job logging so you + | can control how and where failed jobs are stored. Laravel ships with + | support for storing failed jobs in a simple file or in a database. + | + | Supported drivers: "database-uuids", "dynamodb", "file", "null" + | + */ + + 'failed' => [ + 'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'), + 'database' => env('DB_CONNECTION', 'sqlite'), + 'table' => 'failed_jobs', + ], + +]; diff --git a/config/services.php b/config/services.php new file mode 100644 index 0000000..27a3617 --- /dev/null +++ b/config/services.php @@ -0,0 +1,38 @@ + [ + 'token' => env('POSTMARK_TOKEN'), + ], + + 'ses' => [ + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), + ], + + 'resend' => [ + 'key' => env('RESEND_KEY'), + ], + + 'slack' => [ + 'notifications' => [ + 'bot_user_oauth_token' => env('SLACK_BOT_USER_OAUTH_TOKEN'), + 'channel' => env('SLACK_BOT_USER_DEFAULT_CHANNEL'), + ], + ], + +]; diff --git a/config/session.php b/config/session.php new file mode 100644 index 0000000..f0b6541 --- /dev/null +++ b/config/session.php @@ -0,0 +1,217 @@ + env('SESSION_DRIVER', 'database'), + + /* + |-------------------------------------------------------------------------- + | Session Lifetime + |-------------------------------------------------------------------------- + | + | Here you may specify the number of minutes that you wish the session + | to be allowed to remain idle before it expires. If you want them + | to expire immediately when the browser is closed then you may + | indicate that via the expire_on_close configuration option. + | + */ + + 'lifetime' => env('SESSION_LIFETIME', 120), + + 'expire_on_close' => env('SESSION_EXPIRE_ON_CLOSE', false), + + /* + |-------------------------------------------------------------------------- + | Session Encryption + |-------------------------------------------------------------------------- + | + | This option allows you to easily specify that all of your session data + | should be encrypted before it's stored. All encryption is performed + | automatically by Laravel and you may use the session like normal. + | + */ + + 'encrypt' => env('SESSION_ENCRYPT', false), + + /* + |-------------------------------------------------------------------------- + | Session File Location + |-------------------------------------------------------------------------- + | + | When utilizing the "file" session driver, the session files are placed + | on disk. The default storage location is defined here; however, you + | are free to provide another location where they should be stored. + | + */ + + 'files' => storage_path('framework/sessions'), + + /* + |-------------------------------------------------------------------------- + | Session Database Connection + |-------------------------------------------------------------------------- + | + | When using the "database" or "redis" session drivers, you may specify a + | connection that should be used to manage these sessions. This should + | correspond to a connection in your database configuration options. + | + */ + + 'connection' => env('SESSION_CONNECTION'), + + /* + |-------------------------------------------------------------------------- + | Session Database Table + |-------------------------------------------------------------------------- + | + | When using the "database" session driver, you may specify the table to + | be used to store sessions. Of course, a sensible default is defined + | for you; however, you're welcome to change this to another table. + | + */ + + 'table' => env('SESSION_TABLE', 'sessions'), + + /* + |-------------------------------------------------------------------------- + | Session Cache Store + |-------------------------------------------------------------------------- + | + | When using one of the framework's cache driven session backends, you may + | define the cache store which should be used to store the session data + | between requests. This must match one of your defined cache stores. + | + | Affects: "apc", "dynamodb", "memcached", "redis" + | + */ + + 'store' => env('SESSION_STORE'), + + /* + |-------------------------------------------------------------------------- + | Session Sweeping Lottery + |-------------------------------------------------------------------------- + | + | Some session drivers must manually sweep their storage location to get + | rid of old sessions from storage. Here are the chances that it will + | happen on a given request. By default, the odds are 2 out of 100. + | + */ + + 'lottery' => [2, 100], + + /* + |-------------------------------------------------------------------------- + | Session Cookie Name + |-------------------------------------------------------------------------- + | + | Here you may change the name of the session cookie that is created by + | the framework. Typically, you should not need to change this value + | since doing so does not grant a meaningful security improvement. + | + */ + + 'cookie' => env( + 'SESSION_COOKIE', + Str::slug(env('APP_NAME', 'laravel'), '_').'_session' + ), + + /* + |-------------------------------------------------------------------------- + | Session Cookie Path + |-------------------------------------------------------------------------- + | + | The session cookie path determines the path for which the cookie will + | be regarded as available. Typically, this will be the root path of + | your application, but you're free to change this when necessary. + | + */ + + 'path' => env('SESSION_PATH', '/'), + + /* + |-------------------------------------------------------------------------- + | Session Cookie Domain + |-------------------------------------------------------------------------- + | + | This value determines the domain and subdomains the session cookie is + | available to. By default, the cookie will be available to the root + | domain and all subdomains. Typically, this shouldn't be changed. + | + */ + + 'domain' => env('SESSION_DOMAIN'), + + /* + |-------------------------------------------------------------------------- + | HTTPS Only Cookies + |-------------------------------------------------------------------------- + | + | By setting this option to true, session cookies will only be sent back + | to the server if the browser has a HTTPS connection. This will keep + | the cookie from being sent to you when it can't be done securely. + | + */ + + 'secure' => env('SESSION_SECURE_COOKIE'), + + /* + |-------------------------------------------------------------------------- + | HTTP Access Only + |-------------------------------------------------------------------------- + | + | Setting this value to true will prevent JavaScript from accessing the + | value of the cookie and the cookie will only be accessible through + | the HTTP protocol. It's unlikely you should disable this option. + | + */ + + 'http_only' => env('SESSION_HTTP_ONLY', true), + + /* + |-------------------------------------------------------------------------- + | Same-Site Cookies + |-------------------------------------------------------------------------- + | + | This option determines how your cookies behave when cross-site requests + | take place, and can be used to mitigate CSRF attacks. By default, we + | will set this value to "lax" to permit secure cross-site requests. + | + | See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#samesitesamesite-value + | + | Supported: "lax", "strict", "none", null + | + */ + + 'same_site' => env('SESSION_SAME_SITE', 'lax'), + + /* + |-------------------------------------------------------------------------- + | Partitioned Cookies + |-------------------------------------------------------------------------- + | + | Setting this value to true will tie the cookie to the top-level site for + | a cross-site context. Partitioned cookies are accepted by the browser + | when flagged "secure" and the Same-Site attribute is set to "none". + | + */ + + 'partitioned' => env('SESSION_PARTITIONED_COOKIE', false), + +]; diff --git a/config/wechat.php b/config/wechat.php new file mode 100644 index 0000000..4e09b19 --- /dev/null +++ b/config/wechat.php @@ -0,0 +1,49 @@ + +// +---------------------------------------------------------------------- + +return [ + 'wx' => [ + 'app_id' => 'wx8729bae07a068c87', + 'secret' => 'bd76508353688757296ce1e8c1307758', + + 'token' => '', + ], + /** + * OAuth 配置 + * + * scopes:公众平台(snsapi_userinfo / snsapi_base),开放平台:snsapi_login + * callback:OAuth授权完成后的回调页地址 + */ + 'oauth' => [ + 'scopes' => ['snsapi_userinfo'], + 'callback' => '/examples/oauth_callback.php', + ], + + /** + * 接口请求相关配置,超时时间等,具体可用参数请参考: + * https://github.com/symfony/symfony/blob/5.3/src/Symfony/Contracts/HttpClient/HttpClientInterface.php + */ + 'http' => [ + 'timeout' => 5.0, + // 'base_uri' => 'https://api.weixin.qq.com/', // 如果你在国外想要覆盖默认的 url 的时候才使用,根据不同的模块配置不同的 uri + + 'retry' => true, // 使用默认重试配置 + // 'retry' => [ + // // 仅以下状态码重试 + // 'status_codes' => [429, 500] + // // 最大重试次数 + // 'max_retries' => 3, + // // 请求间隔 (毫秒) + // 'delay' => 1000, + // // 如果设置,每次重试的等待时间都会增加这个系数 + // // (例如. 首次:1000ms; 第二次: 3 * 1000ms; etc.) + // 'multiplier' => 3 + // ], + ], +]; diff --git a/database/.gitignore b/database/.gitignore new file mode 100644 index 0000000..9b19b93 --- /dev/null +++ b/database/.gitignore @@ -0,0 +1 @@ +*.sqlite* diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php new file mode 100644 index 0000000..584104c --- /dev/null +++ b/database/factories/UserFactory.php @@ -0,0 +1,44 @@ + + */ +class UserFactory extends Factory +{ + /** + * The current password being used by the factory. + */ + protected static ?string $password; + + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'name' => fake()->name(), + 'email' => fake()->unique()->safeEmail(), + 'email_verified_at' => now(), + 'password' => static::$password ??= Hash::make('password'), + 'remember_token' => Str::random(10), + ]; + } + + /** + * Indicate that the model's email address should be unverified. + */ + public function unverified(): static + { + return $this->state(fn (array $attributes) => [ + 'email_verified_at' => null, + ]); + } +} diff --git a/database/migrations/0001_01_01_000002_create_jobs_table.php b/database/migrations/0001_01_01_000002_create_jobs_table.php new file mode 100644 index 0000000..425e705 --- /dev/null +++ b/database/migrations/0001_01_01_000002_create_jobs_table.php @@ -0,0 +1,57 @@ +id(); + $table->string('queue')->index(); + $table->longText('payload'); + $table->unsignedTinyInteger('attempts'); + $table->unsignedInteger('reserved_at')->nullable(); + $table->unsignedInteger('available_at'); + $table->unsignedInteger('created_at'); + }); + + Schema::create('job_batches', function (Blueprint $table) { + $table->string('id')->primary(); + $table->string('name'); + $table->integer('total_jobs'); + $table->integer('pending_jobs'); + $table->integer('failed_jobs'); + $table->longText('failed_job_ids'); + $table->mediumText('options')->nullable(); + $table->integer('cancelled_at')->nullable(); + $table->integer('created_at'); + $table->integer('finished_at')->nullable(); + }); + + Schema::create('failed_jobs', function (Blueprint $table) { + $table->id(); + $table->string('uuid')->unique(); + $table->text('connection'); + $table->text('queue'); + $table->longText('payload'); + $table->longText('exception'); + $table->timestamp('failed_at')->useCurrent(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('jobs'); + Schema::dropIfExists('job_batches'); + Schema::dropIfExists('failed_jobs'); + } +}; diff --git a/database/migrations/2024_05_17_153707_admin_auth_base_table.php b/database/migrations/2024_05_17_153707_admin_auth_base_table.php new file mode 100644 index 0000000..7346097 --- /dev/null +++ b/database/migrations/2024_05_17_153707_admin_auth_base_table.php @@ -0,0 +1,132 @@ +id('uid')->unique()->comment('用户ID'); + $table->string('username')->unique()->comment('用户名'); + $table->string('nickname')->nullable()->comment('昵称'); + $table->string('email')->nullable()->comment('邮箱'); + $table->string('mobile')->nullable()->comment('手机号码'); + $table->string('password')->comment('密码'); + $table->string('avatar')->nullable()->comment('头像'); + $table->unsignedBigInteger('department_id')->nullable()->comment('部门id'); + $table->unsignedTinyInteger('gender')->default(1)->comment('性别 1男 2女'); + $table->text('remark')->nullable()->comment('备注,个性签名'); + $table->unsignedTinyInteger('status')->default(1)->comment('状态 1正常 2禁用'); + $table->string('last_login_ip')->nullable()->comment('最后登录ip'); + $table->timestamp('last_login_at')->nullable()->comment('最后登录时间'); + $table->timestamp('email_verified_at')->nullable()->comment('邮箱验证时间'); + $table->timestamp('mobile_verified_at')->nullable()->comment('手机验证时间'); + $table->timestamp('created_at')->nullable()->comment('创建时间'); + $table->timestamp('updated_at')->nullable()->comment('更新时间'); + $table->timestamp('deleted_at')->nullable()->comment('删除时间'); + + $table->engine = 'InnoDB'; + $table->charset = 'utf8mb4'; + $table->collation = 'utf8mb4_unicode_ci'; + $table->comment('后台管理员表'); + }); + Schema::create('auth_admins_roles', function (Blueprint $table) { + $table->unsignedBigInteger('role_id')->comment('角色id'); + $table->unsignedBigInteger('uid')->comment('用户id'); + $table->primary(['role_id', 'uid']); + + $table->engine = 'InnoDB'; + $table->charset = 'utf8mb4'; + $table->collation = 'utf8mb4_unicode_ci'; + $table->comment('后台管理员角色对应表'); + }); + Schema::create('auth_roles', function (Blueprint $table) { + $table->id()->uniqid()->comment('主键id'); + $table->string('title', 50)->comment('角色名称'); + $table->string('name', 50)->comment('角色标识'); + $table->string('data_range')->default('all')->comment('数据范围 all全部 department本部门 department_sub本部门及以下 self仅自己'); + $table->integer('sort')->default(0)->comment('排序'); + $table->unsignedTinyInteger('status')->default(1)->comment('状态 1正常 2禁用'); + $table->string('description', 255)->nullable()->comment('角色描述'); + $table->timestamp('created_at')->nullable()->comment('创建时间'); + $table->timestamp('updated_at')->nullable()->comment('更新时间'); + $table->timestamp('deleted_at')->nullable()->comment('删除时间'); + + $table->engine = 'InnoDB'; + $table->charset = 'utf8mb4'; + $table->collation = 'utf8mb4_unicode_ci'; + $table->comment('后台管理员角色表'); + }); + Schema::create('auth_roles_permissions', function (Blueprint $table) { + $table->bigInteger('role_id')->unsigned()->comment('角色ID'); + $table->bigInteger('permission_id')->unsigned()->comment('权限ID'); + $table->primary(['role_id', 'permission_id']); + + $table->engine = 'InnoDB'; + $table->charset = 'utf8mb4'; + $table->collation = 'utf8mb4_unicode_ci'; + $table->comment('后台管理员角色权限表'); + }); + Schema::create('auth_permissions', function (Blueprint $table) { + $table->id()->uniqid()->comment('主键id'); + $table->integer('parent_id')->default(0)->comment('父级id'); + $table->string('title', 50)->comment('权限标题'); + $table->string('name', 50)->comment('权限名称'); + $table->string('icon', 50)->nullable()->comment('图标'); + $table->string('path', 100)->nullable()->comment('权限链接'); + $table->string('redirect', 100)->nullable()->comment('重定向'); + $table->string('component', 200)->nullable()->comment('组件'); + $table->string('type', 20)->default('menu')->comment('类型 menu菜单 button按钮 link外链 iframeIframe'); + $table->string('color', 20)->nullable()->comment('颜色'); + $table->tinyInteger('hidden')->default(0)->comment('是否显示 1显示 2不显示'); + $table->tinyInteger('hiddenBreadcrumb')->default(0)->comment('是否显示面包屑 1显示 2不显示'); + $table->tinyInteger('affix')->default(0)->comment('是否固定 1固定 2不固定'); + $table->tinyInteger('fullpage')->default(0)->comment('是否全屏 1全屏 2不全屏'); + $table->text('apiList')->nullable()->comment('接口列表'); + $table->integer('sort')->default(0)->comment('排序'); + $table->tinyInteger('status')->default(1)->comment('状态 1正常 2禁用'); + $table->timestamp('created_at')->nullable()->comment('创建时间'); + $table->timestamp('updated_at')->nullable()->comment('更新时间'); + $table->timestamp('deleted_at')->nullable()->comment('删除时间'); + + $table->engine = 'InnoDB'; + $table->charset = 'utf8mb4'; + $table->collation = 'utf8mb4_unicode_ci'; + $table->comment('权限表'); + }); + Schema::create('auth_departments', function (Blueprint $table) { + $table->id()->uniqid()->comment('主键id'); + $table->string('parent_id')->default(0)->comment('父级id'); + $table->string('title')->comment('部门名称'); + $table->string('name')->comment('部门标识'); + $table->string('sort')->default(0)->comment('排序'); + $table->tinyInteger('status')->default(1)->comment('状态 1正常 2禁用'); + $table->string('description', 255)->nullable()->comment('部门描述'); + $table->timestamp('created_at')->nullable()->comment('创建时间'); + $table->timestamp('updated_at')->nullable()->comment('更新时间'); + $table->timestamp('deleted_at')->nullable()->comment('删除时间'); + + $table->engine = 'InnoDB'; + $table->charset = 'utf8mb4'; + $table->collation = 'utf8mb4_unicode_ci'; + $table->comment('部门表'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void { + Schema::dropIfExists('auth_admins'); + Schema::dropIfExists('auth_admins_roles'); + Schema::dropIfExists('auth_roles'); + Schema::dropIfExists('auth_roles_permissions'); + Schema::dropIfExists('auth_permissions'); + Schema::dropIfExists('auth_departments'); + } +}; diff --git a/database/migrations/2024_05_20_185456_system_base_table.php b/database/migrations/2024_05_20_185456_system_base_table.php new file mode 100644 index 0000000..63bef9f --- /dev/null +++ b/database/migrations/2024_05_20_185456_system_base_table.php @@ -0,0 +1,203 @@ +id()->uniqid()->comment('主键id'); + $table->unsignedBigInteger('user_id')->comment('用户ID'); + $table->string('title')->comment('日志标题'); + $table->string('name')->comment('日志名称'); + $table->string('url')->comment('请求地址'); + $table->string('method')->comment('请求方法'); + $table->string('client_ip')->nullable()->comment('请求IP'); + $table->text('data')->nullable()->comment('请求数据'); + $table->text('remark')->nullable()->comment('备注'); + $table->text('browser')->nullable()->comment('浏览器'); + $table->tinyInteger('status')->default(1)->comment('状态码'); + $table->timestamp('created_at')->nullable()->comment('创建时间'); + $table->timestamp('updated_at')->nullable()->comment('更新时间'); + + $table->engine = 'InnoDB'; + $table->charset = 'utf8mb4'; + $table->collation = 'utf8mb4_unicode_ci'; + $table->comment('系统日志表'); + }); + Schema::create('system_configs', function (Blueprint $table) { + $table->id()->uniqid()->comment('主键id'); + $table->string('title')->comment('配置标题'); + $table->string('name')->comment('配置名称'); + $table->string('values')->nullable()->comment('配置值'); + $table->string('type')->comment('配置类型'); + $table->string('group')->comment('配置分组'); + $table->jsonb('option')->nullable()->comment('配置选项'); + $table->string('remark')->nullable()->comment('配置备注'); + $table->string('sort')->default(0)->comment('排序'); + $table->tinyInteger('status')->default(1)->comment('状态'); + $table->timestamp('created_at')->nullable()->comment('创建时间'); + $table->timestamp('updated_at')->nullable()->comment('更新时间'); + + $table->engine = 'InnoDB'; + $table->charset = 'utf8mb4'; + $table->collation = 'utf8mb4_unicode_ci'; + $table->comment('系统配置表'); + }); + Schema::create('system_dicts', function (Blueprint $table) { + $table->id()->uniqid()->comment('主键id'); + $table->string('title')->comment('字典标题'); + $table->string('values')->comment('字典值'); + $table->string('group_id')->comment('字典分组'); + $table->string('group_name')->comment('字典分组标识'); + $table->string('sort')->default(0)->comment('排序'); + $table->tinyInteger('status')->default(1)->comment('状态'); + $table->timestamp('created_at')->nullable()->comment('创建时间'); + $table->timestamp('updated_at')->nullable()->comment('更新时间'); + + $table->engine = 'InnoDB'; + $table->charset = 'utf8mb4'; + $table->collation = 'utf8mb4_unicode_ci'; + $table->comment('系统字典表'); + }); + Schema::create('system_dict_groups', function (Blueprint $table) { + $table->id()->uniqid()->comment('主键id'); + $table->string('parent_id')->default(0)->comment('父级id'); + $table->string('title')->comment('字典分组标题'); + $table->string('name')->comment('字典分组名称'); + $table->string('sort')->default(0)->comment('排序'); + $table->tinyInteger('status')->default(1)->comment('状态'); + $table->timestamp('created_at')->nullable()->comment('创建时间'); + $table->timestamp('updated_at')->nullable()->comment('更新时间'); + + $table->engine = 'InnoDB'; + $table->charset = 'utf8mb4'; + $table->collation = 'utf8mb4_unicode_ci'; + $table->comment('系统字典分组表'); + }); + Schema::create('system_areas', function (Blueprint $table) { + $table->id()->uniqid()->comment('主键id'); + $table->string('code')->comment('城市编码'); + $table->string('parent_code')->default(0)->comment('父级id'); + $table->string('title')->comment('城市名称'); + $table->tinyInteger('status')->default(1)->comment('状态'); + $table->timestamp('created_at')->nullable()->comment('创建时间'); + $table->timestamp('updated_at')->nullable()->comment('更新时间'); + + $table->engine = 'InnoDB'; + $table->charset = 'utf8mb4'; + $table->collation = 'utf8mb4_unicode_ci'; + $table->comment('系统城市数据表'); + }); + + Schema::create('system_messages', function (Blueprint $table) { + $table->id()->uniqid()->comment('主键id'); + $table->unsignedBigInteger('send_uid')->comment('发送人uid'); + $table->string('receive_uid')->comment('接受人uid'); + $table->string('receive_group')->nullable()->comment('接受组'); + $table->string('type')->default(1)->comment('消息类型 1系统消息 2用户消息'); + $table->string('title')->comment('消息标题'); + $table->text('content')->comment('消息内容'); + $table->text('params')->nullable()->comment('消息参数'); + $table->string('url')->nullable()->comment('消息链接'); + $table->string('status')->default(1)->comment('状态 1未读 2已读'); + $table->timestamp('created_at')->nullable()->comment('创建时间'); + $table->timestamp('updated_at')->nullable()->comment('更新时间'); + $table->timestamp('read_at')->nullable()->comment('已读时间'); + $table->timestamp('deleted_at')->nullable()->comment('删除时间'); + + $table->engine = 'InnoDB'; + $table->charset = 'utf8mb4'; + $table->collation = 'utf8mb4_unicode_ci'; + $table->comment('系统消息表'); + }); + + Schema::create('system_tasks', function (Blueprint $table) { + $table->id()->uniqid()->comment('主键id'); + $table->unsignedBigInteger('user_id')->default(0)->comment('用户id'); + $table->string('title')->comment('任务名称'); + $table->string('file')->comment('文件路径'); + $table->string('type')->default('export')->comment('任务类型 import export'); + $table->string('status')->default(1)->comment('状态 1正常 2禁用'); + $table->timestamp('created_at')->nullable()->comment('创建时间'); + $table->timestamp('updated_at')->nullable()->comment('更新时间'); + + $table->engine = 'InnoDB'; + $table->charset = 'utf8mb4'; + $table->collation = 'utf8mb4_unicode_ci'; + $table->comment('系统excel任务表'); + }); + Schema::create('system_crontabs', function (Blueprint $table) { + $table->id()->uniqid()->comment('主键id'); + $table->string('title')->comment('定时任务标题'); + $table->string('command')->comment('定时任务命令'); + $table->string('expression')->comment('定时任务表达式'); + $table->string('status')->default(1)->comment('状态 1正常 2禁用'); + $table->timestamp('created_at')->nullable()->comment('创建时间'); + $table->timestamp('updated_at')->nullable()->comment('更新时间'); + + $table->engine = 'InnoDB'; + $table->charset = 'utf8mb4'; + $table->collation = 'utf8mb4_unicode_ci'; + $table->comment('系统定时任务表'); + }); + Schema::create('system_client', function (Blueprint $table) { + $table->id()->uniqid()->comment('主键id'); + $table->string('title')->comment('客户端名称'); + $table->string('app_id')->comment('客户端ID'); + $table->string('secret')->comment('客户端密钥'); + $table->string('redirect')->comment('回调地址'); + $table->string('status')->default(1)->comment('状态 1正常 2禁用'); + $table->timestamp('created_at')->nullable()->comment('创建时间'); + $table->timestamp('updated_at')->nullable()->comment('更新时间'); + + $table->engine = 'InnoDB'; + $table->charset = 'utf8mb4'; + $table->collation = 'utf8mb4_unicode_ci'; + $table->comment('系统客户端表'); + }); + Schema::create('system_client_menu', function (Blueprint $table) { + $table->id()->uniqid()->comment('主键id'); + $table->string('client_id')->comment('客户端ID'); + $table->unsignedBigInteger('parent_id')->default(0)->comment('父级id'); + $table->string('position')->comment('菜单位置'); + $table->string('name')->nullable()->comment('菜单名称标识'); + $table->string('title')->comment('菜单标题'); + $table->string('url')->comment('菜单链接'); + $table->string('cover')->nullable()->comment('菜单图片'); + $table->string('icon')->nullable()->comment('菜单图标'); + $table->string('sort')->default(0)->comment('排序'); + $table->tinyInteger('is_show')->default(1)->comment('是否显示 1显示 2不显示'); + $table->tinyInteger('is_blank')->default(1)->comment('是否新窗口打开 1是 2否'); + $table->string('status')->default(1)->comment('状态 1正常 2禁用'); + $table->timestamp('created_at')->nullable()->comment('创建时间'); + $table->timestamp('updated_at')->nullable()->comment('更新时间'); + + $table->engine = 'InnoDB'; + $table->charset = 'utf8mb4'; + $table->collation = 'utf8mb4_unicode_ci'; + $table->comment('系统客户端菜单表'); + }); + + } + + /** + * Reverse the migrations. + */ + public function down(): void { + Schema::dropIfExists('system_logs'); + Schema::dropIfExists('system_configs'); + Schema::dropIfExists('system_dicts'); + Schema::dropIfExists('system_dict_groups'); + Schema::dropIfExists('system_areas'); + Schema::dropIfExists('system_messages'); + Schema::dropIfExists('system_tasks'); + Schema::dropIfExists('system_crontabs'); + Schema::dropIfExists('system_client'); + Schema::dropIfExists('system_client_menu'); + } +}; diff --git a/database/migrations/2025_04_04_220920_create_modules_table.php b/database/migrations/2025_04_04_220920_create_modules_table.php new file mode 100644 index 0000000..bec9beb --- /dev/null +++ b/database/migrations/2025_04_04_220920_create_modules_table.php @@ -0,0 +1,28 @@ +id(); + $table->string('title', 50)->nullable()->comment('模块标题'); + $table->string('name')->unique()->comment('模块标识'); + $table->text('description')->nullable()->comment('模块描述'); + $table->boolean('status')->comment('模块状态'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void { + Schema::dropIfExists('modules'); + } +}; diff --git a/database/seeders/AdminSeeder.php b/database/seeders/AdminSeeder.php new file mode 100644 index 0000000..6e8bce2 --- /dev/null +++ b/database/seeders/AdminSeeder.php @@ -0,0 +1,64 @@ +insert([ + 'username' => 'admin', + 'nickname' => '超级管理员', + 'email' => 'admin@admin.com', + 'mobile' => '13888888888', + 'password' => Hash::make('admin888'), + 'department_id' => 1, + 'status' => 1, + 'created_at' => date('Y-m-d H:i:s'), + 'updated_at' => date('Y-m-d H:i:s'), + ]); + + DB::table('auth_departments')->insert([ + 'title' => '总部', + 'name' => 'root', + 'parent_id' => 0, + 'sort' => 0, + 'description' => '总部', + 'created_at' => date('Y-m-d H:i:s'), + 'updated_at' => date('Y-m-d H:i:s'), + ]); + + DB::table('auth_roles')->insert([ + 'title' => '超级管理员', + 'name' => 'admin', + 'description' => '超级管理员', + 'created_at' => date('Y-m-d H:i:s'), + 'updated_at' => date('Y-m-d H:i:s'), + ]); + app(MenuService::class)->importMenu($this->getPermissions(), 0); + } + + public function getPermissions(){ + $permissions = [ + ['title' => '首页', 'name' => 'home', 'path' => '/home', 'component' => '', 'type' => 'menu', 'sort' => 0, 'children' => [ + ['title' => '仪表盘', 'name' => 'dashboard', 'path' => '/dashboard', 'component' => 'home', 'type' => 'menu', 'affix' => 1], + ['title' => '个人中心', 'name' => 'ucenter', 'path' => '/ucenter', 'component' => 'ucenter', 'type' => 'menu'], + ]], + ['title' => '权限', 'name' => 'auth', 'path' => '/auth', 'component' => '', 'type' => 'menu', 'sort' => 99, 'children' => [ + ['title' => '用户管理', 'name' => 'auth.user', 'path' => '/auth/user', 'component' => 'auth/user', 'type' => 'menu'], + ['title' => '角色管理', 'name' => 'auth.role', 'path' => '/auth/role', 'component' => 'auth/role', 'type' => 'menu'], + ['title' => '权限管理', 'name' => 'auth.permission', 'path' => '/auth/permission', 'component' => 'auth/permission', 'type' => 'menu'], + ['title' => '部门管理', 'name' => 'auth.department', 'path' => '/auth/department', 'component' => 'auth/department', 'type' => 'menu'], + ]] + ]; + return $permissions; + } +} diff --git a/database/seeders/CitySeeder.php b/database/seeders/CitySeeder.php new file mode 100644 index 0000000..d2dd555 --- /dev/null +++ b/database/seeders/CitySeeder.php @@ -0,0 +1,23 @@ +call([ + AdminSeeder::class, + SystemSeeder::class, + CitySeeder::class + ]); + } +} diff --git a/database/seeders/SystemSeeder.php b/database/seeders/SystemSeeder.php new file mode 100644 index 0000000..764900c --- /dev/null +++ b/database/seeders/SystemSeeder.php @@ -0,0 +1,207 @@ +insertSettingDict(); + $this->insertDataAuthDict(); + $this->insertAdsDict(); + $this->addModelFieldTypeDict(); + $this->insertClientMenuPosition(); + $this->insertPermissions(); + } + + public function insertSettingDict(){ + $group_id = DB::table('system_dict_groups')->insertGetId([ + 'parent_id' => 0, + 'title' => '配置分组', + 'name' => 'setting_group', + 'status' => 1, + 'created_at' => date('Y-m-d H:i:s'), + 'updated_at' => date('Y-m-d H:i:s'), + ]); + if($group_id){ + $dict = DB::table('system_dicts')->insert([ + ['group_id' => $group_id, 'group_name' => 'setting_group', 'title' => '基础配置', 'values' => 'base', 'sort' => 1, 'status' => 1, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s')], + ['group_id' => $group_id, 'group_name' => 'setting_group', 'title' => '上传配置', 'values' => 'upload', 'sort' => 1, 'status' => 1, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s')], + ]); + DB::table('system_configs')->insert([ + ['name' => 'system_name', 'title' => '系统名称', 'values' => 'SentOS', 'type' => 'string', 'group' => 'base', 'sort' => 1, 'status' => 1, 'remark' => '系统名称', 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s')], + ['name' => 'system_logo', 'title' => '系统Logo', 'values' => '', 'type' => 'image', 'group' => 'base', 'sort' => 2, 'status' => 1, 'remark' => '系统Logo', 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s')], + ['name' => 'system_email', 'title' => '邮箱', 'values' => '', 'type' => 'string', 'group' => 'base', 'sort' => 6, 'status' => 1, 'remark' => '邮箱', 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s')], + ['name' => 'system_copyright', 'title' => '版权信息', 'values' => 'Copyright © 2019-2024 SentCMS All Rights Reserved.', 'type' => 'string', 'group' => 'base', 'sort' => 3, 'status' => 1, 'remark' => '版权信息', 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s')], + ['name' => 'system_icp', 'title' => '备案信息', 'values' => '', 'type' => 'string', 'group' =>'base', 'sort' => 4, 'status' => 1, 'remark' => '备案信息', 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s')], + ['name' => 'upload_way', 'title' => '上传引擎', 'values' => 'public', 'type' => 'radio', 'group' =>'upload', 'sort' => 1, 'status' => 1, 'remark' => '上传引擎', 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s')] + ]); + } + } + + public function insertDataAuthDict(){ + $group_id = DB::table('system_dict_groups')->insertGetId([ + 'parent_id' => 0, + 'title' => '数据权限分组', + 'name' => 'data_auth', + 'sort' => 1, + 'status' => 1, + 'created_at' => date('Y-m-d H:i:s'), + 'updated_at' => date('Y-m-d H:i:s'), + ]); + if($group_id){ + DB::table('system_dicts')->insert([ + [ + 'group_id' => $group_id, + 'group_name' => 'data_auth', + 'title' => '全部数据', + 'values' => 'all', + 'sort' => 1, + 'status' => 1, + 'created_at' => date('Y-m-d H:i:s'), + 'updated_at' => date('Y-m-d H:i:s'), + ], + [ + 'group_id' => $group_id, + 'group_name' => 'data_auth', + 'title' => '本部门数据', + 'values' => 'department', + 'sort' => 2, + 'status' => 1, + 'created_at' => date('Y-m-d H:i:s'), + 'updated_at' => date('Y-m-d H:i:s'), + ], + [ + 'group_id' => $group_id, + 'group_name' => 'data_auth', + 'title' => '本部门及以下数据', + 'values' => 'department_sub', + 'sort' => 3, + 'status' => 1, + 'created_at' => date('Y-m-d H:i:s'), + 'updated_at' => date('Y-m-d H:i:s'), + ], + [ + 'group_id' => $group_id, + 'group_name' => 'data_auth', + 'title' => '仅自己数据', + 'values' => 'self', + 'sort' => 4, + 'status' => 1, + 'created_at' => date('Y-m-d H:i:s'), + 'updated_at' => date('Y-m-d H:i:s'), + ] + ]); + } + } + + public function insertAdsDict(){ + $group_id = DB::table('system_dict_groups')->insertGetId([ + 'parent_id' => 0, + 'title' => '广告位', + 'name' => 'ads_places', + 'sort' => 1, + 'status' => 1, + 'created_at' => date('Y-m-d H:i:s'), + 'updated_at' => date('Y-m-d H:i:s'), + ]); + if($group_id){ + DB::table('system_dicts')->insert([ + [ + 'group_id' => $group_id, + 'group_name' => 'ads_places', + 'title' => '首页轮播', + 'values' => 'index_banner', + 'sort' => 2, + 'status' => 1, + 'created_at' => date('Y-m-d H:i:s'), + 'updated_at' => date('Y-m-d H:i:s'), + ] + ]); + } + } + + public function insertClientMenuPosition(){ + $group_id = DB::table('system_dict_groups')->insertGetId([ + 'parent_id' => 0, + 'title' => '菜单位置', + 'name' => 'client_menu_position', + 'sort' => 3, + 'status' => 1, + 'created_at' => date('Y-m-d H:i:s'), + 'updated_at' => date('Y-m-d H:i:s'), + ]); + + if($group_id){ + DB::table('system_dicts')->insert([ + [ + 'group_id' => $group_id, + 'group_name' => 'client_menu_position', + 'title' => '顶部菜单', + 'values' => 'top', + 'sort' => 1, + 'status' => 1, + 'created_at' => date('Y-m-d H:i:s'), + 'updated_at' => date('Y-m-d H:i:s'), + ] + ]); + } + } + /** + * @title 添加字典 + * + * @return void + */ + public function addModelFieldTypeDict(){ + $group_id = DB::table('system_dict_groups')->insertGetId([ + 'parent_id' => 0, + 'title' => '字段类型', + 'name' => 'field_type', + 'status' => 1, + 'created_at' => date('Y-m-d H:i:s'), + 'updated_at' => date('Y-m-d H:i:s'), + ]); + if($group_id){ + $dict = DB::table('system_dicts')->insert([ + ['group_id' => $group_id, 'group_name' => 'field_type', 'title' => '单行文本', 'values' => 'string', 'sort' =>1, 'status' => 1, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s')], + ['group_id' => $group_id, 'group_name' => 'field_type', 'title' => '多行文本', 'values' => 'text', 'sort' => 2, 'status' => 1, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s')], + ['group_id' => $group_id, 'group_name' => 'field_type', 'title' => '单选按钮', 'values' => 'radio', 'sort' => 3, 'status' => 1, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s') ], + ['group_id' => $group_id, 'group_name' => 'field_type', 'title' => '多选按钮', 'values' => 'checkbox', 'sort' => 4, 'status' => 1, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s') ], + ['group_id' => $group_id, 'group_name' => 'field_type', 'title' => '下拉列表', 'values' => 'select', 'sort' => 5, 'status' => 1, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s') ], + ['group_id' => $group_id, 'group_name' => 'field_type', 'title' => '字典选择', 'values' => 'sSelect', 'sort' => 5, 'status' => 1, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' =>date('Y-m-d H:i:s') ], + ['group_id' => $group_id, 'group_name' => 'field_type', 'title' => '树形选择', 'values' => 'sSelectTree', 'sort' => 6, 'status' => 1, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s') ], + ['group_id' => $group_id, 'group_name' => 'field_type', 'title' => '日期', 'values' => 'date', 'sort' => 6, 'status' => 1, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s') ], + ['group_id' => $group_id, 'group_name' => 'field_type', 'title' => '日期时间', 'values' => 'datetime', 'sort' => 7, 'status' => 1, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s') ], + ['group_id' => $group_id, 'group_name' => 'field_type', 'title' => '文件', 'values' => 'file', 'sort' => 8, 'status' => 1, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s') ], + ['group_id' => $group_id, 'group_name' => 'field_type', 'title' => '图片', 'values' => 'image', 'sort' => 9, 'status' => 1, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s') ], + ['group_id' => $group_id, 'group_name' => 'field_type', 'title' => '多图', 'values' => 'images', 'sort' => 10, 'status' => 1, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s') ], + ['group_id' => $group_id, 'group_name' => 'field_type', 'title' => '富文本', 'values' => 'editor', 'sort' => 10, 'status' => 1, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s') ], + ['group_id' => $group_id, 'group_name' => 'field_type', 'title' => '视频', 'values' => 'video', 'sort' => 10, 'status' => 1, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s') ], + ['group_id' => $group_id, 'group_name' => 'field_type', 'title' => '音频', 'values' => 'audio', 'sort' => 10, 'status' => 1, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s')], + ['group_id' => $group_id, 'group_name' => 'field_type', 'title' => '数字', 'values' => 'number', 'sort' => 3, 'status' => 1, 'created_at' => date('Y-m-d H:i:s'), 'updated_at' => date('Y-m-d H:i:s')] + ]); + } + } + + public function insertPermissions(){ + $permissions = [ + ['title' => '系统', 'name' => 'system', 'path' => '/system', 'component' => '', 'type' => 'menu', 'sort' => 100, 'children' => [ + ['title' => '系统设置', 'name' => 'system.setting', 'path' => '/system/setting', 'component' => 'system/setting', 'type' => 'menu'], + ['title' => '城市数据', 'name' => 'system.area', 'path' => '/system/area', 'component' => 'system/area', 'type' => 'menu'], + ['title' => '字典管理', 'name' => 'system.dic', 'path' => '/system/dic', 'component' => 'system/dic', 'type' => 'menu'], + ['title' => '客户端管理', 'name' => 'system.client', 'path' => '/system/client', 'component' => 'system/client', 'type' => 'menu'], + ['title' => '模块管理', 'name' => 'system.modules', 'path' => '/system/modules', 'component' => 'system/modules', 'type' => 'menu'], + ['title' => '系统日志', 'name' => 'system.log', 'path' => '/system/log', 'component' => 'system/log', 'type' => 'menu'], + ['title' => '定时任务', 'name' => 'system.crontab', 'path' => '/system/crontab', 'component' => 'system/crontab', 'type' => 'menu'], + ]] + ]; + app(MenuService::class)->importMenu($permissions, 0); + } +} diff --git a/database/seeders/area.json b/database/seeders/area.json new file mode 100644 index 0000000..1253516 --- /dev/null +++ b/database/seeders/area.json @@ -0,0 +1 @@ +[{"title":"\u5317\u4eac\u5e02","code":"110000","parent_code":0},{"title":"\u5929\u6d25\u5e02","code":"120000","parent_code":0},{"title":"\u6cb3\u5317\u7701","code":"130000","parent_code":0},{"title":"\u5c71\u897f\u7701","code":"140000","parent_code":0},{"title":"\u5185\u8499\u53e4\u81ea\u6cbb\u533a","code":"150000","parent_code":0},{"title":"\u8fbd\u5b81\u7701","code":"210000","parent_code":0},{"title":"\u5409\u6797\u7701","code":"220000","parent_code":0},{"title":"\u9ed1\u9f99\u6c5f\u7701","code":"230000","parent_code":0},{"title":"\u4e0a\u6d77\u5e02","code":"310000","parent_code":0},{"title":"\u6c5f\u82cf\u7701","code":"320000","parent_code":0},{"title":"\u6d59\u6c5f\u7701","code":"330000","parent_code":0},{"title":"\u5b89\u5fbd\u7701","code":"340000","parent_code":0},{"title":"\u798f\u5efa\u7701","code":"350000","parent_code":0},{"title":"\u6c5f\u897f\u7701","code":"360000","parent_code":0},{"title":"\u5c71\u4e1c\u7701","code":"370000","parent_code":0},{"title":"\u6cb3\u5357\u7701","code":"410000","parent_code":0},{"title":"\u6e56\u5317\u7701","code":"420000","parent_code":0},{"title":"\u6e56\u5357\u7701","code":"430000","parent_code":0},{"title":"\u5e7f\u4e1c\u7701","code":"440000","parent_code":0},{"title":"\u5e7f\u897f\u58ee\u65cf\u81ea\u6cbb\u533a","code":"450000","parent_code":0},{"title":"\u6d77\u5357\u7701","code":"460000","parent_code":0},{"title":"\u91cd\u5e86\u5e02","code":"500000","parent_code":0},{"title":"\u56db\u5ddd\u7701","code":"510000","parent_code":0},{"title":"\u8d35\u5dde\u7701","code":"520000","parent_code":0},{"title":"\u4e91\u5357\u7701","code":"530000","parent_code":0},{"title":"\u897f\u85cf\u81ea\u6cbb\u533a","code":"540000","parent_code":0},{"title":"\u9655\u897f\u7701","code":"610000","parent_code":0},{"title":"\u7518\u8083\u7701","code":"620000","parent_code":0},{"title":"\u9752\u6d77\u7701","code":"630000","parent_code":0},{"title":"\u5b81\u590f\u56de\u65cf\u81ea\u6cbb\u533a","code":"640000","parent_code":0},{"title":"\u65b0\u7586\u7ef4\u543e\u5c14\u81ea\u6cbb\u533a","code":"650000","parent_code":0},{"title":"\u53f0\u6e7e\u7701","code":"710000","parent_code":0},{"title":"\u9999\u6e2f\u7279\u522b\u884c\u653f\u533a","code":"810000","parent_code":0},{"title":"\u6fb3\u95e8\u7279\u522b\u884c\u653f\u533a","code":"820000","parent_code":0},{"title":"\u5317\u4eac\u5e02","code":"110100","parent_code":110000},{"title":"\u5929\u6d25\u5e02","code":"120100","parent_code":120000},{"title":"\u77f3\u5bb6\u5e84\u5e02","code":"130100","parent_code":130000},{"title":"\u5510\u5c71\u5e02","code":"130200","parent_code":130000},{"title":"\u79e6\u7687\u5c9b\u5e02","code":"130300","parent_code":130000},{"title":"\u90af\u90f8\u5e02","code":"130400","parent_code":130000},{"title":"\u90a2\u53f0\u5e02","code":"130500","parent_code":130000},{"title":"\u4fdd\u5b9a\u5e02","code":"130600","parent_code":130000},{"title":"\u5f20\u5bb6\u53e3\u5e02","code":"130700","parent_code":130000},{"title":"\u627f\u5fb7\u5e02","code":"130800","parent_code":130000},{"title":"\u6ca7\u5dde\u5e02","code":"130900","parent_code":130000},{"title":"\u5eca\u574a\u5e02","code":"131000","parent_code":130000},{"title":"\u8861\u6c34\u5e02","code":"131100","parent_code":130000},{"title":"\u592a\u539f\u5e02","code":"140100","parent_code":140000},{"title":"\u5927\u540c\u5e02","code":"140200","parent_code":140000},{"title":"\u9633\u6cc9\u5e02","code":"140300","parent_code":140000},{"title":"\u957f\u6cbb\u5e02","code":"140400","parent_code":140000},{"title":"\u664b\u57ce\u5e02","code":"140500","parent_code":140000},{"title":"\u6714\u5dde\u5e02","code":"140600","parent_code":140000},{"title":"\u664b\u4e2d\u5e02","code":"140700","parent_code":140000},{"title":"\u8fd0\u57ce\u5e02","code":"140800","parent_code":140000},{"title":"\u5ffb\u5dde\u5e02","code":"140900","parent_code":140000},{"title":"\u4e34\u6c7e\u5e02","code":"141000","parent_code":140000},{"title":"\u5415\u6881\u5e02","code":"141100","parent_code":140000},{"title":"\u547c\u548c\u6d69\u7279\u5e02","code":"150100","parent_code":150000},{"title":"\u5305\u5934\u5e02","code":"150200","parent_code":150000},{"title":"\u4e4c\u6d77\u5e02","code":"150300","parent_code":150000},{"title":"\u8d64\u5cf0\u5e02","code":"150400","parent_code":150000},{"title":"\u901a\u8fbd\u5e02","code":"150500","parent_code":150000},{"title":"\u9102\u5c14\u591a\u65af\u5e02","code":"150600","parent_code":150000},{"title":"\u547c\u4f26\u8d1d\u5c14\u5e02","code":"150700","parent_code":150000},{"title":"\u5df4\u5f66\u6dd6\u5c14\u5e02","code":"150800","parent_code":150000},{"title":"\u4e4c\u5170\u5bdf\u5e03\u5e02","code":"150900","parent_code":150000},{"title":"\u5174\u5b89\u76df","code":"152200","parent_code":150000},{"title":"\u9521\u6797\u90ed\u52d2\u76df","code":"152500","parent_code":150000},{"title":"\u963f\u62c9\u5584\u76df","code":"152900","parent_code":150000},{"title":"\u6c88\u9633\u5e02","code":"210100","parent_code":210000},{"title":"\u5927\u8fde\u5e02","code":"210200","parent_code":210000},{"title":"\u978d\u5c71\u5e02","code":"210300","parent_code":210000},{"title":"\u629a\u987a\u5e02","code":"210400","parent_code":210000},{"title":"\u672c\u6eaa\u5e02","code":"210500","parent_code":210000},{"title":"\u4e39\u4e1c\u5e02","code":"210600","parent_code":210000},{"title":"\u9526\u5dde\u5e02","code":"210700","parent_code":210000},{"title":"\u8425\u53e3\u5e02","code":"210800","parent_code":210000},{"title":"\u961c\u65b0\u5e02","code":"210900","parent_code":210000},{"title":"\u8fbd\u9633\u5e02","code":"211000","parent_code":210000},{"title":"\u76d8\u9526\u5e02","code":"211100","parent_code":210000},{"title":"\u94c1\u5cad\u5e02","code":"211200","parent_code":210000},{"title":"\u671d\u9633\u5e02","code":"211300","parent_code":210000},{"title":"\u846b\u82a6\u5c9b\u5e02","code":"211400","parent_code":210000},{"title":"\u957f\u6625\u5e02","code":"220100","parent_code":220000},{"title":"\u5409\u6797\u5e02","code":"220200","parent_code":220000},{"title":"\u56db\u5e73\u5e02","code":"220300","parent_code":220000},{"title":"\u8fbd\u6e90\u5e02","code":"220400","parent_code":220000},{"title":"\u901a\u5316\u5e02","code":"220500","parent_code":220000},{"title":"\u767d\u5c71\u5e02","code":"220600","parent_code":220000},{"title":"\u677e\u539f\u5e02","code":"220700","parent_code":220000},{"title":"\u767d\u57ce\u5e02","code":"220800","parent_code":220000},{"title":"\u5ef6\u8fb9\u671d\u9c9c\u65cf\u81ea\u6cbb\u5dde","code":"222400","parent_code":220000},{"title":"\u54c8\u5c14\u6ee8\u5e02","code":"230100","parent_code":230000},{"title":"\u9f50\u9f50\u54c8\u5c14\u5e02","code":"230200","parent_code":230000},{"title":"\u9e21\u897f\u5e02","code":"230300","parent_code":230000},{"title":"\u9e64\u5c97\u5e02","code":"230400","parent_code":230000},{"title":"\u53cc\u9e2d\u5c71\u5e02","code":"230500","parent_code":230000},{"title":"\u5927\u5e86\u5e02","code":"230600","parent_code":230000},{"title":"\u4f0a\u6625\u5e02","code":"230700","parent_code":230000},{"title":"\u4f73\u6728\u65af\u5e02","code":"230800","parent_code":230000},{"title":"\u4e03\u53f0\u6cb3\u5e02","code":"230900","parent_code":230000},{"title":"\u7261\u4e39\u6c5f\u5e02","code":"231000","parent_code":230000},{"title":"\u9ed1\u6cb3\u5e02","code":"231100","parent_code":230000},{"title":"\u7ee5\u5316\u5e02","code":"231200","parent_code":230000},{"title":"\u5927\u5174\u5b89\u5cad\u5730\u533a","code":"232700","parent_code":230000},{"title":"\u4e0a\u6d77\u5e02","code":"310100","parent_code":310000},{"title":"\u5357\u4eac\u5e02","code":"320100","parent_code":320000},{"title":"\u65e0\u9521\u5e02","code":"320200","parent_code":320000},{"title":"\u5f90\u5dde\u5e02","code":"320300","parent_code":320000},{"title":"\u5e38\u5dde\u5e02","code":"320400","parent_code":320000},{"title":"\u82cf\u5dde\u5e02","code":"320500","parent_code":320000},{"title":"\u5357\u901a\u5e02","code":"320600","parent_code":320000},{"title":"\u8fde\u4e91\u6e2f\u5e02","code":"320700","parent_code":320000},{"title":"\u6dee\u5b89\u5e02","code":"320800","parent_code":320000},{"title":"\u76d0\u57ce\u5e02","code":"320900","parent_code":320000},{"title":"\u626c\u5dde\u5e02","code":"321000","parent_code":320000},{"title":"\u9547\u6c5f\u5e02","code":"321100","parent_code":320000},{"title":"\u6cf0\u5dde\u5e02","code":"321200","parent_code":320000},{"title":"\u5bbf\u8fc1\u5e02","code":"321300","parent_code":320000},{"title":"\u676d\u5dde\u5e02","code":"330100","parent_code":330000},{"title":"\u5b81\u6ce2\u5e02","code":"330200","parent_code":330000},{"title":"\u6e29\u5dde\u5e02","code":"330300","parent_code":330000},{"title":"\u5609\u5174\u5e02","code":"330400","parent_code":330000},{"title":"\u6e56\u5dde\u5e02","code":"330500","parent_code":330000},{"title":"\u7ecd\u5174\u5e02","code":"330600","parent_code":330000},{"title":"\u91d1\u534e\u5e02","code":"330700","parent_code":330000},{"title":"\u8862\u5dde\u5e02","code":"330800","parent_code":330000},{"title":"\u821f\u5c71\u5e02","code":"330900","parent_code":330000},{"title":"\u53f0\u5dde\u5e02","code":"331000","parent_code":330000},{"title":"\u4e3d\u6c34\u5e02","code":"331100","parent_code":330000},{"title":"\u5408\u80a5\u5e02","code":"340100","parent_code":340000},{"title":"\u829c\u6e56\u5e02","code":"340200","parent_code":340000},{"title":"\u868c\u57e0\u5e02","code":"340300","parent_code":340000},{"title":"\u6dee\u5357\u5e02","code":"340400","parent_code":340000},{"title":"\u9a6c\u978d\u5c71\u5e02","code":"340500","parent_code":340000},{"title":"\u6dee\u5317\u5e02","code":"340600","parent_code":340000},{"title":"\u94dc\u9675\u5e02","code":"340700","parent_code":340000},{"title":"\u5b89\u5e86\u5e02","code":"340800","parent_code":340000},{"title":"\u9ec4\u5c71\u5e02","code":"341000","parent_code":340000},{"title":"\u6ec1\u5dde\u5e02","code":"341100","parent_code":340000},{"title":"\u961c\u9633\u5e02","code":"341200","parent_code":340000},{"title":"\u5bbf\u5dde\u5e02","code":"341300","parent_code":340000},{"title":"\u516d\u5b89\u5e02","code":"341500","parent_code":340000},{"title":"\u4eb3\u5dde\u5e02","code":"341600","parent_code":340000},{"title":"\u6c60\u5dde\u5e02","code":"341700","parent_code":340000},{"title":"\u5ba3\u57ce\u5e02","code":"341800","parent_code":340000},{"title":"\u798f\u5dde\u5e02","code":"350100","parent_code":350000},{"title":"\u53a6\u95e8\u5e02","code":"350200","parent_code":350000},{"title":"\u8386\u7530\u5e02","code":"350300","parent_code":350000},{"title":"\u4e09\u660e\u5e02","code":"350400","parent_code":350000},{"title":"\u6cc9\u5dde\u5e02","code":"350500","parent_code":350000},{"title":"\u6f33\u5dde\u5e02","code":"350600","parent_code":350000},{"title":"\u5357\u5e73\u5e02","code":"350700","parent_code":350000},{"title":"\u9f99\u5ca9\u5e02","code":"350800","parent_code":350000},{"title":"\u5b81\u5fb7\u5e02","code":"350900","parent_code":350000},{"title":"\u5357\u660c\u5e02","code":"360100","parent_code":360000},{"title":"\u666f\u5fb7\u9547\u5e02","code":"360200","parent_code":360000},{"title":"\u840d\u4e61\u5e02","code":"360300","parent_code":360000},{"title":"\u4e5d\u6c5f\u5e02","code":"360400","parent_code":360000},{"title":"\u65b0\u4f59\u5e02","code":"360500","parent_code":360000},{"title":"\u9e70\u6f6d\u5e02","code":"360600","parent_code":360000},{"title":"\u8d63\u5dde\u5e02","code":"360700","parent_code":360000},{"title":"\u5409\u5b89\u5e02","code":"360800","parent_code":360000},{"title":"\u5b9c\u6625\u5e02","code":"360900","parent_code":360000},{"title":"\u629a\u5dde\u5e02","code":"361000","parent_code":360000},{"title":"\u4e0a\u9976\u5e02","code":"361100","parent_code":360000},{"title":"\u6d4e\u5357\u5e02","code":"370100","parent_code":370000},{"title":"\u9752\u5c9b\u5e02","code":"370200","parent_code":370000},{"title":"\u6dc4\u535a\u5e02","code":"370300","parent_code":370000},{"title":"\u67a3\u5e84\u5e02","code":"370400","parent_code":370000},{"title":"\u4e1c\u8425\u5e02","code":"370500","parent_code":370000},{"title":"\u70df\u53f0\u5e02","code":"370600","parent_code":370000},{"title":"\u6f4d\u574a\u5e02","code":"370700","parent_code":370000},{"title":"\u6d4e\u5b81\u5e02","code":"370800","parent_code":370000},{"title":"\u6cf0\u5b89\u5e02","code":"370900","parent_code":370000},{"title":"\u5a01\u6d77\u5e02","code":"371000","parent_code":370000},{"title":"\u65e5\u7167\u5e02","code":"371100","parent_code":370000},{"title":"\u4e34\u6c82\u5e02","code":"371300","parent_code":370000},{"title":"\u5fb7\u5dde\u5e02","code":"371400","parent_code":370000},{"title":"\u804a\u57ce\u5e02","code":"371500","parent_code":370000},{"title":"\u6ee8\u5dde\u5e02","code":"371600","parent_code":370000},{"title":"\u83cf\u6cfd\u5e02","code":"371700","parent_code":370000},{"title":"\u90d1\u5dde\u5e02","code":"410100","parent_code":410000},{"title":"\u5f00\u5c01\u5e02","code":"410200","parent_code":410000},{"title":"\u6d1b\u9633\u5e02","code":"410300","parent_code":410000},{"title":"\u5e73\u9876\u5c71\u5e02","code":"410400","parent_code":410000},{"title":"\u5b89\u9633\u5e02","code":"410500","parent_code":410000},{"title":"\u9e64\u58c1\u5e02","code":"410600","parent_code":410000},{"title":"\u65b0\u4e61\u5e02","code":"410700","parent_code":410000},{"title":"\u7126\u4f5c\u5e02","code":"410800","parent_code":410000},{"title":"\u6fee\u9633\u5e02","code":"410900","parent_code":410000},{"title":"\u8bb8\u660c\u5e02","code":"411000","parent_code":410000},{"title":"\u6f2f\u6cb3\u5e02","code":"411100","parent_code":410000},{"title":"\u4e09\u95e8\u5ce1\u5e02","code":"411200","parent_code":410000},{"title":"\u5357\u9633\u5e02","code":"411300","parent_code":410000},{"title":"\u5546\u4e18\u5e02","code":"411400","parent_code":410000},{"title":"\u4fe1\u9633\u5e02","code":"411500","parent_code":410000},{"title":"\u5468\u53e3\u5e02","code":"411600","parent_code":410000},{"title":"\u9a7b\u9a6c\u5e97\u5e02","code":"411700","parent_code":410000},{"title":"\u7701\u76f4\u8f96\u53bf","code":"419000","parent_code":410000},{"title":"\u6b66\u6c49\u5e02","code":"420100","parent_code":420000},{"title":"\u9ec4\u77f3\u5e02","code":"420200","parent_code":420000},{"title":"\u5341\u5830\u5e02","code":"420300","parent_code":420000},{"title":"\u5b9c\u660c\u5e02","code":"420500","parent_code":420000},{"title":"\u8944\u9633\u5e02","code":"420600","parent_code":420000},{"title":"\u9102\u5dde\u5e02","code":"420700","parent_code":420000},{"title":"\u8346\u95e8\u5e02","code":"420800","parent_code":420000},{"title":"\u5b5d\u611f\u5e02","code":"420900","parent_code":420000},{"title":"\u8346\u5dde\u5e02","code":"421000","parent_code":420000},{"title":"\u9ec4\u5188\u5e02","code":"421100","parent_code":420000},{"title":"\u54b8\u5b81\u5e02","code":"421200","parent_code":420000},{"title":"\u968f\u5dde\u5e02","code":"421300","parent_code":420000},{"title":"\u6069\u65bd\u571f\u5bb6\u65cf\u82d7\u65cf\u81ea\u6cbb\u5dde","code":"422800","parent_code":420000},{"title":"\u7701\u76f4\u8f96\u53bf","code":"429000","parent_code":420000},{"title":"\u957f\u6c99\u5e02","code":"430100","parent_code":430000},{"title":"\u682a\u6d32\u5e02","code":"430200","parent_code":430000},{"title":"\u6e58\u6f6d\u5e02","code":"430300","parent_code":430000},{"title":"\u8861\u9633\u5e02","code":"430400","parent_code":430000},{"title":"\u90b5\u9633\u5e02","code":"430500","parent_code":430000},{"title":"\u5cb3\u9633\u5e02","code":"430600","parent_code":430000},{"title":"\u5e38\u5fb7\u5e02","code":"430700","parent_code":430000},{"title":"\u5f20\u5bb6\u754c\u5e02","code":"430800","parent_code":430000},{"title":"\u76ca\u9633\u5e02","code":"430900","parent_code":430000},{"title":"\u90f4\u5dde\u5e02","code":"431000","parent_code":430000},{"title":"\u6c38\u5dde\u5e02","code":"431100","parent_code":430000},{"title":"\u6000\u5316\u5e02","code":"431200","parent_code":430000},{"title":"\u5a04\u5e95\u5e02","code":"431300","parent_code":430000},{"title":"\u6e58\u897f\u571f\u5bb6\u65cf\u82d7\u65cf\u81ea\u6cbb\u5dde","code":"433100","parent_code":430000},{"title":"\u5e7f\u5dde\u5e02","code":"440100","parent_code":440000},{"title":"\u97f6\u5173\u5e02","code":"440200","parent_code":440000},{"title":"\u6df1\u5733\u5e02","code":"440300","parent_code":440000},{"title":"\u73e0\u6d77\u5e02","code":"440400","parent_code":440000},{"title":"\u6c55\u5934\u5e02","code":"440500","parent_code":440000},{"title":"\u4f5b\u5c71\u5e02","code":"440600","parent_code":440000},{"title":"\u6c5f\u95e8\u5e02","code":"440700","parent_code":440000},{"title":"\u6e5b\u6c5f\u5e02","code":"440800","parent_code":440000},{"title":"\u8302\u540d\u5e02","code":"440900","parent_code":440000},{"title":"\u8087\u5e86\u5e02","code":"441200","parent_code":440000},{"title":"\u60e0\u5dde\u5e02","code":"441300","parent_code":440000},{"title":"\u6885\u5dde\u5e02","code":"441400","parent_code":440000},{"title":"\u6c55\u5c3e\u5e02","code":"441500","parent_code":440000},{"title":"\u6cb3\u6e90\u5e02","code":"441600","parent_code":440000},{"title":"\u9633\u6c5f\u5e02","code":"441700","parent_code":440000},{"title":"\u6e05\u8fdc\u5e02","code":"441800","parent_code":440000},{"title":"\u4e1c\u839e\u5e02","code":"441900","parent_code":440000},{"title":"\u4e2d\u5c71\u5e02","code":"442000","parent_code":440000},{"title":"\u6f6e\u5dde\u5e02","code":"445100","parent_code":440000},{"title":"\u63ed\u9633\u5e02","code":"445200","parent_code":440000},{"title":"\u4e91\u6d6e\u5e02","code":"445300","parent_code":440000},{"title":"\u5357\u5b81\u5e02","code":"450100","parent_code":450000},{"title":"\u67f3\u5dde\u5e02","code":"450200","parent_code":450000},{"title":"\u6842\u6797\u5e02","code":"450300","parent_code":450000},{"title":"\u68a7\u5dde\u5e02","code":"450400","parent_code":450000},{"title":"\u5317\u6d77\u5e02","code":"450500","parent_code":450000},{"title":"\u9632\u57ce\u6e2f\u5e02","code":"450600","parent_code":450000},{"title":"\u94a6\u5dde\u5e02","code":"450700","parent_code":450000},{"title":"\u8d35\u6e2f\u5e02","code":"450800","parent_code":450000},{"title":"\u7389\u6797\u5e02","code":"450900","parent_code":450000},{"title":"\u767e\u8272\u5e02","code":"451000","parent_code":450000},{"title":"\u8d3a\u5dde\u5e02","code":"451100","parent_code":450000},{"title":"\u6cb3\u6c60\u5e02","code":"451200","parent_code":450000},{"title":"\u6765\u5bbe\u5e02","code":"451300","parent_code":450000},{"title":"\u5d07\u5de6\u5e02","code":"451400","parent_code":450000},{"title":"\u6d77\u53e3\u5e02","code":"460100","parent_code":460000},{"title":"\u4e09\u4e9a\u5e02","code":"460200","parent_code":460000},{"title":"\u4e09\u6c99\u5e02","code":"460300","parent_code":460000},{"title":"\u510b\u5dde\u5e02","code":"460400","parent_code":460000},{"title":"\u7701\u76f4\u8f96\u53bf","code":"469000","parent_code":460000},{"title":"\u91cd\u5e86\u5e02","code":"500100","parent_code":500000},{"title":"\u53bf","code":"500200","parent_code":500000},{"title":"\u6210\u90fd\u5e02","code":"510100","parent_code":510000},{"title":"\u81ea\u8d21\u5e02","code":"510300","parent_code":510000},{"title":"\u6500\u679d\u82b1\u5e02","code":"510400","parent_code":510000},{"title":"\u6cf8\u5dde\u5e02","code":"510500","parent_code":510000},{"title":"\u5fb7\u9633\u5e02","code":"510600","parent_code":510000},{"title":"\u7ef5\u9633\u5e02","code":"510700","parent_code":510000},{"title":"\u5e7f\u5143\u5e02","code":"510800","parent_code":510000},{"title":"\u9042\u5b81\u5e02","code":"510900","parent_code":510000},{"title":"\u5185\u6c5f\u5e02","code":"511000","parent_code":510000},{"title":"\u4e50\u5c71\u5e02","code":"511100","parent_code":510000},{"title":"\u5357\u5145\u5e02","code":"511300","parent_code":510000},{"title":"\u7709\u5c71\u5e02","code":"511400","parent_code":510000},{"title":"\u5b9c\u5bbe\u5e02","code":"511500","parent_code":510000},{"title":"\u5e7f\u5b89\u5e02","code":"511600","parent_code":510000},{"title":"\u8fbe\u5dde\u5e02","code":"511700","parent_code":510000},{"title":"\u96c5\u5b89\u5e02","code":"511800","parent_code":510000},{"title":"\u5df4\u4e2d\u5e02","code":"511900","parent_code":510000},{"title":"\u8d44\u9633\u5e02","code":"512000","parent_code":510000},{"title":"\u963f\u575d\u85cf\u65cf\u7f8c\u65cf\u81ea\u6cbb\u5dde","code":"513200","parent_code":510000},{"title":"\u7518\u5b5c\u85cf\u65cf\u81ea\u6cbb\u5dde","code":"513300","parent_code":510000},{"title":"\u51c9\u5c71\u5f5d\u65cf\u81ea\u6cbb\u5dde","code":"513400","parent_code":510000},{"title":"\u8d35\u9633\u5e02","code":"520100","parent_code":520000},{"title":"\u516d\u76d8\u6c34\u5e02","code":"520200","parent_code":520000},{"title":"\u9075\u4e49\u5e02","code":"520300","parent_code":520000},{"title":"\u5b89\u987a\u5e02","code":"520400","parent_code":520000},{"title":"\u6bd5\u8282\u5e02","code":"520500","parent_code":520000},{"title":"\u94dc\u4ec1\u5e02","code":"520600","parent_code":520000},{"title":"\u9ed4\u897f\u5357\u5e03\u4f9d\u65cf\u82d7\u65cf\u81ea\u6cbb\u5dde","code":"522300","parent_code":520000},{"title":"\u9ed4\u4e1c\u5357\u82d7\u65cf\u4f97\u65cf\u81ea\u6cbb\u5dde","code":"522600","parent_code":520000},{"title":"\u9ed4\u5357\u5e03\u4f9d\u65cf\u82d7\u65cf\u81ea\u6cbb\u5dde","code":"522700","parent_code":520000},{"title":"\u6606\u660e\u5e02","code":"530100","parent_code":530000},{"title":"\u66f2\u9756\u5e02","code":"530300","parent_code":530000},{"title":"\u7389\u6eaa\u5e02","code":"530400","parent_code":530000},{"title":"\u4fdd\u5c71\u5e02","code":"530500","parent_code":530000},{"title":"\u662d\u901a\u5e02","code":"530600","parent_code":530000},{"title":"\u4e3d\u6c5f\u5e02","code":"530700","parent_code":530000},{"title":"\u666e\u6d31\u5e02","code":"530800","parent_code":530000},{"title":"\u4e34\u6ca7\u5e02","code":"530900","parent_code":530000},{"title":"\u695a\u96c4\u5f5d\u65cf\u81ea\u6cbb\u5dde","code":"532300","parent_code":530000},{"title":"\u7ea2\u6cb3\u54c8\u5c3c\u65cf\u5f5d\u65cf\u81ea\u6cbb\u5dde","code":"532500","parent_code":530000},{"title":"\u6587\u5c71\u58ee\u65cf\u82d7\u65cf\u81ea\u6cbb\u5dde","code":"532600","parent_code":530000},{"title":"\u897f\u53cc\u7248\u7eb3\u50a3\u65cf\u81ea\u6cbb\u5dde","code":"532800","parent_code":530000},{"title":"\u5927\u7406\u767d\u65cf\u81ea\u6cbb\u5dde","code":"532900","parent_code":530000},{"title":"\u5fb7\u5b8f\u50a3\u65cf\u666f\u9887\u65cf\u81ea\u6cbb\u5dde","code":"533100","parent_code":530000},{"title":"\u6012\u6c5f\u5088\u50f3\u65cf\u81ea\u6cbb\u5dde","code":"533300","parent_code":530000},{"title":"\u8fea\u5e86\u85cf\u65cf\u81ea\u6cbb\u5dde","code":"533400","parent_code":530000},{"title":"\u62c9\u8428\u5e02","code":"540100","parent_code":540000},{"title":"\u65e5\u5580\u5219\u5e02","code":"540200","parent_code":540000},{"title":"\u660c\u90fd\u5e02","code":"540300","parent_code":540000},{"title":"\u6797\u829d\u5e02","code":"540400","parent_code":540000},{"title":"\u5c71\u5357\u5e02","code":"540500","parent_code":540000},{"title":"\u90a3\u66f2\u5e02","code":"540600","parent_code":540000},{"title":"\u963f\u91cc\u5730\u533a","code":"542500","parent_code":540000},{"title":"\u897f\u5b89\u5e02","code":"610100","parent_code":610000},{"title":"\u94dc\u5ddd\u5e02","code":"610200","parent_code":610000},{"title":"\u5b9d\u9e21\u5e02","code":"610300","parent_code":610000},{"title":"\u54b8\u9633\u5e02","code":"610400","parent_code":610000},{"title":"\u6e2d\u5357\u5e02","code":"610500","parent_code":610000},{"title":"\u5ef6\u5b89\u5e02","code":"610600","parent_code":610000},{"title":"\u6c49\u4e2d\u5e02","code":"610700","parent_code":610000},{"title":"\u6986\u6797\u5e02","code":"610800","parent_code":610000},{"title":"\u5b89\u5eb7\u5e02","code":"610900","parent_code":610000},{"title":"\u5546\u6d1b\u5e02","code":"611000","parent_code":610000},{"title":"\u5170\u5dde\u5e02","code":"620100","parent_code":620000},{"title":"\u5609\u5cea\u5173\u5e02","code":"620200","parent_code":620000},{"title":"\u91d1\u660c\u5e02","code":"620300","parent_code":620000},{"title":"\u767d\u94f6\u5e02","code":"620400","parent_code":620000},{"title":"\u5929\u6c34\u5e02","code":"620500","parent_code":620000},{"title":"\u6b66\u5a01\u5e02","code":"620600","parent_code":620000},{"title":"\u5f20\u6396\u5e02","code":"620700","parent_code":620000},{"title":"\u5e73\u51c9\u5e02","code":"620800","parent_code":620000},{"title":"\u9152\u6cc9\u5e02","code":"620900","parent_code":620000},{"title":"\u5e86\u9633\u5e02","code":"621000","parent_code":620000},{"title":"\u5b9a\u897f\u5e02","code":"621100","parent_code":620000},{"title":"\u9647\u5357\u5e02","code":"621200","parent_code":620000},{"title":"\u4e34\u590f\u56de\u65cf\u81ea\u6cbb\u5dde","code":"622900","parent_code":620000},{"title":"\u7518\u5357\u85cf\u65cf\u81ea\u6cbb\u5dde","code":"623000","parent_code":620000},{"title":"\u897f\u5b81\u5e02","code":"630100","parent_code":630000},{"title":"\u6d77\u4e1c\u5e02","code":"630200","parent_code":630000},{"title":"\u6d77\u5317\u85cf\u65cf\u81ea\u6cbb\u5dde","code":"632200","parent_code":630000},{"title":"\u9ec4\u5357\u85cf\u65cf\u81ea\u6cbb\u5dde","code":"632300","parent_code":630000},{"title":"\u6d77\u5357\u85cf\u65cf\u81ea\u6cbb\u5dde","code":"632500","parent_code":630000},{"title":"\u679c\u6d1b\u85cf\u65cf\u81ea\u6cbb\u5dde","code":"632600","parent_code":630000},{"title":"\u7389\u6811\u85cf\u65cf\u81ea\u6cbb\u5dde","code":"632700","parent_code":630000},{"title":"\u6d77\u897f\u8499\u53e4\u65cf\u85cf\u65cf\u81ea\u6cbb\u5dde","code":"632800","parent_code":630000},{"title":"\u94f6\u5ddd\u5e02","code":"640100","parent_code":640000},{"title":"\u77f3\u5634\u5c71\u5e02","code":"640200","parent_code":640000},{"title":"\u5434\u5fe0\u5e02","code":"640300","parent_code":640000},{"title":"\u56fa\u539f\u5e02","code":"640400","parent_code":640000},{"title":"\u4e2d\u536b\u5e02","code":"640500","parent_code":640000},{"title":"\u4e4c\u9c81\u6728\u9f50\u5e02","code":"650100","parent_code":650000},{"title":"\u514b\u62c9\u739b\u4f9d\u5e02","code":"650200","parent_code":650000},{"title":"\u5410\u9c81\u756a\u5e02","code":"650400","parent_code":650000},{"title":"\u54c8\u5bc6\u5e02","code":"650500","parent_code":650000},{"title":"\u660c\u5409\u56de\u65cf\u81ea\u6cbb\u5dde","code":"652300","parent_code":650000},{"title":"\u535a\u5c14\u5854\u62c9\u8499\u53e4\u81ea\u6cbb\u5dde","code":"652700","parent_code":650000},{"title":"\u5df4\u97f3\u90ed\u695e\u8499\u53e4\u81ea\u6cbb\u5dde","code":"652800","parent_code":650000},{"title":"\u963f\u514b\u82cf\u5730\u533a","code":"652900","parent_code":650000},{"title":"\u514b\u5b5c\u52d2\u82cf\u67ef\u5c14\u514b\u5b5c\u81ea\u6cbb\u5dde","code":"653000","parent_code":650000},{"title":"\u5580\u4ec0\u5730\u533a","code":"653100","parent_code":650000},{"title":"\u548c\u7530\u5730\u533a","code":"653200","parent_code":650000},{"title":"\u4f0a\u7281\u54c8\u8428\u514b\u81ea\u6cbb\u5dde","code":"654000","parent_code":650000},{"title":"\u5854\u57ce\u5730\u533a","code":"654200","parent_code":650000},{"title":"\u963f\u52d2\u6cf0\u5730\u533a","code":"654300","parent_code":650000},{"title":"\u81ea\u6cbb\u533a\u76f4\u8f96\u53bf\u7ea7\u884c\u653f\u533a\u5212","code":"659000","parent_code":650000},{"title":"\u53f0\u5317\u5e02","code":"710100","parent_code":710000},{"title":"\u9ad8\u96c4\u5e02","code":"710200","parent_code":710000},{"title":"\u53f0\u5357\u5e02","code":"710300","parent_code":710000},{"title":"\u53f0\u4e2d\u5e02","code":"710400","parent_code":710000},{"title":"\u91d1\u95e8\u53bf","code":"710500","parent_code":710000},{"title":"\u5357\u6295\u53bf","code":"710600","parent_code":710000},{"title":"\u57fa\u9686\u5e02","code":"710700","parent_code":710000},{"title":"\u65b0\u7af9\u5e02","code":"710800","parent_code":710000},{"title":"\u5609\u4e49\u5e02","code":"710900","parent_code":710000},{"title":"\u65b0\u5317\u5e02","code":"711100","parent_code":710000},{"title":"\u5b9c\u5170\u53bf","code":"711200","parent_code":710000},{"title":"\u65b0\u7af9\u53bf","code":"711300","parent_code":710000},{"title":"\u6843\u56ed\u53bf","code":"711400","parent_code":710000},{"title":"\u82d7\u6817\u53bf","code":"711500","parent_code":710000},{"title":"\u5f70\u5316\u53bf","code":"711700","parent_code":710000},{"title":"\u5609\u4e49\u53bf","code":"711900","parent_code":710000},{"title":"\u4e91\u6797\u53bf","code":"712100","parent_code":710000},{"title":"\u5c4f\u4e1c\u53bf","code":"712400","parent_code":710000},{"title":"\u53f0\u4e1c\u53bf","code":"712500","parent_code":710000},{"title":"\u82b1\u83b2\u53bf","code":"712600","parent_code":710000},{"title":"\u6f8e\u6e56\u53bf","code":"712700","parent_code":710000},{"title":"\u8fde\u6c5f\u53bf","code":"712800","parent_code":710000},{"title":"\u9999\u6e2f\u5c9b","code":"810100","parent_code":810000},{"title":"\u4e5d\u9f99","code":"810200","parent_code":810000},{"title":"\u65b0\u754c","code":"810300","parent_code":810000},{"title":"\u6fb3\u95e8\u534a\u5c9b","code":"820100","parent_code":820000},{"title":"\u79bb\u5c9b","code":"820200","parent_code":820000},{"title":"\u4e1c\u57ce\u533a","code":"110101","parent_code":110100},{"title":"\u897f\u57ce\u533a","code":"110102","parent_code":110100},{"title":"\u671d\u9633\u533a","code":"110105","parent_code":110100},{"title":"\u4e30\u53f0\u533a","code":"110106","parent_code":110100},{"title":"\u77f3\u666f\u5c71\u533a","code":"110107","parent_code":110100},{"title":"\u6d77\u6dc0\u533a","code":"110108","parent_code":110100},{"title":"\u95e8\u5934\u6c9f\u533a","code":"110109","parent_code":110100},{"title":"\u623f\u5c71\u533a","code":"110111","parent_code":110100},{"title":"\u901a\u5dde\u533a","code":"110112","parent_code":110100},{"title":"\u987a\u4e49\u533a","code":"110113","parent_code":110100},{"title":"\u660c\u5e73\u533a","code":"110114","parent_code":110100},{"title":"\u5927\u5174\u533a","code":"110115","parent_code":110100},{"title":"\u6000\u67d4\u533a","code":"110116","parent_code":110100},{"title":"\u5e73\u8c37\u533a","code":"110117","parent_code":110100},{"title":"\u5bc6\u4e91\u533a","code":"110118","parent_code":110100},{"title":"\u5ef6\u5e86\u533a","code":"110119","parent_code":110100},{"title":"\u548c\u5e73\u533a","code":"120101","parent_code":120100},{"title":"\u6cb3\u4e1c\u533a","code":"120102","parent_code":120100},{"title":"\u6cb3\u897f\u533a","code":"120103","parent_code":120100},{"title":"\u5357\u5f00\u533a","code":"120104","parent_code":120100},{"title":"\u6cb3\u5317\u533a","code":"120105","parent_code":120100},{"title":"\u7ea2\u6865\u533a","code":"120106","parent_code":120100},{"title":"\u4e1c\u4e3d\u533a","code":"120110","parent_code":120100},{"title":"\u897f\u9752\u533a","code":"120111","parent_code":120100},{"title":"\u6d25\u5357\u533a","code":"120112","parent_code":120100},{"title":"\u5317\u8fb0\u533a","code":"120113","parent_code":120100},{"title":"\u6b66\u6e05\u533a","code":"120114","parent_code":120100},{"title":"\u5b9d\u577b\u533a","code":"120115","parent_code":120100},{"title":"\u6ee8\u6d77\u65b0\u533a","code":"120116","parent_code":120100},{"title":"\u5b81\u6cb3\u533a","code":"120117","parent_code":120100},{"title":"\u9759\u6d77\u533a","code":"120118","parent_code":120100},{"title":"\u84df\u5dde\u533a","code":"120119","parent_code":120100},{"title":"\u957f\u5b89\u533a","code":"130102","parent_code":130100},{"title":"\u6865\u897f\u533a","code":"130104","parent_code":130100},{"title":"\u65b0\u534e\u533a","code":"130105","parent_code":130100},{"title":"\u4e95\u9649\u77ff\u533a","code":"130107","parent_code":130100},{"title":"\u88d5\u534e\u533a","code":"130108","parent_code":130100},{"title":"\u85c1\u57ce\u533a","code":"130109","parent_code":130100},{"title":"\u9e7f\u6cc9\u533a","code":"130110","parent_code":130100},{"title":"\u683e\u57ce\u533a","code":"130111","parent_code":130100},{"title":"\u4e95\u9649\u53bf","code":"130121","parent_code":130100},{"title":"\u6b63\u5b9a\u53bf","code":"130123","parent_code":130100},{"title":"\u884c\u5510\u53bf","code":"130125","parent_code":130100},{"title":"\u7075\u5bff\u53bf","code":"130126","parent_code":130100},{"title":"\u9ad8\u9091\u53bf","code":"130127","parent_code":130100},{"title":"\u6df1\u6cfd\u53bf","code":"130128","parent_code":130100},{"title":"\u8d5e\u7687\u53bf","code":"130129","parent_code":130100},{"title":"\u65e0\u6781\u53bf","code":"130130","parent_code":130100},{"title":"\u5e73\u5c71\u53bf","code":"130131","parent_code":130100},{"title":"\u5143\u6c0f\u53bf","code":"130132","parent_code":130100},{"title":"\u8d75\u53bf","code":"130133","parent_code":130100},{"title":"\u77f3\u5bb6\u5e84\u9ad8\u65b0\u6280\u672f\u4ea7\u4e1a\u5f00\u53d1\u533a","code":"130171","parent_code":130100},{"title":"\u77f3\u5bb6\u5e84\u5faa\u73af\u5316\u5de5\u56ed\u533a","code":"130172","parent_code":130100},{"title":"\u8f9b\u96c6\u5e02","code":"130181","parent_code":130100},{"title":"\u664b\u5dde\u5e02","code":"130183","parent_code":130100},{"title":"\u65b0\u4e50\u5e02","code":"130184","parent_code":130100},{"title":"\u8def\u5357\u533a","code":"130202","parent_code":130200},{"title":"\u8def\u5317\u533a","code":"130203","parent_code":130200},{"title":"\u53e4\u51b6\u533a","code":"130204","parent_code":130200},{"title":"\u5f00\u5e73\u533a","code":"130205","parent_code":130200},{"title":"\u4e30\u5357\u533a","code":"130207","parent_code":130200},{"title":"\u4e30\u6da6\u533a","code":"130208","parent_code":130200},{"title":"\u66f9\u5983\u7538\u533a","code":"130209","parent_code":130200},{"title":"\u6ee6\u5357\u53bf","code":"130224","parent_code":130200},{"title":"\u4e50\u4ead\u53bf","code":"130225","parent_code":130200},{"title":"\u8fc1\u897f\u53bf","code":"130227","parent_code":130200},{"title":"\u7389\u7530\u53bf","code":"130229","parent_code":130200},{"title":"\u5510\u5c71\u9ad8\u65b0\u6280\u672f\u4ea7\u4e1a\u5f00\u53d1\u533a","code":"130273","parent_code":130200},{"title":"\u6cb3\u5317\u5510\u5c71\u6d77\u6e2f\u7ecf\u6d4e\u5f00\u53d1\u533a","code":"130274","parent_code":130200},{"title":"\u9075\u5316\u5e02","code":"130281","parent_code":130200},{"title":"\u8fc1\u5b89\u5e02","code":"130283","parent_code":130200},{"title":"\u6ee6\u5dde\u5e02","code":"130284","parent_code":130200},{"title":"\u6d77\u6e2f\u533a","code":"130302","parent_code":130300},{"title":"\u5c71\u6d77\u5173\u533a","code":"130303","parent_code":130300},{"title":"\u5317\u6234\u6cb3\u533a","code":"130304","parent_code":130300},{"title":"\u629a\u5b81\u533a","code":"130306","parent_code":130300},{"title":"\u9752\u9f99\u6ee1\u65cf\u81ea\u6cbb\u53bf","code":"130321","parent_code":130300},{"title":"\u660c\u9ece\u53bf","code":"130322","parent_code":130300},{"title":"\u5362\u9f99\u53bf","code":"130324","parent_code":130300},{"title":"\u79e6\u7687\u5c9b\u5e02\u7ecf\u6d4e\u6280\u672f\u5f00\u53d1\u533a","code":"130371","parent_code":130300},{"title":"\u5317\u6234\u6cb3\u65b0\u533a","code":"130372","parent_code":130300},{"title":"\u7ecf\u6d4e\u6280\u672f\u5f00\u53d1\u533a","code":"130390","parent_code":130300},{"title":"\u90af\u5c71\u533a","code":"130402","parent_code":130400},{"title":"\u4e1b\u53f0\u533a","code":"130403","parent_code":130400},{"title":"\u590d\u5174\u533a","code":"130404","parent_code":130400},{"title":"\u5cf0\u5cf0\u77ff\u533a","code":"130406","parent_code":130400},{"title":"\u80a5\u4e61\u533a","code":"130407","parent_code":130400},{"title":"\u6c38\u5e74\u533a","code":"130408","parent_code":130400},{"title":"\u4e34\u6f33\u53bf","code":"130423","parent_code":130400},{"title":"\u6210\u5b89\u53bf","code":"130424","parent_code":130400},{"title":"\u5927\u540d\u53bf","code":"130425","parent_code":130400},{"title":"\u6d89\u53bf","code":"130426","parent_code":130400},{"title":"\u78c1\u53bf","code":"130427","parent_code":130400},{"title":"\u90b1\u53bf","code":"130430","parent_code":130400},{"title":"\u9e21\u6cfd\u53bf","code":"130431","parent_code":130400},{"title":"\u5e7f\u5e73\u53bf","code":"130432","parent_code":130400},{"title":"\u9986\u9676\u53bf","code":"130433","parent_code":130400},{"title":"\u9b4f\u53bf","code":"130434","parent_code":130400},{"title":"\u66f2\u5468\u53bf","code":"130435","parent_code":130400},{"title":"\u90af\u90f8\u7ecf\u6d4e\u6280\u672f\u5f00\u53d1\u533a","code":"130471","parent_code":130400},{"title":"\u90af\u90f8\u5180\u5357\u65b0\u533a","code":"130473","parent_code":130400},{"title":"\u6b66\u5b89\u5e02","code":"130481","parent_code":130400},{"title":"\u6865\u4e1c\u533a","code":"130502","parent_code":130500},{"title":"\u6865\u897f\u533a","code":"130503","parent_code":130500},{"title":"\u90a2\u53f0\u53bf","code":"130521","parent_code":130500},{"title":"\u4e34\u57ce\u53bf","code":"130522","parent_code":130500},{"title":"\u5185\u4e18\u53bf","code":"130523","parent_code":130500},{"title":"\u67cf\u4e61\u53bf","code":"130524","parent_code":130500},{"title":"\u9686\u5c27\u53bf","code":"130525","parent_code":130500},{"title":"\u4efb\u53bf","code":"130526","parent_code":130500},{"title":"\u5357\u548c\u53bf","code":"130527","parent_code":130500},{"title":"\u5b81\u664b\u53bf","code":"130528","parent_code":130500},{"title":"\u5de8\u9e7f\u53bf","code":"130529","parent_code":130500},{"title":"\u65b0\u6cb3\u53bf","code":"130530","parent_code":130500},{"title":"\u5e7f\u5b97\u53bf","code":"130531","parent_code":130500},{"title":"\u5e73\u4e61\u53bf","code":"130532","parent_code":130500},{"title":"\u5a01\u53bf","code":"130533","parent_code":130500},{"title":"\u6e05\u6cb3\u53bf","code":"130534","parent_code":130500},{"title":"\u4e34\u897f\u53bf","code":"130535","parent_code":130500},{"title":"\u6cb3\u5317\u90a2\u53f0\u7ecf\u6d4e\u5f00\u53d1\u533a","code":"130571","parent_code":130500},{"title":"\u5357\u5bab\u5e02","code":"130581","parent_code":130500},{"title":"\u6c99\u6cb3\u5e02","code":"130582","parent_code":130500},{"title":"\u7ade\u79c0\u533a","code":"130602","parent_code":130600},{"title":"\u83b2\u6c60\u533a","code":"130606","parent_code":130600},{"title":"\u6ee1\u57ce\u533a","code":"130607","parent_code":130600},{"title":"\u6e05\u82d1\u533a","code":"130608","parent_code":130600},{"title":"\u5f90\u6c34\u533a","code":"130609","parent_code":130600},{"title":"\u6d9e\u6c34\u53bf","code":"130623","parent_code":130600},{"title":"\u961c\u5e73\u53bf","code":"130624","parent_code":130600},{"title":"\u5b9a\u5174\u53bf","code":"130626","parent_code":130600},{"title":"\u5510\u53bf","code":"130627","parent_code":130600},{"title":"\u9ad8\u9633\u53bf","code":"130628","parent_code":130600},{"title":"\u5bb9\u57ce\u53bf","code":"130629","parent_code":130600},{"title":"\u6d9e\u6e90\u53bf","code":"130630","parent_code":130600},{"title":"\u671b\u90fd\u53bf","code":"130631","parent_code":130600},{"title":"\u5b89\u65b0\u53bf","code":"130632","parent_code":130600},{"title":"\u6613\u53bf","code":"130633","parent_code":130600},{"title":"\u66f2\u9633\u53bf","code":"130634","parent_code":130600},{"title":"\u8821\u53bf","code":"130635","parent_code":130600},{"title":"\u987a\u5e73\u53bf","code":"130636","parent_code":130600},{"title":"\u535a\u91ce\u53bf","code":"130637","parent_code":130600},{"title":"\u96c4\u53bf","code":"130638","parent_code":130600},{"title":"\u4fdd\u5b9a\u9ad8\u65b0\u6280\u672f\u4ea7\u4e1a\u5f00\u53d1\u533a","code":"130671","parent_code":130600},{"title":"\u4fdd\u5b9a\u767d\u6c9f\u65b0\u57ce","code":"130672","parent_code":130600},{"title":"\u6dbf\u5dde\u5e02","code":"130681","parent_code":130600},{"title":"\u5b9a\u5dde\u5e02","code":"130682","parent_code":130600},{"title":"\u5b89\u56fd\u5e02","code":"130683","parent_code":130600},{"title":"\u9ad8\u7891\u5e97\u5e02","code":"130684","parent_code":130600},{"title":"\u6865\u4e1c\u533a","code":"130702","parent_code":130700},{"title":"\u6865\u897f\u533a","code":"130703","parent_code":130700},{"title":"\u5ba3\u5316\u533a","code":"130705","parent_code":130700},{"title":"\u4e0b\u82b1\u56ed\u533a","code":"130706","parent_code":130700},{"title":"\u4e07\u5168\u533a","code":"130708","parent_code":130700},{"title":"\u5d07\u793c\u533a","code":"130709","parent_code":130700},{"title":"\u5f20\u5317\u53bf","code":"130722","parent_code":130700},{"title":"\u5eb7\u4fdd\u53bf","code":"130723","parent_code":130700},{"title":"\u6cbd\u6e90\u53bf","code":"130724","parent_code":130700},{"title":"\u5c1a\u4e49\u53bf","code":"130725","parent_code":130700},{"title":"\u851a\u53bf","code":"130726","parent_code":130700},{"title":"\u9633\u539f\u53bf","code":"130727","parent_code":130700},{"title":"\u6000\u5b89\u53bf","code":"130728","parent_code":130700},{"title":"\u6000\u6765\u53bf","code":"130730","parent_code":130700},{"title":"\u6dbf\u9e7f\u53bf","code":"130731","parent_code":130700},{"title":"\u8d64\u57ce\u53bf","code":"130732","parent_code":130700},{"title":"\u5f20\u5bb6\u53e3\u5e02\u5bdf\u5317\u7ba1\u7406\u533a","code":"130772","parent_code":130700},{"title":"\u53cc\u6865\u533a","code":"130802","parent_code":130800},{"title":"\u53cc\u6ee6\u533a","code":"130803","parent_code":130800},{"title":"\u9e70\u624b\u8425\u5b50\u77ff\u533a","code":"130804","parent_code":130800},{"title":"\u627f\u5fb7\u53bf","code":"130821","parent_code":130800},{"title":"\u5174\u9686\u53bf","code":"130822","parent_code":130800},{"title":"\u6ee6\u5e73\u53bf","code":"130824","parent_code":130800},{"title":"\u9686\u5316\u53bf","code":"130825","parent_code":130800},{"title":"\u4e30\u5b81\u6ee1\u65cf\u81ea\u6cbb\u53bf","code":"130826","parent_code":130800},{"title":"\u5bbd\u57ce\u6ee1\u65cf\u81ea\u6cbb\u53bf","code":"130827","parent_code":130800},{"title":"\u56f4\u573a\u6ee1\u65cf\u8499\u53e4\u65cf\u81ea\u6cbb\u53bf","code":"130828","parent_code":130800},{"title":"\u627f\u5fb7\u9ad8\u65b0\u6280\u672f\u4ea7\u4e1a\u5f00\u53d1\u533a","code":"130871","parent_code":130800},{"title":"\u5e73\u6cc9\u5e02","code":"130881","parent_code":130800},{"title":"\u65b0\u534e\u533a","code":"130902","parent_code":130900},{"title":"\u8fd0\u6cb3\u533a","code":"130903","parent_code":130900},{"title":"\u6ca7\u53bf","code":"130921","parent_code":130900},{"title":"\u9752\u53bf","code":"130922","parent_code":130900},{"title":"\u4e1c\u5149\u53bf","code":"130923","parent_code":130900},{"title":"\u6d77\u5174\u53bf","code":"130924","parent_code":130900},{"title":"\u76d0\u5c71\u53bf","code":"130925","parent_code":130900},{"title":"\u8083\u5b81\u53bf","code":"130926","parent_code":130900},{"title":"\u5357\u76ae\u53bf","code":"130927","parent_code":130900},{"title":"\u5434\u6865\u53bf","code":"130928","parent_code":130900},{"title":"\u732e\u53bf","code":"130929","parent_code":130900},{"title":"\u5b5f\u6751\u56de\u65cf\u81ea\u6cbb\u53bf","code":"130930","parent_code":130900},{"title":"\u6cb3\u5317\u6ca7\u5dde\u7ecf\u6d4e\u5f00\u53d1\u533a","code":"130971","parent_code":130900},{"title":"\u6ca7\u5dde\u9ad8\u65b0\u6280\u672f\u4ea7\u4e1a\u5f00\u53d1\u533a","code":"130972","parent_code":130900},{"title":"\u6ca7\u5dde\u6e24\u6d77\u65b0\u533a","code":"130973","parent_code":130900},{"title":"\u6cca\u5934\u5e02","code":"130981","parent_code":130900},{"title":"\u4efb\u4e18\u5e02","code":"130982","parent_code":130900},{"title":"\u9ec4\u9a85\u5e02","code":"130983","parent_code":130900},{"title":"\u6cb3\u95f4\u5e02","code":"130984","parent_code":130900},{"title":"\u5b89\u6b21\u533a","code":"131002","parent_code":131000},{"title":"\u5e7f\u9633\u533a","code":"131003","parent_code":131000},{"title":"\u56fa\u5b89\u53bf","code":"131022","parent_code":131000},{"title":"\u6c38\u6e05\u53bf","code":"131023","parent_code":131000},{"title":"\u9999\u6cb3\u53bf","code":"131024","parent_code":131000},{"title":"\u5927\u57ce\u53bf","code":"131025","parent_code":131000},{"title":"\u6587\u5b89\u53bf","code":"131026","parent_code":131000},{"title":"\u5927\u5382\u56de\u65cf\u81ea\u6cbb\u53bf","code":"131028","parent_code":131000},{"title":"\u5eca\u574a\u7ecf\u6d4e\u6280\u672f\u5f00\u53d1\u533a","code":"131071","parent_code":131000},{"title":"\u9738\u5dde\u5e02","code":"131081","parent_code":131000},{"title":"\u4e09\u6cb3\u5e02","code":"131082","parent_code":131000},{"title":"\u5f00\u53d1\u533a","code":"131090","parent_code":131000},{"title":"\u6843\u57ce\u533a","code":"131102","parent_code":131100},{"title":"\u5180\u5dde\u533a","code":"131103","parent_code":131100},{"title":"\u67a3\u5f3a\u53bf","code":"131121","parent_code":131100},{"title":"\u6b66\u9091\u53bf","code":"131122","parent_code":131100},{"title":"\u6b66\u5f3a\u53bf","code":"131123","parent_code":131100},{"title":"\u9976\u9633\u53bf","code":"131124","parent_code":131100},{"title":"\u5b89\u5e73\u53bf","code":"131125","parent_code":131100},{"title":"\u6545\u57ce\u53bf","code":"131126","parent_code":131100},{"title":"\u666f\u53bf","code":"131127","parent_code":131100},{"title":"\u961c\u57ce\u53bf","code":"131128","parent_code":131100},{"title":"\u6cb3\u5317\u8861\u6c34\u7ecf\u6d4e\u5f00\u53d1\u533a","code":"131171","parent_code":131100},{"title":"\u8861\u6c34\u6ee8\u6e56\u65b0\u533a","code":"131172","parent_code":131100},{"title":"\u6df1\u5dde\u5e02","code":"131182","parent_code":131100},{"title":"\u5c0f\u5e97\u533a","code":"140105","parent_code":140100},{"title":"\u8fce\u6cfd\u533a","code":"140106","parent_code":140100},{"title":"\u674f\u82b1\u5cad\u533a","code":"140107","parent_code":140100},{"title":"\u5c16\u8349\u576a\u533a","code":"140108","parent_code":140100},{"title":"\u4e07\u67cf\u6797\u533a","code":"140109","parent_code":140100},{"title":"\u664b\u6e90\u533a","code":"140110","parent_code":140100},{"title":"\u6e05\u5f90\u53bf","code":"140121","parent_code":140100},{"title":"\u9633\u66f2\u53bf","code":"140122","parent_code":140100},{"title":"\u5a04\u70e6\u53bf","code":"140123","parent_code":140100},{"title":"\u53e4\u4ea4\u5e02","code":"140181","parent_code":140100},{"title":"\u65b0\u8363\u533a","code":"140212","parent_code":140200},{"title":"\u5e73\u57ce\u533a","code":"140213","parent_code":140200},{"title":"\u4e91\u5188\u533a","code":"140214","parent_code":140200},{"title":"\u4e91\u5dde\u533a","code":"140215","parent_code":140200},{"title":"\u9633\u9ad8\u53bf","code":"140221","parent_code":140200},{"title":"\u5929\u9547\u53bf","code":"140222","parent_code":140200},{"title":"\u5e7f\u7075\u53bf","code":"140223","parent_code":140200},{"title":"\u7075\u4e18\u53bf","code":"140224","parent_code":140200},{"title":"\u6d51\u6e90\u53bf","code":"140225","parent_code":140200},{"title":"\u5de6\u4e91\u53bf","code":"140226","parent_code":140200},{"title":"\u5c71\u897f\u5927\u540c\u7ecf\u6d4e\u5f00\u53d1\u533a","code":"140271","parent_code":140200},{"title":"\u57ce\u533a","code":"140302","parent_code":140300},{"title":"\u77ff\u533a","code":"140303","parent_code":140300},{"title":"\u90ca\u533a","code":"140311","parent_code":140300},{"title":"\u5e73\u5b9a\u53bf","code":"140321","parent_code":140300},{"title":"\u76c2\u53bf","code":"140322","parent_code":140300},{"title":"\u6f5e\u5dde\u533a","code":"140403","parent_code":140400},{"title":"\u4e0a\u515a\u533a","code":"140404","parent_code":140400},{"title":"\u5c6f\u7559\u533a","code":"140405","parent_code":140400},{"title":"\u6f5e\u57ce\u533a","code":"140406","parent_code":140400},{"title":"\u8944\u57a3\u53bf","code":"140423","parent_code":140400},{"title":"\u5e73\u987a\u53bf","code":"140425","parent_code":140400},{"title":"\u9ece\u57ce\u53bf","code":"140426","parent_code":140400},{"title":"\u58f6\u5173\u53bf","code":"140427","parent_code":140400},{"title":"\u957f\u5b50\u53bf","code":"140428","parent_code":140400},{"title":"\u6b66\u4e61\u53bf","code":"140429","parent_code":140400},{"title":"\u6c81\u53bf","code":"140430","parent_code":140400},{"title":"\u6c81\u6e90\u53bf","code":"140431","parent_code":140400},{"title":"\u5c71\u897f\u957f\u6cbb\u9ad8\u65b0\u6280\u672f\u4ea7\u4e1a\u56ed\u533a","code":"140471","parent_code":140400},{"title":"\u57ce\u533a","code":"140502","parent_code":140500},{"title":"\u6c81\u6c34\u53bf","code":"140521","parent_code":140500},{"title":"\u9633\u57ce\u53bf","code":"140522","parent_code":140500},{"title":"\u9675\u5ddd\u53bf","code":"140524","parent_code":140500},{"title":"\u6cfd\u5dde\u53bf","code":"140525","parent_code":140500},{"title":"\u9ad8\u5e73\u5e02","code":"140581","parent_code":140500},{"title":"\u6714\u57ce\u533a","code":"140602","parent_code":140600},{"title":"\u5e73\u9c81\u533a","code":"140603","parent_code":140600},{"title":"\u5c71\u9634\u53bf","code":"140621","parent_code":140600},{"title":"\u5e94\u53bf","code":"140622","parent_code":140600},{"title":"\u53f3\u7389\u53bf","code":"140623","parent_code":140600},{"title":"\u5c71\u897f\u6714\u5dde\u7ecf\u6d4e\u5f00\u53d1\u533a","code":"140671","parent_code":140600},{"title":"\u6000\u4ec1\u5e02","code":"140681","parent_code":140600},{"title":"\u6986\u6b21\u533a","code":"140702","parent_code":140700},{"title":"\u592a\u8c37\u533a","code":"140703","parent_code":140700},{"title":"\u6986\u793e\u53bf","code":"140721","parent_code":140700},{"title":"\u5de6\u6743\u53bf","code":"140722","parent_code":140700},{"title":"\u548c\u987a\u53bf","code":"140723","parent_code":140700},{"title":"\u6614\u9633\u53bf","code":"140724","parent_code":140700},{"title":"\u5bff\u9633\u53bf","code":"140725","parent_code":140700},{"title":"\u7941\u53bf","code":"140727","parent_code":140700},{"title":"\u5e73\u9065\u53bf","code":"140728","parent_code":140700},{"title":"\u7075\u77f3\u53bf","code":"140729","parent_code":140700},{"title":"\u4ecb\u4f11\u5e02","code":"140781","parent_code":140700},{"title":"\u76d0\u6e56\u533a","code":"140802","parent_code":140800},{"title":"\u4e34\u7317\u53bf","code":"140821","parent_code":140800},{"title":"\u4e07\u8363\u53bf","code":"140822","parent_code":140800},{"title":"\u95fb\u559c\u53bf","code":"140823","parent_code":140800},{"title":"\u7a37\u5c71\u53bf","code":"140824","parent_code":140800},{"title":"\u65b0\u7edb\u53bf","code":"140825","parent_code":140800},{"title":"\u7edb\u53bf","code":"140826","parent_code":140800},{"title":"\u57a3\u66f2\u53bf","code":"140827","parent_code":140800},{"title":"\u590f\u53bf","code":"140828","parent_code":140800},{"title":"\u5e73\u9646\u53bf","code":"140829","parent_code":140800},{"title":"\u82ae\u57ce\u53bf","code":"140830","parent_code":140800},{"title":"\u6c38\u6d4e\u5e02","code":"140881","parent_code":140800},{"title":"\u6cb3\u6d25\u5e02","code":"140882","parent_code":140800},{"title":"\u5ffb\u5e9c\u533a","code":"140902","parent_code":140900},{"title":"\u5b9a\u8944\u53bf","code":"140921","parent_code":140900},{"title":"\u4e94\u53f0\u53bf","code":"140922","parent_code":140900},{"title":"\u4ee3\u53bf","code":"140923","parent_code":140900},{"title":"\u7e41\u5cd9\u53bf","code":"140924","parent_code":140900},{"title":"\u5b81\u6b66\u53bf","code":"140925","parent_code":140900},{"title":"\u9759\u4e50\u53bf","code":"140926","parent_code":140900},{"title":"\u795e\u6c60\u53bf","code":"140927","parent_code":140900},{"title":"\u4e94\u5be8\u53bf","code":"140928","parent_code":140900},{"title":"\u5ca2\u5c9a\u53bf","code":"140929","parent_code":140900},{"title":"\u6cb3\u66f2\u53bf","code":"140930","parent_code":140900},{"title":"\u4fdd\u5fb7\u53bf","code":"140931","parent_code":140900},{"title":"\u504f\u5173\u53bf","code":"140932","parent_code":140900},{"title":"\u4e94\u53f0\u5c71\u98ce\u666f\u540d\u80dc\u533a","code":"140971","parent_code":140900},{"title":"\u539f\u5e73\u5e02","code":"140981","parent_code":140900},{"title":"\u5c27\u90fd\u533a","code":"141002","parent_code":141000},{"title":"\u66f2\u6c83\u53bf","code":"141021","parent_code":141000},{"title":"\u7ffc\u57ce\u53bf","code":"141022","parent_code":141000},{"title":"\u8944\u6c7e\u53bf","code":"141023","parent_code":141000},{"title":"\u6d2a\u6d1e\u53bf","code":"141024","parent_code":141000},{"title":"\u53e4\u53bf","code":"141025","parent_code":141000},{"title":"\u5b89\u6cfd\u53bf","code":"141026","parent_code":141000},{"title":"\u6d6e\u5c71\u53bf","code":"141027","parent_code":141000},{"title":"\u5409\u53bf","code":"141028","parent_code":141000},{"title":"\u4e61\u5b81\u53bf","code":"141029","parent_code":141000},{"title":"\u5927\u5b81\u53bf","code":"141030","parent_code":141000},{"title":"\u96b0\u53bf","code":"141031","parent_code":141000},{"title":"\u6c38\u548c\u53bf","code":"141032","parent_code":141000},{"title":"\u84b2\u53bf","code":"141033","parent_code":141000},{"title":"\u6c7e\u897f\u53bf","code":"141034","parent_code":141000},{"title":"\u4faf\u9a6c\u5e02","code":"141081","parent_code":141000},{"title":"\u970d\u5dde\u5e02","code":"141082","parent_code":141000},{"title":"\u79bb\u77f3\u533a","code":"141102","parent_code":141100},{"title":"\u6587\u6c34\u53bf","code":"141121","parent_code":141100},{"title":"\u4ea4\u57ce\u53bf","code":"141122","parent_code":141100},{"title":"\u5174\u53bf","code":"141123","parent_code":141100},{"title":"\u4e34\u53bf","code":"141124","parent_code":141100},{"title":"\u67f3\u6797\u53bf","code":"141125","parent_code":141100},{"title":"\u77f3\u697c\u53bf","code":"141126","parent_code":141100},{"title":"\u5c9a\u53bf","code":"141127","parent_code":141100},{"title":"\u65b9\u5c71\u53bf","code":"141128","parent_code":141100},{"title":"\u4e2d\u9633\u53bf","code":"141129","parent_code":141100},{"title":"\u4ea4\u53e3\u53bf","code":"141130","parent_code":141100},{"title":"\u5b5d\u4e49\u5e02","code":"141181","parent_code":141100},{"title":"\u6c7e\u9633\u5e02","code":"141182","parent_code":141100},{"title":"\u65b0\u57ce\u533a","code":"150102","parent_code":150100},{"title":"\u56de\u6c11\u533a","code":"150103","parent_code":150100},{"title":"\u7389\u6cc9\u533a","code":"150104","parent_code":150100},{"title":"\u8d5b\u7f55\u533a","code":"150105","parent_code":150100},{"title":"\u571f\u9ed8\u7279\u5de6\u65d7","code":"150121","parent_code":150100},{"title":"\u6258\u514b\u6258\u53bf","code":"150122","parent_code":150100},{"title":"\u548c\u6797\u683c\u5c14\u53bf","code":"150123","parent_code":150100},{"title":"\u6e05\u6c34\u6cb3\u53bf","code":"150124","parent_code":150100},{"title":"\u6b66\u5ddd\u53bf","code":"150125","parent_code":150100},{"title":"\u547c\u548c\u6d69\u7279\u7ecf\u6d4e\u6280\u672f\u5f00\u53d1\u533a","code":"150172","parent_code":150100},{"title":"\u4e1c\u6cb3\u533a","code":"150202","parent_code":150200},{"title":"\u6606\u90fd\u4ed1\u533a","code":"150203","parent_code":150200},{"title":"\u9752\u5c71\u533a","code":"150204","parent_code":150200},{"title":"\u77f3\u62d0\u533a","code":"150205","parent_code":150200},{"title":"\u767d\u4e91\u9102\u535a\u77ff\u533a","code":"150206","parent_code":150200},{"title":"\u4e5d\u539f\u533a","code":"150207","parent_code":150200},{"title":"\u571f\u9ed8\u7279\u53f3\u65d7","code":"150221","parent_code":150200},{"title":"\u56fa\u9633\u53bf","code":"150222","parent_code":150200},{"title":"\u8fbe\u5c14\u7f55\u8302\u660e\u5b89\u8054\u5408\u65d7","code":"150223","parent_code":150200},{"title":"\u5305\u5934\u7a00\u571f\u9ad8\u65b0\u6280\u672f\u4ea7\u4e1a\u5f00\u53d1\u533a","code":"150271","parent_code":150200},{"title":"\u6d77\u52c3\u6e7e\u533a","code":"150302","parent_code":150300},{"title":"\u6d77\u5357\u533a","code":"150303","parent_code":150300},{"title":"\u4e4c\u8fbe\u533a","code":"150304","parent_code":150300},{"title":"\u7ea2\u5c71\u533a","code":"150402","parent_code":150400},{"title":"\u5143\u5b9d\u5c71\u533a","code":"150403","parent_code":150400},{"title":"\u677e\u5c71\u533a","code":"150404","parent_code":150400},{"title":"\u963f\u9c81\u79d1\u5c14\u6c81\u65d7","code":"150421","parent_code":150400},{"title":"\u5df4\u6797\u5de6\u65d7","code":"150422","parent_code":150400},{"title":"\u5df4\u6797\u53f3\u65d7","code":"150423","parent_code":150400},{"title":"\u6797\u897f\u53bf","code":"150424","parent_code":150400},{"title":"\u514b\u4ec0\u514b\u817e\u65d7","code":"150425","parent_code":150400},{"title":"\u7fc1\u725b\u7279\u65d7","code":"150426","parent_code":150400},{"title":"\u5580\u5587\u6c81\u65d7","code":"150428","parent_code":150400},{"title":"\u5b81\u57ce\u53bf","code":"150429","parent_code":150400},{"title":"\u6556\u6c49\u65d7","code":"150430","parent_code":150400},{"title":"\u79d1\u5c14\u6c81\u533a","code":"150502","parent_code":150500},{"title":"\u79d1\u5c14\u6c81\u5de6\u7ffc\u4e2d\u65d7","code":"150521","parent_code":150500},{"title":"\u79d1\u5c14\u6c81\u5de6\u7ffc\u540e\u65d7","code":"150522","parent_code":150500},{"title":"\u5f00\u9c81\u53bf","code":"150523","parent_code":150500},{"title":"\u5e93\u4f26\u65d7","code":"150524","parent_code":150500},{"title":"\u5948\u66fc\u65d7","code":"150525","parent_code":150500},{"title":"\u624e\u9c81\u7279\u65d7","code":"150526","parent_code":150500},{"title":"\u901a\u8fbd\u7ecf\u6d4e\u6280\u672f\u5f00\u53d1\u533a","code":"150571","parent_code":150500},{"title":"\u970d\u6797\u90ed\u52d2\u5e02","code":"150581","parent_code":150500},{"title":"\u4e1c\u80dc\u533a","code":"150602","parent_code":150600},{"title":"\u5eb7\u5df4\u4ec0\u533a","code":"150603","parent_code":150600},{"title":"\u8fbe\u62c9\u7279\u65d7","code":"150621","parent_code":150600},{"title":"\u51c6\u683c\u5c14\u65d7","code":"150622","parent_code":150600},{"title":"\u9102\u6258\u514b\u524d\u65d7","code":"150623","parent_code":150600},{"title":"\u9102\u6258\u514b\u65d7","code":"150624","parent_code":150600},{"title":"\u676d\u9526\u65d7","code":"150625","parent_code":150600},{"title":"\u4e4c\u5ba1\u65d7","code":"150626","parent_code":150600},{"title":"\u4f0a\u91d1\u970d\u6d1b\u65d7","code":"150627","parent_code":150600},{"title":"\u6d77\u62c9\u5c14\u533a","code":"150702","parent_code":150700},{"title":"\u624e\u8d49\u8bfa\u5c14\u533a","code":"150703","parent_code":150700},{"title":"\u963f\u8363\u65d7","code":"150721","parent_code":150700},{"title":"\u83ab\u529b\u8fbe\u74e6\u8fbe\u65a1\u5c14\u65cf\u81ea\u6cbb\u65d7","code":"150722","parent_code":150700},{"title":"\u9102\u4f26\u6625\u81ea\u6cbb\u65d7","code":"150723","parent_code":150700},{"title":"\u9102\u6e29\u514b\u65cf\u81ea\u6cbb\u65d7","code":"150724","parent_code":150700},{"title":"\u9648\u5df4\u5c14\u864e\u65d7","code":"150725","parent_code":150700},{"title":"\u65b0\u5df4\u5c14\u864e\u5de6\u65d7","code":"150726","parent_code":150700},{"title":"\u65b0\u5df4\u5c14\u864e\u53f3\u65d7","code":"150727","parent_code":150700},{"title":"\u6ee1\u6d32\u91cc\u5e02","code":"150781","parent_code":150700},{"title":"\u7259\u514b\u77f3\u5e02","code":"150782","parent_code":150700},{"title":"\u624e\u5170\u5c6f\u5e02","code":"150783","parent_code":150700},{"title":"\u989d\u5c14\u53e4\u7eb3\u5e02","code":"150784","parent_code":150700},{"title":"\u6839\u6cb3\u5e02","code":"150785","parent_code":150700},{"title":"\u4e34\u6cb3\u533a","code":"150802","parent_code":150800},{"title":"\u4e94\u539f\u53bf","code":"150821","parent_code":150800},{"title":"\u78f4\u53e3\u53bf","code":"150822","parent_code":150800},{"title":"\u4e4c\u62c9\u7279\u524d\u65d7","code":"150823","parent_code":150800},{"title":"\u4e4c\u62c9\u7279\u4e2d\u65d7","code":"150824","parent_code":150800},{"title":"\u4e4c\u62c9\u7279\u540e\u65d7","code":"150825","parent_code":150800},{"title":"\u676d\u9526\u540e\u65d7","code":"150826","parent_code":150800},{"title":"\u96c6\u5b81\u533a","code":"150902","parent_code":150900},{"title":"\u5353\u8d44\u53bf","code":"150921","parent_code":150900},{"title":"\u5316\u5fb7\u53bf","code":"150922","parent_code":150900},{"title":"\u5546\u90fd\u53bf","code":"150923","parent_code":150900},{"title":"\u5174\u548c\u53bf","code":"150924","parent_code":150900},{"title":"\u51c9\u57ce\u53bf","code":"150925","parent_code":150900},{"title":"\u5bdf\u54c8\u5c14\u53f3\u7ffc\u524d\u65d7","code":"150926","parent_code":150900},{"title":"\u5bdf\u54c8\u5c14\u53f3\u7ffc\u4e2d\u65d7","code":"150927","parent_code":150900},{"title":"\u5bdf\u54c8\u5c14\u53f3\u7ffc\u540e\u65d7","code":"150928","parent_code":150900},{"title":"\u56db\u5b50\u738b\u65d7","code":"150929","parent_code":150900},{"title":"\u4e30\u9547\u5e02","code":"150981","parent_code":150900},{"title":"\u4e4c\u5170\u6d69\u7279\u5e02","code":"152201","parent_code":152200},{"title":"\u963f\u5c14\u5c71\u5e02","code":"152202","parent_code":152200},{"title":"\u79d1\u5c14\u6c81\u53f3\u7ffc\u524d\u65d7","code":"152221","parent_code":152200},{"title":"\u79d1\u5c14\u6c81\u53f3\u7ffc\u4e2d\u65d7","code":"152222","parent_code":152200},{"title":"\u624e\u8d49\u7279\u65d7","code":"152223","parent_code":152200},{"title":"\u7a81\u6cc9\u53bf","code":"152224","parent_code":152200},{"title":"\u4e8c\u8fde\u6d69\u7279\u5e02","code":"152501","parent_code":152500},{"title":"\u9521\u6797\u6d69\u7279\u5e02","code":"152502","parent_code":152500},{"title":"\u963f\u5df4\u560e\u65d7","code":"152522","parent_code":152500},{"title":"\u82cf\u5c3c\u7279\u5de6\u65d7","code":"152523","parent_code":152500},{"title":"\u82cf\u5c3c\u7279\u53f3\u65d7","code":"152524","parent_code":152500},{"title":"\u4e1c\u4e4c\u73e0\u7a46\u6c81\u65d7","code":"152525","parent_code":152500},{"title":"\u897f\u4e4c\u73e0\u7a46\u6c81\u65d7","code":"152526","parent_code":152500},{"title":"\u592a\u4ec6\u5bfa\u65d7","code":"152527","parent_code":152500},{"title":"\u9576\u9ec4\u65d7","code":"152528","parent_code":152500},{"title":"\u6b63\u9576\u767d\u65d7","code":"152529","parent_code":152500},{"title":"\u6b63\u84dd\u65d7","code":"152530","parent_code":152500},{"title":"\u591a\u4f26\u53bf","code":"152531","parent_code":152500},{"title":"\u4e4c\u62c9\u76d6\u7ba1\u59d4\u4f1a","code":"152571","parent_code":152500},{"title":"\u963f\u62c9\u5584\u5de6\u65d7","code":"152921","parent_code":152900},{"title":"\u963f\u62c9\u5584\u53f3\u65d7","code":"152922","parent_code":152900},{"title":"\u989d\u6d4e\u7eb3\u65d7","code":"152923","parent_code":152900},{"title":"\u5185\u8499\u53e4\u963f\u62c9\u5584\u7ecf\u6d4e\u5f00\u53d1\u533a","code":"152971","parent_code":152900},{"title":"\u548c\u5e73\u533a","code":"210102","parent_code":210100},{"title":"\u6c88\u6cb3\u533a","code":"210103","parent_code":210100},{"title":"\u5927\u4e1c\u533a","code":"210104","parent_code":210100},{"title":"\u7687\u59d1\u533a","code":"210105","parent_code":210100},{"title":"\u94c1\u897f\u533a","code":"210106","parent_code":210100},{"title":"\u82cf\u5bb6\u5c6f\u533a","code":"210111","parent_code":210100},{"title":"\u6d51\u5357\u533a","code":"210112","parent_code":210100},{"title":"\u6c88\u5317\u65b0\u533a","code":"210113","parent_code":210100},{"title":"\u4e8e\u6d2a\u533a","code":"210114","parent_code":210100},{"title":"\u8fbd\u4e2d\u533a","code":"210115","parent_code":210100},{"title":"\u5eb7\u5e73\u53bf","code":"210123","parent_code":210100},{"title":"\u6cd5\u5e93\u53bf","code":"210124","parent_code":210100},{"title":"\u65b0\u6c11\u5e02","code":"210181","parent_code":210100},{"title":"\u7ecf\u6d4e\u6280\u672f\u5f00\u53d1\u533a","code":"210190","parent_code":210100},{"title":"\u4e2d\u5c71\u533a","code":"210202","parent_code":210200},{"title":"\u897f\u5c97\u533a","code":"210203","parent_code":210200},{"title":"\u6c99\u6cb3\u53e3\u533a","code":"210204","parent_code":210200},{"title":"\u7518\u4e95\u5b50\u533a","code":"210211","parent_code":210200},{"title":"\u65c5\u987a\u53e3\u533a","code":"210212","parent_code":210200},{"title":"\u91d1\u5dde\u533a","code":"210213","parent_code":210200},{"title":"\u666e\u5170\u5e97\u533a","code":"210214","parent_code":210200},{"title":"\u957f\u6d77\u53bf","code":"210224","parent_code":210200},{"title":"\u74e6\u623f\u5e97\u5e02","code":"210281","parent_code":210200},{"title":"\u5e84\u6cb3\u5e02","code":"210283","parent_code":210200},{"title":"\u94c1\u4e1c\u533a","code":"210302","parent_code":210300},{"title":"\u94c1\u897f\u533a","code":"210303","parent_code":210300},{"title":"\u7acb\u5c71\u533a","code":"210304","parent_code":210300},{"title":"\u5343\u5c71\u533a","code":"210311","parent_code":210300},{"title":"\u53f0\u5b89\u53bf","code":"210321","parent_code":210300},{"title":"\u5cab\u5ca9\u6ee1\u65cf\u81ea\u6cbb\u53bf","code":"210323","parent_code":210300},{"title":"\u6d77\u57ce\u5e02","code":"210381","parent_code":210300},{"title":"\u9ad8\u65b0\u533a","code":"210390","parent_code":210300},{"title":"\u65b0\u629a\u533a","code":"210402","parent_code":210400},{"title":"\u4e1c\u6d32\u533a","code":"210403","parent_code":210400},{"title":"\u671b\u82b1\u533a","code":"210404","parent_code":210400},{"title":"\u987a\u57ce\u533a","code":"210411","parent_code":210400},{"title":"\u629a\u987a\u53bf","code":"210421","parent_code":210400},{"title":"\u65b0\u5bbe\u6ee1\u65cf\u81ea\u6cbb\u53bf","code":"210422","parent_code":210400},{"title":"\u6e05\u539f\u6ee1\u65cf\u81ea\u6cbb\u53bf","code":"210423","parent_code":210400},{"title":"\u5e73\u5c71\u533a","code":"210502","parent_code":210500},{"title":"\u6eaa\u6e56\u533a","code":"210503","parent_code":210500},{"title":"\u660e\u5c71\u533a","code":"210504","parent_code":210500},{"title":"\u5357\u82ac\u533a","code":"210505","parent_code":210500},{"title":"\u672c\u6eaa\u6ee1\u65cf\u81ea\u6cbb\u53bf","code":"210521","parent_code":210500},{"title":"\u6853\u4ec1\u6ee1\u65cf\u81ea\u6cbb\u53bf","code":"210522","parent_code":210500},{"title":"\u5143\u5b9d\u533a","code":"210602","parent_code":210600},{"title":"\u632f\u5174\u533a","code":"210603","parent_code":210600},{"title":"\u632f\u5b89\u533a","code":"210604","parent_code":210600},{"title":"\u5bbd\u7538\u6ee1\u65cf\u81ea\u6cbb\u53bf","code":"210624","parent_code":210600},{"title":"\u4e1c\u6e2f\u5e02","code":"210681","parent_code":210600},{"title":"\u51e4\u57ce\u5e02","code":"210682","parent_code":210600},{"title":"\u53e4\u5854\u533a","code":"210702","parent_code":210700},{"title":"\u51cc\u6cb3\u533a","code":"210703","parent_code":210700},{"title":"\u592a\u548c\u533a","code":"210711","parent_code":210700},{"title":"\u9ed1\u5c71\u53bf","code":"210726","parent_code":210700},{"title":"\u4e49\u53bf","code":"210727","parent_code":210700},{"title":"\u51cc\u6d77\u5e02","code":"210781","parent_code":210700},{"title":"\u5317\u9547\u5e02","code":"210782","parent_code":210700},{"title":"\u7ecf\u6d4e\u6280\u672f\u5f00\u53d1\u533a","code":"210793","parent_code":210700},{"title":"\u7ad9\u524d\u533a","code":"210802","parent_code":210800},{"title":"\u897f\u5e02\u533a","code":"210803","parent_code":210800},{"title":"\u9c85\u9c7c\u5708\u533a","code":"210804","parent_code":210800},{"title":"\u8001\u8fb9\u533a","code":"210811","parent_code":210800},{"title":"\u76d6\u5dde\u5e02","code":"210881","parent_code":210800},{"title":"\u5927\u77f3\u6865\u5e02","code":"210882","parent_code":210800},{"title":"\u6d77\u5dde\u533a","code":"210902","parent_code":210900},{"title":"\u65b0\u90b1\u533a","code":"210903","parent_code":210900},{"title":"\u592a\u5e73\u533a","code":"210904","parent_code":210900},{"title":"\u6e05\u6cb3\u95e8\u533a","code":"210905","parent_code":210900},{"title":"\u7ec6\u6cb3\u533a","code":"210911","parent_code":210900},{"title":"\u961c\u65b0\u8499\u53e4\u65cf\u81ea\u6cbb\u53bf","code":"210921","parent_code":210900},{"title":"\u5f70\u6b66\u53bf","code":"210922","parent_code":210900},{"title":"\u767d\u5854\u533a","code":"211002","parent_code":211000},{"title":"\u6587\u5723\u533a","code":"211003","parent_code":211000},{"title":"\u5b8f\u4f1f\u533a","code":"211004","parent_code":211000},{"title":"\u5f13\u957f\u5cad\u533a","code":"211005","parent_code":211000},{"title":"\u592a\u5b50\u6cb3\u533a","code":"211011","parent_code":211000},{"title":"\u8fbd\u9633\u53bf","code":"211021","parent_code":211000},{"title":"\u706f\u5854\u5e02","code":"211081","parent_code":211000},{"title":"\u53cc\u53f0\u5b50\u533a","code":"211102","parent_code":211100},{"title":"\u5174\u9686\u53f0\u533a","code":"211103","parent_code":211100},{"title":"\u5927\u6d3c\u533a","code":"211104","parent_code":211100},{"title":"\u76d8\u5c71\u53bf","code":"211122","parent_code":211100},{"title":"\u94f6\u5dde\u533a","code":"211202","parent_code":211200},{"title":"\u6e05\u6cb3\u533a","code":"211204","parent_code":211200},{"title":"\u94c1\u5cad\u53bf","code":"211221","parent_code":211200},{"title":"\u897f\u4e30\u53bf","code":"211223","parent_code":211200},{"title":"\u660c\u56fe\u53bf","code":"211224","parent_code":211200},{"title":"\u8c03\u5175\u5c71\u5e02","code":"211281","parent_code":211200},{"title":"\u5f00\u539f\u5e02","code":"211282","parent_code":211200},{"title":"\u53cc\u5854\u533a","code":"211302","parent_code":211300},{"title":"\u9f99\u57ce\u533a","code":"211303","parent_code":211300},{"title":"\u671d\u9633\u53bf","code":"211321","parent_code":211300},{"title":"\u5efa\u5e73\u53bf","code":"211322","parent_code":211300},{"title":"\u5580\u5587\u6c81\u5de6\u7ffc\u8499\u53e4\u65cf\u81ea\u6cbb\u53bf","code":"211324","parent_code":211300},{"title":"\u5317\u7968\u5e02","code":"211381","parent_code":211300},{"title":"\u51cc\u6e90\u5e02","code":"211382","parent_code":211300},{"title":"\u8fde\u5c71\u533a","code":"211402","parent_code":211400},{"title":"\u9f99\u6e2f\u533a","code":"211403","parent_code":211400},{"title":"\u5357\u7968\u533a","code":"211404","parent_code":211400},{"title":"\u7ee5\u4e2d\u53bf","code":"211421","parent_code":211400},{"title":"\u5efa\u660c\u53bf","code":"211422","parent_code":211400},{"title":"\u5174\u57ce\u5e02","code":"211481","parent_code":211400},{"title":"\u5357\u5173\u533a","code":"220102","parent_code":220100},{"title":"\u5bbd\u57ce\u533a","code":"220103","parent_code":220100},{"title":"\u671d\u9633\u533a","code":"220104","parent_code":220100},{"title":"\u4e8c\u9053\u533a","code":"220105","parent_code":220100},{"title":"\u7eff\u56ed\u533a","code":"220106","parent_code":220100},{"title":"\u53cc\u9633\u533a","code":"220112","parent_code":220100},{"title":"\u4e5d\u53f0\u533a","code":"220113","parent_code":220100},{"title":"\u519c\u5b89\u53bf","code":"220122","parent_code":220100},{"title":"\u957f\u6625\u7ecf\u6d4e\u6280\u672f\u5f00\u53d1\u533a","code":"220171","parent_code":220100},{"title":"\u957f\u6625\u51c0\u6708\u9ad8\u65b0\u6280\u672f\u4ea7\u4e1a\u5f00\u53d1\u533a","code":"220172","parent_code":220100},{"title":"\u957f\u6625\u9ad8\u65b0\u6280\u672f\u4ea7\u4e1a\u5f00\u53d1\u533a","code":"220173","parent_code":220100},{"title":"\u957f\u6625\u6c7d\u8f66\u7ecf\u6d4e\u6280\u672f\u5f00\u53d1\u533a","code":"220174","parent_code":220100},{"title":"\u6986\u6811\u5e02","code":"220182","parent_code":220100},{"title":"\u5fb7\u60e0\u5e02","code":"220183","parent_code":220100},{"title":"\u7ecf\u6d4e\u6280\u672f\u5f00\u53d1\u533a","code":"220192","parent_code":220100},{"title":"\u660c\u9091\u533a","code":"220202","parent_code":220200},{"title":"\u9f99\u6f6d\u533a","code":"220203","parent_code":220200},{"title":"\u8239\u8425\u533a","code":"220204","parent_code":220200},{"title":"\u4e30\u6ee1\u533a","code":"220211","parent_code":220200},{"title":"\u6c38\u5409\u53bf","code":"220221","parent_code":220200},{"title":"\u5409\u6797\u7ecf\u6d4e\u5f00\u53d1\u533a","code":"220271","parent_code":220200},{"title":"\u5409\u6797\u9ad8\u65b0\u6280\u672f\u4ea7\u4e1a\u5f00\u53d1\u533a","code":"220272","parent_code":220200},{"title":"\u86df\u6cb3\u5e02","code":"220281","parent_code":220200},{"title":"\u6866\u7538\u5e02","code":"220282","parent_code":220200},{"title":"\u8212\u5170\u5e02","code":"220283","parent_code":220200},{"title":"\u78d0\u77f3\u5e02","code":"220284","parent_code":220200},{"title":"\u94c1\u897f\u533a","code":"220302","parent_code":220300},{"title":"\u94c1\u4e1c\u533a","code":"220303","parent_code":220300},{"title":"\u68a8\u6811\u53bf","code":"220322","parent_code":220300},{"title":"\u4f0a\u901a\u6ee1\u65cf\u81ea\u6cbb\u53bf","code":"220323","parent_code":220300},{"title":"\u516c\u4e3b\u5cad\u5e02","code":"220381","parent_code":220300},{"title":"\u53cc\u8fbd\u5e02","code":"220382","parent_code":220300},{"title":"\u9f99\u5c71\u533a","code":"220402","parent_code":220400},{"title":"\u897f\u5b89\u533a","code":"220403","parent_code":220400},{"title":"\u4e1c\u4e30\u53bf","code":"220421","parent_code":220400},{"title":"\u4e1c\u8fbd\u53bf","code":"220422","parent_code":220400},{"title":"\u4e1c\u660c\u533a","code":"220502","parent_code":220500},{"title":"\u4e8c\u9053\u6c5f\u533a","code":"220503","parent_code":220500},{"title":"\u901a\u5316\u53bf","code":"220521","parent_code":220500},{"title":"\u8f89\u5357\u53bf","code":"220523","parent_code":220500},{"title":"\u67f3\u6cb3\u53bf","code":"220524","parent_code":220500},{"title":"\u6885\u6cb3\u53e3\u5e02","code":"220581","parent_code":220500},{"title":"\u96c6\u5b89\u5e02","code":"220582","parent_code":220500},{"title":"\u6d51\u6c5f\u533a","code":"220602","parent_code":220600},{"title":"\u6c5f\u6e90\u533a","code":"220605","parent_code":220600},{"title":"\u629a\u677e\u53bf","code":"220621","parent_code":220600},{"title":"\u9756\u5b87\u53bf","code":"220622","parent_code":220600},{"title":"\u957f\u767d\u671d\u9c9c\u65cf\u81ea\u6cbb\u53bf","code":"220623","parent_code":220600},{"title":"\u4e34\u6c5f\u5e02","code":"220681","parent_code":220600},{"title":"\u5b81\u6c5f\u533a","code":"220702","parent_code":220700},{"title":"\u524d\u90ed\u5c14\u7f57\u65af\u8499\u53e4\u65cf\u81ea\u6cbb\u53bf","code":"220721","parent_code":220700},{"title":"\u957f\u5cad\u53bf","code":"220722","parent_code":220700},{"title":"\u4e7e\u5b89\u53bf","code":"220723","parent_code":220700},{"title":"\u5409\u6797\u677e\u539f\u7ecf\u6d4e\u5f00\u53d1\u533a","code":"220771","parent_code":220700},{"title":"\u6276\u4f59\u5e02","code":"220781","parent_code":220700},{"title":"\u6d2e\u5317\u533a","code":"220802","parent_code":220800},{"title":"\u9547\u8d49\u53bf","code":"220821","parent_code":220800},{"title":"\u901a\u6986\u53bf","code":"220822","parent_code":220800},{"title":"\u5409\u6797\u767d\u57ce\u7ecf\u6d4e\u5f00\u53d1\u533a","code":"220871","parent_code":220800},{"title":"\u6d2e\u5357\u5e02","code":"220881","parent_code":220800},{"title":"\u5927\u5b89\u5e02","code":"220882","parent_code":220800},{"title":"\u5ef6\u5409\u5e02","code":"222401","parent_code":222400},{"title":"\u56fe\u4eec\u5e02","code":"222402","parent_code":222400},{"title":"\u6566\u5316\u5e02","code":"222403","parent_code":222400},{"title":"\u73f2\u6625\u5e02","code":"222404","parent_code":222400},{"title":"\u9f99\u4e95\u5e02","code":"222405","parent_code":222400},{"title":"\u548c\u9f99\u5e02","code":"222406","parent_code":222400},{"title":"\u6c6a\u6e05\u53bf","code":"222424","parent_code":222400},{"title":"\u5b89\u56fe\u53bf","code":"222426","parent_code":222400},{"title":"\u9053\u91cc\u533a","code":"230102","parent_code":230100},{"title":"\u5357\u5c97\u533a","code":"230103","parent_code":230100},{"title":"\u9053\u5916\u533a","code":"230104","parent_code":230100},{"title":"\u5e73\u623f\u533a","code":"230108","parent_code":230100},{"title":"\u677e\u5317\u533a","code":"230109","parent_code":230100},{"title":"\u9999\u574a\u533a","code":"230110","parent_code":230100},{"title":"\u547c\u5170\u533a","code":"230111","parent_code":230100},{"title":"\u963f\u57ce\u533a","code":"230112","parent_code":230100},{"title":"\u53cc\u57ce\u533a","code":"230113","parent_code":230100},{"title":"\u4f9d\u5170\u53bf","code":"230123","parent_code":230100},{"title":"\u65b9\u6b63\u53bf","code":"230124","parent_code":230100},{"title":"\u5bbe\u53bf","code":"230125","parent_code":230100},{"title":"\u5df4\u5f66\u53bf","code":"230126","parent_code":230100},{"title":"\u6728\u5170\u53bf","code":"230127","parent_code":230100},{"title":"\u901a\u6cb3\u53bf","code":"230128","parent_code":230100},{"title":"\u5ef6\u5bff\u53bf","code":"230129","parent_code":230100},{"title":"\u5c1a\u5fd7\u5e02","code":"230183","parent_code":230100},{"title":"\u4e94\u5e38\u5e02","code":"230184","parent_code":230100},{"title":"\u9f99\u6c99\u533a","code":"230202","parent_code":230200},{"title":"\u5efa\u534e\u533a","code":"230203","parent_code":230200},{"title":"\u94c1\u950b\u533a","code":"230204","parent_code":230200},{"title":"\u6602\u6602\u6eaa\u533a","code":"230205","parent_code":230200},{"title":"\u5bcc\u62c9\u5c14\u57fa\u533a","code":"230206","parent_code":230200},{"title":"\u78be\u5b50\u5c71\u533a","code":"230207","parent_code":230200},{"title":"\u6885\u91cc\u65af\u8fbe\u65a1\u5c14\u65cf\u533a","code":"230208","parent_code":230200},{"title":"\u9f99\u6c5f\u53bf","code":"230221","parent_code":230200},{"title":"\u4f9d\u5b89\u53bf","code":"230223","parent_code":230200},{"title":"\u6cf0\u6765\u53bf","code":"230224","parent_code":230200},{"title":"\u7518\u5357\u53bf","code":"230225","parent_code":230200},{"title":"\u5bcc\u88d5\u53bf","code":"230227","parent_code":230200},{"title":"\u514b\u5c71\u53bf","code":"230229","parent_code":230200},{"title":"\u514b\u4e1c\u53bf","code":"230230","parent_code":230200},{"title":"\u62dc\u6cc9\u53bf","code":"230231","parent_code":230200},{"title":"\u8bb7\u6cb3\u5e02","code":"230281","parent_code":230200},{"title":"\u9e21\u51a0\u533a","code":"230302","parent_code":230300},{"title":"\u6052\u5c71\u533a","code":"230303","parent_code":230300},{"title":"\u6ef4\u9053\u533a","code":"230304","parent_code":230300},{"title":"\u68a8\u6811\u533a","code":"230305","parent_code":230300},{"title":"\u57ce\u5b50\u6cb3\u533a","code":"230306","parent_code":230300},{"title":"\u9ebb\u5c71\u533a","code":"230307","parent_code":230300},{"title":"\u9e21\u4e1c\u53bf","code":"230321","parent_code":230300},{"title":"\u864e\u6797\u5e02","code":"230381","parent_code":230300},{"title":"\u5bc6\u5c71\u5e02","code":"230382","parent_code":230300},{"title":"\u5411\u9633\u533a","code":"230402","parent_code":230400},{"title":"\u5de5\u519c\u533a","code":"230403","parent_code":230400},{"title":"\u5357\u5c71\u533a","code":"230404","parent_code":230400},{"title":"\u5174\u5b89\u533a","code":"230405","parent_code":230400},{"title":"\u4e1c\u5c71\u533a","code":"230406","parent_code":230400},{"title":"\u5174\u5c71\u533a","code":"230407","parent_code":230400},{"title":"\u841d\u5317\u53bf","code":"230421","parent_code":230400},{"title":"\u7ee5\u6ee8\u53bf","code":"230422","parent_code":230400},{"title":"\u5c16\u5c71\u533a","code":"230502","parent_code":230500},{"title":"\u5cad\u4e1c\u533a","code":"230503","parent_code":230500},{"title":"\u56db\u65b9\u53f0\u533a","code":"230505","parent_code":230500},{"title":"\u5b9d\u5c71\u533a","code":"230506","parent_code":230500},{"title":"\u96c6\u8d24\u53bf","code":"230521","parent_code":230500},{"title":"\u53cb\u8c0a\u53bf","code":"230522","parent_code":230500},{"title":"\u5b9d\u6e05\u53bf","code":"230523","parent_code":230500},{"title":"\u9976\u6cb3\u53bf","code":"230524","parent_code":230500},{"title":"\u8428\u5c14\u56fe\u533a","code":"230602","parent_code":230600},{"title":"\u9f99\u51e4\u533a","code":"230603","parent_code":230600},{"title":"\u8ba9\u80e1\u8def\u533a","code":"230604","parent_code":230600},{"title":"\u7ea2\u5c97\u533a","code":"230605","parent_code":230600},{"title":"\u5927\u540c\u533a","code":"230606","parent_code":230600},{"title":"\u8087\u5dde\u53bf","code":"230621","parent_code":230600},{"title":"\u8087\u6e90\u53bf","code":"230622","parent_code":230600},{"title":"\u6797\u7538\u53bf","code":"230623","parent_code":230600},{"title":"\u675c\u5c14\u4f2f\u7279\u8499\u53e4\u65cf\u81ea\u6cbb\u53bf","code":"230624","parent_code":230600},{"title":"\u5927\u5e86\u9ad8\u65b0\u6280\u672f\u4ea7\u4e1a\u5f00\u53d1\u533a","code":"230671","parent_code":230600},{"title":"\u4f0a\u7f8e\u533a","code":"230717","parent_code":230700},{"title":"\u4e4c\u7fe0\u533a","code":"230718","parent_code":230700},{"title":"\u53cb\u597d\u533a","code":"230719","parent_code":230700},{"title":"\u5609\u836b\u53bf","code":"230722","parent_code":230700},{"title":"\u6c64\u65fa\u53bf","code":"230723","parent_code":230700},{"title":"\u4e30\u6797\u53bf","code":"230724","parent_code":230700},{"title":"\u5927\u7b90\u5c71\u53bf","code":"230725","parent_code":230700},{"title":"\u5357\u5c94\u53bf","code":"230726","parent_code":230700},{"title":"\u91d1\u6797\u533a","code":"230751","parent_code":230700},{"title":"\u94c1\u529b\u5e02","code":"230781","parent_code":230700},{"title":"\u5411\u9633\u533a","code":"230803","parent_code":230800},{"title":"\u524d\u8fdb\u533a","code":"230804","parent_code":230800},{"title":"\u4e1c\u98ce\u533a","code":"230805","parent_code":230800},{"title":"\u90ca\u533a","code":"230811","parent_code":230800},{"title":"\u6866\u5357\u53bf","code":"230822","parent_code":230800},{"title":"\u6866\u5ddd\u53bf","code":"230826","parent_code":230800},{"title":"\u6c64\u539f\u53bf","code":"230828","parent_code":230800},{"title":"\u540c\u6c5f\u5e02","code":"230881","parent_code":230800},{"title":"\u5bcc\u9526\u5e02","code":"230882","parent_code":230800},{"title":"\u629a\u8fdc\u5e02","code":"230883","parent_code":230800},{"title":"\u65b0\u5174\u533a","code":"230902","parent_code":230900},{"title":"\u6843\u5c71\u533a","code":"230903","parent_code":230900},{"title":"\u8304\u5b50\u6cb3\u533a","code":"230904","parent_code":230900},{"title":"\u52c3\u5229\u53bf","code":"230921","parent_code":230900},{"title":"\u4e1c\u5b89\u533a","code":"231002","parent_code":231000},{"title":"\u9633\u660e\u533a","code":"231003","parent_code":231000},{"title":"\u7231\u6c11\u533a","code":"231004","parent_code":231000},{"title":"\u897f\u5b89\u533a","code":"231005","parent_code":231000},{"title":"\u6797\u53e3\u53bf","code":"231025","parent_code":231000},{"title":"\u7ee5\u82ac\u6cb3\u5e02","code":"231081","parent_code":231000},{"title":"\u6d77\u6797\u5e02","code":"231083","parent_code":231000},{"title":"\u5b81\u5b89\u5e02","code":"231084","parent_code":231000},{"title":"\u7a46\u68f1\u5e02","code":"231085","parent_code":231000},{"title":"\u4e1c\u5b81\u5e02","code":"231086","parent_code":231000},{"title":"\u7231\u8f89\u533a","code":"231102","parent_code":231100},{"title":"\u900a\u514b\u53bf","code":"231123","parent_code":231100},{"title":"\u5b59\u5434\u53bf","code":"231124","parent_code":231100},{"title":"\u5317\u5b89\u5e02","code":"231181","parent_code":231100},{"title":"\u4e94\u5927\u8fde\u6c60\u5e02","code":"231182","parent_code":231100},{"title":"\u5ae9\u6c5f\u5e02","code":"231183","parent_code":231100},{"title":"\u5317\u6797\u533a","code":"231202","parent_code":231200},{"title":"\u671b\u594e\u53bf","code":"231221","parent_code":231200},{"title":"\u5170\u897f\u53bf","code":"231222","parent_code":231200},{"title":"\u9752\u5188\u53bf","code":"231223","parent_code":231200},{"title":"\u5e86\u5b89\u53bf","code":"231224","parent_code":231200},{"title":"\u660e\u6c34\u53bf","code":"231225","parent_code":231200},{"title":"\u7ee5\u68f1\u53bf","code":"231226","parent_code":231200},{"title":"\u5b89\u8fbe\u5e02","code":"231281","parent_code":231200},{"title":"\u8087\u4e1c\u5e02","code":"231282","parent_code":231200},{"title":"\u6d77\u4f26\u5e02","code":"231283","parent_code":231200},{"title":"\u6f20\u6cb3\u5e02","code":"232701","parent_code":232700},{"title":"\u547c\u739b\u53bf","code":"232721","parent_code":232700},{"title":"\u5854\u6cb3\u53bf","code":"232722","parent_code":232700},{"title":"\u677e\u5cad\u533a","code":"232790","parent_code":232700},{"title":"\u547c\u4e2d\u533a","code":"232791","parent_code":232700},{"title":"\u52a0\u683c\u8fbe\u5947\u533a","code":"232792","parent_code":232700},{"title":"\u65b0\u6797\u533a","code":"232793","parent_code":232700},{"title":"\u9ec4\u6d66\u533a","code":"310101","parent_code":310100},{"title":"\u5f90\u6c47\u533a","code":"310104","parent_code":310100},{"title":"\u957f\u5b81\u533a","code":"310105","parent_code":310100},{"title":"\u9759\u5b89\u533a","code":"310106","parent_code":310100},{"title":"\u666e\u9640\u533a","code":"310107","parent_code":310100},{"title":"\u8679\u53e3\u533a","code":"310109","parent_code":310100},{"title":"\u6768\u6d66\u533a","code":"310110","parent_code":310100},{"title":"\u95f5\u884c\u533a","code":"310112","parent_code":310100},{"title":"\u5b9d\u5c71\u533a","code":"310113","parent_code":310100},{"title":"\u5609\u5b9a\u533a","code":"310114","parent_code":310100},{"title":"\u6d66\u4e1c\u65b0\u533a","code":"310115","parent_code":310100},{"title":"\u91d1\u5c71\u533a","code":"310116","parent_code":310100},{"title":"\u677e\u6c5f\u533a","code":"310117","parent_code":310100},{"title":"\u9752\u6d66\u533a","code":"310118","parent_code":310100},{"title":"\u5949\u8d24\u533a","code":"310120","parent_code":310100},{"title":"\u5d07\u660e\u533a","code":"310151","parent_code":310100},{"title":"\u7384\u6b66\u533a","code":"320102","parent_code":320100},{"title":"\u79e6\u6dee\u533a","code":"320104","parent_code":320100},{"title":"\u5efa\u90ba\u533a","code":"320105","parent_code":320100},{"title":"\u9f13\u697c\u533a","code":"320106","parent_code":320100},{"title":"\u6d66\u53e3\u533a","code":"320111","parent_code":320100},{"title":"\u6816\u971e\u533a","code":"320113","parent_code":320100},{"title":"\u96e8\u82b1\u53f0\u533a","code":"320114","parent_code":320100},{"title":"\u6c5f\u5b81\u533a","code":"320115","parent_code":320100},{"title":"\u516d\u5408\u533a","code":"320116","parent_code":320100},{"title":"\u6ea7\u6c34\u533a","code":"320117","parent_code":320100},{"title":"\u9ad8\u6df3\u533a","code":"320118","parent_code":320100},{"title":"\u9521\u5c71\u533a","code":"320205","parent_code":320200},{"title":"\u60e0\u5c71\u533a","code":"320206","parent_code":320200},{"title":"\u6ee8\u6e56\u533a","code":"320211","parent_code":320200},{"title":"\u6881\u6eaa\u533a","code":"320213","parent_code":320200},{"title":"\u65b0\u5434\u533a","code":"320214","parent_code":320200},{"title":"\u6c5f\u9634\u5e02","code":"320281","parent_code":320200},{"title":"\u5b9c\u5174\u5e02","code":"320282","parent_code":320200},{"title":"\u9f13\u697c\u533a","code":"320302","parent_code":320300},{"title":"\u4e91\u9f99\u533a","code":"320303","parent_code":320300},{"title":"\u8d3e\u6c6a\u533a","code":"320305","parent_code":320300},{"title":"\u6cc9\u5c71\u533a","code":"320311","parent_code":320300},{"title":"\u94dc\u5c71\u533a","code":"320312","parent_code":320300},{"title":"\u4e30\u53bf","code":"320321","parent_code":320300},{"title":"\u6c9b\u53bf","code":"320322","parent_code":320300},{"title":"\u7762\u5b81\u53bf","code":"320324","parent_code":320300},{"title":"\u5f90\u5dde\u7ecf\u6d4e\u6280\u672f\u5f00\u53d1\u533a","code":"320371","parent_code":320300},{"title":"\u65b0\u6c82\u5e02","code":"320381","parent_code":320300},{"title":"\u90b3\u5dde\u5e02","code":"320382","parent_code":320300},{"title":"\u5de5\u4e1a\u56ed\u533a","code":"320391","parent_code":320300},{"title":"\u5929\u5b81\u533a","code":"320402","parent_code":320400},{"title":"\u949f\u697c\u533a","code":"320404","parent_code":320400},{"title":"\u65b0\u5317\u533a","code":"320411","parent_code":320400},{"title":"\u6b66\u8fdb\u533a","code":"320412","parent_code":320400},{"title":"\u91d1\u575b\u533a","code":"320413","parent_code":320400},{"title":"\u6ea7\u9633\u5e02","code":"320481","parent_code":320400},{"title":"\u864e\u4e18\u533a","code":"320505","parent_code":320500},{"title":"\u5434\u4e2d\u533a","code":"320506","parent_code":320500},{"title":"\u76f8\u57ce\u533a","code":"320507","parent_code":320500},{"title":"\u59d1\u82cf\u533a","code":"320508","parent_code":320500},{"title":"\u5434\u6c5f\u533a","code":"320509","parent_code":320500},{"title":"\u82cf\u5dde\u5de5\u4e1a\u56ed\u533a","code":"320571","parent_code":320500},{"title":"\u5e38\u719f\u5e02","code":"320581","parent_code":320500},{"title":"\u5f20\u5bb6\u6e2f\u5e02","code":"320582","parent_code":320500},{"title":"\u6606\u5c71\u5e02","code":"320583","parent_code":320500},{"title":"\u592a\u4ed3\u5e02","code":"320585","parent_code":320500},{"title":"\u5de5\u4e1a\u56ed\u533a","code":"320590","parent_code":320500},{"title":"\u9ad8\u65b0\u533a","code":"320591","parent_code":320500},{"title":"\u5d07\u5ddd\u533a","code":"320602","parent_code":320600},{"title":"\u6e2f\u95f8\u533a","code":"320611","parent_code":320600},{"title":"\u901a\u5dde\u533a","code":"320612","parent_code":320600},{"title":"\u5982\u4e1c\u53bf","code":"320623","parent_code":320600},{"title":"\u542f\u4e1c\u5e02","code":"320681","parent_code":320600},{"title":"\u5982\u768b\u5e02","code":"320682","parent_code":320600},{"title":"\u6d77\u95e8\u5e02","code":"320684","parent_code":320600},{"title":"\u6d77\u5b89\u5e02","code":"320685","parent_code":320600},{"title":"\u9ad8\u65b0\u533a","code":"320691","parent_code":320600},{"title":"\u8fde\u4e91\u533a","code":"320703","parent_code":320700},{"title":"\u6d77\u5dde\u533a","code":"320706","parent_code":320700},{"title":"\u8d63\u6986\u533a","code":"320707","parent_code":320700},{"title":"\u4e1c\u6d77\u53bf","code":"320722","parent_code":320700},{"title":"\u704c\u4e91\u53bf","code":"320723","parent_code":320700},{"title":"\u704c\u5357\u53bf","code":"320724","parent_code":320700},{"title":"\u8fde\u4e91\u6e2f\u7ecf\u6d4e\u6280\u672f\u5f00\u53d1\u533a","code":"320771","parent_code":320700},{"title":"\u6dee\u5b89\u533a","code":"320803","parent_code":320800},{"title":"\u6dee\u9634\u533a","code":"320804","parent_code":320800},{"title":"\u6e05\u6c5f\u6d66\u533a","code":"320812","parent_code":320800},{"title":"\u6d2a\u6cfd\u533a","code":"320813","parent_code":320800},{"title":"\u6d9f\u6c34\u53bf","code":"320826","parent_code":320800},{"title":"\u76f1\u7719\u53bf","code":"320830","parent_code":320800},{"title":"\u91d1\u6e56\u53bf","code":"320831","parent_code":320800},{"title":"\u6dee\u5b89\u7ecf\u6d4e\u6280\u672f\u5f00\u53d1\u533a","code":"320871","parent_code":320800},{"title":"\u7ecf\u6d4e\u5f00\u53d1\u533a","code":"320890","parent_code":320800},{"title":"\u4ead\u6e56\u533a","code":"320902","parent_code":320900},{"title":"\u76d0\u90fd\u533a","code":"320903","parent_code":320900},{"title":"\u5927\u4e30\u533a","code":"320904","parent_code":320900},{"title":"\u54cd\u6c34\u53bf","code":"320921","parent_code":320900},{"title":"\u6ee8\u6d77\u53bf","code":"320922","parent_code":320900},{"title":"\u961c\u5b81\u53bf","code":"320923","parent_code":320900},{"title":"\u5c04\u9633\u53bf","code":"320924","parent_code":320900},{"title":"\u5efa\u6e56\u53bf","code":"320925","parent_code":320900},{"title":"\u76d0\u57ce\u7ecf\u6d4e\u6280\u672f\u5f00\u53d1\u533a","code":"320971","parent_code":320900},{"title":"\u4e1c\u53f0\u5e02","code":"320981","parent_code":320900},{"title":"\u5e7f\u9675\u533a","code":"321002","parent_code":321000},{"title":"\u9097\u6c5f\u533a","code":"321003","parent_code":321000},{"title":"\u6c5f\u90fd\u533a","code":"321012","parent_code":321000},{"title":"\u5b9d\u5e94\u53bf","code":"321023","parent_code":321000},{"title":"\u626c\u5dde\u7ecf\u6d4e\u6280\u672f\u5f00\u53d1\u533a","code":"321071","parent_code":321000},{"title":"\u4eea\u5f81\u5e02","code":"321081","parent_code":321000},{"title":"\u9ad8\u90ae\u5e02","code":"321084","parent_code":321000},{"title":"\u7ecf\u6d4e\u5f00\u53d1\u533a","code":"321090","parent_code":321000},{"title":"\u4eac\u53e3\u533a","code":"321102","parent_code":321100},{"title":"\u6da6\u5dde\u533a","code":"321111","parent_code":321100},{"title":"\u4e39\u5f92\u533a","code":"321112","parent_code":321100},{"title":"\u9547\u6c5f\u65b0\u533a","code":"321150","parent_code":321100},{"title":"\u4e39\u9633\u5e02","code":"321181","parent_code":321100},{"title":"\u626c\u4e2d\u5e02","code":"321182","parent_code":321100},{"title":"\u53e5\u5bb9\u5e02","code":"321183","parent_code":321100},{"title":"\u6d77\u9675\u533a","code":"321202","parent_code":321200},{"title":"\u9ad8\u6e2f\u533a","code":"321203","parent_code":321200},{"title":"\u59dc\u5830\u533a","code":"321204","parent_code":321200},{"title":"\u6cf0\u5dde\u533b\u836f\u9ad8\u65b0\u6280\u672f\u4ea7\u4e1a\u5f00\u53d1\u533a","code":"321271","parent_code":321200},{"title":"\u5174\u5316\u5e02","code":"321281","parent_code":321200},{"title":"\u9756\u6c5f\u5e02","code":"321282","parent_code":321200},{"title":"\u6cf0\u5174\u5e02","code":"321283","parent_code":321200},{"title":"\u5bbf\u57ce\u533a","code":"321302","parent_code":321300},{"title":"\u5bbf\u8c6b\u533a","code":"321311","parent_code":321300},{"title":"\u6cad\u9633\u53bf","code":"321322","parent_code":321300},{"title":"\u6cd7\u9633\u53bf","code":"321323","parent_code":321300},{"title":"\u6cd7\u6d2a\u53bf","code":"321324","parent_code":321300},{"title":"\u5bbf\u8fc1\u7ecf\u6d4e\u6280\u672f\u5f00\u53d1\u533a","code":"321371","parent_code":321300},{"title":"\u4e0a\u57ce\u533a","code":"330102","parent_code":330100},{"title":"\u62f1\u5885\u533a","code":"330105","parent_code":330100},{"title":"\u897f\u6e56\u533a","code":"330106","parent_code":330100},{"title":"\u6ee8\u6c5f\u533a","code":"330108","parent_code":330100},{"title":"\u8427\u5c71\u533a","code":"330109","parent_code":330100},{"title":"\u4f59\u676d\u533a","code":"330110","parent_code":330100},{"title":"\u5bcc\u9633\u533a","code":"330111","parent_code":330100},{"title":"\u4e34\u5b89\u533a","code":"330112","parent_code":330100},{"title":"\u94b1\u5858\u533a","code":"330113","parent_code":330100},{"title":"\u4e34\u5e73\u533a","code":"330114","parent_code":330100},{"title":"\u6850\u5e90\u53bf","code":"330122","parent_code":330100},{"title":"\u6df3\u5b89\u53bf","code":"330127","parent_code":330100},{"title":"\u5efa\u5fb7\u5e02","code":"330182","parent_code":330100},{"title":"\u6d77\u66d9\u533a","code":"330203","parent_code":330200},{"title":"\u6c5f\u5317\u533a","code":"330205","parent_code":330200},{"title":"\u5317\u4ed1\u533a","code":"330206","parent_code":330200},{"title":"\u9547\u6d77\u533a","code":"330211","parent_code":330200},{"title":"\u911e\u5dde\u533a","code":"330212","parent_code":330200},{"title":"\u5949\u5316\u533a","code":"330213","parent_code":330200},{"title":"\u8c61\u5c71\u53bf","code":"330225","parent_code":330200},{"title":"\u5b81\u6d77\u53bf","code":"330226","parent_code":330200},{"title":"\u4f59\u59da\u5e02","code":"330281","parent_code":330200},{"title":"\u6148\u6eaa\u5e02","code":"330282","parent_code":330200},{"title":"\u9e7f\u57ce\u533a","code":"330302","parent_code":330300},{"title":"\u9f99\u6e7e\u533a","code":"330303","parent_code":330300},{"title":"\u74ef\u6d77\u533a","code":"330304","parent_code":330300},{"title":"\u6d1e\u5934\u533a","code":"330305","parent_code":330300},{"title":"\u6c38\u5609\u53bf","code":"330324","parent_code":330300},{"title":"\u5e73\u9633\u53bf","code":"330326","parent_code":330300},{"title":"\u82cd\u5357\u53bf","code":"330327","parent_code":330300},{"title":"\u6587\u6210\u53bf","code":"330328","parent_code":330300},{"title":"\u6cf0\u987a\u53bf","code":"330329","parent_code":330300},{"title":"\u745e\u5b89\u5e02","code":"330381","parent_code":330300},{"title":"\u4e50\u6e05\u5e02","code":"330382","parent_code":330300},{"title":"\u9f99\u6e2f\u5e02","code":"330383","parent_code":330300},{"title":"\u5357\u6e56\u533a","code":"330402","parent_code":330400},{"title":"\u79c0\u6d32\u533a","code":"330411","parent_code":330400},{"title":"\u5609\u5584\u53bf","code":"330421","parent_code":330400},{"title":"\u6d77\u76d0\u53bf","code":"330424","parent_code":330400},{"title":"\u6d77\u5b81\u5e02","code":"330481","parent_code":330400},{"title":"\u5e73\u6e56\u5e02","code":"330482","parent_code":330400},{"title":"\u6850\u4e61\u5e02","code":"330483","parent_code":330400},{"title":"\u5434\u5174\u533a","code":"330502","parent_code":330500},{"title":"\u5357\u6d54\u533a","code":"330503","parent_code":330500},{"title":"\u5fb7\u6e05\u53bf","code":"330521","parent_code":330500},{"title":"\u957f\u5174\u53bf","code":"330522","parent_code":330500},{"title":"\u5b89\u5409\u53bf","code":"330523","parent_code":330500},{"title":"\u8d8a\u57ce\u533a","code":"330602","parent_code":330600},{"title":"\u67ef\u6865\u533a","code":"330603","parent_code":330600},{"title":"\u4e0a\u865e\u533a","code":"330604","parent_code":330600},{"title":"\u65b0\u660c\u53bf","code":"330624","parent_code":330600},{"title":"\u8bf8\u66a8\u5e02","code":"330681","parent_code":330600},{"title":"\u5d4a\u5dde\u5e02","code":"330683","parent_code":330600},{"title":"\u5a7a\u57ce\u533a","code":"330702","parent_code":330700},{"title":"\u91d1\u4e1c\u533a","code":"330703","parent_code":330700},{"title":"\u6b66\u4e49\u53bf","code":"330723","parent_code":330700},{"title":"\u6d66\u6c5f\u53bf","code":"330726","parent_code":330700},{"title":"\u78d0\u5b89\u53bf","code":"330727","parent_code":330700},{"title":"\u5170\u6eaa\u5e02","code":"330781","parent_code":330700},{"title":"\u4e49\u4e4c\u5e02","code":"330782","parent_code":330700},{"title":"\u4e1c\u9633\u5e02","code":"330783","parent_code":330700},{"title":"\u6c38\u5eb7\u5e02","code":"330784","parent_code":330700},{"title":"\u67ef\u57ce\u533a","code":"330802","parent_code":330800},{"title":"\u8862\u6c5f\u533a","code":"330803","parent_code":330800},{"title":"\u5e38\u5c71\u53bf","code":"330822","parent_code":330800},{"title":"\u5f00\u5316\u53bf","code":"330824","parent_code":330800},{"title":"\u9f99\u6e38\u53bf","code":"330825","parent_code":330800},{"title":"\u6c5f\u5c71\u5e02","code":"330881","parent_code":330800},{"title":"\u5b9a\u6d77\u533a","code":"330902","parent_code":330900},{"title":"\u666e\u9640\u533a","code":"330903","parent_code":330900},{"title":"\u5cb1\u5c71\u53bf","code":"330921","parent_code":330900},{"title":"\u5d4a\u6cd7\u53bf","code":"330922","parent_code":330900},{"title":"\u6912\u6c5f\u533a","code":"331002","parent_code":331000},{"title":"\u9ec4\u5ca9\u533a","code":"331003","parent_code":331000},{"title":"\u8def\u6865\u533a","code":"331004","parent_code":331000},{"title":"\u4e09\u95e8\u53bf","code":"331022","parent_code":331000},{"title":"\u5929\u53f0\u53bf","code":"331023","parent_code":331000},{"title":"\u4ed9\u5c45\u53bf","code":"331024","parent_code":331000},{"title":"\u6e29\u5cad\u5e02","code":"331081","parent_code":331000},{"title":"\u4e34\u6d77\u5e02","code":"331082","parent_code":331000},{"title":"\u7389\u73af\u5e02","code":"331083","parent_code":331000},{"title":"\u83b2\u90fd\u533a","code":"331102","parent_code":331100},{"title":"\u9752\u7530\u53bf","code":"331121","parent_code":331100},{"title":"\u7f19\u4e91\u53bf","code":"331122","parent_code":331100},{"title":"\u9042\u660c\u53bf","code":"331123","parent_code":331100},{"title":"\u677e\u9633\u53bf","code":"331124","parent_code":331100},{"title":"\u4e91\u548c\u53bf","code":"331125","parent_code":331100},{"title":"\u5e86\u5143\u53bf","code":"331126","parent_code":331100},{"title":"\u666f\u5b81\u7572\u65cf\u81ea\u6cbb\u53bf","code":"331127","parent_code":331100},{"title":"\u9f99\u6cc9\u5e02","code":"331181","parent_code":331100},{"title":"\u7476\u6d77\u533a","code":"340102","parent_code":340100},{"title":"\u5e90\u9633\u533a","code":"340103","parent_code":340100},{"title":"\u8700\u5c71\u533a","code":"340104","parent_code":340100},{"title":"\u5305\u6cb3\u533a","code":"340111","parent_code":340100},{"title":"\u957f\u4e30\u53bf","code":"340121","parent_code":340100},{"title":"\u80a5\u4e1c\u53bf","code":"340122","parent_code":340100},{"title":"\u80a5\u897f\u53bf","code":"340123","parent_code":340100},{"title":"\u5e90\u6c5f\u53bf","code":"340124","parent_code":340100},{"title":"\u5408\u80a5\u9ad8\u65b0\u6280\u672f\u4ea7\u4e1a\u5f00\u53d1\u533a","code":"340171","parent_code":340100},{"title":"\u5408\u80a5\u7ecf\u6d4e\u6280\u672f\u5f00\u53d1\u533a","code":"340172","parent_code":340100},{"title":"\u5408\u80a5\u65b0\u7ad9\u9ad8\u65b0\u6280\u672f\u4ea7\u4e1a\u5f00\u53d1\u533a","code":"340173","parent_code":340100},{"title":"\u5de2\u6e56\u5e02","code":"340181","parent_code":340100},{"title":"\u9ad8\u65b0\u6280\u672f\u5f00\u53d1\u533a","code":"340190","parent_code":340100},{"title":"\u7ecf\u6d4e\u6280\u672f\u5f00\u53d1\u533a","code":"340191","parent_code":340100},{"title":"\u955c\u6e56\u533a","code":"340202","parent_code":340200},{"title":"\u5f0b\u6c5f\u533a","code":"340203","parent_code":340200},{"title":"\u9e20\u6c5f\u533a","code":"340207","parent_code":340200},{"title":"\u4e09\u5c71\u533a","code":"340208","parent_code":340200},{"title":"\u829c\u6e56\u53bf","code":"340221","parent_code":340200},{"title":"\u7e41\u660c\u53bf","code":"340222","parent_code":340200},{"title":"\u5357\u9675\u53bf","code":"340223","parent_code":340200},{"title":"\u65e0\u4e3a\u5e02","code":"340281","parent_code":340200},{"title":"\u9f99\u5b50\u6e56\u533a","code":"340302","parent_code":340300},{"title":"\u868c\u5c71\u533a","code":"340303","parent_code":340300},{"title":"\u79b9\u4f1a\u533a","code":"340304","parent_code":340300},{"title":"\u6dee\u4e0a\u533a","code":"340311","parent_code":340300},{"title":"\u6000\u8fdc\u53bf","code":"340321","parent_code":340300},{"title":"\u4e94\u6cb3\u53bf","code":"340322","parent_code":340300},{"title":"\u56fa\u9547\u53bf","code":"340323","parent_code":340300},{"title":"\u868c\u57e0\u5e02\u9ad8\u65b0\u6280\u672f\u5f00\u53d1\u533a","code":"340371","parent_code":340300},{"title":"\u868c\u57e0\u5e02\u7ecf\u6d4e\u5f00\u53d1\u533a","code":"340372","parent_code":340300},{"title":"\u5927\u901a\u533a","code":"340402","parent_code":340400},{"title":"\u7530\u5bb6\u5eb5\u533a","code":"340403","parent_code":340400},{"title":"\u8c22\u5bb6\u96c6\u533a","code":"340404","parent_code":340400},{"title":"\u516b\u516c\u5c71\u533a","code":"340405","parent_code":340400},{"title":"\u6f58\u96c6\u533a","code":"340406","parent_code":340400},{"title":"\u51e4\u53f0\u53bf","code":"340421","parent_code":340400},{"title":"\u5bff\u53bf","code":"340422","parent_code":340400},{"title":"\u82b1\u5c71\u533a","code":"340503","parent_code":340500},{"title":"\u96e8\u5c71\u533a","code":"340504","parent_code":340500},{"title":"\u535a\u671b\u533a","code":"340506","parent_code":340500},{"title":"\u5f53\u6d82\u53bf","code":"340521","parent_code":340500},{"title":"\u542b\u5c71\u53bf","code":"340522","parent_code":340500},{"title":"\u548c\u53bf","code":"340523","parent_code":340500},{"title":"\u675c\u96c6\u533a","code":"340602","parent_code":340600},{"title":"\u76f8\u5c71\u533a","code":"340603","parent_code":340600},{"title":"\u70c8\u5c71\u533a","code":"340604","parent_code":340600},{"title":"\u6fc9\u6eaa\u53bf","code":"340621","parent_code":340600},{"title":"\u94dc\u5b98\u533a","code":"340705","parent_code":340700},{"title":"\u4e49\u5b89\u533a","code":"340706","parent_code":340700},{"title":"\u90ca\u533a","code":"340711","parent_code":340700},{"title":"\u679e\u9633\u53bf","code":"340722","parent_code":340700},{"title":"\u8fce\u6c5f\u533a","code":"340802","parent_code":340800},{"title":"\u5927\u89c2\u533a","code":"340803","parent_code":340800},{"title":"\u5b9c\u79c0\u533a","code":"340811","parent_code":340800},{"title":"\u6000\u5b81\u53bf","code":"340822","parent_code":340800},{"title":"\u592a\u6e56\u53bf","code":"340825","parent_code":340800},{"title":"\u5bbf\u677e\u53bf","code":"340826","parent_code":340800},{"title":"\u671b\u6c5f\u53bf","code":"340827","parent_code":340800},{"title":"\u5cb3\u897f\u53bf","code":"340828","parent_code":340800},{"title":"\u6850\u57ce\u5e02","code":"340881","parent_code":340800},{"title":"\u6f5c\u5c71\u5e02","code":"340882","parent_code":340800},{"title":"\u5c6f\u6eaa\u533a","code":"341002","parent_code":341000},{"title":"\u9ec4\u5c71\u533a","code":"341003","parent_code":341000},{"title":"\u5fbd\u5dde\u533a","code":"341004","parent_code":341000},{"title":"\u6b59\u53bf","code":"341021","parent_code":341000},{"title":"\u4f11\u5b81\u53bf","code":"341022","parent_code":341000},{"title":"\u9edf\u53bf","code":"341023","parent_code":341000},{"title":"\u7941\u95e8\u53bf","code":"341024","parent_code":341000},{"title":"\u7405\u740a\u533a","code":"341102","parent_code":341100},{"title":"\u5357\u8c2f\u533a","code":"341103","parent_code":341100},{"title":"\u6765\u5b89\u53bf","code":"341122","parent_code":341100},{"title":"\u5168\u6912\u53bf","code":"341124","parent_code":341100},{"title":"\u5b9a\u8fdc\u53bf","code":"341125","parent_code":341100},{"title":"\u51e4\u9633\u53bf","code":"341126","parent_code":341100},{"title":"\u5929\u957f\u5e02","code":"341181","parent_code":341100},{"title":"\u660e\u5149\u5e02","code":"341182","parent_code":341100},{"title":"\u988d\u5dde\u533a","code":"341202","parent_code":341200},{"title":"\u988d\u4e1c\u533a","code":"341203","parent_code":341200},{"title":"\u988d\u6cc9\u533a","code":"341204","parent_code":341200},{"title":"\u4e34\u6cc9\u53bf","code":"341221","parent_code":341200},{"title":"\u592a\u548c\u53bf","code":"341222","parent_code":341200},{"title":"\u961c\u5357\u53bf","code":"341225","parent_code":341200},{"title":"\u988d\u4e0a\u53bf","code":"341226","parent_code":341200},{"title":"\u961c\u9633\u5408\u80a5\u73b0\u4ee3\u4ea7\u4e1a\u56ed\u533a","code":"341271","parent_code":341200},{"title":"\u754c\u9996\u5e02","code":"341282","parent_code":341200},{"title":"\u57c7\u6865\u533a","code":"341302","parent_code":341300},{"title":"\u7800\u5c71\u53bf","code":"341321","parent_code":341300},{"title":"\u8427\u53bf","code":"341322","parent_code":341300},{"title":"\u7075\u74a7\u53bf","code":"341323","parent_code":341300},{"title":"\u6cd7\u53bf","code":"341324","parent_code":341300},{"title":"\u5bbf\u5dde\u9a6c\u978d\u5c71\u73b0\u4ee3\u4ea7\u4e1a\u56ed\u533a","code":"341371","parent_code":341300},{"title":"\u5bbf\u5dde\u7ecf\u6d4e\u6280\u672f\u5f00\u53d1\u533a","code":"341372","parent_code":341300},{"title":"\u7ecf\u6d4e\u5f00\u53d1\u533a","code":"341390","parent_code":341300},{"title":"\u91d1\u5b89\u533a","code":"341502","parent_code":341500},{"title":"\u88d5\u5b89\u533a","code":"341503","parent_code":341500},{"title":"\u53f6\u96c6\u533a","code":"341504","parent_code":341500},{"title":"\u970d\u90b1\u53bf","code":"341522","parent_code":341500},{"title":"\u8212\u57ce\u53bf","code":"341523","parent_code":341500},{"title":"\u91d1\u5be8\u53bf","code":"341524","parent_code":341500},{"title":"\u970d\u5c71\u53bf","code":"341525","parent_code":341500},{"title":"\u8c2f\u57ce\u533a","code":"341602","parent_code":341600},{"title":"\u6da1\u9633\u53bf","code":"341621","parent_code":341600},{"title":"\u8499\u57ce\u53bf","code":"341622","parent_code":341600},{"title":"\u5229\u8f9b\u53bf","code":"341623","parent_code":341600},{"title":"\u8d35\u6c60\u533a","code":"341702","parent_code":341700},{"title":"\u4e1c\u81f3\u53bf","code":"341721","parent_code":341700},{"title":"\u77f3\u53f0\u53bf","code":"341722","parent_code":341700},{"title":"\u9752\u9633\u53bf","code":"341723","parent_code":341700},{"title":"\u5ba3\u5dde\u533a","code":"341802","parent_code":341800},{"title":"\u90ce\u6eaa\u53bf","code":"341821","parent_code":341800},{"title":"\u6cfe\u53bf","code":"341823","parent_code":341800},{"title":"\u7ee9\u6eaa\u53bf","code":"341824","parent_code":341800},{"title":"\u65cc\u5fb7\u53bf","code":"341825","parent_code":341800},{"title":"\u5ba3\u57ce\u5e02\u7ecf\u6d4e\u5f00\u53d1\u533a","code":"341871","parent_code":341800},{"title":"\u5b81\u56fd\u5e02","code":"341881","parent_code":341800},{"title":"\u5e7f\u5fb7\u5e02","code":"341882","parent_code":341800},{"title":"\u9f13\u697c\u533a","code":"350102","parent_code":350100},{"title":"\u53f0\u6c5f\u533a","code":"350103","parent_code":350100},{"title":"\u4ed3\u5c71\u533a","code":"350104","parent_code":350100},{"title":"\u9a6c\u5c3e\u533a","code":"350105","parent_code":350100},{"title":"\u664b\u5b89\u533a","code":"350111","parent_code":350100},{"title":"\u957f\u4e50\u533a","code":"350112","parent_code":350100},{"title":"\u95fd\u4faf\u53bf","code":"350121","parent_code":350100},{"title":"\u8fde\u6c5f\u53bf","code":"350122","parent_code":350100},{"title":"\u7f57\u6e90\u53bf","code":"350123","parent_code":350100},{"title":"\u95fd\u6e05\u53bf","code":"350124","parent_code":350100},{"title":"\u6c38\u6cf0\u53bf","code":"350125","parent_code":350100},{"title":"\u5e73\u6f6d\u53bf","code":"350128","parent_code":350100},{"title":"\u798f\u6e05\u5e02","code":"350181","parent_code":350100},{"title":"\u601d\u660e\u533a","code":"350203","parent_code":350200},{"title":"\u6d77\u6ca7\u533a","code":"350205","parent_code":350200},{"title":"\u6e56\u91cc\u533a","code":"350206","parent_code":350200},{"title":"\u96c6\u7f8e\u533a","code":"350211","parent_code":350200},{"title":"\u540c\u5b89\u533a","code":"350212","parent_code":350200},{"title":"\u7fd4\u5b89\u533a","code":"350213","parent_code":350200},{"title":"\u57ce\u53a2\u533a","code":"350302","parent_code":350300},{"title":"\u6db5\u6c5f\u533a","code":"350303","parent_code":350300},{"title":"\u8354\u57ce\u533a","code":"350304","parent_code":350300},{"title":"\u79c0\u5c7f\u533a","code":"350305","parent_code":350300},{"title":"\u4ed9\u6e38\u53bf","code":"350322","parent_code":350300},{"title":"\u6885\u5217\u533a","code":"350402","parent_code":350400},{"title":"\u4e09\u5143\u533a","code":"350403","parent_code":350400},{"title":"\u660e\u6eaa\u53bf","code":"350421","parent_code":350400},{"title":"\u6e05\u6d41\u53bf","code":"350423","parent_code":350400},{"title":"\u5b81\u5316\u53bf","code":"350424","parent_code":350400},{"title":"\u5927\u7530\u53bf","code":"350425","parent_code":350400},{"title":"\u5c24\u6eaa\u53bf","code":"350426","parent_code":350400},{"title":"\u6c99\u53bf","code":"350427","parent_code":350400},{"title":"\u5c06\u4e50\u53bf","code":"350428","parent_code":350400},{"title":"\u6cf0\u5b81\u53bf","code":"350429","parent_code":350400},{"title":"\u5efa\u5b81\u53bf","code":"350430","parent_code":350400},{"title":"\u6c38\u5b89\u5e02","code":"350481","parent_code":350400},{"title":"\u9ca4\u57ce\u533a","code":"350502","parent_code":350500},{"title":"\u4e30\u6cfd\u533a","code":"350503","parent_code":350500},{"title":"\u6d1b\u6c5f\u533a","code":"350504","parent_code":350500},{"title":"\u6cc9\u6e2f\u533a","code":"350505","parent_code":350500},{"title":"\u60e0\u5b89\u53bf","code":"350521","parent_code":350500},{"title":"\u5b89\u6eaa\u53bf","code":"350524","parent_code":350500},{"title":"\u6c38\u6625\u53bf","code":"350525","parent_code":350500},{"title":"\u5fb7\u5316\u53bf","code":"350526","parent_code":350500},{"title":"\u91d1\u95e8\u53bf","code":"350527","parent_code":350500},{"title":"\u77f3\u72ee\u5e02","code":"350581","parent_code":350500},{"title":"\u664b\u6c5f\u5e02","code":"350582","parent_code":350500},{"title":"\u5357\u5b89\u5e02","code":"350583","parent_code":350500},{"title":"\u8297\u57ce\u533a","code":"350602","parent_code":350600},{"title":"\u9f99\u6587\u533a","code":"350603","parent_code":350600},{"title":"\u4e91\u9704\u53bf","code":"350622","parent_code":350600},{"title":"\u6f33\u6d66\u53bf","code":"350623","parent_code":350600},{"title":"\u8bcf\u5b89\u53bf","code":"350624","parent_code":350600},{"title":"\u957f\u6cf0\u53bf","code":"350625","parent_code":350600},{"title":"\u4e1c\u5c71\u53bf","code":"350626","parent_code":350600},{"title":"\u5357\u9756\u53bf","code":"350627","parent_code":350600},{"title":"\u5e73\u548c\u53bf","code":"350628","parent_code":350600},{"title":"\u534e\u5b89\u53bf","code":"350629","parent_code":350600},{"title":"\u9f99\u6d77\u5e02","code":"350681","parent_code":350600},{"title":"\u5ef6\u5e73\u533a","code":"350702","parent_code":350700},{"title":"\u5efa\u9633\u533a","code":"350703","parent_code":350700},{"title":"\u987a\u660c\u53bf","code":"350721","parent_code":350700},{"title":"\u6d66\u57ce\u53bf","code":"350722","parent_code":350700},{"title":"\u5149\u6cfd\u53bf","code":"350723","parent_code":350700},{"title":"\u677e\u6eaa\u53bf","code":"350724","parent_code":350700},{"title":"\u653f\u548c\u53bf","code":"350725","parent_code":350700},{"title":"\u90b5\u6b66\u5e02","code":"350781","parent_code":350700},{"title":"\u6b66\u5937\u5c71\u5e02","code":"350782","parent_code":350700},{"title":"\u5efa\u74ef\u5e02","code":"350783","parent_code":350700},{"title":"\u65b0\u7f57\u533a","code":"350802","parent_code":350800},{"title":"\u6c38\u5b9a\u533a","code":"350803","parent_code":350800},{"title":"\u957f\u6c40\u53bf","code":"350821","parent_code":350800},{"title":"\u4e0a\u676d\u53bf","code":"350823","parent_code":350800},{"title":"\u6b66\u5e73\u53bf","code":"350824","parent_code":350800},{"title":"\u8fde\u57ce\u53bf","code":"350825","parent_code":350800},{"title":"\u6f33\u5e73\u5e02","code":"350881","parent_code":350800},{"title":"\u8549\u57ce\u533a","code":"350902","parent_code":350900},{"title":"\u971e\u6d66\u53bf","code":"350921","parent_code":350900},{"title":"\u53e4\u7530\u53bf","code":"350922","parent_code":350900},{"title":"\u5c4f\u5357\u53bf","code":"350923","parent_code":350900},{"title":"\u5bff\u5b81\u53bf","code":"350924","parent_code":350900},{"title":"\u5468\u5b81\u53bf","code":"350925","parent_code":350900},{"title":"\u67d8\u8363\u53bf","code":"350926","parent_code":350900},{"title":"\u798f\u5b89\u5e02","code":"350981","parent_code":350900},{"title":"\u798f\u9f0e\u5e02","code":"350982","parent_code":350900},{"title":"\u4e1c\u6e56\u533a","code":"360102","parent_code":360100},{"title":"\u897f\u6e56\u533a","code":"360103","parent_code":360100},{"title":"\u9752\u4e91\u8c31\u533a","code":"360104","parent_code":360100},{"title":"\u9752\u5c71\u6e56\u533a","code":"360111","parent_code":360100},{"title":"\u65b0\u5efa\u533a","code":"360112","parent_code":360100},{"title":"\u7ea2\u8c37\u6ee9\u533a","code":"360113","parent_code":360100},{"title":"\u5357\u660c\u53bf","code":"360121","parent_code":360100},{"title":"\u5b89\u4e49\u53bf","code":"360123","parent_code":360100},{"title":"\u8fdb\u8d24\u53bf","code":"360124","parent_code":360100},{"title":"\u7ecf\u6d4e\u6280\u672f\u5f00\u53d1\u533a","code":"360190","parent_code":360100},{"title":"\u9ad8\u65b0\u533a","code":"360192","parent_code":360100},{"title":"\u660c\u6c5f\u533a","code":"360202","parent_code":360200},{"title":"\u73e0\u5c71\u533a","code":"360203","parent_code":360200},{"title":"\u6d6e\u6881\u53bf","code":"360222","parent_code":360200},{"title":"\u4e50\u5e73\u5e02","code":"360281","parent_code":360200},{"title":"\u5b89\u6e90\u533a","code":"360302","parent_code":360300},{"title":"\u6e58\u4e1c\u533a","code":"360313","parent_code":360300},{"title":"\u83b2\u82b1\u53bf","code":"360321","parent_code":360300},{"title":"\u4e0a\u6817\u53bf","code":"360322","parent_code":360300},{"title":"\u82a6\u6eaa\u53bf","code":"360323","parent_code":360300},{"title":"\u6fc2\u6eaa\u533a","code":"360402","parent_code":360400},{"title":"\u6d54\u9633\u533a","code":"360403","parent_code":360400},{"title":"\u67f4\u6851\u533a","code":"360404","parent_code":360400},{"title":"\u6b66\u5b81\u53bf","code":"360423","parent_code":360400},{"title":"\u4fee\u6c34\u53bf","code":"360424","parent_code":360400},{"title":"\u6c38\u4fee\u53bf","code":"360425","parent_code":360400},{"title":"\u5fb7\u5b89\u53bf","code":"360426","parent_code":360400},{"title":"\u90fd\u660c\u53bf","code":"360428","parent_code":360400},{"title":"\u6e56\u53e3\u53bf","code":"360429","parent_code":360400},{"title":"\u5f6d\u6cfd\u53bf","code":"360430","parent_code":360400},{"title":"\u745e\u660c\u5e02","code":"360481","parent_code":360400},{"title":"\u5171\u9752\u57ce\u5e02","code":"360482","parent_code":360400},{"title":"\u5e90\u5c71\u5e02","code":"360483","parent_code":360400},{"title":"\u7ecf\u6d4e\u6280\u672f\u5f00\u53d1\u533a","code":"360490","parent_code":360400},{"title":"\u6e1d\u6c34\u533a","code":"360502","parent_code":360500},{"title":"\u5206\u5b9c\u53bf","code":"360521","parent_code":360500},{"title":"\u6708\u6e56\u533a","code":"360602","parent_code":360600},{"title":"\u4f59\u6c5f\u533a","code":"360603","parent_code":360600},{"title":"\u8d35\u6eaa\u5e02","code":"360681","parent_code":360600},{"title":"\u7ae0\u8d21\u533a","code":"360702","parent_code":360700},{"title":"\u5357\u5eb7\u533a","code":"360703","parent_code":360700},{"title":"\u8d63\u53bf\u533a","code":"360704","parent_code":360700},{"title":"\u4fe1\u4e30\u53bf","code":"360722","parent_code":360700},{"title":"\u5927\u4f59\u53bf","code":"360723","parent_code":360700},{"title":"\u4e0a\u72b9\u53bf","code":"360724","parent_code":360700},{"title":"\u5d07\u4e49\u53bf","code":"360725","parent_code":360700},{"title":"\u5b89\u8fdc\u53bf","code":"360726","parent_code":360700},{"title":"\u9f99\u5357\u53bf","code":"360727","parent_code":360700},{"title":"\u5b9a\u5357\u53bf","code":"360728","parent_code":360700},{"title":"\u5168\u5357\u53bf","code":"360729","parent_code":360700},{"title":"\u5b81\u90fd\u53bf","code":"360730","parent_code":360700},{"title":"\u4e8e\u90fd\u53bf","code":"360731","parent_code":360700},{"title":"\u5174\u56fd\u53bf","code":"360732","parent_code":360700},{"title":"\u4f1a\u660c\u53bf","code":"360733","parent_code":360700},{"title":"\u5bfb\u4e4c\u53bf","code":"360734","parent_code":360700},{"title":"\u77f3\u57ce\u53bf","code":"360735","parent_code":360700},{"title":"\u745e\u91d1\u5e02","code":"360781","parent_code":360700},{"title":"\u5409\u5dde\u533a","code":"360802","parent_code":360800},{"title":"\u9752\u539f\u533a","code":"360803","parent_code":360800},{"title":"\u5409\u5b89\u53bf","code":"360821","parent_code":360800},{"title":"\u5409\u6c34\u53bf","code":"360822","parent_code":360800},{"title":"\u5ce1\u6c5f\u53bf","code":"360823","parent_code":360800},{"title":"\u65b0\u5e72\u53bf","code":"360824","parent_code":360800},{"title":"\u6c38\u4e30\u53bf","code":"360825","parent_code":360800},{"title":"\u6cf0\u548c\u53bf","code":"360826","parent_code":360800},{"title":"\u9042\u5ddd\u53bf","code":"360827","parent_code":360800},{"title":"\u4e07\u5b89\u53bf","code":"360828","parent_code":360800},{"title":"\u5b89\u798f\u53bf","code":"360829","parent_code":360800},{"title":"\u6c38\u65b0\u53bf","code":"360830","parent_code":360800},{"title":"\u4e95\u5188\u5c71\u5e02","code":"360881","parent_code":360800},{"title":"\u8881\u5dde\u533a","code":"360902","parent_code":360900},{"title":"\u5949\u65b0\u53bf","code":"360921","parent_code":360900},{"title":"\u4e07\u8f7d\u53bf","code":"360922","parent_code":360900},{"title":"\u4e0a\u9ad8\u53bf","code":"360923","parent_code":360900},{"title":"\u5b9c\u4e30\u53bf","code":"360924","parent_code":360900},{"title":"\u9756\u5b89\u53bf","code":"360925","parent_code":360900},{"title":"\u94dc\u9f13\u53bf","code":"360926","parent_code":360900},{"title":"\u4e30\u57ce\u5e02","code":"360981","parent_code":360900},{"title":"\u6a1f\u6811\u5e02","code":"360982","parent_code":360900},{"title":"\u9ad8\u5b89\u5e02","code":"360983","parent_code":360900},{"title":"\u4e34\u5ddd\u533a","code":"361002","parent_code":361000},{"title":"\u4e1c\u4e61\u533a","code":"361003","parent_code":361000},{"title":"\u5357\u57ce\u53bf","code":"361021","parent_code":361000},{"title":"\u9ece\u5ddd\u53bf","code":"361022","parent_code":361000},{"title":"\u5357\u4e30\u53bf","code":"361023","parent_code":361000},{"title":"\u5d07\u4ec1\u53bf","code":"361024","parent_code":361000},{"title":"\u4e50\u5b89\u53bf","code":"361025","parent_code":361000},{"title":"\u5b9c\u9ec4\u53bf","code":"361026","parent_code":361000},{"title":"\u91d1\u6eaa\u53bf","code":"361027","parent_code":361000},{"title":"\u8d44\u6eaa\u53bf","code":"361028","parent_code":361000},{"title":"\u5e7f\u660c\u53bf","code":"361030","parent_code":361000},{"title":"\u4fe1\u5dde\u533a","code":"361102","parent_code":361100},{"title":"\u5e7f\u4e30\u533a","code":"361103","parent_code":361100},{"title":"\u5e7f\u4fe1\u533a","code":"361104","parent_code":361100},{"title":"\u7389\u5c71\u53bf","code":"361123","parent_code":361100},{"title":"\u94c5\u5c71\u53bf","code":"361124","parent_code":361100},{"title":"\u6a2a\u5cf0\u53bf","code":"361125","parent_code":361100},{"title":"\u5f0b\u9633\u53bf","code":"361126","parent_code":361100},{"title":"\u4f59\u5e72\u53bf","code":"361127","parent_code":361100},{"title":"\u9131\u9633\u53bf","code":"361128","parent_code":361100},{"title":"\u4e07\u5e74\u53bf","code":"361129","parent_code":361100},{"title":"\u5a7a\u6e90\u53bf","code":"361130","parent_code":361100},{"title":"\u5fb7\u5174\u5e02","code":"361181","parent_code":361100},{"title":"\u5386\u4e0b\u533a","code":"370102","parent_code":370100},{"title":"\u5e02\u4e2d\u533a","code":"370103","parent_code":370100},{"title":"\u69d0\u836b\u533a","code":"370104","parent_code":370100},{"title":"\u5929\u6865\u533a","code":"370105","parent_code":370100},{"title":"\u5386\u57ce\u533a","code":"370112","parent_code":370100},{"title":"\u957f\u6e05\u533a","code":"370113","parent_code":370100},{"title":"\u7ae0\u4e18\u533a","code":"370114","parent_code":370100},{"title":"\u6d4e\u9633\u533a","code":"370115","parent_code":370100},{"title":"\u83b1\u829c\u533a","code":"370116","parent_code":370100},{"title":"\u94a2\u57ce\u533a","code":"370117","parent_code":370100},{"title":"\u5e73\u9634\u53bf","code":"370124","parent_code":370100},{"title":"\u5546\u6cb3\u53bf","code":"370126","parent_code":370100},{"title":"\u6d4e\u5357\u9ad8\u65b0\u6280\u672f\u4ea7\u4e1a\u5f00\u53d1\u533a","code":"370171","parent_code":370100},{"title":"\u9ad8\u65b0\u533a","code":"370190","parent_code":370100},{"title":"\u5e02\u5357\u533a","code":"370202","parent_code":370200},{"title":"\u5e02\u5317\u533a","code":"370203","parent_code":370200},{"title":"\u9ec4\u5c9b\u533a","code":"370211","parent_code":370200},{"title":"\u5d02\u5c71\u533a","code":"370212","parent_code":370200},{"title":"\u674e\u6ca7\u533a","code":"370213","parent_code":370200},{"title":"\u57ce\u9633\u533a","code":"370214","parent_code":370200},{"title":"\u5373\u58a8\u533a","code":"370215","parent_code":370200},{"title":"\u9752\u5c9b\u9ad8\u65b0\u6280\u672f\u4ea7\u4e1a\u5f00\u53d1\u533a","code":"370271","parent_code":370200},{"title":"\u80f6\u5dde\u5e02","code":"370281","parent_code":370200},{"title":"\u5e73\u5ea6\u5e02","code":"370283","parent_code":370200},{"title":"\u83b1\u897f\u5e02","code":"370285","parent_code":370200},{"title":"\u5f00\u53d1\u533a","code":"370290","parent_code":370200},{"title":"\u6dc4\u5ddd\u533a","code":"370302","parent_code":370300},{"title":"\u5f20\u5e97\u533a","code":"370303","parent_code":370300},{"title":"\u535a\u5c71\u533a","code":"370304","parent_code":370300},{"title":"\u4e34\u6dc4\u533a","code":"370305","parent_code":370300},{"title":"\u5468\u6751\u533a","code":"370306","parent_code":370300},{"title":"\u6853\u53f0\u53bf","code":"370321","parent_code":370300},{"title":"\u9ad8\u9752\u53bf","code":"370322","parent_code":370300},{"title":"\u6c82\u6e90\u53bf","code":"370323","parent_code":370300},{"title":"\u5e02\u4e2d\u533a","code":"370402","parent_code":370400},{"title":"\u859b\u57ce\u533a","code":"370403","parent_code":370400},{"title":"\u5cc4\u57ce\u533a","code":"370404","parent_code":370400},{"title":"\u53f0\u513f\u5e84\u533a","code":"370405","parent_code":370400},{"title":"\u5c71\u4ead\u533a","code":"370406","parent_code":370400},{"title":"\u6ed5\u5dde\u5e02","code":"370481","parent_code":370400},{"title":"\u4e1c\u8425\u533a","code":"370502","parent_code":370500},{"title":"\u6cb3\u53e3\u533a","code":"370503","parent_code":370500},{"title":"\u57a6\u5229\u533a","code":"370505","parent_code":370500},{"title":"\u5229\u6d25\u53bf","code":"370522","parent_code":370500},{"title":"\u5e7f\u9976\u53bf","code":"370523","parent_code":370500},{"title":"\u4e1c\u8425\u7ecf\u6d4e\u6280\u672f\u5f00\u53d1\u533a","code":"370571","parent_code":370500},{"title":"\u4e1c\u8425\u6e2f\u7ecf\u6d4e\u5f00\u53d1\u533a","code":"370572","parent_code":370500},{"title":"\u829d\u7f58\u533a","code":"370602","parent_code":370600},{"title":"\u798f\u5c71\u533a","code":"370611","parent_code":370600},{"title":"\u725f\u5e73\u533a","code":"370612","parent_code":370600},{"title":"\u83b1\u5c71\u533a","code":"370613","parent_code":370600},{"title":"\u957f\u5c9b\u53bf","code":"370634","parent_code":370600},{"title":"\u70df\u53f0\u9ad8\u65b0\u6280\u672f\u4ea7\u4e1a\u5f00\u53d1\u533a","code":"370671","parent_code":370600},{"title":"\u70df\u53f0\u7ecf\u6d4e\u6280\u672f\u5f00\u53d1\u533a","code":"370672","parent_code":370600},{"title":"\u9f99\u53e3\u5e02","code":"370681","parent_code":370600},{"title":"\u83b1\u9633\u5e02","code":"370682","parent_code":370600},{"title":"\u83b1\u5dde\u5e02","code":"370683","parent_code":370600},{"title":"\u84ec\u83b1\u5e02","code":"370684","parent_code":370600},{"title":"\u62db\u8fdc\u5e02","code":"370685","parent_code":370600},{"title":"\u6816\u971e\u5e02","code":"370686","parent_code":370600},{"title":"\u6d77\u9633\u5e02","code":"370687","parent_code":370600},{"title":"\u5f00\u53d1\u533a","code":"370690","parent_code":370600},{"title":"\u6f4d\u57ce\u533a","code":"370702","parent_code":370700},{"title":"\u5bd2\u4ead\u533a","code":"370703","parent_code":370700},{"title":"\u574a\u5b50\u533a","code":"370704","parent_code":370700},{"title":"\u594e\u6587\u533a","code":"370705","parent_code":370700},{"title":"\u4e34\u6710\u53bf","code":"370724","parent_code":370700},{"title":"\u660c\u4e50\u53bf","code":"370725","parent_code":370700},{"title":"\u6f4d\u574a\u6ee8\u6d77\u7ecf\u6d4e\u6280\u672f\u5f00\u53d1\u533a","code":"370772","parent_code":370700},{"title":"\u9752\u5dde\u5e02","code":"370781","parent_code":370700},{"title":"\u8bf8\u57ce\u5e02","code":"370782","parent_code":370700},{"title":"\u5bff\u5149\u5e02","code":"370783","parent_code":370700},{"title":"\u5b89\u4e18\u5e02","code":"370784","parent_code":370700},{"title":"\u9ad8\u5bc6\u5e02","code":"370785","parent_code":370700},{"title":"\u660c\u9091\u5e02","code":"370786","parent_code":370700},{"title":"\u5f00\u53d1\u533a","code":"370790","parent_code":370700},{"title":"\u9ad8\u65b0\u533a","code":"370791","parent_code":370700},{"title":"\u4efb\u57ce\u533a","code":"370811","parent_code":370800},{"title":"\u5156\u5dde\u533a","code":"370812","parent_code":370800},{"title":"\u5fae\u5c71\u53bf","code":"370826","parent_code":370800},{"title":"\u9c7c\u53f0\u53bf","code":"370827","parent_code":370800},{"title":"\u91d1\u4e61\u53bf","code":"370828","parent_code":370800},{"title":"\u5609\u7965\u53bf","code":"370829","parent_code":370800},{"title":"\u6c76\u4e0a\u53bf","code":"370830","parent_code":370800},{"title":"\u6cd7\u6c34\u53bf","code":"370831","parent_code":370800},{"title":"\u6881\u5c71\u53bf","code":"370832","parent_code":370800},{"title":"\u6d4e\u5b81\u9ad8\u65b0\u6280\u672f\u4ea7\u4e1a\u5f00\u53d1\u533a","code":"370871","parent_code":370800},{"title":"\u66f2\u961c\u5e02","code":"370881","parent_code":370800},{"title":"\u90b9\u57ce\u5e02","code":"370883","parent_code":370800},{"title":"\u9ad8\u65b0\u533a","code":"370890","parent_code":370800},{"title":"\u6cf0\u5c71\u533a","code":"370902","parent_code":370900},{"title":"\u5cb1\u5cb3\u533a","code":"370911","parent_code":370900},{"title":"\u5b81\u9633\u53bf","code":"370921","parent_code":370900},{"title":"\u4e1c\u5e73\u53bf","code":"370923","parent_code":370900},{"title":"\u65b0\u6cf0\u5e02","code":"370982","parent_code":370900},{"title":"\u80a5\u57ce\u5e02","code":"370983","parent_code":370900},{"title":"\u73af\u7fe0\u533a","code":"371002","parent_code":371000},{"title":"\u6587\u767b\u533a","code":"371003","parent_code":371000},{"title":"\u5a01\u6d77\u706b\u70ac\u9ad8\u6280\u672f\u4ea7\u4e1a\u5f00\u53d1\u533a","code":"371071","parent_code":371000},{"title":"\u5a01\u6d77\u7ecf\u6d4e\u6280\u672f\u5f00\u53d1\u533a","code":"371072","parent_code":371000},{"title":"\u8363\u6210\u5e02","code":"371082","parent_code":371000},{"title":"\u4e73\u5c71\u5e02","code":"371083","parent_code":371000},{"title":"\u7ecf\u6d4e\u6280\u672f\u5f00\u53d1\u533a","code":"371091","parent_code":371000},{"title":"\u4e1c\u6e2f\u533a","code":"371102","parent_code":371100},{"title":"\u5c9a\u5c71\u533a","code":"371103","parent_code":371100},{"title":"\u4e94\u83b2\u53bf","code":"371121","parent_code":371100},{"title":"\u8392\u53bf","code":"371122","parent_code":371100},{"title":"\u65e5\u7167\u7ecf\u6d4e\u6280\u672f\u5f00\u53d1\u533a","code":"371171","parent_code":371100},{"title":"\u5170\u5c71\u533a","code":"371302","parent_code":371300},{"title":"\u7f57\u5e84\u533a","code":"371311","parent_code":371300},{"title":"\u6cb3\u4e1c\u533a","code":"371312","parent_code":371300},{"title":"\u6c82\u5357\u53bf","code":"371321","parent_code":371300},{"title":"\u90ef\u57ce\u53bf","code":"371322","parent_code":371300},{"title":"\u6c82\u6c34\u53bf","code":"371323","parent_code":371300},{"title":"\u5170\u9675\u53bf","code":"371324","parent_code":371300},{"title":"\u8d39\u53bf","code":"371325","parent_code":371300},{"title":"\u5e73\u9091\u53bf","code":"371326","parent_code":371300},{"title":"\u8392\u5357\u53bf","code":"371327","parent_code":371300},{"title":"\u8499\u9634\u53bf","code":"371328","parent_code":371300},{"title":"\u4e34\u6cad\u53bf","code":"371329","parent_code":371300},{"title":"\u4e34\u6c82\u9ad8\u65b0\u6280\u672f\u4ea7\u4e1a\u5f00\u53d1\u533a","code":"371371","parent_code":371300},{"title":"\u5fb7\u57ce\u533a","code":"371402","parent_code":371400},{"title":"\u9675\u57ce\u533a","code":"371403","parent_code":371400},{"title":"\u5b81\u6d25\u53bf","code":"371422","parent_code":371400},{"title":"\u5e86\u4e91\u53bf","code":"371423","parent_code":371400},{"title":"\u4e34\u9091\u53bf","code":"371424","parent_code":371400},{"title":"\u9f50\u6cb3\u53bf","code":"371425","parent_code":371400},{"title":"\u5e73\u539f\u53bf","code":"371426","parent_code":371400},{"title":"\u590f\u6d25\u53bf","code":"371427","parent_code":371400},{"title":"\u6b66\u57ce\u53bf","code":"371428","parent_code":371400},{"title":"\u5fb7\u5dde\u8fd0\u6cb3\u7ecf\u6d4e\u5f00\u53d1\u533a","code":"371472","parent_code":371400},{"title":"\u4e50\u9675\u5e02","code":"371481","parent_code":371400},{"title":"\u79b9\u57ce\u5e02","code":"371482","parent_code":371400},{"title":"\u4e1c\u660c\u5e9c\u533a","code":"371502","parent_code":371500},{"title":"\u830c\u5e73\u533a","code":"371503","parent_code":371500},{"title":"\u9633\u8c37\u53bf","code":"371521","parent_code":371500},{"title":"\u8398\u53bf","code":"371522","parent_code":371500},{"title":"\u4e1c\u963f\u53bf","code":"371524","parent_code":371500},{"title":"\u51a0\u53bf","code":"371525","parent_code":371500},{"title":"\u9ad8\u5510\u53bf","code":"371526","parent_code":371500},{"title":"\u4e34\u6e05\u5e02","code":"371581","parent_code":371500},{"title":"\u6ee8\u57ce\u533a","code":"371602","parent_code":371600},{"title":"\u6cbe\u5316\u533a","code":"371603","parent_code":371600},{"title":"\u60e0\u6c11\u53bf","code":"371621","parent_code":371600},{"title":"\u9633\u4fe1\u53bf","code":"371622","parent_code":371600},{"title":"\u65e0\u68e3\u53bf","code":"371623","parent_code":371600},{"title":"\u535a\u5174\u53bf","code":"371625","parent_code":371600},{"title":"\u90b9\u5e73\u5e02","code":"371681","parent_code":371600},{"title":"\u7261\u4e39\u533a","code":"371702","parent_code":371700},{"title":"\u5b9a\u9676\u533a","code":"371703","parent_code":371700},{"title":"\u66f9\u53bf","code":"371721","parent_code":371700},{"title":"\u5355\u53bf","code":"371722","parent_code":371700},{"title":"\u6210\u6b66\u53bf","code":"371723","parent_code":371700},{"title":"\u5de8\u91ce\u53bf","code":"371724","parent_code":371700},{"title":"\u90d3\u57ce\u53bf","code":"371725","parent_code":371700},{"title":"\u9104\u57ce\u53bf","code":"371726","parent_code":371700},{"title":"\u4e1c\u660e\u53bf","code":"371728","parent_code":371700},{"title":"\u83cf\u6cfd\u7ecf\u6d4e\u6280\u672f\u5f00\u53d1\u533a","code":"371771","parent_code":371700},{"title":"\u83cf\u6cfd\u9ad8\u65b0\u6280\u672f\u5f00\u53d1\u533a","code":"371772","parent_code":371700},{"title":"\u4e2d\u539f\u533a","code":"410102","parent_code":410100},{"title":"\u4e8c\u4e03\u533a","code":"410103","parent_code":410100},{"title":"\u7ba1\u57ce\u56de\u65cf\u533a","code":"410104","parent_code":410100},{"title":"\u91d1\u6c34\u533a","code":"410105","parent_code":410100},{"title":"\u4e0a\u8857\u533a","code":"410106","parent_code":410100},{"title":"\u60e0\u6d4e\u533a","code":"410108","parent_code":410100},{"title":"\u4e2d\u725f\u53bf","code":"410122","parent_code":410100},{"title":"\u90d1\u5dde\u7ecf\u6d4e\u6280\u672f\u5f00\u53d1\u533a","code":"410171","parent_code":410100},{"title":"\u90d1\u5dde\u9ad8\u65b0\u6280\u672f\u4ea7\u4e1a\u5f00\u53d1\u533a","code":"410172","parent_code":410100},{"title":"\u90d1\u5dde\u822a\u7a7a\u6e2f\u7ecf\u6d4e\u7efc\u5408\u5b9e\u9a8c\u533a","code":"410173","parent_code":410100},{"title":"\u5de9\u4e49\u5e02","code":"410181","parent_code":410100},{"title":"\u8365\u9633\u5e02","code":"410182","parent_code":410100},{"title":"\u65b0\u5bc6\u5e02","code":"410183","parent_code":410100},{"title":"\u65b0\u90d1\u5e02","code":"410184","parent_code":410100},{"title":"\u767b\u5c01\u5e02","code":"410185","parent_code":410100},{"title":"\u9ad8\u65b0\u6280\u672f\u5f00\u53d1\u533a","code":"410190","parent_code":410100},{"title":"\u7ecf\u6d4e\u6280\u672f\u5f00\u53d1\u533a","code":"410191","parent_code":410100},{"title":"\u9f99\u4ead\u533a","code":"410202","parent_code":410200},{"title":"\u987a\u6cb3\u56de\u65cf\u533a","code":"410203","parent_code":410200},{"title":"\u9f13\u697c\u533a","code":"410204","parent_code":410200},{"title":"\u79b9\u738b\u53f0\u533a","code":"410205","parent_code":410200},{"title":"\u7965\u7b26\u533a","code":"410212","parent_code":410200},{"title":"\u675e\u53bf","code":"410221","parent_code":410200},{"title":"\u901a\u8bb8\u53bf","code":"410222","parent_code":410200},{"title":"\u5c09\u6c0f\u53bf","code":"410223","parent_code":410200},{"title":"\u5170\u8003\u53bf","code":"410225","parent_code":410200},{"title":"\u8001\u57ce\u533a","code":"410302","parent_code":410300},{"title":"\u897f\u5de5\u533a","code":"410303","parent_code":410300},{"title":"\u700d\u6cb3\u56de\u65cf\u533a","code":"410304","parent_code":410300},{"title":"\u6da7\u897f\u533a","code":"410305","parent_code":410300},{"title":"\u5409\u5229\u533a","code":"410306","parent_code":410300},{"title":"\u6d1b\u9f99\u533a","code":"410311","parent_code":410300},{"title":"\u5b5f\u6d25\u53bf","code":"410322","parent_code":410300},{"title":"\u65b0\u5b89\u53bf","code":"410323","parent_code":410300},{"title":"\u683e\u5ddd\u53bf","code":"410324","parent_code":410300},{"title":"\u5d69\u53bf","code":"410325","parent_code":410300},{"title":"\u6c5d\u9633\u53bf","code":"410326","parent_code":410300},{"title":"\u5b9c\u9633\u53bf","code":"410327","parent_code":410300},{"title":"\u6d1b\u5b81\u53bf","code":"410328","parent_code":410300},{"title":"\u4f0a\u5ddd\u53bf","code":"410329","parent_code":410300},{"title":"\u5043\u5e08\u5e02","code":"410381","parent_code":410300},{"title":"\u65b0\u534e\u533a","code":"410402","parent_code":410400},{"title":"\u536b\u4e1c\u533a","code":"410403","parent_code":410400},{"title":"\u77f3\u9f99\u533a","code":"410404","parent_code":410400},{"title":"\u6e5b\u6cb3\u533a","code":"410411","parent_code":410400},{"title":"\u5b9d\u4e30\u53bf","code":"410421","parent_code":410400},{"title":"\u53f6\u53bf","code":"410422","parent_code":410400},{"title":"\u9c81\u5c71\u53bf","code":"410423","parent_code":410400},{"title":"\u90cf\u53bf","code":"410425","parent_code":410400},{"title":"\u5e73\u9876\u5c71\u9ad8\u65b0\u6280\u672f\u4ea7\u4e1a\u5f00\u53d1\u533a","code":"410471","parent_code":410400},{"title":"\u821e\u94a2\u5e02","code":"410481","parent_code":410400},{"title":"\u6c5d\u5dde\u5e02","code":"410482","parent_code":410400},{"title":"\u6587\u5cf0\u533a","code":"410502","parent_code":410500},{"title":"\u5317\u5173\u533a","code":"410503","parent_code":410500},{"title":"\u6bb7\u90fd\u533a","code":"410505","parent_code":410500},{"title":"\u9f99\u5b89\u533a","code":"410506","parent_code":410500},{"title":"\u5b89\u9633\u53bf","code":"410522","parent_code":410500},{"title":"\u6c64\u9634\u53bf","code":"410523","parent_code":410500},{"title":"\u6ed1\u53bf","code":"410526","parent_code":410500},{"title":"\u5185\u9ec4\u53bf","code":"410527","parent_code":410500},{"title":"\u6797\u5dde\u5e02","code":"410581","parent_code":410500},{"title":"\u5f00\u53d1\u533a","code":"410590","parent_code":410500},{"title":"\u9e64\u5c71\u533a","code":"410602","parent_code":410600},{"title":"\u5c71\u57ce\u533a","code":"410603","parent_code":410600},{"title":"\u6dc7\u6ee8\u533a","code":"410611","parent_code":410600},{"title":"\u6d5a\u53bf","code":"410621","parent_code":410600},{"title":"\u6dc7\u53bf","code":"410622","parent_code":410600},{"title":"\u7ea2\u65d7\u533a","code":"410702","parent_code":410700},{"title":"\u536b\u6ee8\u533a","code":"410703","parent_code":410700},{"title":"\u51e4\u6cc9\u533a","code":"410704","parent_code":410700},{"title":"\u7267\u91ce\u533a","code":"410711","parent_code":410700},{"title":"\u65b0\u4e61\u53bf","code":"410721","parent_code":410700},{"title":"\u83b7\u5609\u53bf","code":"410724","parent_code":410700},{"title":"\u539f\u9633\u53bf","code":"410725","parent_code":410700},{"title":"\u5ef6\u6d25\u53bf","code":"410726","parent_code":410700},{"title":"\u5c01\u4e18\u53bf","code":"410727","parent_code":410700},{"title":"\u65b0\u4e61\u9ad8\u65b0\u6280\u672f\u4ea7\u4e1a\u5f00\u53d1\u533a","code":"410771","parent_code":410700},{"title":"\u65b0\u4e61\u7ecf\u6d4e\u6280\u672f\u5f00\u53d1\u533a","code":"410772","parent_code":410700},{"title":"\u536b\u8f89\u5e02","code":"410781","parent_code":410700},{"title":"\u8f89\u53bf\u5e02","code":"410782","parent_code":410700},{"title":"\u957f\u57a3\u5e02","code":"410783","parent_code":410700},{"title":"\u89e3\u653e\u533a","code":"410802","parent_code":410800},{"title":"\u4e2d\u7ad9\u533a","code":"410803","parent_code":410800},{"title":"\u9a6c\u6751\u533a","code":"410804","parent_code":410800},{"title":"\u5c71\u9633\u533a","code":"410811","parent_code":410800},{"title":"\u4fee\u6b66\u53bf","code":"410821","parent_code":410800},{"title":"\u535a\u7231\u53bf","code":"410822","parent_code":410800},{"title":"\u6b66\u965f\u53bf","code":"410823","parent_code":410800},{"title":"\u6e29\u53bf","code":"410825","parent_code":410800},{"title":"\u7126\u4f5c\u57ce\u4e61\u4e00\u4f53\u5316\u793a\u8303\u533a","code":"410871","parent_code":410800},{"title":"\u6c81\u9633\u5e02","code":"410882","parent_code":410800},{"title":"\u5b5f\u5dde\u5e02","code":"410883","parent_code":410800},{"title":"\u534e\u9f99\u533a","code":"410902","parent_code":410900},{"title":"\u6e05\u4e30\u53bf","code":"410922","parent_code":410900},{"title":"\u5357\u4e50\u53bf","code":"410923","parent_code":410900},{"title":"\u8303\u53bf","code":"410926","parent_code":410900},{"title":"\u53f0\u524d\u53bf","code":"410927","parent_code":410900},{"title":"\u6fee\u9633\u53bf","code":"410928","parent_code":410900},{"title":"\u6cb3\u5357\u6fee\u9633\u5de5\u4e1a\u56ed\u533a","code":"410971","parent_code":410900},{"title":"\u9b4f\u90fd\u533a","code":"411002","parent_code":411000},{"title":"\u5efa\u5b89\u533a","code":"411003","parent_code":411000},{"title":"\u9122\u9675\u53bf","code":"411024","parent_code":411000},{"title":"\u8944\u57ce\u53bf","code":"411025","parent_code":411000},{"title":"\u8bb8\u660c\u7ecf\u6d4e\u6280\u672f\u5f00\u53d1\u533a","code":"411071","parent_code":411000},{"title":"\u79b9\u5dde\u5e02","code":"411081","parent_code":411000},{"title":"\u957f\u845b\u5e02","code":"411082","parent_code":411000},{"title":"\u6e90\u6c47\u533a","code":"411102","parent_code":411100},{"title":"\u90fe\u57ce\u533a","code":"411103","parent_code":411100},{"title":"\u53ec\u9675\u533a","code":"411104","parent_code":411100},{"title":"\u821e\u9633\u53bf","code":"411121","parent_code":411100},{"title":"\u4e34\u988d\u53bf","code":"411122","parent_code":411100},{"title":"\u6f2f\u6cb3\u7ecf\u6d4e\u6280\u672f\u5f00\u53d1\u533a","code":"411171","parent_code":411100},{"title":"\u6e56\u6ee8\u533a","code":"411202","parent_code":411200},{"title":"\u9655\u5dde\u533a","code":"411203","parent_code":411200},{"title":"\u6e11\u6c60\u53bf","code":"411221","parent_code":411200},{"title":"\u5362\u6c0f\u53bf","code":"411224","parent_code":411200},{"title":"\u6cb3\u5357\u4e09\u95e8\u5ce1\u7ecf\u6d4e\u5f00\u53d1\u533a","code":"411271","parent_code":411200},{"title":"\u4e49\u9a6c\u5e02","code":"411281","parent_code":411200},{"title":"\u7075\u5b9d\u5e02","code":"411282","parent_code":411200},{"title":"\u5b9b\u57ce\u533a","code":"411302","parent_code":411300},{"title":"\u5367\u9f99\u533a","code":"411303","parent_code":411300},{"title":"\u5357\u53ec\u53bf","code":"411321","parent_code":411300},{"title":"\u65b9\u57ce\u53bf","code":"411322","parent_code":411300},{"title":"\u897f\u5ce1\u53bf","code":"411323","parent_code":411300},{"title":"\u9547\u5e73\u53bf","code":"411324","parent_code":411300},{"title":"\u5185\u4e61\u53bf","code":"411325","parent_code":411300},{"title":"\u6dc5\u5ddd\u53bf","code":"411326","parent_code":411300},{"title":"\u793e\u65d7\u53bf","code":"411327","parent_code":411300},{"title":"\u5510\u6cb3\u53bf","code":"411328","parent_code":411300},{"title":"\u65b0\u91ce\u53bf","code":"411329","parent_code":411300},{"title":"\u6850\u67cf\u53bf","code":"411330","parent_code":411300},{"title":"\u5357\u9633\u5e02\u57ce\u4e61\u4e00\u4f53\u5316\u793a\u8303\u533a","code":"411372","parent_code":411300},{"title":"\u9093\u5dde\u5e02","code":"411381","parent_code":411300},{"title":"\u6881\u56ed\u533a","code":"411402","parent_code":411400},{"title":"\u7762\u9633\u533a","code":"411403","parent_code":411400},{"title":"\u6c11\u6743\u53bf","code":"411421","parent_code":411400},{"title":"\u7762\u53bf","code":"411422","parent_code":411400},{"title":"\u5b81\u9675\u53bf","code":"411423","parent_code":411400},{"title":"\u67d8\u57ce\u53bf","code":"411424","parent_code":411400},{"title":"\u865e\u57ce\u53bf","code":"411425","parent_code":411400},{"title":"\u590f\u9091\u53bf","code":"411426","parent_code":411400},{"title":"\u6c38\u57ce\u5e02","code":"411481","parent_code":411400},{"title":"\u6d49\u6cb3\u533a","code":"411502","parent_code":411500},{"title":"\u5e73\u6865\u533a","code":"411503","parent_code":411500},{"title":"\u7f57\u5c71\u53bf","code":"411521","parent_code":411500},{"title":"\u5149\u5c71\u53bf","code":"411522","parent_code":411500},{"title":"\u65b0\u53bf","code":"411523","parent_code":411500},{"title":"\u5546\u57ce\u53bf","code":"411524","parent_code":411500},{"title":"\u56fa\u59cb\u53bf","code":"411525","parent_code":411500},{"title":"\u6f62\u5ddd\u53bf","code":"411526","parent_code":411500},{"title":"\u6dee\u6ee8\u53bf","code":"411527","parent_code":411500},{"title":"\u606f\u53bf","code":"411528","parent_code":411500},{"title":"\u5ddd\u6c47\u533a","code":"411602","parent_code":411600},{"title":"\u6dee\u9633\u533a","code":"411603","parent_code":411600},{"title":"\u6276\u6c9f\u53bf","code":"411621","parent_code":411600},{"title":"\u897f\u534e\u53bf","code":"411622","parent_code":411600},{"title":"\u5546\u6c34\u53bf","code":"411623","parent_code":411600},{"title":"\u6c88\u4e18\u53bf","code":"411624","parent_code":411600},{"title":"\u90f8\u57ce\u53bf","code":"411625","parent_code":411600},{"title":"\u592a\u5eb7\u53bf","code":"411627","parent_code":411600},{"title":"\u9e7f\u9091\u53bf","code":"411628","parent_code":411600},{"title":"\u6cb3\u5357\u5468\u53e3\u7ecf\u6d4e\u5f00\u53d1\u533a","code":"411671","parent_code":411600},{"title":"\u9879\u57ce\u5e02","code":"411681","parent_code":411600},{"title":"\u7ecf\u6d4e\u5f00\u53d1\u533a","code":"411690","parent_code":411600},{"title":"\u9a7f\u57ce\u533a","code":"411702","parent_code":411700},{"title":"\u897f\u5e73\u53bf","code":"411721","parent_code":411700},{"title":"\u4e0a\u8521\u53bf","code":"411722","parent_code":411700},{"title":"\u5e73\u8206\u53bf","code":"411723","parent_code":411700},{"title":"\u6b63\u9633\u53bf","code":"411724","parent_code":411700},{"title":"\u786e\u5c71\u53bf","code":"411725","parent_code":411700},{"title":"\u6ccc\u9633\u53bf","code":"411726","parent_code":411700},{"title":"\u6c5d\u5357\u53bf","code":"411727","parent_code":411700},{"title":"\u9042\u5e73\u53bf","code":"411728","parent_code":411700},{"title":"\u65b0\u8521\u53bf","code":"411729","parent_code":411700},{"title":"\u6d4e\u6e90\u5e02","code":"419001","parent_code":419000},{"title":"\u6c5f\u5cb8\u533a","code":"420102","parent_code":420100},{"title":"\u6c5f\u6c49\u533a","code":"420103","parent_code":420100},{"title":"\u785a\u53e3\u533a","code":"420104","parent_code":420100},{"title":"\u6c49\u9633\u533a","code":"420105","parent_code":420100},{"title":"\u6b66\u660c\u533a","code":"420106","parent_code":420100},{"title":"\u9752\u5c71\u533a","code":"420107","parent_code":420100},{"title":"\u6d2a\u5c71\u533a","code":"420111","parent_code":420100},{"title":"\u4e1c\u897f\u6e56\u533a","code":"420112","parent_code":420100},{"title":"\u6c49\u5357\u533a","code":"420113","parent_code":420100},{"title":"\u8521\u7538\u533a","code":"420114","parent_code":420100},{"title":"\u6c5f\u590f\u533a","code":"420115","parent_code":420100},{"title":"\u9ec4\u9642\u533a","code":"420116","parent_code":420100},{"title":"\u65b0\u6d32\u533a","code":"420117","parent_code":420100},{"title":"\u9ec4\u77f3\u6e2f\u533a","code":"420202","parent_code":420200},{"title":"\u897f\u585e\u5c71\u533a","code":"420203","parent_code":420200},{"title":"\u4e0b\u9646\u533a","code":"420204","parent_code":420200},{"title":"\u94c1\u5c71\u533a","code":"420205","parent_code":420200},{"title":"\u9633\u65b0\u53bf","code":"420222","parent_code":420200},{"title":"\u5927\u51b6\u5e02","code":"420281","parent_code":420200},{"title":"\u8305\u7bad\u533a","code":"420302","parent_code":420300},{"title":"\u5f20\u6e7e\u533a","code":"420303","parent_code":420300},{"title":"\u90e7\u9633\u533a","code":"420304","parent_code":420300},{"title":"\u90e7\u897f\u53bf","code":"420322","parent_code":420300},{"title":"\u7af9\u5c71\u53bf","code":"420323","parent_code":420300},{"title":"\u7af9\u6eaa\u53bf","code":"420324","parent_code":420300},{"title":"\u623f\u53bf","code":"420325","parent_code":420300},{"title":"\u4e39\u6c5f\u53e3\u5e02","code":"420381","parent_code":420300},{"title":"\u897f\u9675\u533a","code":"420502","parent_code":420500},{"title":"\u4f0d\u5bb6\u5c97\u533a","code":"420503","parent_code":420500},{"title":"\u70b9\u519b\u533a","code":"420504","parent_code":420500},{"title":"\u7307\u4ead\u533a","code":"420505","parent_code":420500},{"title":"\u5937\u9675\u533a","code":"420506","parent_code":420500},{"title":"\u8fdc\u5b89\u53bf","code":"420525","parent_code":420500},{"title":"\u5174\u5c71\u53bf","code":"420526","parent_code":420500},{"title":"\u79ed\u5f52\u53bf","code":"420527","parent_code":420500},{"title":"\u957f\u9633\u571f\u5bb6\u65cf\u81ea\u6cbb\u53bf","code":"420528","parent_code":420500},{"title":"\u4e94\u5cf0\u571f\u5bb6\u65cf\u81ea\u6cbb\u53bf","code":"420529","parent_code":420500},{"title":"\u5b9c\u90fd\u5e02","code":"420581","parent_code":420500},{"title":"\u5f53\u9633\u5e02","code":"420582","parent_code":420500},{"title":"\u679d\u6c5f\u5e02","code":"420583","parent_code":420500},{"title":"\u7ecf\u6d4e\u5f00\u53d1\u533a","code":"420590","parent_code":420500},{"title":"\u8944\u57ce\u533a","code":"420602","parent_code":420600},{"title":"\u6a0a\u57ce\u533a","code":"420606","parent_code":420600},{"title":"\u8944\u5dde\u533a","code":"420607","parent_code":420600},{"title":"\u5357\u6f33\u53bf","code":"420624","parent_code":420600},{"title":"\u8c37\u57ce\u53bf","code":"420625","parent_code":420600},{"title":"\u4fdd\u5eb7\u53bf","code":"420626","parent_code":420600},{"title":"\u8001\u6cb3\u53e3\u5e02","code":"420682","parent_code":420600},{"title":"\u67a3\u9633\u5e02","code":"420683","parent_code":420600},{"title":"\u5b9c\u57ce\u5e02","code":"420684","parent_code":420600},{"title":"\u6881\u5b50\u6e56\u533a","code":"420702","parent_code":420700},{"title":"\u534e\u5bb9\u533a","code":"420703","parent_code":420700},{"title":"\u9102\u57ce\u533a","code":"420704","parent_code":420700},{"title":"\u4e1c\u5b9d\u533a","code":"420802","parent_code":420800},{"title":"\u6387\u5200\u533a","code":"420804","parent_code":420800},{"title":"\u6c99\u6d0b\u53bf","code":"420822","parent_code":420800},{"title":"\u949f\u7965\u5e02","code":"420881","parent_code":420800},{"title":"\u4eac\u5c71\u5e02","code":"420882","parent_code":420800},{"title":"\u5b5d\u5357\u533a","code":"420902","parent_code":420900},{"title":"\u5b5d\u660c\u53bf","code":"420921","parent_code":420900},{"title":"\u5927\u609f\u53bf","code":"420922","parent_code":420900},{"title":"\u4e91\u68a6\u53bf","code":"420923","parent_code":420900},{"title":"\u5e94\u57ce\u5e02","code":"420981","parent_code":420900},{"title":"\u5b89\u9646\u5e02","code":"420982","parent_code":420900},{"title":"\u6c49\u5ddd\u5e02","code":"420984","parent_code":420900},{"title":"\u6c99\u5e02\u533a","code":"421002","parent_code":421000},{"title":"\u8346\u5dde\u533a","code":"421003","parent_code":421000},{"title":"\u516c\u5b89\u53bf","code":"421022","parent_code":421000},{"title":"\u76d1\u5229\u53bf","code":"421023","parent_code":421000},{"title":"\u6c5f\u9675\u53bf","code":"421024","parent_code":421000},{"title":"\u77f3\u9996\u5e02","code":"421081","parent_code":421000},{"title":"\u6d2a\u6e56\u5e02","code":"421083","parent_code":421000},{"title":"\u677e\u6ecb\u5e02","code":"421087","parent_code":421000},{"title":"\u9ec4\u5dde\u533a","code":"421102","parent_code":421100},{"title":"\u56e2\u98ce\u53bf","code":"421121","parent_code":421100},{"title":"\u7ea2\u5b89\u53bf","code":"421122","parent_code":421100},{"title":"\u7f57\u7530\u53bf","code":"421123","parent_code":421100},{"title":"\u82f1\u5c71\u53bf","code":"421124","parent_code":421100},{"title":"\u6d60\u6c34\u53bf","code":"421125","parent_code":421100},{"title":"\u8572\u6625\u53bf","code":"421126","parent_code":421100},{"title":"\u9ec4\u6885\u53bf","code":"421127","parent_code":421100},{"title":"\u9f99\u611f\u6e56\u7ba1\u7406\u533a","code":"421171","parent_code":421100},{"title":"\u9ebb\u57ce\u5e02","code":"421181","parent_code":421100},{"title":"\u6b66\u7a74\u5e02","code":"421182","parent_code":421100},{"title":"\u54b8\u5b89\u533a","code":"421202","parent_code":421200},{"title":"\u5609\u9c7c\u53bf","code":"421221","parent_code":421200},{"title":"\u901a\u57ce\u53bf","code":"421222","parent_code":421200},{"title":"\u5d07\u9633\u53bf","code":"421223","parent_code":421200},{"title":"\u901a\u5c71\u53bf","code":"421224","parent_code":421200},{"title":"\u8d64\u58c1\u5e02","code":"421281","parent_code":421200},{"title":"\u66fe\u90fd\u533a","code":"421303","parent_code":421300},{"title":"\u968f\u53bf","code":"421321","parent_code":421300},{"title":"\u5e7f\u6c34\u5e02","code":"421381","parent_code":421300},{"title":"\u6069\u65bd\u5e02","code":"422801","parent_code":422800},{"title":"\u5229\u5ddd\u5e02","code":"422802","parent_code":422800},{"title":"\u5efa\u59cb\u53bf","code":"422822","parent_code":422800},{"title":"\u5df4\u4e1c\u53bf","code":"422823","parent_code":422800},{"title":"\u5ba3\u6069\u53bf","code":"422825","parent_code":422800},{"title":"\u54b8\u4e30\u53bf","code":"422826","parent_code":422800},{"title":"\u6765\u51e4\u53bf","code":"422827","parent_code":422800},{"title":"\u9e64\u5cf0\u53bf","code":"422828","parent_code":422800},{"title":"\u4ed9\u6843\u5e02","code":"429004","parent_code":429000},{"title":"\u6f5c\u6c5f\u5e02","code":"429005","parent_code":429000},{"title":"\u5929\u95e8\u5e02","code":"429006","parent_code":429000},{"title":"\u795e\u519c\u67b6\u6797\u533a","code":"429021","parent_code":429000},{"title":"\u8299\u84c9\u533a","code":"430102","parent_code":430100},{"title":"\u5929\u5fc3\u533a","code":"430103","parent_code":430100},{"title":"\u5cb3\u9e93\u533a","code":"430104","parent_code":430100},{"title":"\u5f00\u798f\u533a","code":"430105","parent_code":430100},{"title":"\u96e8\u82b1\u533a","code":"430111","parent_code":430100},{"title":"\u671b\u57ce\u533a","code":"430112","parent_code":430100},{"title":"\u957f\u6c99\u53bf","code":"430121","parent_code":430100},{"title":"\u6d4f\u9633\u5e02","code":"430181","parent_code":430100},{"title":"\u5b81\u4e61\u5e02","code":"430182","parent_code":430100},{"title":"\u8377\u5858\u533a","code":"430202","parent_code":430200},{"title":"\u82a6\u6dde\u533a","code":"430203","parent_code":430200},{"title":"\u77f3\u5cf0\u533a","code":"430204","parent_code":430200},{"title":"\u5929\u5143\u533a","code":"430211","parent_code":430200},{"title":"\u6e0c\u53e3\u533a","code":"430212","parent_code":430200},{"title":"\u6538\u53bf","code":"430223","parent_code":430200},{"title":"\u8336\u9675\u53bf","code":"430224","parent_code":430200},{"title":"\u708e\u9675\u53bf","code":"430225","parent_code":430200},{"title":"\u4e91\u9f99\u793a\u8303\u533a","code":"430271","parent_code":430200},{"title":"\u91b4\u9675\u5e02","code":"430281","parent_code":430200},{"title":"\u96e8\u6e56\u533a","code":"430302","parent_code":430300},{"title":"\u5cb3\u5858\u533a","code":"430304","parent_code":430300},{"title":"\u6e58\u6f6d\u53bf","code":"430321","parent_code":430300},{"title":"\u6e58\u6f6d\u4e5d\u534e\u793a\u8303\u533a","code":"430373","parent_code":430300},{"title":"\u6e58\u4e61\u5e02","code":"430381","parent_code":430300},{"title":"\u97f6\u5c71\u5e02","code":"430382","parent_code":430300},{"title":"\u73e0\u6656\u533a","code":"430405","parent_code":430400},{"title":"\u96c1\u5cf0\u533a","code":"430406","parent_code":430400},{"title":"\u77f3\u9f13\u533a","code":"430407","parent_code":430400},{"title":"\u84b8\u6e58\u533a","code":"430408","parent_code":430400},{"title":"\u5357\u5cb3\u533a","code":"430412","parent_code":430400},{"title":"\u8861\u9633\u53bf","code":"430421","parent_code":430400},{"title":"\u8861\u5357\u53bf","code":"430422","parent_code":430400},{"title":"\u8861\u5c71\u53bf","code":"430423","parent_code":430400},{"title":"\u8861\u4e1c\u53bf","code":"430424","parent_code":430400},{"title":"\u7941\u4e1c\u53bf","code":"430426","parent_code":430400},{"title":"\u8012\u9633\u5e02","code":"430481","parent_code":430400},{"title":"\u5e38\u5b81\u5e02","code":"430482","parent_code":430400},{"title":"\u53cc\u6e05\u533a","code":"430502","parent_code":430500},{"title":"\u5927\u7965\u533a","code":"430503","parent_code":430500},{"title":"\u5317\u5854\u533a","code":"430511","parent_code":430500},{"title":"\u65b0\u90b5\u53bf","code":"430522","parent_code":430500},{"title":"\u90b5\u9633\u53bf","code":"430523","parent_code":430500},{"title":"\u9686\u56de\u53bf","code":"430524","parent_code":430500},{"title":"\u6d1e\u53e3\u53bf","code":"430525","parent_code":430500},{"title":"\u7ee5\u5b81\u53bf","code":"430527","parent_code":430500},{"title":"\u65b0\u5b81\u53bf","code":"430528","parent_code":430500},{"title":"\u57ce\u6b65\u82d7\u65cf\u81ea\u6cbb\u53bf","code":"430529","parent_code":430500},{"title":"\u6b66\u5188\u5e02","code":"430581","parent_code":430500},{"title":"\u90b5\u4e1c\u5e02","code":"430582","parent_code":430500},{"title":"\u5cb3\u9633\u697c\u533a","code":"430602","parent_code":430600},{"title":"\u4e91\u6eaa\u533a","code":"430603","parent_code":430600},{"title":"\u541b\u5c71\u533a","code":"430611","parent_code":430600},{"title":"\u5cb3\u9633\u53bf","code":"430621","parent_code":430600},{"title":"\u534e\u5bb9\u53bf","code":"430623","parent_code":430600},{"title":"\u6e58\u9634\u53bf","code":"430624","parent_code":430600},{"title":"\u5e73\u6c5f\u53bf","code":"430626","parent_code":430600},{"title":"\u6c68\u7f57\u5e02","code":"430681","parent_code":430600},{"title":"\u4e34\u6e58\u5e02","code":"430682","parent_code":430600},{"title":"\u6b66\u9675\u533a","code":"430702","parent_code":430700},{"title":"\u9f0e\u57ce\u533a","code":"430703","parent_code":430700},{"title":"\u5b89\u4e61\u53bf","code":"430721","parent_code":430700},{"title":"\u6c49\u5bff\u53bf","code":"430722","parent_code":430700},{"title":"\u6fa7\u53bf","code":"430723","parent_code":430700},{"title":"\u4e34\u6fa7\u53bf","code":"430724","parent_code":430700},{"title":"\u6843\u6e90\u53bf","code":"430725","parent_code":430700},{"title":"\u77f3\u95e8\u53bf","code":"430726","parent_code":430700},{"title":"\u6d25\u5e02\u5e02","code":"430781","parent_code":430700},{"title":"\u6c38\u5b9a\u533a","code":"430802","parent_code":430800},{"title":"\u6b66\u9675\u6e90\u533a","code":"430811","parent_code":430800},{"title":"\u6148\u5229\u53bf","code":"430821","parent_code":430800},{"title":"\u6851\u690d\u53bf","code":"430822","parent_code":430800},{"title":"\u8d44\u9633\u533a","code":"430902","parent_code":430900},{"title":"\u8d6b\u5c71\u533a","code":"430903","parent_code":430900},{"title":"\u5357\u53bf","code":"430921","parent_code":430900},{"title":"\u6843\u6c5f\u53bf","code":"430922","parent_code":430900},{"title":"\u5b89\u5316\u53bf","code":"430923","parent_code":430900},{"title":"\u76ca\u9633\u5e02\u5927\u901a\u6e56\u7ba1\u7406\u533a","code":"430971","parent_code":430900},{"title":"\u6c85\u6c5f\u5e02","code":"430981","parent_code":430900},{"title":"\u5317\u6e56\u533a","code":"431002","parent_code":431000},{"title":"\u82cf\u4ed9\u533a","code":"431003","parent_code":431000},{"title":"\u6842\u9633\u53bf","code":"431021","parent_code":431000},{"title":"\u5b9c\u7ae0\u53bf","code":"431022","parent_code":431000},{"title":"\u6c38\u5174\u53bf","code":"431023","parent_code":431000},{"title":"\u5609\u79be\u53bf","code":"431024","parent_code":431000},{"title":"\u4e34\u6b66\u53bf","code":"431025","parent_code":431000},{"title":"\u6c5d\u57ce\u53bf","code":"431026","parent_code":431000},{"title":"\u6842\u4e1c\u53bf","code":"431027","parent_code":431000},{"title":"\u5b89\u4ec1\u53bf","code":"431028","parent_code":431000},{"title":"\u8d44\u5174\u5e02","code":"431081","parent_code":431000},{"title":"\u96f6\u9675\u533a","code":"431102","parent_code":431100},{"title":"\u51b7\u6c34\u6ee9\u533a","code":"431103","parent_code":431100},{"title":"\u7941\u9633\u53bf","code":"431121","parent_code":431100},{"title":"\u4e1c\u5b89\u53bf","code":"431122","parent_code":431100},{"title":"\u53cc\u724c\u53bf","code":"431123","parent_code":431100},{"title":"\u9053\u53bf","code":"431124","parent_code":431100},{"title":"\u6c5f\u6c38\u53bf","code":"431125","parent_code":431100},{"title":"\u5b81\u8fdc\u53bf","code":"431126","parent_code":431100},{"title":"\u84dd\u5c71\u53bf","code":"431127","parent_code":431100},{"title":"\u65b0\u7530\u53bf","code":"431128","parent_code":431100},{"title":"\u6c5f\u534e\u7476\u65cf\u81ea\u6cbb\u53bf","code":"431129","parent_code":431100},{"title":"\u9e64\u57ce\u533a","code":"431202","parent_code":431200},{"title":"\u4e2d\u65b9\u53bf","code":"431221","parent_code":431200},{"title":"\u6c85\u9675\u53bf","code":"431222","parent_code":431200},{"title":"\u8fb0\u6eaa\u53bf","code":"431223","parent_code":431200},{"title":"\u6e86\u6d66\u53bf","code":"431224","parent_code":431200},{"title":"\u4f1a\u540c\u53bf","code":"431225","parent_code":431200},{"title":"\u9ebb\u9633\u82d7\u65cf\u81ea\u6cbb\u53bf","code":"431226","parent_code":431200},{"title":"\u65b0\u6643\u4f97\u65cf\u81ea\u6cbb\u53bf","code":"431227","parent_code":431200},{"title":"\u82b7\u6c5f\u4f97\u65cf\u81ea\u6cbb\u53bf","code":"431228","parent_code":431200},{"title":"\u9756\u5dde\u82d7\u65cf\u4f97\u65cf\u81ea\u6cbb\u53bf","code":"431229","parent_code":431200},{"title":"\u901a\u9053\u4f97\u65cf\u81ea\u6cbb\u53bf","code":"431230","parent_code":431200},{"title":"\u6000\u5316\u5e02\u6d2a\u6c5f\u7ba1\u7406\u533a","code":"431271","parent_code":431200},{"title":"\u6d2a\u6c5f\u5e02","code":"431281","parent_code":431200},{"title":"\u5a04\u661f\u533a","code":"431302","parent_code":431300},{"title":"\u53cc\u5cf0\u53bf","code":"431321","parent_code":431300},{"title":"\u65b0\u5316\u53bf","code":"431322","parent_code":431300},{"title":"\u51b7\u6c34\u6c5f\u5e02","code":"431381","parent_code":431300},{"title":"\u6d9f\u6e90\u5e02","code":"431382","parent_code":431300},{"title":"\u5409\u9996\u5e02","code":"433101","parent_code":433100},{"title":"\u6cf8\u6eaa\u53bf","code":"433122","parent_code":433100},{"title":"\u51e4\u51f0\u53bf","code":"433123","parent_code":433100},{"title":"\u82b1\u57a3\u53bf","code":"433124","parent_code":433100},{"title":"\u4fdd\u9756\u53bf","code":"433125","parent_code":433100},{"title":"\u53e4\u4e08\u53bf","code":"433126","parent_code":433100},{"title":"\u6c38\u987a\u53bf","code":"433127","parent_code":433100},{"title":"\u9f99\u5c71\u53bf","code":"433130","parent_code":433100},{"title":"\u8354\u6e7e\u533a","code":"440103","parent_code":440100},{"title":"\u8d8a\u79c0\u533a","code":"440104","parent_code":440100},{"title":"\u6d77\u73e0\u533a","code":"440105","parent_code":440100},{"title":"\u5929\u6cb3\u533a","code":"440106","parent_code":440100},{"title":"\u767d\u4e91\u533a","code":"440111","parent_code":440100},{"title":"\u9ec4\u57d4\u533a","code":"440112","parent_code":440100},{"title":"\u756a\u79ba\u533a","code":"440113","parent_code":440100},{"title":"\u82b1\u90fd\u533a","code":"440114","parent_code":440100},{"title":"\u5357\u6c99\u533a","code":"440115","parent_code":440100},{"title":"\u4ece\u5316\u533a","code":"440117","parent_code":440100},{"title":"\u589e\u57ce\u533a","code":"440118","parent_code":440100},{"title":"\u6b66\u6c5f\u533a","code":"440203","parent_code":440200},{"title":"\u6d48\u6c5f\u533a","code":"440204","parent_code":440200},{"title":"\u66f2\u6c5f\u533a","code":"440205","parent_code":440200},{"title":"\u59cb\u5174\u53bf","code":"440222","parent_code":440200},{"title":"\u4ec1\u5316\u53bf","code":"440224","parent_code":440200},{"title":"\u7fc1\u6e90\u53bf","code":"440229","parent_code":440200},{"title":"\u4e73\u6e90\u7476\u65cf\u81ea\u6cbb\u53bf","code":"440232","parent_code":440200},{"title":"\u65b0\u4e30\u53bf","code":"440233","parent_code":440200},{"title":"\u4e50\u660c\u5e02","code":"440281","parent_code":440200},{"title":"\u5357\u96c4\u5e02","code":"440282","parent_code":440200},{"title":"\u7f57\u6e56\u533a","code":"440303","parent_code":440300},{"title":"\u798f\u7530\u533a","code":"440304","parent_code":440300},{"title":"\u5357\u5c71\u533a","code":"440305","parent_code":440300},{"title":"\u5b9d\u5b89\u533a","code":"440306","parent_code":440300},{"title":"\u9f99\u5c97\u533a","code":"440307","parent_code":440300},{"title":"\u76d0\u7530\u533a","code":"440308","parent_code":440300},{"title":"\u9f99\u534e\u533a","code":"440309","parent_code":440300},{"title":"\u576a\u5c71\u533a","code":"440310","parent_code":440300},{"title":"\u5149\u660e\u533a","code":"440311","parent_code":440300},{"title":"\u9999\u6d32\u533a","code":"440402","parent_code":440400},{"title":"\u6597\u95e8\u533a","code":"440403","parent_code":440400},{"title":"\u91d1\u6e7e\u533a","code":"440404","parent_code":440400},{"title":"\u9f99\u6e56\u533a","code":"440507","parent_code":440500},{"title":"\u91d1\u5e73\u533a","code":"440511","parent_code":440500},{"title":"\u6fe0\u6c5f\u533a","code":"440512","parent_code":440500},{"title":"\u6f6e\u9633\u533a","code":"440513","parent_code":440500},{"title":"\u6f6e\u5357\u533a","code":"440514","parent_code":440500},{"title":"\u6f84\u6d77\u533a","code":"440515","parent_code":440500},{"title":"\u5357\u6fb3\u53bf","code":"440523","parent_code":440500},{"title":"\u7985\u57ce\u533a","code":"440604","parent_code":440600},{"title":"\u5357\u6d77\u533a","code":"440605","parent_code":440600},{"title":"\u987a\u5fb7\u533a","code":"440606","parent_code":440600},{"title":"\u4e09\u6c34\u533a","code":"440607","parent_code":440600},{"title":"\u9ad8\u660e\u533a","code":"440608","parent_code":440600},{"title":"\u84ec\u6c5f\u533a","code":"440703","parent_code":440700},{"title":"\u6c5f\u6d77\u533a","code":"440704","parent_code":440700},{"title":"\u65b0\u4f1a\u533a","code":"440705","parent_code":440700},{"title":"\u53f0\u5c71\u5e02","code":"440781","parent_code":440700},{"title":"\u5f00\u5e73\u5e02","code":"440783","parent_code":440700},{"title":"\u9e64\u5c71\u5e02","code":"440784","parent_code":440700},{"title":"\u6069\u5e73\u5e02","code":"440785","parent_code":440700},{"title":"\u8d64\u574e\u533a","code":"440802","parent_code":440800},{"title":"\u971e\u5c71\u533a","code":"440803","parent_code":440800},{"title":"\u5761\u5934\u533a","code":"440804","parent_code":440800},{"title":"\u9ebb\u7ae0\u533a","code":"440811","parent_code":440800},{"title":"\u9042\u6eaa\u53bf","code":"440823","parent_code":440800},{"title":"\u5f90\u95fb\u53bf","code":"440825","parent_code":440800},{"title":"\u5ec9\u6c5f\u5e02","code":"440881","parent_code":440800},{"title":"\u96f7\u5dde\u5e02","code":"440882","parent_code":440800},{"title":"\u5434\u5ddd\u5e02","code":"440883","parent_code":440800},{"title":"\u7ecf\u6d4e\u6280\u672f\u5f00\u53d1\u533a","code":"440890","parent_code":440800},{"title":"\u8302\u5357\u533a","code":"440902","parent_code":440900},{"title":"\u7535\u767d\u533a","code":"440904","parent_code":440900},{"title":"\u9ad8\u5dde\u5e02","code":"440981","parent_code":440900},{"title":"\u5316\u5dde\u5e02","code":"440982","parent_code":440900},{"title":"\u4fe1\u5b9c\u5e02","code":"440983","parent_code":440900},{"title":"\u7aef\u5dde\u533a","code":"441202","parent_code":441200},{"title":"\u9f0e\u6e56\u533a","code":"441203","parent_code":441200},{"title":"\u9ad8\u8981\u533a","code":"441204","parent_code":441200},{"title":"\u5e7f\u5b81\u53bf","code":"441223","parent_code":441200},{"title":"\u6000\u96c6\u53bf","code":"441224","parent_code":441200},{"title":"\u5c01\u5f00\u53bf","code":"441225","parent_code":441200},{"title":"\u5fb7\u5e86\u53bf","code":"441226","parent_code":441200},{"title":"\u56db\u4f1a\u5e02","code":"441284","parent_code":441200},{"title":"\u60e0\u57ce\u533a","code":"441302","parent_code":441300},{"title":"\u60e0\u9633\u533a","code":"441303","parent_code":441300},{"title":"\u535a\u7f57\u53bf","code":"441322","parent_code":441300},{"title":"\u60e0\u4e1c\u53bf","code":"441323","parent_code":441300},{"title":"\u9f99\u95e8\u53bf","code":"441324","parent_code":441300},{"title":"\u6885\u6c5f\u533a","code":"441402","parent_code":441400},{"title":"\u6885\u53bf\u533a","code":"441403","parent_code":441400},{"title":"\u5927\u57d4\u53bf","code":"441422","parent_code":441400},{"title":"\u4e30\u987a\u53bf","code":"441423","parent_code":441400},{"title":"\u4e94\u534e\u53bf","code":"441424","parent_code":441400},{"title":"\u5e73\u8fdc\u53bf","code":"441426","parent_code":441400},{"title":"\u8549\u5cad\u53bf","code":"441427","parent_code":441400},{"title":"\u5174\u5b81\u5e02","code":"441481","parent_code":441400},{"title":"\u57ce\u533a","code":"441502","parent_code":441500},{"title":"\u6d77\u4e30\u53bf","code":"441521","parent_code":441500},{"title":"\u9646\u6cb3\u53bf","code":"441523","parent_code":441500},{"title":"\u9646\u4e30\u5e02","code":"441581","parent_code":441500},{"title":"\u6e90\u57ce\u533a","code":"441602","parent_code":441600},{"title":"\u7d2b\u91d1\u53bf","code":"441621","parent_code":441600},{"title":"\u9f99\u5ddd\u53bf","code":"441622","parent_code":441600},{"title":"\u8fde\u5e73\u53bf","code":"441623","parent_code":441600},{"title":"\u548c\u5e73\u53bf","code":"441624","parent_code":441600},{"title":"\u4e1c\u6e90\u53bf","code":"441625","parent_code":441600},{"title":"\u6c5f\u57ce\u533a","code":"441702","parent_code":441700},{"title":"\u9633\u4e1c\u533a","code":"441704","parent_code":441700},{"title":"\u9633\u897f\u53bf","code":"441721","parent_code":441700},{"title":"\u9633\u6625\u5e02","code":"441781","parent_code":441700},{"title":"\u6e05\u57ce\u533a","code":"441802","parent_code":441800},{"title":"\u6e05\u65b0\u533a","code":"441803","parent_code":441800},{"title":"\u4f5b\u5188\u53bf","code":"441821","parent_code":441800},{"title":"\u9633\u5c71\u53bf","code":"441823","parent_code":441800},{"title":"\u8fde\u5c71\u58ee\u65cf\u7476\u65cf\u81ea\u6cbb\u53bf","code":"441825","parent_code":441800},{"title":"\u8fde\u5357\u7476\u65cf\u81ea\u6cbb\u53bf","code":"441826","parent_code":441800},{"title":"\u82f1\u5fb7\u5e02","code":"441881","parent_code":441800},{"title":"\u8fde\u5dde\u5e02","code":"441882","parent_code":441800},{"title":"\u4e2d\u5802\u9547","code":"441901","parent_code":441900},{"title":"\u5357\u57ce\u8857\u9053","code":"441903","parent_code":441900},{"title":"\u957f\u5b89\u9547","code":"441904","parent_code":441900},{"title":"\u4e1c\u5751\u9547","code":"441905","parent_code":441900},{"title":"\u6a1f\u6728\u5934\u9547","code":"441906","parent_code":441900},{"title":"\u839e\u57ce\u8857\u9053","code":"441907","parent_code":441900},{"title":"\u77f3\u9f99\u9547","code":"441908","parent_code":441900},{"title":"\u6865\u5934\u9547","code":"441909","parent_code":441900},{"title":"\u4e07\u6c5f\u8857\u9053","code":"441910","parent_code":441900},{"title":"\u9ebb\u6d8c\u9547","code":"441911","parent_code":441900},{"title":"\u864e\u95e8\u9547","code":"441912","parent_code":441900},{"title":"\u8c22\u5c97\u9547","code":"441913","parent_code":441900},{"title":"\u77f3\u78a3\u9547","code":"441914","parent_code":441900},{"title":"\u8336\u5c71\u9547","code":"441915","parent_code":441900},{"title":"\u4e1c\u57ce\u8857\u9053","code":"441916","parent_code":441900},{"title":"\u6d2a\u6885\u9547","code":"441917","parent_code":441900},{"title":"\u9053\u6ed8\u9547","code":"441918","parent_code":441900},{"title":"\u9ad8\u57d7\u9547","code":"441919","parent_code":441900},{"title":"\u4f01\u77f3\u9547","code":"441920","parent_code":441900},{"title":"\u51e4\u5c97\u9547","code":"441921","parent_code":441900},{"title":"\u5927\u5cad\u5c71\u9547","code":"441922","parent_code":441900},{"title":"\u677e\u5c71\u6e56","code":"441923","parent_code":441900},{"title":"\u6e05\u6eaa\u9547","code":"441924","parent_code":441900},{"title":"\u671b\u725b\u58a9\u9547","code":"441925","parent_code":441900},{"title":"\u539a\u8857\u9547","code":"441926","parent_code":441900},{"title":"\u5e38\u5e73\u9547","code":"441927","parent_code":441900},{"title":"\u5bee\u6b65\u9547","code":"441928","parent_code":441900},{"title":"\u77f3\u6392\u9547","code":"441929","parent_code":441900},{"title":"\u6a2a\u6ca5\u9547","code":"441930","parent_code":441900},{"title":"\u5858\u53a6\u9547","code":"441931","parent_code":441900},{"title":"\u9ec4\u6c5f\u9547","code":"441932","parent_code":441900},{"title":"\u5927\u6717\u9547","code":"441933","parent_code":441900},{"title":"\u4e1c\u839e\u6e2f","code":"441934","parent_code":441900},{"title":"\u4e1c\u839e\u751f\u6001\u56ed","code":"441935","parent_code":441900},{"title":"\u6c99\u7530\u9547","code":"441990","parent_code":441900},{"title":"\u5357\u5934\u9547","code":"442001","parent_code":442000},{"title":"\u795e\u6e7e\u9547","code":"442002","parent_code":442000},{"title":"\u4e1c\u51e4\u9547","code":"442003","parent_code":442000},{"title":"\u4e94\u6842\u5c71\u8857\u9053","code":"442004","parent_code":442000},{"title":"\u9ec4\u5703\u9547","code":"442005","parent_code":442000},{"title":"\u5c0f\u6984\u9547","code":"442006","parent_code":442000},{"title":"\u77f3\u5c90\u8857\u9053","code":"442007","parent_code":442000},{"title":"\u6a2a\u680f\u9547","code":"442008","parent_code":442000},{"title":"\u4e09\u89d2\u9547","code":"442009","parent_code":442000},{"title":"\u4e09\u4e61\u9547","code":"442010","parent_code":442000},{"title":"\u6e2f\u53e3\u9547","code":"442011","parent_code":442000},{"title":"\u6c99\u6eaa\u9547","code":"442012","parent_code":442000},{"title":"\u677f\u8299\u9547","code":"442013","parent_code":442000},{"title":"\u4e1c\u5347\u9547","code":"442015","parent_code":442000},{"title":"\u961c\u6c99\u9547","code":"442016","parent_code":442000},{"title":"\u6c11\u4f17\u9547","code":"442017","parent_code":442000},{"title":"\u4e1c\u533a\u8857\u9053","code":"442018","parent_code":442000},{"title":"\u706b\u70ac\u5f00\u53d1\u533a\u8857\u9053\u529e\u4e8b\u5904","code":"442019","parent_code":442000},{"title":"\u897f\u533a\u8857\u9053","code":"442020","parent_code":442000},{"title":"\u5357\u533a\u8857\u9053","code":"442021","parent_code":442000},{"title":"\u53e4\u9547\u9547","code":"442022","parent_code":442000},{"title":"\u5766\u6d32\u9547","code":"442023","parent_code":442000},{"title":"\u5927\u6d8c\u9547","code":"442024","parent_code":442000},{"title":"\u5357\u6717\u9547","code":"442025","parent_code":442000},{"title":"\u6e58\u6865\u533a","code":"445102","parent_code":445100},{"title":"\u6f6e\u5b89\u533a","code":"445103","parent_code":445100},{"title":"\u9976\u5e73\u53bf","code":"445122","parent_code":445100},{"title":"\u6995\u57ce\u533a","code":"445202","parent_code":445200},{"title":"\u63ed\u4e1c\u533a","code":"445203","parent_code":445200},{"title":"\u63ed\u897f\u53bf","code":"445222","parent_code":445200},{"title":"\u60e0\u6765\u53bf","code":"445224","parent_code":445200},{"title":"\u666e\u5b81\u5e02","code":"445281","parent_code":445200},{"title":"\u4e91\u57ce\u533a","code":"445302","parent_code":445300},{"title":"\u4e91\u5b89\u533a","code":"445303","parent_code":445300},{"title":"\u65b0\u5174\u53bf","code":"445321","parent_code":445300},{"title":"\u90c1\u5357\u53bf","code":"445322","parent_code":445300},{"title":"\u7f57\u5b9a\u5e02","code":"445381","parent_code":445300},{"title":"\u5174\u5b81\u533a","code":"450102","parent_code":450100},{"title":"\u9752\u79c0\u533a","code":"450103","parent_code":450100},{"title":"\u6c5f\u5357\u533a","code":"450105","parent_code":450100},{"title":"\u897f\u4e61\u5858\u533a","code":"450107","parent_code":450100},{"title":"\u826f\u5e86\u533a","code":"450108","parent_code":450100},{"title":"\u9095\u5b81\u533a","code":"450109","parent_code":450100},{"title":"\u6b66\u9e23\u533a","code":"450110","parent_code":450100},{"title":"\u9686\u5b89\u53bf","code":"450123","parent_code":450100},{"title":"\u9a6c\u5c71\u53bf","code":"450124","parent_code":450100},{"title":"\u4e0a\u6797\u53bf","code":"450125","parent_code":450100},{"title":"\u5bbe\u9633\u53bf","code":"450126","parent_code":450100},{"title":"\u6a2a\u53bf","code":"450127","parent_code":450100},{"title":"\u57ce\u4e2d\u533a","code":"450202","parent_code":450200},{"title":"\u9c7c\u5cf0\u533a","code":"450203","parent_code":450200},{"title":"\u67f3\u5357\u533a","code":"450204","parent_code":450200},{"title":"\u67f3\u5317\u533a","code":"450205","parent_code":450200},{"title":"\u67f3\u6c5f\u533a","code":"450206","parent_code":450200},{"title":"\u67f3\u57ce\u53bf","code":"450222","parent_code":450200},{"title":"\u9e7f\u5be8\u53bf","code":"450223","parent_code":450200},{"title":"\u878d\u5b89\u53bf","code":"450224","parent_code":450200},{"title":"\u878d\u6c34\u82d7\u65cf\u81ea\u6cbb\u53bf","code":"450225","parent_code":450200},{"title":"\u4e09\u6c5f\u4f97\u65cf\u81ea\u6cbb\u53bf","code":"450226","parent_code":450200},{"title":"\u79c0\u5cf0\u533a","code":"450302","parent_code":450300},{"title":"\u53e0\u5f69\u533a","code":"450303","parent_code":450300},{"title":"\u8c61\u5c71\u533a","code":"450304","parent_code":450300},{"title":"\u4e03\u661f\u533a","code":"450305","parent_code":450300},{"title":"\u96c1\u5c71\u533a","code":"450311","parent_code":450300},{"title":"\u4e34\u6842\u533a","code":"450312","parent_code":450300},{"title":"\u9633\u6714\u53bf","code":"450321","parent_code":450300},{"title":"\u7075\u5ddd\u53bf","code":"450323","parent_code":450300},{"title":"\u5168\u5dde\u53bf","code":"450324","parent_code":450300},{"title":"\u5174\u5b89\u53bf","code":"450325","parent_code":450300},{"title":"\u6c38\u798f\u53bf","code":"450326","parent_code":450300},{"title":"\u704c\u9633\u53bf","code":"450327","parent_code":450300},{"title":"\u9f99\u80dc\u5404\u65cf\u81ea\u6cbb\u53bf","code":"450328","parent_code":450300},{"title":"\u8d44\u6e90\u53bf","code":"450329","parent_code":450300},{"title":"\u5e73\u4e50\u53bf","code":"450330","parent_code":450300},{"title":"\u606d\u57ce\u7476\u65cf\u81ea\u6cbb\u53bf","code":"450332","parent_code":450300},{"title":"\u8354\u6d66\u5e02","code":"450381","parent_code":450300},{"title":"\u4e07\u79c0\u533a","code":"450403","parent_code":450400},{"title":"\u957f\u6d32\u533a","code":"450405","parent_code":450400},{"title":"\u9f99\u5729\u533a","code":"450406","parent_code":450400},{"title":"\u82cd\u68a7\u53bf","code":"450421","parent_code":450400},{"title":"\u85e4\u53bf","code":"450422","parent_code":450400},{"title":"\u8499\u5c71\u53bf","code":"450423","parent_code":450400},{"title":"\u5c91\u6eaa\u5e02","code":"450481","parent_code":450400},{"title":"\u6d77\u57ce\u533a","code":"450502","parent_code":450500},{"title":"\u94f6\u6d77\u533a","code":"450503","parent_code":450500},{"title":"\u94c1\u5c71\u6e2f\u533a","code":"450512","parent_code":450500},{"title":"\u5408\u6d66\u53bf","code":"450521","parent_code":450500},{"title":"\u6e2f\u53e3\u533a","code":"450602","parent_code":450600},{"title":"\u9632\u57ce\u533a","code":"450603","parent_code":450600},{"title":"\u4e0a\u601d\u53bf","code":"450621","parent_code":450600},{"title":"\u4e1c\u5174\u5e02","code":"450681","parent_code":450600},{"title":"\u94a6\u5357\u533a","code":"450702","parent_code":450700},{"title":"\u94a6\u5317\u533a","code":"450703","parent_code":450700},{"title":"\u7075\u5c71\u53bf","code":"450721","parent_code":450700},{"title":"\u6d66\u5317\u53bf","code":"450722","parent_code":450700},{"title":"\u6e2f\u5317\u533a","code":"450802","parent_code":450800},{"title":"\u6e2f\u5357\u533a","code":"450803","parent_code":450800},{"title":"\u8983\u5858\u533a","code":"450804","parent_code":450800},{"title":"\u5e73\u5357\u53bf","code":"450821","parent_code":450800},{"title":"\u6842\u5e73\u5e02","code":"450881","parent_code":450800},{"title":"\u7389\u5dde\u533a","code":"450902","parent_code":450900},{"title":"\u798f\u7ef5\u533a","code":"450903","parent_code":450900},{"title":"\u5bb9\u53bf","code":"450921","parent_code":450900},{"title":"\u9646\u5ddd\u53bf","code":"450922","parent_code":450900},{"title":"\u535a\u767d\u53bf","code":"450923","parent_code":450900},{"title":"\u5174\u4e1a\u53bf","code":"450924","parent_code":450900},{"title":"\u5317\u6d41\u5e02","code":"450981","parent_code":450900},{"title":"\u53f3\u6c5f\u533a","code":"451002","parent_code":451000},{"title":"\u7530\u9633\u533a","code":"451003","parent_code":451000},{"title":"\u7530\u4e1c\u53bf","code":"451022","parent_code":451000},{"title":"\u5fb7\u4fdd\u53bf","code":"451024","parent_code":451000},{"title":"\u90a3\u5761\u53bf","code":"451026","parent_code":451000},{"title":"\u51cc\u4e91\u53bf","code":"451027","parent_code":451000},{"title":"\u4e50\u4e1a\u53bf","code":"451028","parent_code":451000},{"title":"\u7530\u6797\u53bf","code":"451029","parent_code":451000},{"title":"\u897f\u6797\u53bf","code":"451030","parent_code":451000},{"title":"\u9686\u6797\u5404\u65cf\u81ea\u6cbb\u53bf","code":"451031","parent_code":451000},{"title":"\u9756\u897f\u5e02","code":"451081","parent_code":451000},{"title":"\u5e73\u679c\u5e02","code":"451082","parent_code":451000},{"title":"\u516b\u6b65\u533a","code":"451102","parent_code":451100},{"title":"\u5e73\u6842\u533a","code":"451103","parent_code":451100},{"title":"\u662d\u5e73\u53bf","code":"451121","parent_code":451100},{"title":"\u949f\u5c71\u53bf","code":"451122","parent_code":451100},{"title":"\u5bcc\u5ddd\u7476\u65cf\u81ea\u6cbb\u53bf","code":"451123","parent_code":451100},{"title":"\u91d1\u57ce\u6c5f\u533a","code":"451202","parent_code":451200},{"title":"\u5b9c\u5dde\u533a","code":"451203","parent_code":451200},{"title":"\u5357\u4e39\u53bf","code":"451221","parent_code":451200},{"title":"\u5929\u5ce8\u53bf","code":"451222","parent_code":451200},{"title":"\u51e4\u5c71\u53bf","code":"451223","parent_code":451200},{"title":"\u4e1c\u5170\u53bf","code":"451224","parent_code":451200},{"title":"\u7f57\u57ce\u4eeb\u4f6c\u65cf\u81ea\u6cbb\u53bf","code":"451225","parent_code":451200},{"title":"\u73af\u6c5f\u6bdb\u5357\u65cf\u81ea\u6cbb\u53bf","code":"451226","parent_code":451200},{"title":"\u5df4\u9a6c\u7476\u65cf\u81ea\u6cbb\u53bf","code":"451227","parent_code":451200},{"title":"\u90fd\u5b89\u7476\u65cf\u81ea\u6cbb\u53bf","code":"451228","parent_code":451200},{"title":"\u5927\u5316\u7476\u65cf\u81ea\u6cbb\u53bf","code":"451229","parent_code":451200},{"title":"\u5174\u5bbe\u533a","code":"451302","parent_code":451300},{"title":"\u5ffb\u57ce\u53bf","code":"451321","parent_code":451300},{"title":"\u8c61\u5dde\u53bf","code":"451322","parent_code":451300},{"title":"\u6b66\u5ba3\u53bf","code":"451323","parent_code":451300},{"title":"\u91d1\u79c0\u7476\u65cf\u81ea\u6cbb\u53bf","code":"451324","parent_code":451300},{"title":"\u5408\u5c71\u5e02","code":"451381","parent_code":451300},{"title":"\u6c5f\u5dde\u533a","code":"451402","parent_code":451400},{"title":"\u6276\u7ee5\u53bf","code":"451421","parent_code":451400},{"title":"\u5b81\u660e\u53bf","code":"451422","parent_code":451400},{"title":"\u9f99\u5dde\u53bf","code":"451423","parent_code":451400},{"title":"\u5927\u65b0\u53bf","code":"451424","parent_code":451400},{"title":"\u5929\u7b49\u53bf","code":"451425","parent_code":451400},{"title":"\u51ed\u7965\u5e02","code":"451481","parent_code":451400},{"title":"\u79c0\u82f1\u533a","code":"460105","parent_code":460100},{"title":"\u9f99\u534e\u533a","code":"460106","parent_code":460100},{"title":"\u743c\u5c71\u533a","code":"460107","parent_code":460100},{"title":"\u7f8e\u5170\u533a","code":"460108","parent_code":460100},{"title":"\u6d77\u68e0\u533a","code":"460202","parent_code":460200},{"title":"\u5409\u9633\u533a","code":"460203","parent_code":460200},{"title":"\u5929\u6daf\u533a","code":"460204","parent_code":460200},{"title":"\u5d16\u5dde\u533a","code":"460205","parent_code":460200},{"title":"\u897f\u6c99\u533a","code":"460321","parent_code":460300},{"title":"\u5357\u6c99\u533a","code":"460322","parent_code":460300},{"title":"\u90a3\u5927\u9547","code":"460401","parent_code":460400},{"title":"\u548c\u5e86\u9547","code":"460402","parent_code":460400},{"title":"\u5357\u4e30\u9547","code":"460403","parent_code":460400},{"title":"\u5927\u6210\u9547","code":"460404","parent_code":460400},{"title":"\u96c5\u661f\u9547","code":"460405","parent_code":460400},{"title":"\u5170\u6d0b\u9547","code":"460406","parent_code":460400},{"title":"\u5149\u6751\u9547","code":"460407","parent_code":460400},{"title":"\u6728\u68e0\u9547","code":"460408","parent_code":460400},{"title":"\u6d77\u5934\u9547","code":"460409","parent_code":460400},{"title":"\u5ce8\u8513\u9547","code":"460410","parent_code":460400},{"title":"\u738b\u4e94\u9547","code":"460411","parent_code":460400},{"title":"\u767d\u9a6c\u4e95\u9547","code":"460412","parent_code":460400},{"title":"\u4e2d\u548c\u9547","code":"460413","parent_code":460400},{"title":"\u6392\u6d66\u9547","code":"460414","parent_code":460400},{"title":"\u4e1c\u6210\u9547","code":"460415","parent_code":460400},{"title":"\u65b0\u5dde\u9547","code":"460416","parent_code":460400},{"title":"\u6d0b\u6d66\u7ecf\u6d4e\u5f00\u53d1\u533a","code":"460417","parent_code":460400},{"title":"\u534e\u5357\u70ed\u4f5c\u5b66\u9662","code":"460418","parent_code":460400},{"title":"\u4e94\u6307\u5c71\u5e02","code":"469001","parent_code":469000},{"title":"\u743c\u6d77\u5e02","code":"469002","parent_code":469000},{"title":"\u6587\u660c\u5e02","code":"469005","parent_code":469000},{"title":"\u4e07\u5b81\u5e02","code":"469006","parent_code":469000},{"title":"\u4e1c\u65b9\u5e02","code":"469007","parent_code":469000},{"title":"\u5b9a\u5b89\u53bf","code":"469021","parent_code":469000},{"title":"\u5c6f\u660c\u53bf","code":"469022","parent_code":469000},{"title":"\u6f84\u8fc8\u53bf","code":"469023","parent_code":469000},{"title":"\u4e34\u9ad8\u53bf","code":"469024","parent_code":469000},{"title":"\u767d\u6c99\u9ece\u65cf\u81ea\u6cbb\u53bf","code":"469025","parent_code":469000},{"title":"\u660c\u6c5f\u9ece\u65cf\u81ea\u6cbb\u53bf","code":"469026","parent_code":469000},{"title":"\u4e50\u4e1c\u9ece\u65cf\u81ea\u6cbb\u53bf","code":"469027","parent_code":469000},{"title":"\u9675\u6c34\u9ece\u65cf\u81ea\u6cbb\u53bf","code":"469028","parent_code":469000},{"title":"\u4fdd\u4ead\u9ece\u65cf\u82d7\u65cf\u81ea\u6cbb\u53bf","code":"469029","parent_code":469000},{"title":"\u743c\u4e2d\u9ece\u65cf\u82d7\u65cf\u81ea\u6cbb\u53bf","code":"469030","parent_code":469000},{"title":"\u4e07\u5dde\u533a","code":"500101","parent_code":500100},{"title":"\u6daa\u9675\u533a","code":"500102","parent_code":500100},{"title":"\u6e1d\u4e2d\u533a","code":"500103","parent_code":500100},{"title":"\u5927\u6e21\u53e3\u533a","code":"500104","parent_code":500100},{"title":"\u6c5f\u5317\u533a","code":"500105","parent_code":500100},{"title":"\u6c99\u576a\u575d\u533a","code":"500106","parent_code":500100},{"title":"\u4e5d\u9f99\u5761\u533a","code":"500107","parent_code":500100},{"title":"\u5357\u5cb8\u533a","code":"500108","parent_code":500100},{"title":"\u5317\u789a\u533a","code":"500109","parent_code":500100},{"title":"\u7da6\u6c5f\u533a","code":"500110","parent_code":500100},{"title":"\u5927\u8db3\u533a","code":"500111","parent_code":500100},{"title":"\u6e1d\u5317\u533a","code":"500112","parent_code":500100},{"title":"\u5df4\u5357\u533a","code":"500113","parent_code":500100},{"title":"\u9ed4\u6c5f\u533a","code":"500114","parent_code":500100},{"title":"\u957f\u5bff\u533a","code":"500115","parent_code":500100},{"title":"\u6c5f\u6d25\u533a","code":"500116","parent_code":500100},{"title":"\u5408\u5ddd\u533a","code":"500117","parent_code":500100},{"title":"\u6c38\u5ddd\u533a","code":"500118","parent_code":500100},{"title":"\u5357\u5ddd\u533a","code":"500119","parent_code":500100},{"title":"\u74a7\u5c71\u533a","code":"500120","parent_code":500100},{"title":"\u94dc\u6881\u533a","code":"500151","parent_code":500100},{"title":"\u6f7c\u5357\u533a","code":"500152","parent_code":500100},{"title":"\u8363\u660c\u533a","code":"500153","parent_code":500100},{"title":"\u5f00\u5dde\u533a","code":"500154","parent_code":500100},{"title":"\u6881\u5e73\u533a","code":"500155","parent_code":500100},{"title":"\u6b66\u9686\u533a","code":"500156","parent_code":500100},{"title":"\u57ce\u53e3\u53bf","code":"500229","parent_code":500200},{"title":"\u4e30\u90fd\u53bf","code":"500230","parent_code":500200},{"title":"\u57ab\u6c5f\u53bf","code":"500231","parent_code":500200},{"title":"\u5fe0\u53bf","code":"500233","parent_code":500200},{"title":"\u4e91\u9633\u53bf","code":"500235","parent_code":500200},{"title":"\u5949\u8282\u53bf","code":"500236","parent_code":500200},{"title":"\u5deb\u5c71\u53bf","code":"500237","parent_code":500200},{"title":"\u5deb\u6eaa\u53bf","code":"500238","parent_code":500200},{"title":"\u77f3\u67f1\u571f\u5bb6\u65cf\u81ea\u6cbb\u53bf","code":"500240","parent_code":500200},{"title":"\u79c0\u5c71\u571f\u5bb6\u65cf\u82d7\u65cf\u81ea\u6cbb\u53bf","code":"500241","parent_code":500200},{"title":"\u9149\u9633\u571f\u5bb6\u65cf\u82d7\u65cf\u81ea\u6cbb\u53bf","code":"500242","parent_code":500200},{"title":"\u5f6d\u6c34\u82d7\u65cf\u571f\u5bb6\u65cf\u81ea\u6cbb\u53bf","code":"500243","parent_code":500200},{"title":"\u9526\u6c5f\u533a","code":"510104","parent_code":510100},{"title":"\u9752\u7f8a\u533a","code":"510105","parent_code":510100},{"title":"\u91d1\u725b\u533a","code":"510106","parent_code":510100},{"title":"\u6b66\u4faf\u533a","code":"510107","parent_code":510100},{"title":"\u6210\u534e\u533a","code":"510108","parent_code":510100},{"title":"\u9f99\u6cc9\u9a7f\u533a","code":"510112","parent_code":510100},{"title":"\u9752\u767d\u6c5f\u533a","code":"510113","parent_code":510100},{"title":"\u65b0\u90fd\u533a","code":"510114","parent_code":510100},{"title":"\u6e29\u6c5f\u533a","code":"510115","parent_code":510100},{"title":"\u53cc\u6d41\u533a","code":"510116","parent_code":510100},{"title":"\u90eb\u90fd\u533a","code":"510117","parent_code":510100},{"title":"\u91d1\u5802\u53bf","code":"510121","parent_code":510100},{"title":"\u5927\u9091\u53bf","code":"510129","parent_code":510100},{"title":"\u84b2\u6c5f\u53bf","code":"510131","parent_code":510100},{"title":"\u65b0\u6d25\u53bf","code":"510132","parent_code":510100},{"title":"\u90fd\u6c5f\u5830\u5e02","code":"510181","parent_code":510100},{"title":"\u5f6d\u5dde\u5e02","code":"510182","parent_code":510100},{"title":"\u909b\u5d03\u5e02","code":"510183","parent_code":510100},{"title":"\u5d07\u5dde\u5e02","code":"510184","parent_code":510100},{"title":"\u7b80\u9633\u5e02","code":"510185","parent_code":510100},{"title":"\u9ad8\u65b0\u533a","code":"510191","parent_code":510100},{"title":"\u81ea\u6d41\u4e95\u533a","code":"510302","parent_code":510300},{"title":"\u8d21\u4e95\u533a","code":"510303","parent_code":510300},{"title":"\u5927\u5b89\u533a","code":"510304","parent_code":510300},{"title":"\u6cbf\u6ee9\u533a","code":"510311","parent_code":510300},{"title":"\u8363\u53bf","code":"510321","parent_code":510300},{"title":"\u5bcc\u987a\u53bf","code":"510322","parent_code":510300},{"title":"\u4e1c\u533a","code":"510402","parent_code":510400},{"title":"\u897f\u533a","code":"510403","parent_code":510400},{"title":"\u4ec1\u548c\u533a","code":"510411","parent_code":510400},{"title":"\u7c73\u6613\u53bf","code":"510421","parent_code":510400},{"title":"\u76d0\u8fb9\u53bf","code":"510422","parent_code":510400},{"title":"\u6c5f\u9633\u533a","code":"510502","parent_code":510500},{"title":"\u7eb3\u6eaa\u533a","code":"510503","parent_code":510500},{"title":"\u9f99\u9a6c\u6f6d\u533a","code":"510504","parent_code":510500},{"title":"\u6cf8\u53bf","code":"510521","parent_code":510500},{"title":"\u5408\u6c5f\u53bf","code":"510522","parent_code":510500},{"title":"\u53d9\u6c38\u53bf","code":"510524","parent_code":510500},{"title":"\u53e4\u853a\u53bf","code":"510525","parent_code":510500},{"title":"\u65cc\u9633\u533a","code":"510603","parent_code":510600},{"title":"\u7f57\u6c5f\u533a","code":"510604","parent_code":510600},{"title":"\u4e2d\u6c5f\u53bf","code":"510623","parent_code":510600},{"title":"\u5e7f\u6c49\u5e02","code":"510681","parent_code":510600},{"title":"\u4ec0\u90a1\u5e02","code":"510682","parent_code":510600},{"title":"\u7ef5\u7af9\u5e02","code":"510683","parent_code":510600},{"title":"\u6daa\u57ce\u533a","code":"510703","parent_code":510700},{"title":"\u6e38\u4ed9\u533a","code":"510704","parent_code":510700},{"title":"\u5b89\u5dde\u533a","code":"510705","parent_code":510700},{"title":"\u4e09\u53f0\u53bf","code":"510722","parent_code":510700},{"title":"\u76d0\u4ead\u53bf","code":"510723","parent_code":510700},{"title":"\u6893\u6f7c\u53bf","code":"510725","parent_code":510700},{"title":"\u5317\u5ddd\u7f8c\u65cf\u81ea\u6cbb\u53bf","code":"510726","parent_code":510700},{"title":"\u5e73\u6b66\u53bf","code":"510727","parent_code":510700},{"title":"\u6c5f\u6cb9\u5e02","code":"510781","parent_code":510700},{"title":"\u9ad8\u65b0\u533a","code":"510791","parent_code":510700},{"title":"\u5229\u5dde\u533a","code":"510802","parent_code":510800},{"title":"\u662d\u5316\u533a","code":"510811","parent_code":510800},{"title":"\u671d\u5929\u533a","code":"510812","parent_code":510800},{"title":"\u65fa\u82cd\u53bf","code":"510821","parent_code":510800},{"title":"\u9752\u5ddd\u53bf","code":"510822","parent_code":510800},{"title":"\u5251\u9601\u53bf","code":"510823","parent_code":510800},{"title":"\u82cd\u6eaa\u53bf","code":"510824","parent_code":510800},{"title":"\u8239\u5c71\u533a","code":"510903","parent_code":510900},{"title":"\u5b89\u5c45\u533a","code":"510904","parent_code":510900},{"title":"\u84ec\u6eaa\u53bf","code":"510921","parent_code":510900},{"title":"\u5927\u82f1\u53bf","code":"510923","parent_code":510900},{"title":"\u5c04\u6d2a\u5e02","code":"510981","parent_code":510900},{"title":"\u5e02\u4e2d\u533a","code":"511002","parent_code":511000},{"title":"\u4e1c\u5174\u533a","code":"511011","parent_code":511000},{"title":"\u5a01\u8fdc\u53bf","code":"511024","parent_code":511000},{"title":"\u8d44\u4e2d\u53bf","code":"511025","parent_code":511000},{"title":"\u9686\u660c\u5e02","code":"511083","parent_code":511000},{"title":"\u5e02\u4e2d\u533a","code":"511102","parent_code":511100},{"title":"\u6c99\u6e7e\u533a","code":"511111","parent_code":511100},{"title":"\u4e94\u901a\u6865\u533a","code":"511112","parent_code":511100},{"title":"\u91d1\u53e3\u6cb3\u533a","code":"511113","parent_code":511100},{"title":"\u728d\u4e3a\u53bf","code":"511123","parent_code":511100},{"title":"\u4e95\u7814\u53bf","code":"511124","parent_code":511100},{"title":"\u5939\u6c5f\u53bf","code":"511126","parent_code":511100},{"title":"\u6c90\u5ddd\u53bf","code":"511129","parent_code":511100},{"title":"\u5ce8\u8fb9\u5f5d\u65cf\u81ea\u6cbb\u53bf","code":"511132","parent_code":511100},{"title":"\u9a6c\u8fb9\u5f5d\u65cf\u81ea\u6cbb\u53bf","code":"511133","parent_code":511100},{"title":"\u5ce8\u7709\u5c71\u5e02","code":"511181","parent_code":511100},{"title":"\u987a\u5e86\u533a","code":"511302","parent_code":511300},{"title":"\u9ad8\u576a\u533a","code":"511303","parent_code":511300},{"title":"\u5609\u9675\u533a","code":"511304","parent_code":511300},{"title":"\u5357\u90e8\u53bf","code":"511321","parent_code":511300},{"title":"\u8425\u5c71\u53bf","code":"511322","parent_code":511300},{"title":"\u84ec\u5b89\u53bf","code":"511323","parent_code":511300},{"title":"\u4eea\u9647\u53bf","code":"511324","parent_code":511300},{"title":"\u897f\u5145\u53bf","code":"511325","parent_code":511300},{"title":"\u9606\u4e2d\u5e02","code":"511381","parent_code":511300},{"title":"\u4e1c\u5761\u533a","code":"511402","parent_code":511400},{"title":"\u5f6d\u5c71\u533a","code":"511403","parent_code":511400},{"title":"\u4ec1\u5bff\u53bf","code":"511421","parent_code":511400},{"title":"\u6d2a\u96c5\u53bf","code":"511423","parent_code":511400},{"title":"\u4e39\u68f1\u53bf","code":"511424","parent_code":511400},{"title":"\u9752\u795e\u53bf","code":"511425","parent_code":511400},{"title":"\u7fe0\u5c4f\u533a","code":"511502","parent_code":511500},{"title":"\u5357\u6eaa\u533a","code":"511503","parent_code":511500},{"title":"\u53d9\u5dde\u533a","code":"511504","parent_code":511500},{"title":"\u6c5f\u5b89\u53bf","code":"511523","parent_code":511500},{"title":"\u957f\u5b81\u53bf","code":"511524","parent_code":511500},{"title":"\u9ad8\u53bf","code":"511525","parent_code":511500},{"title":"\u73d9\u53bf","code":"511526","parent_code":511500},{"title":"\u7b60\u8fde\u53bf","code":"511527","parent_code":511500},{"title":"\u5174\u6587\u53bf","code":"511528","parent_code":511500},{"title":"\u5c4f\u5c71\u53bf","code":"511529","parent_code":511500},{"title":"\u5e7f\u5b89\u533a","code":"511602","parent_code":511600},{"title":"\u524d\u950b\u533a","code":"511603","parent_code":511600},{"title":"\u5cb3\u6c60\u53bf","code":"511621","parent_code":511600},{"title":"\u6b66\u80dc\u53bf","code":"511622","parent_code":511600},{"title":"\u90bb\u6c34\u53bf","code":"511623","parent_code":511600},{"title":"\u534e\u84e5\u5e02","code":"511681","parent_code":511600},{"title":"\u901a\u5ddd\u533a","code":"511702","parent_code":511700},{"title":"\u8fbe\u5ddd\u533a","code":"511703","parent_code":511700},{"title":"\u5ba3\u6c49\u53bf","code":"511722","parent_code":511700},{"title":"\u5f00\u6c5f\u53bf","code":"511723","parent_code":511700},{"title":"\u5927\u7af9\u53bf","code":"511724","parent_code":511700},{"title":"\u6e20\u53bf","code":"511725","parent_code":511700},{"title":"\u4e07\u6e90\u5e02","code":"511781","parent_code":511700},{"title":"\u96e8\u57ce\u533a","code":"511802","parent_code":511800},{"title":"\u540d\u5c71\u533a","code":"511803","parent_code":511800},{"title":"\u8365\u7ecf\u53bf","code":"511822","parent_code":511800},{"title":"\u6c49\u6e90\u53bf","code":"511823","parent_code":511800},{"title":"\u77f3\u68c9\u53bf","code":"511824","parent_code":511800},{"title":"\u5929\u5168\u53bf","code":"511825","parent_code":511800},{"title":"\u82a6\u5c71\u53bf","code":"511826","parent_code":511800},{"title":"\u5b9d\u5174\u53bf","code":"511827","parent_code":511800},{"title":"\u5df4\u5dde\u533a","code":"511902","parent_code":511900},{"title":"\u6069\u9633\u533a","code":"511903","parent_code":511900},{"title":"\u901a\u6c5f\u53bf","code":"511921","parent_code":511900},{"title":"\u5357\u6c5f\u53bf","code":"511922","parent_code":511900},{"title":"\u5e73\u660c\u53bf","code":"511923","parent_code":511900},{"title":"\u5df4\u4e2d\u7ecf\u6d4e\u5f00\u53d1\u533a","code":"511971","parent_code":511900},{"title":"\u96c1\u6c5f\u533a","code":"512002","parent_code":512000},{"title":"\u5b89\u5cb3\u53bf","code":"512021","parent_code":512000},{"title":"\u4e50\u81f3\u53bf","code":"512022","parent_code":512000},{"title":"\u9a6c\u5c14\u5eb7\u5e02","code":"513201","parent_code":513200},{"title":"\u6c76\u5ddd\u53bf","code":"513221","parent_code":513200},{"title":"\u7406\u53bf","code":"513222","parent_code":513200},{"title":"\u8302\u53bf","code":"513223","parent_code":513200},{"title":"\u677e\u6f58\u53bf","code":"513224","parent_code":513200},{"title":"\u4e5d\u5be8\u6c9f\u53bf","code":"513225","parent_code":513200},{"title":"\u91d1\u5ddd\u53bf","code":"513226","parent_code":513200},{"title":"\u5c0f\u91d1\u53bf","code":"513227","parent_code":513200},{"title":"\u9ed1\u6c34\u53bf","code":"513228","parent_code":513200},{"title":"\u58e4\u5858\u53bf","code":"513230","parent_code":513200},{"title":"\u963f\u575d\u53bf","code":"513231","parent_code":513200},{"title":"\u82e5\u5c14\u76d6\u53bf","code":"513232","parent_code":513200},{"title":"\u7ea2\u539f\u53bf","code":"513233","parent_code":513200},{"title":"\u5eb7\u5b9a\u5e02","code":"513301","parent_code":513300},{"title":"\u6cf8\u5b9a\u53bf","code":"513322","parent_code":513300},{"title":"\u4e39\u5df4\u53bf","code":"513323","parent_code":513300},{"title":"\u4e5d\u9f99\u53bf","code":"513324","parent_code":513300},{"title":"\u96c5\u6c5f\u53bf","code":"513325","parent_code":513300},{"title":"\u9053\u5b5a\u53bf","code":"513326","parent_code":513300},{"title":"\u7089\u970d\u53bf","code":"513327","parent_code":513300},{"title":"\u7518\u5b5c\u53bf","code":"513328","parent_code":513300},{"title":"\u65b0\u9f99\u53bf","code":"513329","parent_code":513300},{"title":"\u5fb7\u683c\u53bf","code":"513330","parent_code":513300},{"title":"\u767d\u7389\u53bf","code":"513331","parent_code":513300},{"title":"\u77f3\u6e20\u53bf","code":"513332","parent_code":513300},{"title":"\u8272\u8fbe\u53bf","code":"513333","parent_code":513300},{"title":"\u7406\u5858\u53bf","code":"513334","parent_code":513300},{"title":"\u5df4\u5858\u53bf","code":"513335","parent_code":513300},{"title":"\u4e61\u57ce\u53bf","code":"513336","parent_code":513300},{"title":"\u7a3b\u57ce\u53bf","code":"513337","parent_code":513300},{"title":"\u5f97\u8363\u53bf","code":"513338","parent_code":513300},{"title":"\u897f\u660c\u5e02","code":"513401","parent_code":513400},{"title":"\u6728\u91cc\u85cf\u65cf\u81ea\u6cbb\u53bf","code":"513422","parent_code":513400},{"title":"\u76d0\u6e90\u53bf","code":"513423","parent_code":513400},{"title":"\u5fb7\u660c\u53bf","code":"513424","parent_code":513400},{"title":"\u4f1a\u7406\u53bf","code":"513425","parent_code":513400},{"title":"\u4f1a\u4e1c\u53bf","code":"513426","parent_code":513400},{"title":"\u5b81\u5357\u53bf","code":"513427","parent_code":513400},{"title":"\u666e\u683c\u53bf","code":"513428","parent_code":513400},{"title":"\u5e03\u62d6\u53bf","code":"513429","parent_code":513400},{"title":"\u91d1\u9633\u53bf","code":"513430","parent_code":513400},{"title":"\u662d\u89c9\u53bf","code":"513431","parent_code":513400},{"title":"\u559c\u5fb7\u53bf","code":"513432","parent_code":513400},{"title":"\u5195\u5b81\u53bf","code":"513433","parent_code":513400},{"title":"\u8d8a\u897f\u53bf","code":"513434","parent_code":513400},{"title":"\u7518\u6d1b\u53bf","code":"513435","parent_code":513400},{"title":"\u7f8e\u59d1\u53bf","code":"513436","parent_code":513400},{"title":"\u96f7\u6ce2\u53bf","code":"513437","parent_code":513400},{"title":"\u5357\u660e\u533a","code":"520102","parent_code":520100},{"title":"\u4e91\u5ca9\u533a","code":"520103","parent_code":520100},{"title":"\u82b1\u6eaa\u533a","code":"520111","parent_code":520100},{"title":"\u4e4c\u5f53\u533a","code":"520112","parent_code":520100},{"title":"\u767d\u4e91\u533a","code":"520113","parent_code":520100},{"title":"\u89c2\u5c71\u6e56\u533a","code":"520115","parent_code":520100},{"title":"\u5f00\u9633\u53bf","code":"520121","parent_code":520100},{"title":"\u606f\u70fd\u53bf","code":"520122","parent_code":520100},{"title":"\u4fee\u6587\u53bf","code":"520123","parent_code":520100},{"title":"\u6e05\u9547\u5e02","code":"520181","parent_code":520100},{"title":"\u949f\u5c71\u533a","code":"520201","parent_code":520200},{"title":"\u516d\u679d\u7279\u533a","code":"520203","parent_code":520200},{"title":"\u6c34\u57ce\u53bf","code":"520221","parent_code":520200},{"title":"\u76d8\u5dde\u5e02","code":"520281","parent_code":520200},{"title":"\u7ea2\u82b1\u5c97\u533a","code":"520302","parent_code":520300},{"title":"\u6c47\u5ddd\u533a","code":"520303","parent_code":520300},{"title":"\u64ad\u5dde\u533a","code":"520304","parent_code":520300},{"title":"\u6850\u6893\u53bf","code":"520322","parent_code":520300},{"title":"\u7ee5\u9633\u53bf","code":"520323","parent_code":520300},{"title":"\u6b63\u5b89\u53bf","code":"520324","parent_code":520300},{"title":"\u9053\u771f\u4ee1\u4f6c\u65cf\u82d7\u65cf\u81ea\u6cbb\u53bf","code":"520325","parent_code":520300},{"title":"\u52a1\u5ddd\u4ee1\u4f6c\u65cf\u82d7\u65cf\u81ea\u6cbb\u53bf","code":"520326","parent_code":520300},{"title":"\u51e4\u5188\u53bf","code":"520327","parent_code":520300},{"title":"\u6e44\u6f6d\u53bf","code":"520328","parent_code":520300},{"title":"\u4f59\u5e86\u53bf","code":"520329","parent_code":520300},{"title":"\u4e60\u6c34\u53bf","code":"520330","parent_code":520300},{"title":"\u8d64\u6c34\u5e02","code":"520381","parent_code":520300},{"title":"\u4ec1\u6000\u5e02","code":"520382","parent_code":520300},{"title":"\u897f\u79c0\u533a","code":"520402","parent_code":520400},{"title":"\u5e73\u575d\u533a","code":"520403","parent_code":520400},{"title":"\u666e\u5b9a\u53bf","code":"520422","parent_code":520400},{"title":"\u9547\u5b81\u5e03\u4f9d\u65cf\u82d7\u65cf\u81ea\u6cbb\u53bf","code":"520423","parent_code":520400},{"title":"\u5173\u5cad\u5e03\u4f9d\u65cf\u82d7\u65cf\u81ea\u6cbb\u53bf","code":"520424","parent_code":520400},{"title":"\u7d2b\u4e91\u82d7\u65cf\u5e03\u4f9d\u65cf\u81ea\u6cbb\u53bf","code":"520425","parent_code":520400},{"title":"\u4e03\u661f\u5173\u533a","code":"520502","parent_code":520500},{"title":"\u5927\u65b9\u53bf","code":"520521","parent_code":520500},{"title":"\u9ed4\u897f\u53bf","code":"520522","parent_code":520500},{"title":"\u91d1\u6c99\u53bf","code":"520523","parent_code":520500},{"title":"\u7ec7\u91d1\u53bf","code":"520524","parent_code":520500},{"title":"\u7eb3\u96cd\u53bf","code":"520525","parent_code":520500},{"title":"\u5a01\u5b81\u5f5d\u65cf\u56de\u65cf\u82d7\u65cf\u81ea\u6cbb\u53bf","code":"520526","parent_code":520500},{"title":"\u8d6b\u7ae0\u53bf","code":"520527","parent_code":520500},{"title":"\u78a7\u6c5f\u533a","code":"520602","parent_code":520600},{"title":"\u4e07\u5c71\u533a","code":"520603","parent_code":520600},{"title":"\u6c5f\u53e3\u53bf","code":"520621","parent_code":520600},{"title":"\u7389\u5c4f\u4f97\u65cf\u81ea\u6cbb\u53bf","code":"520622","parent_code":520600},{"title":"\u77f3\u9621\u53bf","code":"520623","parent_code":520600},{"title":"\u601d\u5357\u53bf","code":"520624","parent_code":520600},{"title":"\u5370\u6c5f\u571f\u5bb6\u65cf\u82d7\u65cf\u81ea\u6cbb\u53bf","code":"520625","parent_code":520600},{"title":"\u5fb7\u6c5f\u53bf","code":"520626","parent_code":520600},{"title":"\u6cbf\u6cb3\u571f\u5bb6\u65cf\u81ea\u6cbb\u53bf","code":"520627","parent_code":520600},{"title":"\u677e\u6843\u82d7\u65cf\u81ea\u6cbb\u53bf","code":"520628","parent_code":520600},{"title":"\u5174\u4e49\u5e02","code":"522301","parent_code":522300},{"title":"\u5174\u4ec1\u5e02","code":"522302","parent_code":522300},{"title":"\u666e\u5b89\u53bf","code":"522323","parent_code":522300},{"title":"\u6674\u9686\u53bf","code":"522324","parent_code":522300},{"title":"\u8d1e\u4e30\u53bf","code":"522325","parent_code":522300},{"title":"\u671b\u8c1f\u53bf","code":"522326","parent_code":522300},{"title":"\u518c\u4ea8\u53bf","code":"522327","parent_code":522300},{"title":"\u5b89\u9f99\u53bf","code":"522328","parent_code":522300},{"title":"\u51ef\u91cc\u5e02","code":"522601","parent_code":522600},{"title":"\u9ec4\u5e73\u53bf","code":"522622","parent_code":522600},{"title":"\u65bd\u79c9\u53bf","code":"522623","parent_code":522600},{"title":"\u4e09\u7a57\u53bf","code":"522624","parent_code":522600},{"title":"\u9547\u8fdc\u53bf","code":"522625","parent_code":522600},{"title":"\u5c91\u5de9\u53bf","code":"522626","parent_code":522600},{"title":"\u5929\u67f1\u53bf","code":"522627","parent_code":522600},{"title":"\u9526\u5c4f\u53bf","code":"522628","parent_code":522600},{"title":"\u5251\u6cb3\u53bf","code":"522629","parent_code":522600},{"title":"\u53f0\u6c5f\u53bf","code":"522630","parent_code":522600},{"title":"\u9ece\u5e73\u53bf","code":"522631","parent_code":522600},{"title":"\u6995\u6c5f\u53bf","code":"522632","parent_code":522600},{"title":"\u4ece\u6c5f\u53bf","code":"522633","parent_code":522600},{"title":"\u96f7\u5c71\u53bf","code":"522634","parent_code":522600},{"title":"\u9ebb\u6c5f\u53bf","code":"522635","parent_code":522600},{"title":"\u4e39\u5be8\u53bf","code":"522636","parent_code":522600},{"title":"\u90fd\u5300\u5e02","code":"522701","parent_code":522700},{"title":"\u798f\u6cc9\u5e02","code":"522702","parent_code":522700},{"title":"\u8354\u6ce2\u53bf","code":"522722","parent_code":522700},{"title":"\u8d35\u5b9a\u53bf","code":"522723","parent_code":522700},{"title":"\u74ee\u5b89\u53bf","code":"522725","parent_code":522700},{"title":"\u72ec\u5c71\u53bf","code":"522726","parent_code":522700},{"title":"\u5e73\u5858\u53bf","code":"522727","parent_code":522700},{"title":"\u7f57\u7538\u53bf","code":"522728","parent_code":522700},{"title":"\u957f\u987a\u53bf","code":"522729","parent_code":522700},{"title":"\u9f99\u91cc\u53bf","code":"522730","parent_code":522700},{"title":"\u60e0\u6c34\u53bf","code":"522731","parent_code":522700},{"title":"\u4e09\u90fd\u6c34\u65cf\u81ea\u6cbb\u53bf","code":"522732","parent_code":522700},{"title":"\u4e94\u534e\u533a","code":"530102","parent_code":530100},{"title":"\u76d8\u9f99\u533a","code":"530103","parent_code":530100},{"title":"\u5b98\u6e21\u533a","code":"530111","parent_code":530100},{"title":"\u897f\u5c71\u533a","code":"530112","parent_code":530100},{"title":"\u4e1c\u5ddd\u533a","code":"530113","parent_code":530100},{"title":"\u5448\u8d21\u533a","code":"530114","parent_code":530100},{"title":"\u664b\u5b81\u533a","code":"530115","parent_code":530100},{"title":"\u5bcc\u6c11\u53bf","code":"530124","parent_code":530100},{"title":"\u5b9c\u826f\u53bf","code":"530125","parent_code":530100},{"title":"\u77f3\u6797\u5f5d\u65cf\u81ea\u6cbb\u53bf","code":"530126","parent_code":530100},{"title":"\u5d69\u660e\u53bf","code":"530127","parent_code":530100},{"title":"\u7984\u529d\u5f5d\u65cf\u82d7\u65cf\u81ea\u6cbb\u53bf","code":"530128","parent_code":530100},{"title":"\u5bfb\u7538\u56de\u65cf\u5f5d\u65cf\u81ea\u6cbb\u53bf","code":"530129","parent_code":530100},{"title":"\u5b89\u5b81\u5e02","code":"530181","parent_code":530100},{"title":"\u9e92\u9e9f\u533a","code":"530302","parent_code":530300},{"title":"\u6cbe\u76ca\u533a","code":"530303","parent_code":530300},{"title":"\u9a6c\u9f99\u533a","code":"530304","parent_code":530300},{"title":"\u9646\u826f\u53bf","code":"530322","parent_code":530300},{"title":"\u5e08\u5b97\u53bf","code":"530323","parent_code":530300},{"title":"\u7f57\u5e73\u53bf","code":"530324","parent_code":530300},{"title":"\u5bcc\u6e90\u53bf","code":"530325","parent_code":530300},{"title":"\u4f1a\u6cfd\u53bf","code":"530326","parent_code":530300},{"title":"\u5ba3\u5a01\u5e02","code":"530381","parent_code":530300},{"title":"\u7ea2\u5854\u533a","code":"530402","parent_code":530400},{"title":"\u6c5f\u5ddd\u533a","code":"530403","parent_code":530400},{"title":"\u901a\u6d77\u53bf","code":"530423","parent_code":530400},{"title":"\u534e\u5b81\u53bf","code":"530424","parent_code":530400},{"title":"\u6613\u95e8\u53bf","code":"530425","parent_code":530400},{"title":"\u5ce8\u5c71\u5f5d\u65cf\u81ea\u6cbb\u53bf","code":"530426","parent_code":530400},{"title":"\u65b0\u5e73\u5f5d\u65cf\u50a3\u65cf\u81ea\u6cbb\u53bf","code":"530427","parent_code":530400},{"title":"\u5143\u6c5f\u54c8\u5c3c\u65cf\u5f5d\u65cf\u50a3\u65cf\u81ea\u6cbb\u53bf","code":"530428","parent_code":530400},{"title":"\u6f84\u6c5f\u5e02","code":"530481","parent_code":530400},{"title":"\u9686\u9633\u533a","code":"530502","parent_code":530500},{"title":"\u65bd\u7538\u53bf","code":"530521","parent_code":530500},{"title":"\u9f99\u9675\u53bf","code":"530523","parent_code":530500},{"title":"\u660c\u5b81\u53bf","code":"530524","parent_code":530500},{"title":"\u817e\u51b2\u5e02","code":"530581","parent_code":530500},{"title":"\u662d\u9633\u533a","code":"530602","parent_code":530600},{"title":"\u9c81\u7538\u53bf","code":"530621","parent_code":530600},{"title":"\u5de7\u5bb6\u53bf","code":"530622","parent_code":530600},{"title":"\u76d0\u6d25\u53bf","code":"530623","parent_code":530600},{"title":"\u5927\u5173\u53bf","code":"530624","parent_code":530600},{"title":"\u6c38\u5584\u53bf","code":"530625","parent_code":530600},{"title":"\u7ee5\u6c5f\u53bf","code":"530626","parent_code":530600},{"title":"\u9547\u96c4\u53bf","code":"530627","parent_code":530600},{"title":"\u5f5d\u826f\u53bf","code":"530628","parent_code":530600},{"title":"\u5a01\u4fe1\u53bf","code":"530629","parent_code":530600},{"title":"\u6c34\u5bcc\u5e02","code":"530681","parent_code":530600},{"title":"\u53e4\u57ce\u533a","code":"530702","parent_code":530700},{"title":"\u7389\u9f99\u7eb3\u897f\u65cf\u81ea\u6cbb\u53bf","code":"530721","parent_code":530700},{"title":"\u6c38\u80dc\u53bf","code":"530722","parent_code":530700},{"title":"\u534e\u576a\u53bf","code":"530723","parent_code":530700},{"title":"\u5b81\u8497\u5f5d\u65cf\u81ea\u6cbb\u53bf","code":"530724","parent_code":530700},{"title":"\u601d\u8305\u533a","code":"530802","parent_code":530800},{"title":"\u5b81\u6d31\u54c8\u5c3c\u65cf\u5f5d\u65cf\u81ea\u6cbb\u53bf","code":"530821","parent_code":530800},{"title":"\u58a8\u6c5f\u54c8\u5c3c\u65cf\u81ea\u6cbb\u53bf","code":"530822","parent_code":530800},{"title":"\u666f\u4e1c\u5f5d\u65cf\u81ea\u6cbb\u53bf","code":"530823","parent_code":530800},{"title":"\u666f\u8c37\u50a3\u65cf\u5f5d\u65cf\u81ea\u6cbb\u53bf","code":"530824","parent_code":530800},{"title":"\u9547\u6c85\u5f5d\u65cf\u54c8\u5c3c\u65cf\u62c9\u795c\u65cf\u81ea\u6cbb\u53bf","code":"530825","parent_code":530800},{"title":"\u6c5f\u57ce\u54c8\u5c3c\u65cf\u5f5d\u65cf\u81ea\u6cbb\u53bf","code":"530826","parent_code":530800},{"title":"\u5b5f\u8fde\u50a3\u65cf\u62c9\u795c\u65cf\u4f64\u65cf\u81ea\u6cbb\u53bf","code":"530827","parent_code":530800},{"title":"\u6f9c\u6ca7\u62c9\u795c\u65cf\u81ea\u6cbb\u53bf","code":"530828","parent_code":530800},{"title":"\u897f\u76df\u4f64\u65cf\u81ea\u6cbb\u53bf","code":"530829","parent_code":530800},{"title":"\u4e34\u7fd4\u533a","code":"530902","parent_code":530900},{"title":"\u51e4\u5e86\u53bf","code":"530921","parent_code":530900},{"title":"\u4e91\u53bf","code":"530922","parent_code":530900},{"title":"\u6c38\u5fb7\u53bf","code":"530923","parent_code":530900},{"title":"\u9547\u5eb7\u53bf","code":"530924","parent_code":530900},{"title":"\u53cc\u6c5f\u62c9\u795c\u65cf\u4f64\u65cf\u5e03\u6717\u65cf\u50a3\u65cf\u81ea\u6cbb\u53bf","code":"530925","parent_code":530900},{"title":"\u803f\u9a6c\u50a3\u65cf\u4f64\u65cf\u81ea\u6cbb\u53bf","code":"530926","parent_code":530900},{"title":"\u6ca7\u6e90\u4f64\u65cf\u81ea\u6cbb\u53bf","code":"530927","parent_code":530900},{"title":"\u695a\u96c4\u5e02","code":"532301","parent_code":532300},{"title":"\u53cc\u67cf\u53bf","code":"532322","parent_code":532300},{"title":"\u725f\u5b9a\u53bf","code":"532323","parent_code":532300},{"title":"\u5357\u534e\u53bf","code":"532324","parent_code":532300},{"title":"\u59da\u5b89\u53bf","code":"532325","parent_code":532300},{"title":"\u5927\u59da\u53bf","code":"532326","parent_code":532300},{"title":"\u6c38\u4ec1\u53bf","code":"532327","parent_code":532300},{"title":"\u5143\u8c0b\u53bf","code":"532328","parent_code":532300},{"title":"\u6b66\u5b9a\u53bf","code":"532329","parent_code":532300},{"title":"\u7984\u4e30\u53bf","code":"532331","parent_code":532300},{"title":"\u4e2a\u65e7\u5e02","code":"532501","parent_code":532500},{"title":"\u5f00\u8fdc\u5e02","code":"532502","parent_code":532500},{"title":"\u8499\u81ea\u5e02","code":"532503","parent_code":532500},{"title":"\u5f25\u52d2\u5e02","code":"532504","parent_code":532500},{"title":"\u5c4f\u8fb9\u82d7\u65cf\u81ea\u6cbb\u53bf","code":"532523","parent_code":532500},{"title":"\u5efa\u6c34\u53bf","code":"532524","parent_code":532500},{"title":"\u77f3\u5c4f\u53bf","code":"532525","parent_code":532500},{"title":"\u6cf8\u897f\u53bf","code":"532527","parent_code":532500},{"title":"\u5143\u9633\u53bf","code":"532528","parent_code":532500},{"title":"\u7ea2\u6cb3\u53bf","code":"532529","parent_code":532500},{"title":"\u91d1\u5e73\u82d7\u65cf\u7476\u65cf\u50a3\u65cf\u81ea\u6cbb\u53bf","code":"532530","parent_code":532500},{"title":"\u7eff\u6625\u53bf","code":"532531","parent_code":532500},{"title":"\u6cb3\u53e3\u7476\u65cf\u81ea\u6cbb\u53bf","code":"532532","parent_code":532500},{"title":"\u6587\u5c71\u5e02","code":"532601","parent_code":532600},{"title":"\u781a\u5c71\u53bf","code":"532622","parent_code":532600},{"title":"\u897f\u7574\u53bf","code":"532623","parent_code":532600},{"title":"\u9ebb\u6817\u5761\u53bf","code":"532624","parent_code":532600},{"title":"\u9a6c\u5173\u53bf","code":"532625","parent_code":532600},{"title":"\u4e18\u5317\u53bf","code":"532626","parent_code":532600},{"title":"\u5e7f\u5357\u53bf","code":"532627","parent_code":532600},{"title":"\u5bcc\u5b81\u53bf","code":"532628","parent_code":532600},{"title":"\u666f\u6d2a\u5e02","code":"532801","parent_code":532800},{"title":"\u52d0\u6d77\u53bf","code":"532822","parent_code":532800},{"title":"\u52d0\u814a\u53bf","code":"532823","parent_code":532800},{"title":"\u5927\u7406\u5e02","code":"532901","parent_code":532900},{"title":"\u6f3e\u6fde\u5f5d\u65cf\u81ea\u6cbb\u53bf","code":"532922","parent_code":532900},{"title":"\u7965\u4e91\u53bf","code":"532923","parent_code":532900},{"title":"\u5bbe\u5ddd\u53bf","code":"532924","parent_code":532900},{"title":"\u5f25\u6e21\u53bf","code":"532925","parent_code":532900},{"title":"\u5357\u6da7\u5f5d\u65cf\u81ea\u6cbb\u53bf","code":"532926","parent_code":532900},{"title":"\u5dcd\u5c71\u5f5d\u65cf\u56de\u65cf\u81ea\u6cbb\u53bf","code":"532927","parent_code":532900},{"title":"\u6c38\u5e73\u53bf","code":"532928","parent_code":532900},{"title":"\u4e91\u9f99\u53bf","code":"532929","parent_code":532900},{"title":"\u6d31\u6e90\u53bf","code":"532930","parent_code":532900},{"title":"\u5251\u5ddd\u53bf","code":"532931","parent_code":532900},{"title":"\u9e64\u5e86\u53bf","code":"532932","parent_code":532900},{"title":"\u745e\u4e3d\u5e02","code":"533102","parent_code":533100},{"title":"\u8292\u5e02","code":"533103","parent_code":533100},{"title":"\u6881\u6cb3\u53bf","code":"533122","parent_code":533100},{"title":"\u76c8\u6c5f\u53bf","code":"533123","parent_code":533100},{"title":"\u9647\u5ddd\u53bf","code":"533124","parent_code":533100},{"title":"\u6cf8\u6c34\u5e02","code":"533301","parent_code":533300},{"title":"\u798f\u8d21\u53bf","code":"533323","parent_code":533300},{"title":"\u8d21\u5c71\u72ec\u9f99\u65cf\u6012\u65cf\u81ea\u6cbb\u53bf","code":"533324","parent_code":533300},{"title":"\u5170\u576a\u767d\u65cf\u666e\u7c73\u65cf\u81ea\u6cbb\u53bf","code":"533325","parent_code":533300},{"title":"\u9999\u683c\u91cc\u62c9\u5e02","code":"533401","parent_code":533400},{"title":"\u5fb7\u94a6\u53bf","code":"533422","parent_code":533400},{"title":"\u7ef4\u897f\u5088\u50f3\u65cf\u81ea\u6cbb\u53bf","code":"533423","parent_code":533400},{"title":"\u57ce\u5173\u533a","code":"540102","parent_code":540100},{"title":"\u5806\u9f99\u5fb7\u5e86\u533a","code":"540103","parent_code":540100},{"title":"\u8fbe\u5b5c\u533a","code":"540104","parent_code":540100},{"title":"\u6797\u5468\u53bf","code":"540121","parent_code":540100},{"title":"\u5f53\u96c4\u53bf","code":"540122","parent_code":540100},{"title":"\u5c3c\u6728\u53bf","code":"540123","parent_code":540100},{"title":"\u66f2\u6c34\u53bf","code":"540124","parent_code":540100},{"title":"\u58a8\u7af9\u5de5\u5361\u53bf","code":"540127","parent_code":540100},{"title":"\u6851\u73e0\u5b5c\u533a","code":"540202","parent_code":540200},{"title":"\u5357\u6728\u6797\u53bf","code":"540221","parent_code":540200},{"title":"\u6c5f\u5b5c\u53bf","code":"540222","parent_code":540200},{"title":"\u5b9a\u65e5\u53bf","code":"540223","parent_code":540200},{"title":"\u8428\u8fe6\u53bf","code":"540224","parent_code":540200},{"title":"\u62c9\u5b5c\u53bf","code":"540225","parent_code":540200},{"title":"\u6602\u4ec1\u53bf","code":"540226","parent_code":540200},{"title":"\u8c22\u901a\u95e8\u53bf","code":"540227","parent_code":540200},{"title":"\u767d\u6717\u53bf","code":"540228","parent_code":540200},{"title":"\u4ec1\u5e03\u53bf","code":"540229","parent_code":540200},{"title":"\u5eb7\u9a6c\u53bf","code":"540230","parent_code":540200},{"title":"\u5b9a\u7ed3\u53bf","code":"540231","parent_code":540200},{"title":"\u4ef2\u5df4\u53bf","code":"540232","parent_code":540200},{"title":"\u4e9a\u4e1c\u53bf","code":"540233","parent_code":540200},{"title":"\u5409\u9686\u53bf","code":"540234","parent_code":540200},{"title":"\u8042\u62c9\u6728\u53bf","code":"540235","parent_code":540200},{"title":"\u8428\u560e\u53bf","code":"540236","parent_code":540200},{"title":"\u5c97\u5df4\u53bf","code":"540237","parent_code":540200},{"title":"\u5361\u82e5\u533a","code":"540302","parent_code":540300},{"title":"\u6c5f\u8fbe\u53bf","code":"540321","parent_code":540300},{"title":"\u8d21\u89c9\u53bf","code":"540322","parent_code":540300},{"title":"\u7c7b\u4e4c\u9f50\u53bf","code":"540323","parent_code":540300},{"title":"\u4e01\u9752\u53bf","code":"540324","parent_code":540300},{"title":"\u5bdf\u96c5\u53bf","code":"540325","parent_code":540300},{"title":"\u516b\u5bbf\u53bf","code":"540326","parent_code":540300},{"title":"\u5de6\u8d21\u53bf","code":"540327","parent_code":540300},{"title":"\u8292\u5eb7\u53bf","code":"540328","parent_code":540300},{"title":"\u6d1b\u9686\u53bf","code":"540329","parent_code":540300},{"title":"\u8fb9\u575d\u53bf","code":"540330","parent_code":540300},{"title":"\u5df4\u5b9c\u533a","code":"540402","parent_code":540400},{"title":"\u5de5\u5e03\u6c5f\u8fbe\u53bf","code":"540421","parent_code":540400},{"title":"\u7c73\u6797\u53bf","code":"540422","parent_code":540400},{"title":"\u58a8\u8131\u53bf","code":"540423","parent_code":540400},{"title":"\u6ce2\u5bc6\u53bf","code":"540424","parent_code":540400},{"title":"\u5bdf\u9685\u53bf","code":"540425","parent_code":540400},{"title":"\u6717\u53bf","code":"540426","parent_code":540400},{"title":"\u4e43\u4e1c\u533a","code":"540502","parent_code":540500},{"title":"\u624e\u56ca\u53bf","code":"540521","parent_code":540500},{"title":"\u8d21\u560e\u53bf","code":"540522","parent_code":540500},{"title":"\u6851\u65e5\u53bf","code":"540523","parent_code":540500},{"title":"\u743c\u7ed3\u53bf","code":"540524","parent_code":540500},{"title":"\u66f2\u677e\u53bf","code":"540525","parent_code":540500},{"title":"\u63aa\u7f8e\u53bf","code":"540526","parent_code":540500},{"title":"\u6d1b\u624e\u53bf","code":"540527","parent_code":540500},{"title":"\u52a0\u67e5\u53bf","code":"540528","parent_code":540500},{"title":"\u9686\u5b50\u53bf","code":"540529","parent_code":540500},{"title":"\u9519\u90a3\u53bf","code":"540530","parent_code":540500},{"title":"\u6d6a\u5361\u5b50\u53bf","code":"540531","parent_code":540500},{"title":"\u8272\u5c3c\u533a","code":"540602","parent_code":540600},{"title":"\u5609\u9ece\u53bf","code":"540621","parent_code":540600},{"title":"\u6bd4\u5982\u53bf","code":"540622","parent_code":540600},{"title":"\u8042\u8363\u53bf","code":"540623","parent_code":540600},{"title":"\u5b89\u591a\u53bf","code":"540624","parent_code":540600},{"title":"\u7533\u624e\u53bf","code":"540625","parent_code":540600},{"title":"\u7d22\u53bf","code":"540626","parent_code":540600},{"title":"\u73ed\u6208\u53bf","code":"540627","parent_code":540600},{"title":"\u5df4\u9752\u53bf","code":"540628","parent_code":540600},{"title":"\u5c3c\u739b\u53bf","code":"540629","parent_code":540600},{"title":"\u53cc\u6e56\u53bf","code":"540630","parent_code":540600},{"title":"\u666e\u5170\u53bf","code":"542521","parent_code":542500},{"title":"\u672d\u8fbe\u53bf","code":"542522","parent_code":542500},{"title":"\u5676\u5c14\u53bf","code":"542523","parent_code":542500},{"title":"\u65e5\u571f\u53bf","code":"542524","parent_code":542500},{"title":"\u9769\u5409\u53bf","code":"542525","parent_code":542500},{"title":"\u6539\u5219\u53bf","code":"542526","parent_code":542500},{"title":"\u63aa\u52e4\u53bf","code":"542527","parent_code":542500},{"title":"\u65b0\u57ce\u533a","code":"610102","parent_code":610100},{"title":"\u7891\u6797\u533a","code":"610103","parent_code":610100},{"title":"\u83b2\u6e56\u533a","code":"610104","parent_code":610100},{"title":"\u705e\u6865\u533a","code":"610111","parent_code":610100},{"title":"\u672a\u592e\u533a","code":"610112","parent_code":610100},{"title":"\u96c1\u5854\u533a","code":"610113","parent_code":610100},{"title":"\u960e\u826f\u533a","code":"610114","parent_code":610100},{"title":"\u4e34\u6f7c\u533a","code":"610115","parent_code":610100},{"title":"\u957f\u5b89\u533a","code":"610116","parent_code":610100},{"title":"\u9ad8\u9675\u533a","code":"610117","parent_code":610100},{"title":"\u9120\u9091\u533a","code":"610118","parent_code":610100},{"title":"\u84dd\u7530\u53bf","code":"610122","parent_code":610100},{"title":"\u5468\u81f3\u53bf","code":"610124","parent_code":610100},{"title":"\u738b\u76ca\u533a","code":"610202","parent_code":610200},{"title":"\u5370\u53f0\u533a","code":"610203","parent_code":610200},{"title":"\u8000\u5dde\u533a","code":"610204","parent_code":610200},{"title":"\u5b9c\u541b\u53bf","code":"610222","parent_code":610200},{"title":"\u6e2d\u6ee8\u533a","code":"610302","parent_code":610300},{"title":"\u91d1\u53f0\u533a","code":"610303","parent_code":610300},{"title":"\u9648\u4ed3\u533a","code":"610304","parent_code":610300},{"title":"\u51e4\u7fd4\u53bf","code":"610322","parent_code":610300},{"title":"\u5c90\u5c71\u53bf","code":"610323","parent_code":610300},{"title":"\u6276\u98ce\u53bf","code":"610324","parent_code":610300},{"title":"\u7709\u53bf","code":"610326","parent_code":610300},{"title":"\u9647\u53bf","code":"610327","parent_code":610300},{"title":"\u5343\u9633\u53bf","code":"610328","parent_code":610300},{"title":"\u9e9f\u6e38\u53bf","code":"610329","parent_code":610300},{"title":"\u51e4\u53bf","code":"610330","parent_code":610300},{"title":"\u592a\u767d\u53bf","code":"610331","parent_code":610300},{"title":"\u79e6\u90fd\u533a","code":"610402","parent_code":610400},{"title":"\u6768\u9675\u533a","code":"610403","parent_code":610400},{"title":"\u6e2d\u57ce\u533a","code":"610404","parent_code":610400},{"title":"\u4e09\u539f\u53bf","code":"610422","parent_code":610400},{"title":"\u6cfe\u9633\u53bf","code":"610423","parent_code":610400},{"title":"\u4e7e\u53bf","code":"610424","parent_code":610400},{"title":"\u793c\u6cc9\u53bf","code":"610425","parent_code":610400},{"title":"\u6c38\u5bff\u53bf","code":"610426","parent_code":610400},{"title":"\u957f\u6b66\u53bf","code":"610428","parent_code":610400},{"title":"\u65ec\u9091\u53bf","code":"610429","parent_code":610400},{"title":"\u6df3\u5316\u53bf","code":"610430","parent_code":610400},{"title":"\u6b66\u529f\u53bf","code":"610431","parent_code":610400},{"title":"\u5174\u5e73\u5e02","code":"610481","parent_code":610400},{"title":"\u5f6c\u5dde\u5e02","code":"610482","parent_code":610400},{"title":"\u4e34\u6e2d\u533a","code":"610502","parent_code":610500},{"title":"\u534e\u5dde\u533a","code":"610503","parent_code":610500},{"title":"\u6f7c\u5173\u53bf","code":"610522","parent_code":610500},{"title":"\u5927\u8354\u53bf","code":"610523","parent_code":610500},{"title":"\u5408\u9633\u53bf","code":"610524","parent_code":610500},{"title":"\u6f84\u57ce\u53bf","code":"610525","parent_code":610500},{"title":"\u84b2\u57ce\u53bf","code":"610526","parent_code":610500},{"title":"\u767d\u6c34\u53bf","code":"610527","parent_code":610500},{"title":"\u5bcc\u5e73\u53bf","code":"610528","parent_code":610500},{"title":"\u97e9\u57ce\u5e02","code":"610581","parent_code":610500},{"title":"\u534e\u9634\u5e02","code":"610582","parent_code":610500},{"title":"\u5b9d\u5854\u533a","code":"610602","parent_code":610600},{"title":"\u5b89\u585e\u533a","code":"610603","parent_code":610600},{"title":"\u5ef6\u957f\u53bf","code":"610621","parent_code":610600},{"title":"\u5ef6\u5ddd\u53bf","code":"610622","parent_code":610600},{"title":"\u5fd7\u4e39\u53bf","code":"610625","parent_code":610600},{"title":"\u5434\u8d77\u53bf","code":"610626","parent_code":610600},{"title":"\u7518\u6cc9\u53bf","code":"610627","parent_code":610600},{"title":"\u5bcc\u53bf","code":"610628","parent_code":610600},{"title":"\u6d1b\u5ddd\u53bf","code":"610629","parent_code":610600},{"title":"\u5b9c\u5ddd\u53bf","code":"610630","parent_code":610600},{"title":"\u9ec4\u9f99\u53bf","code":"610631","parent_code":610600},{"title":"\u9ec4\u9675\u53bf","code":"610632","parent_code":610600},{"title":"\u5b50\u957f\u5e02","code":"610681","parent_code":610600},{"title":"\u6c49\u53f0\u533a","code":"610702","parent_code":610700},{"title":"\u5357\u90d1\u533a","code":"610703","parent_code":610700},{"title":"\u57ce\u56fa\u53bf","code":"610722","parent_code":610700},{"title":"\u6d0b\u53bf","code":"610723","parent_code":610700},{"title":"\u897f\u4e61\u53bf","code":"610724","parent_code":610700},{"title":"\u52c9\u53bf","code":"610725","parent_code":610700},{"title":"\u5b81\u5f3a\u53bf","code":"610726","parent_code":610700},{"title":"\u7565\u9633\u53bf","code":"610727","parent_code":610700},{"title":"\u9547\u5df4\u53bf","code":"610728","parent_code":610700},{"title":"\u7559\u575d\u53bf","code":"610729","parent_code":610700},{"title":"\u4f5b\u576a\u53bf","code":"610730","parent_code":610700},{"title":"\u6986\u9633\u533a","code":"610802","parent_code":610800},{"title":"\u6a2a\u5c71\u533a","code":"610803","parent_code":610800},{"title":"\u5e9c\u8c37\u53bf","code":"610822","parent_code":610800},{"title":"\u9756\u8fb9\u53bf","code":"610824","parent_code":610800},{"title":"\u5b9a\u8fb9\u53bf","code":"610825","parent_code":610800},{"title":"\u7ee5\u5fb7\u53bf","code":"610826","parent_code":610800},{"title":"\u7c73\u8102\u53bf","code":"610827","parent_code":610800},{"title":"\u4f73\u53bf","code":"610828","parent_code":610800},{"title":"\u5434\u5821\u53bf","code":"610829","parent_code":610800},{"title":"\u6e05\u6da7\u53bf","code":"610830","parent_code":610800},{"title":"\u5b50\u6d32\u53bf","code":"610831","parent_code":610800},{"title":"\u795e\u6728\u5e02","code":"610881","parent_code":610800},{"title":"\u6c49\u6ee8\u533a","code":"610902","parent_code":610900},{"title":"\u6c49\u9634\u53bf","code":"610921","parent_code":610900},{"title":"\u77f3\u6cc9\u53bf","code":"610922","parent_code":610900},{"title":"\u5b81\u9655\u53bf","code":"610923","parent_code":610900},{"title":"\u7d2b\u9633\u53bf","code":"610924","parent_code":610900},{"title":"\u5c9a\u768b\u53bf","code":"610925","parent_code":610900},{"title":"\u5e73\u5229\u53bf","code":"610926","parent_code":610900},{"title":"\u9547\u576a\u53bf","code":"610927","parent_code":610900},{"title":"\u65ec\u9633\u53bf","code":"610928","parent_code":610900},{"title":"\u767d\u6cb3\u53bf","code":"610929","parent_code":610900},{"title":"\u5546\u5dde\u533a","code":"611002","parent_code":611000},{"title":"\u6d1b\u5357\u53bf","code":"611021","parent_code":611000},{"title":"\u4e39\u51e4\u53bf","code":"611022","parent_code":611000},{"title":"\u5546\u5357\u53bf","code":"611023","parent_code":611000},{"title":"\u5c71\u9633\u53bf","code":"611024","parent_code":611000},{"title":"\u9547\u5b89\u53bf","code":"611025","parent_code":611000},{"title":"\u67de\u6c34\u53bf","code":"611026","parent_code":611000},{"title":"\u57ce\u5173\u533a","code":"620102","parent_code":620100},{"title":"\u4e03\u91cc\u6cb3\u533a","code":"620103","parent_code":620100},{"title":"\u897f\u56fa\u533a","code":"620104","parent_code":620100},{"title":"\u5b89\u5b81\u533a","code":"620105","parent_code":620100},{"title":"\u7ea2\u53e4\u533a","code":"620111","parent_code":620100},{"title":"\u6c38\u767b\u53bf","code":"620121","parent_code":620100},{"title":"\u768b\u5170\u53bf","code":"620122","parent_code":620100},{"title":"\u6986\u4e2d\u53bf","code":"620123","parent_code":620100},{"title":"\u5170\u5dde\u65b0\u533a","code":"620171","parent_code":620100},{"title":"\u5e02\u8f96\u533a","code":"620201","parent_code":620200},{"title":"\u96c4\u5173\u533a","code":"620290","parent_code":620200},{"title":"\u957f\u57ce\u533a","code":"620291","parent_code":620200},{"title":"\u955c\u94c1\u533a","code":"620292","parent_code":620200},{"title":"\u65b0\u57ce\u9547","code":"620293","parent_code":620200},{"title":"\u5cea\u6cc9\u9547","code":"620294","parent_code":620200},{"title":"\u6587\u6b8a\u9547","code":"620295","parent_code":620200},{"title":"\u91d1\u5ddd\u533a","code":"620302","parent_code":620300},{"title":"\u6c38\u660c\u53bf","code":"620321","parent_code":620300},{"title":"\u767d\u94f6\u533a","code":"620402","parent_code":620400},{"title":"\u5e73\u5ddd\u533a","code":"620403","parent_code":620400},{"title":"\u9756\u8fdc\u53bf","code":"620421","parent_code":620400},{"title":"\u4f1a\u5b81\u53bf","code":"620422","parent_code":620400},{"title":"\u666f\u6cf0\u53bf","code":"620423","parent_code":620400},{"title":"\u79e6\u5dde\u533a","code":"620502","parent_code":620500},{"title":"\u9ea6\u79ef\u533a","code":"620503","parent_code":620500},{"title":"\u6e05\u6c34\u53bf","code":"620521","parent_code":620500},{"title":"\u79e6\u5b89\u53bf","code":"620522","parent_code":620500},{"title":"\u7518\u8c37\u53bf","code":"620523","parent_code":620500},{"title":"\u6b66\u5c71\u53bf","code":"620524","parent_code":620500},{"title":"\u5f20\u5bb6\u5ddd\u56de\u65cf\u81ea\u6cbb\u53bf","code":"620525","parent_code":620500},{"title":"\u51c9\u5dde\u533a","code":"620602","parent_code":620600},{"title":"\u6c11\u52e4\u53bf","code":"620621","parent_code":620600},{"title":"\u53e4\u6d6a\u53bf","code":"620622","parent_code":620600},{"title":"\u5929\u795d\u85cf\u65cf\u81ea\u6cbb\u53bf","code":"620623","parent_code":620600},{"title":"\u7518\u5dde\u533a","code":"620702","parent_code":620700},{"title":"\u8083\u5357\u88d5\u56fa\u65cf\u81ea\u6cbb\u53bf","code":"620721","parent_code":620700},{"title":"\u6c11\u4e50\u53bf","code":"620722","parent_code":620700},{"title":"\u4e34\u6cfd\u53bf","code":"620723","parent_code":620700},{"title":"\u9ad8\u53f0\u53bf","code":"620724","parent_code":620700},{"title":"\u5c71\u4e39\u53bf","code":"620725","parent_code":620700},{"title":"\u5d06\u5cd2\u533a","code":"620802","parent_code":620800},{"title":"\u6cfe\u5ddd\u53bf","code":"620821","parent_code":620800},{"title":"\u7075\u53f0\u53bf","code":"620822","parent_code":620800},{"title":"\u5d07\u4fe1\u53bf","code":"620823","parent_code":620800},{"title":"\u5e84\u6d6a\u53bf","code":"620825","parent_code":620800},{"title":"\u9759\u5b81\u53bf","code":"620826","parent_code":620800},{"title":"\u534e\u4ead\u5e02","code":"620881","parent_code":620800},{"title":"\u8083\u5dde\u533a","code":"620902","parent_code":620900},{"title":"\u91d1\u5854\u53bf","code":"620921","parent_code":620900},{"title":"\u74dc\u5dde\u53bf","code":"620922","parent_code":620900},{"title":"\u8083\u5317\u8499\u53e4\u65cf\u81ea\u6cbb\u53bf","code":"620923","parent_code":620900},{"title":"\u963f\u514b\u585e\u54c8\u8428\u514b\u65cf\u81ea\u6cbb\u53bf","code":"620924","parent_code":620900},{"title":"\u7389\u95e8\u5e02","code":"620981","parent_code":620900},{"title":"\u6566\u714c\u5e02","code":"620982","parent_code":620900},{"title":"\u897f\u5cf0\u533a","code":"621002","parent_code":621000},{"title":"\u5e86\u57ce\u53bf","code":"621021","parent_code":621000},{"title":"\u73af\u53bf","code":"621022","parent_code":621000},{"title":"\u534e\u6c60\u53bf","code":"621023","parent_code":621000},{"title":"\u5408\u6c34\u53bf","code":"621024","parent_code":621000},{"title":"\u6b63\u5b81\u53bf","code":"621025","parent_code":621000},{"title":"\u5b81\u53bf","code":"621026","parent_code":621000},{"title":"\u9547\u539f\u53bf","code":"621027","parent_code":621000},{"title":"\u5b89\u5b9a\u533a","code":"621102","parent_code":621100},{"title":"\u901a\u6e2d\u53bf","code":"621121","parent_code":621100},{"title":"\u9647\u897f\u53bf","code":"621122","parent_code":621100},{"title":"\u6e2d\u6e90\u53bf","code":"621123","parent_code":621100},{"title":"\u4e34\u6d2e\u53bf","code":"621124","parent_code":621100},{"title":"\u6f33\u53bf","code":"621125","parent_code":621100},{"title":"\u5cb7\u53bf","code":"621126","parent_code":621100},{"title":"\u6b66\u90fd\u533a","code":"621202","parent_code":621200},{"title":"\u6210\u53bf","code":"621221","parent_code":621200},{"title":"\u6587\u53bf","code":"621222","parent_code":621200},{"title":"\u5b95\u660c\u53bf","code":"621223","parent_code":621200},{"title":"\u5eb7\u53bf","code":"621224","parent_code":621200},{"title":"\u897f\u548c\u53bf","code":"621225","parent_code":621200},{"title":"\u793c\u53bf","code":"621226","parent_code":621200},{"title":"\u5fbd\u53bf","code":"621227","parent_code":621200},{"title":"\u4e24\u5f53\u53bf","code":"621228","parent_code":621200},{"title":"\u4e34\u590f\u5e02","code":"622901","parent_code":622900},{"title":"\u4e34\u590f\u53bf","code":"622921","parent_code":622900},{"title":"\u5eb7\u4e50\u53bf","code":"622922","parent_code":622900},{"title":"\u6c38\u9756\u53bf","code":"622923","parent_code":622900},{"title":"\u5e7f\u6cb3\u53bf","code":"622924","parent_code":622900},{"title":"\u548c\u653f\u53bf","code":"622925","parent_code":622900},{"title":"\u4e1c\u4e61\u65cf\u81ea\u6cbb\u53bf","code":"622926","parent_code":622900},{"title":"\u79ef\u77f3\u5c71\u4fdd\u5b89\u65cf\u4e1c\u4e61\u65cf\u6492\u62c9\u65cf\u81ea\u6cbb\u53bf","code":"622927","parent_code":622900},{"title":"\u5408\u4f5c\u5e02","code":"623001","parent_code":623000},{"title":"\u4e34\u6f6d\u53bf","code":"623021","parent_code":623000},{"title":"\u5353\u5c3c\u53bf","code":"623022","parent_code":623000},{"title":"\u821f\u66f2\u53bf","code":"623023","parent_code":623000},{"title":"\u8fed\u90e8\u53bf","code":"623024","parent_code":623000},{"title":"\u739b\u66f2\u53bf","code":"623025","parent_code":623000},{"title":"\u788c\u66f2\u53bf","code":"623026","parent_code":623000},{"title":"\u590f\u6cb3\u53bf","code":"623027","parent_code":623000},{"title":"\u57ce\u4e1c\u533a","code":"630102","parent_code":630100},{"title":"\u57ce\u4e2d\u533a","code":"630103","parent_code":630100},{"title":"\u57ce\u897f\u533a","code":"630104","parent_code":630100},{"title":"\u57ce\u5317\u533a","code":"630105","parent_code":630100},{"title":"\u6e5f\u4e2d\u533a","code":"630106","parent_code":630100},{"title":"\u5927\u901a\u56de\u65cf\u571f\u65cf\u81ea\u6cbb\u53bf","code":"630121","parent_code":630100},{"title":"\u6e5f\u6e90\u53bf","code":"630123","parent_code":630100},{"title":"\u4e50\u90fd\u533a","code":"630202","parent_code":630200},{"title":"\u5e73\u5b89\u533a","code":"630203","parent_code":630200},{"title":"\u6c11\u548c\u56de\u65cf\u571f\u65cf\u81ea\u6cbb\u53bf","code":"630222","parent_code":630200},{"title":"\u4e92\u52a9\u571f\u65cf\u81ea\u6cbb\u53bf","code":"630223","parent_code":630200},{"title":"\u5316\u9686\u56de\u65cf\u81ea\u6cbb\u53bf","code":"630224","parent_code":630200},{"title":"\u5faa\u5316\u6492\u62c9\u65cf\u81ea\u6cbb\u53bf","code":"630225","parent_code":630200},{"title":"\u95e8\u6e90\u56de\u65cf\u81ea\u6cbb\u53bf","code":"632221","parent_code":632200},{"title":"\u7941\u8fde\u53bf","code":"632222","parent_code":632200},{"title":"\u6d77\u664f\u53bf","code":"632223","parent_code":632200},{"title":"\u521a\u5bdf\u53bf","code":"632224","parent_code":632200},{"title":"\u540c\u4ec1\u53bf","code":"632321","parent_code":632300},{"title":"\u5c16\u624e\u53bf","code":"632322","parent_code":632300},{"title":"\u6cfd\u5e93\u53bf","code":"632323","parent_code":632300},{"title":"\u6cb3\u5357\u8499\u53e4\u65cf\u81ea\u6cbb\u53bf","code":"632324","parent_code":632300},{"title":"\u5171\u548c\u53bf","code":"632521","parent_code":632500},{"title":"\u540c\u5fb7\u53bf","code":"632522","parent_code":632500},{"title":"\u8d35\u5fb7\u53bf","code":"632523","parent_code":632500},{"title":"\u5174\u6d77\u53bf","code":"632524","parent_code":632500},{"title":"\u8d35\u5357\u53bf","code":"632525","parent_code":632500},{"title":"\u739b\u6c81\u53bf","code":"632621","parent_code":632600},{"title":"\u73ed\u739b\u53bf","code":"632622","parent_code":632600},{"title":"\u7518\u5fb7\u53bf","code":"632623","parent_code":632600},{"title":"\u8fbe\u65e5\u53bf","code":"632624","parent_code":632600},{"title":"\u4e45\u6cbb\u53bf","code":"632625","parent_code":632600},{"title":"\u739b\u591a\u53bf","code":"632626","parent_code":632600},{"title":"\u7389\u6811\u5e02","code":"632701","parent_code":632700},{"title":"\u6742\u591a\u53bf","code":"632722","parent_code":632700},{"title":"\u79f0\u591a\u53bf","code":"632723","parent_code":632700},{"title":"\u6cbb\u591a\u53bf","code":"632724","parent_code":632700},{"title":"\u56ca\u8c26\u53bf","code":"632725","parent_code":632700},{"title":"\u66f2\u9ebb\u83b1\u53bf","code":"632726","parent_code":632700},{"title":"\u683c\u5c14\u6728\u5e02","code":"632801","parent_code":632800},{"title":"\u5fb7\u4ee4\u54c8\u5e02","code":"632802","parent_code":632800},{"title":"\u832b\u5d16\u5e02","code":"632803","parent_code":632800},{"title":"\u4e4c\u5170\u53bf","code":"632821","parent_code":632800},{"title":"\u90fd\u5170\u53bf","code":"632822","parent_code":632800},{"title":"\u5929\u5cfb\u53bf","code":"632823","parent_code":632800},{"title":"\u5927\u67f4\u65e6\u884c\u653f\u59d4\u5458\u4f1a","code":"632857","parent_code":632800},{"title":"\u5174\u5e86\u533a","code":"640104","parent_code":640100},{"title":"\u897f\u590f\u533a","code":"640105","parent_code":640100},{"title":"\u91d1\u51e4\u533a","code":"640106","parent_code":640100},{"title":"\u6c38\u5b81\u53bf","code":"640121","parent_code":640100},{"title":"\u8d3a\u5170\u53bf","code":"640122","parent_code":640100},{"title":"\u7075\u6b66\u5e02","code":"640181","parent_code":640100},{"title":"\u5927\u6b66\u53e3\u533a","code":"640202","parent_code":640200},{"title":"\u60e0\u519c\u533a","code":"640205","parent_code":640200},{"title":"\u5e73\u7f57\u53bf","code":"640221","parent_code":640200},{"title":"\u5229\u901a\u533a","code":"640302","parent_code":640300},{"title":"\u7ea2\u5bfa\u5821\u533a","code":"640303","parent_code":640300},{"title":"\u76d0\u6c60\u53bf","code":"640323","parent_code":640300},{"title":"\u540c\u5fc3\u53bf","code":"640324","parent_code":640300},{"title":"\u9752\u94dc\u5ce1\u5e02","code":"640381","parent_code":640300},{"title":"\u539f\u5dde\u533a","code":"640402","parent_code":640400},{"title":"\u897f\u5409\u53bf","code":"640422","parent_code":640400},{"title":"\u9686\u5fb7\u53bf","code":"640423","parent_code":640400},{"title":"\u6cfe\u6e90\u53bf","code":"640424","parent_code":640400},{"title":"\u5f6d\u9633\u53bf","code":"640425","parent_code":640400},{"title":"\u6c99\u5761\u5934\u533a","code":"640502","parent_code":640500},{"title":"\u4e2d\u5b81\u53bf","code":"640521","parent_code":640500},{"title":"\u6d77\u539f\u53bf","code":"640522","parent_code":640500},{"title":"\u5929\u5c71\u533a","code":"650102","parent_code":650100},{"title":"\u6c99\u4f9d\u5df4\u514b\u533a","code":"650103","parent_code":650100},{"title":"\u65b0\u5e02\u533a","code":"650104","parent_code":650100},{"title":"\u6c34\u78e8\u6c9f\u533a","code":"650105","parent_code":650100},{"title":"\u5934\u5c6f\u6cb3\u533a","code":"650106","parent_code":650100},{"title":"\u8fbe\u5742\u57ce\u533a","code":"650107","parent_code":650100},{"title":"\u7c73\u4e1c\u533a","code":"650109","parent_code":650100},{"title":"\u4e4c\u9c81\u6728\u9f50\u53bf","code":"650121","parent_code":650100},{"title":"\u72ec\u5c71\u5b50\u533a","code":"650202","parent_code":650200},{"title":"\u514b\u62c9\u739b\u4f9d\u533a","code":"650203","parent_code":650200},{"title":"\u767d\u78b1\u6ee9\u533a","code":"650204","parent_code":650200},{"title":"\u4e4c\u5c14\u79be\u533a","code":"650205","parent_code":650200},{"title":"\u9ad8\u660c\u533a","code":"650402","parent_code":650400},{"title":"\u912f\u5584\u53bf","code":"650421","parent_code":650400},{"title":"\u6258\u514b\u900a\u53bf","code":"650422","parent_code":650400},{"title":"\u4f0a\u5dde\u533a","code":"650502","parent_code":650500},{"title":"\u5df4\u91cc\u5764\u54c8\u8428\u514b\u81ea\u6cbb\u53bf","code":"650521","parent_code":650500},{"title":"\u4f0a\u543e\u53bf","code":"650522","parent_code":650500},{"title":"\u660c\u5409\u5e02","code":"652301","parent_code":652300},{"title":"\u961c\u5eb7\u5e02","code":"652302","parent_code":652300},{"title":"\u547c\u56fe\u58c1\u53bf","code":"652323","parent_code":652300},{"title":"\u739b\u7eb3\u65af\u53bf","code":"652324","parent_code":652300},{"title":"\u5947\u53f0\u53bf","code":"652325","parent_code":652300},{"title":"\u5409\u6728\u8428\u5c14\u53bf","code":"652327","parent_code":652300},{"title":"\u6728\u5792\u54c8\u8428\u514b\u81ea\u6cbb\u53bf","code":"652328","parent_code":652300},{"title":"\u535a\u4e50\u5e02","code":"652701","parent_code":652700},{"title":"\u963f\u62c9\u5c71\u53e3\u5e02","code":"652702","parent_code":652700},{"title":"\u7cbe\u6cb3\u53bf","code":"652722","parent_code":652700},{"title":"\u6e29\u6cc9\u53bf","code":"652723","parent_code":652700},{"title":"\u5e93\u5c14\u52d2\u5e02","code":"652801","parent_code":652800},{"title":"\u8f6e\u53f0\u53bf","code":"652822","parent_code":652800},{"title":"\u5c09\u7281\u53bf","code":"652823","parent_code":652800},{"title":"\u82e5\u7f8c\u53bf","code":"652824","parent_code":652800},{"title":"\u4e14\u672b\u53bf","code":"652825","parent_code":652800},{"title":"\u7109\u8006\u56de\u65cf\u81ea\u6cbb\u53bf","code":"652826","parent_code":652800},{"title":"\u548c\u9759\u53bf","code":"652827","parent_code":652800},{"title":"\u548c\u7855\u53bf","code":"652828","parent_code":652800},{"title":"\u535a\u6e56\u53bf","code":"652829","parent_code":652800},{"title":"\u963f\u514b\u82cf\u5e02","code":"652901","parent_code":652900},{"title":"\u5e93\u8f66\u5e02","code":"652902","parent_code":652900},{"title":"\u6e29\u5bbf\u53bf","code":"652922","parent_code":652900},{"title":"\u6c99\u96c5\u53bf","code":"652924","parent_code":652900},{"title":"\u65b0\u548c\u53bf","code":"652925","parent_code":652900},{"title":"\u62dc\u57ce\u53bf","code":"652926","parent_code":652900},{"title":"\u4e4c\u4ec0\u53bf","code":"652927","parent_code":652900},{"title":"\u963f\u74e6\u63d0\u53bf","code":"652928","parent_code":652900},{"title":"\u67ef\u576a\u53bf","code":"652929","parent_code":652900},{"title":"\u963f\u56fe\u4ec0\u5e02","code":"653001","parent_code":653000},{"title":"\u963f\u514b\u9676\u53bf","code":"653022","parent_code":653000},{"title":"\u963f\u5408\u5947\u53bf","code":"653023","parent_code":653000},{"title":"\u4e4c\u6070\u53bf","code":"653024","parent_code":653000},{"title":"\u5580\u4ec0\u5e02","code":"653101","parent_code":653100},{"title":"\u758f\u9644\u53bf","code":"653121","parent_code":653100},{"title":"\u758f\u52d2\u53bf","code":"653122","parent_code":653100},{"title":"\u82f1\u5409\u6c99\u53bf","code":"653123","parent_code":653100},{"title":"\u6cfd\u666e\u53bf","code":"653124","parent_code":653100},{"title":"\u838e\u8f66\u53bf","code":"653125","parent_code":653100},{"title":"\u53f6\u57ce\u53bf","code":"653126","parent_code":653100},{"title":"\u9ea6\u76d6\u63d0\u53bf","code":"653127","parent_code":653100},{"title":"\u5cb3\u666e\u6e56\u53bf","code":"653128","parent_code":653100},{"title":"\u4f3d\u5e08\u53bf","code":"653129","parent_code":653100},{"title":"\u5df4\u695a\u53bf","code":"653130","parent_code":653100},{"title":"\u5854\u4ec0\u5e93\u5c14\u5e72\u5854\u5409\u514b\u81ea\u6cbb\u53bf","code":"653131","parent_code":653100},{"title":"\u548c\u7530\u5e02","code":"653201","parent_code":653200},{"title":"\u548c\u7530\u53bf","code":"653221","parent_code":653200},{"title":"\u58a8\u7389\u53bf","code":"653222","parent_code":653200},{"title":"\u76ae\u5c71\u53bf","code":"653223","parent_code":653200},{"title":"\u6d1b\u6d66\u53bf","code":"653224","parent_code":653200},{"title":"\u7b56\u52d2\u53bf","code":"653225","parent_code":653200},{"title":"\u4e8e\u7530\u53bf","code":"653226","parent_code":653200},{"title":"\u6c11\u4e30\u53bf","code":"653227","parent_code":653200},{"title":"\u4f0a\u5b81\u5e02","code":"654002","parent_code":654000},{"title":"\u594e\u5c6f\u5e02","code":"654003","parent_code":654000},{"title":"\u970d\u5c14\u679c\u65af\u5e02","code":"654004","parent_code":654000},{"title":"\u4f0a\u5b81\u53bf","code":"654021","parent_code":654000},{"title":"\u5bdf\u5e03\u67e5\u5c14\u9521\u4f2f\u81ea\u6cbb\u53bf","code":"654022","parent_code":654000},{"title":"\u970d\u57ce\u53bf","code":"654023","parent_code":654000},{"title":"\u5de9\u7559\u53bf","code":"654024","parent_code":654000},{"title":"\u65b0\u6e90\u53bf","code":"654025","parent_code":654000},{"title":"\u662d\u82cf\u53bf","code":"654026","parent_code":654000},{"title":"\u7279\u514b\u65af\u53bf","code":"654027","parent_code":654000},{"title":"\u5c3c\u52d2\u514b\u53bf","code":"654028","parent_code":654000},{"title":"\u5854\u57ce\u5e02","code":"654201","parent_code":654200},{"title":"\u4e4c\u82cf\u5e02","code":"654202","parent_code":654200},{"title":"\u989d\u654f\u53bf","code":"654221","parent_code":654200},{"title":"\u6c99\u6e7e\u53bf","code":"654223","parent_code":654200},{"title":"\u6258\u91cc\u53bf","code":"654224","parent_code":654200},{"title":"\u88d5\u6c11\u53bf","code":"654225","parent_code":654200},{"title":"\u548c\u5e03\u514b\u8d5b\u5c14\u8499\u53e4\u81ea\u6cbb\u53bf","code":"654226","parent_code":654200},{"title":"\u963f\u52d2\u6cf0\u5e02","code":"654301","parent_code":654300},{"title":"\u5e03\u5c14\u6d25\u53bf","code":"654321","parent_code":654300},{"title":"\u5bcc\u8574\u53bf","code":"654322","parent_code":654300},{"title":"\u798f\u6d77\u53bf","code":"654323","parent_code":654300},{"title":"\u54c8\u5df4\u6cb3\u53bf","code":"654324","parent_code":654300},{"title":"\u9752\u6cb3\u53bf","code":"654325","parent_code":654300},{"title":"\u5409\u6728\u4e43\u53bf","code":"654326","parent_code":654300},{"title":"\u77f3\u6cb3\u5b50\u5e02","code":"659001","parent_code":659000},{"title":"\u963f\u62c9\u5c14\u5e02","code":"659002","parent_code":659000},{"title":"\u56fe\u6728\u8212\u514b\u5e02","code":"659003","parent_code":659000},{"title":"\u4e94\u5bb6\u6e20\u5e02","code":"659004","parent_code":659000},{"title":"\u5317\u5c6f\u5e02","code":"659005","parent_code":659000},{"title":"\u94c1\u95e8\u5173\u5e02","code":"659006","parent_code":659000},{"title":"\u53cc\u6cb3\u5e02","code":"659007","parent_code":659000},{"title":"\u53ef\u514b\u8fbe\u62c9\u5e02","code":"659008","parent_code":659000},{"title":"\u6606\u7389\u5e02","code":"659009","parent_code":659000},{"title":"\u80e1\u6768\u6cb3\u5e02","code":"659010","parent_code":659000},{"title":"\u4e2d\u6b63\u533a","code":"710101","parent_code":710100},{"title":"\u5927\u540c\u533a","code":"710102","parent_code":710100},{"title":"\u4e2d\u5c71\u533a","code":"710103","parent_code":710100},{"title":"\u677e\u5c71\u533a","code":"710104","parent_code":710100},{"title":"\u5927\u5b89\u533a","code":"710105","parent_code":710100},{"title":"\u4e07\u534e\u533a","code":"710106","parent_code":710100},{"title":"\u4fe1\u4e49\u533a","code":"710107","parent_code":710100},{"title":"\u58eb\u6797\u533a","code":"710108","parent_code":710100},{"title":"\u5317\u6295\u533a","code":"710109","parent_code":710100},{"title":"\u5185\u6e56\u533a","code":"710110","parent_code":710100},{"title":"\u5357\u6e2f\u533a","code":"710111","parent_code":710100},{"title":"\u6587\u5c71\u533a","code":"710112","parent_code":710100},{"title":"\u5176\u5b83\u533a","code":"710199","parent_code":710100},{"title":"\u65b0\u5174\u533a","code":"710201","parent_code":710200},{"title":"\u524d\u91d1\u533a","code":"710202","parent_code":710200},{"title":"\u82a9\u96c5\u533a","code":"710203","parent_code":710200},{"title":"\u76d0\u57d5\u533a","code":"710204","parent_code":710200},{"title":"\u9f13\u5c71\u533a","code":"710205","parent_code":710200},{"title":"\u65d7\u6d25\u533a","code":"710206","parent_code":710200},{"title":"\u524d\u9547\u533a","code":"710207","parent_code":710200},{"title":"\u4e09\u6c11\u533a","code":"710208","parent_code":710200},{"title":"\u5de6\u8425\u533a","code":"710209","parent_code":710200},{"title":"\u6960\u6893\u533a","code":"710210","parent_code":710200},{"title":"\u5c0f\u6e2f\u533a","code":"710211","parent_code":710200},{"title":"\u82d3\u96c5\u533a","code":"710241","parent_code":710200},{"title":"\u4ec1\u6b66\u533a","code":"710242","parent_code":710200},{"title":"\u5927\u793e\u533a","code":"710243","parent_code":710200},{"title":"\u5188\u5c71\u533a","code":"710244","parent_code":710200},{"title":"\u8def\u7af9\u533a","code":"710245","parent_code":710200},{"title":"\u963f\u83b2\u533a","code":"710246","parent_code":710200},{"title":"\u7530\u5bee\u533a","code":"710247","parent_code":710200},{"title":"\u71d5\u5de2\u533a","code":"710248","parent_code":710200},{"title":"\u6865\u5934\u533a","code":"710249","parent_code":710200},{"title":"\u6893\u5b98\u533a","code":"710250","parent_code":710200},{"title":"\u5f25\u9640\u533a","code":"710251","parent_code":710200},{"title":"\u6c38\u5b89\u533a","code":"710252","parent_code":710200},{"title":"\u6e56\u5185\u533a","code":"710253","parent_code":710200},{"title":"\u51e4\u5c71\u533a","code":"710254","parent_code":710200},{"title":"\u5927\u5bee\u533a","code":"710255","parent_code":710200},{"title":"\u6797\u56ed\u533a","code":"710256","parent_code":710200},{"title":"\u9e1f\u677e\u533a","code":"710257","parent_code":710200},{"title":"\u5927\u6811\u533a","code":"710258","parent_code":710200},{"title":"\u65d7\u5c71\u533a","code":"710259","parent_code":710200},{"title":"\u7f8e\u6d53\u533a","code":"710260","parent_code":710200},{"title":"\u516d\u9f9f\u533a","code":"710261","parent_code":710200},{"title":"\u5185\u95e8\u533a","code":"710262","parent_code":710200},{"title":"\u6749\u6797\u533a","code":"710263","parent_code":710200},{"title":"\u7532\u4ed9\u533a","code":"710264","parent_code":710200},{"title":"\u6843\u6e90\u533a","code":"710265","parent_code":710200},{"title":"\u90a3\u739b\u590f\u533a","code":"710266","parent_code":710200},{"title":"\u8302\u6797\u533a","code":"710267","parent_code":710200},{"title":"\u8304\u8423\u533a","code":"710268","parent_code":710200},{"title":"\u5176\u5b83\u533a","code":"710299","parent_code":710200},{"title":"\u4e2d\u897f\u533a","code":"710301","parent_code":710300},{"title":"\u4e1c\u533a","code":"710302","parent_code":710300},{"title":"\u5357\u533a","code":"710303","parent_code":710300},{"title":"\u5317\u533a","code":"710304","parent_code":710300},{"title":"\u5b89\u5e73\u533a","code":"710305","parent_code":710300},{"title":"\u5b89\u5357\u533a","code":"710306","parent_code":710300},{"title":"\u6c38\u5eb7\u533a","code":"710339","parent_code":710300},{"title":"\u5f52\u4ec1\u533a","code":"710340","parent_code":710300},{"title":"\u65b0\u5316\u533a","code":"710341","parent_code":710300},{"title":"\u5de6\u9547\u533a","code":"710342","parent_code":710300},{"title":"\u7389\u4e95\u533a","code":"710343","parent_code":710300},{"title":"\u6960\u897f\u533a","code":"710344","parent_code":710300},{"title":"\u5357\u5316\u533a","code":"710345","parent_code":710300},{"title":"\u4ec1\u5fb7\u533a","code":"710346","parent_code":710300},{"title":"\u5173\u5e99\u533a","code":"710347","parent_code":710300},{"title":"\u9f99\u5d0e\u533a","code":"710348","parent_code":710300},{"title":"\u5b98\u7530\u533a","code":"710349","parent_code":710300},{"title":"\u9ebb\u8c46\u533a","code":"710350","parent_code":710300},{"title":"\u4f73\u91cc\u533a","code":"710351","parent_code":710300},{"title":"\u897f\u6e2f\u533a","code":"710352","parent_code":710300},{"title":"\u4e03\u80a1\u533a","code":"710353","parent_code":710300},{"title":"\u5c06\u519b\u533a","code":"710354","parent_code":710300},{"title":"\u5b66\u7532\u533a","code":"710355","parent_code":710300},{"title":"\u5317\u95e8\u533a","code":"710356","parent_code":710300},{"title":"\u65b0\u8425\u533a","code":"710357","parent_code":710300},{"title":"\u540e\u58c1\u533a","code":"710358","parent_code":710300},{"title":"\u767d\u6cb3\u533a","code":"710359","parent_code":710300},{"title":"\u4e1c\u5c71\u533a","code":"710360","parent_code":710300},{"title":"\u516d\u7532\u533a","code":"710361","parent_code":710300},{"title":"\u4e0b\u8425\u533a","code":"710362","parent_code":710300},{"title":"\u67f3\u8425\u533a","code":"710363","parent_code":710300},{"title":"\u76d0\u6c34\u533a","code":"710364","parent_code":710300},{"title":"\u5584\u5316\u533a","code":"710365","parent_code":710300},{"title":"\u5927\u5185\u533a","code":"710366","parent_code":710300},{"title":"\u5c71\u4e0a\u533a","code":"710367","parent_code":710300},{"title":"\u65b0\u5e02\u533a","code":"710368","parent_code":710300},{"title":"\u5b89\u5b9a\u533a","code":"710369","parent_code":710300},{"title":"\u5176\u5b83\u533a","code":"710399","parent_code":710300},{"title":"\u4e2d\u533a","code":"710401","parent_code":710400},{"title":"\u4e1c\u533a","code":"710402","parent_code":710400},{"title":"\u5357\u533a","code":"710403","parent_code":710400},{"title":"\u897f\u533a","code":"710404","parent_code":710400},{"title":"\u5317\u533a","code":"710405","parent_code":710400},{"title":"\u5317\u5c6f\u533a","code":"710406","parent_code":710400},{"title":"\u897f\u5c6f\u533a","code":"710407","parent_code":710400},{"title":"\u5357\u5c6f\u533a","code":"710408","parent_code":710400},{"title":"\u592a\u5e73\u533a","code":"710431","parent_code":710400},{"title":"\u5927\u91cc\u533a","code":"710432","parent_code":710400},{"title":"\u96fe\u5cf0\u533a","code":"710433","parent_code":710400},{"title":"\u4e4c\u65e5\u533a","code":"710434","parent_code":710400},{"title":"\u4e30\u539f\u533a","code":"710435","parent_code":710400},{"title":"\u540e\u91cc\u533a","code":"710436","parent_code":710400},{"title":"\u77f3\u5188\u533a","code":"710437","parent_code":710400},{"title":"\u4e1c\u52bf\u533a","code":"710438","parent_code":710400},{"title":"\u548c\u5e73\u533a","code":"710439","parent_code":710400},{"title":"\u65b0\u793e\u533a","code":"710440","parent_code":710400},{"title":"\u6f6d\u5b50\u533a","code":"710441","parent_code":710400},{"title":"\u5927\u96c5\u533a","code":"710442","parent_code":710400},{"title":"\u795e\u5188\u533a","code":"710443","parent_code":710400},{"title":"\u5927\u809a\u533a","code":"710444","parent_code":710400},{"title":"\u6c99\u9e7f\u533a","code":"710445","parent_code":710400},{"title":"\u9f99\u4e95\u533a","code":"710446","parent_code":710400},{"title":"\u68a7\u6816\u533a","code":"710447","parent_code":710400},{"title":"\u6e05\u6c34\u533a","code":"710448","parent_code":710400},{"title":"\u5927\u7532\u533a","code":"710449","parent_code":710400},{"title":"\u5916\u57d4\u533a","code":"710450","parent_code":710400},{"title":"\u5927\u5b89\u533a","code":"710451","parent_code":710400},{"title":"\u5176\u5b83\u533a","code":"710499","parent_code":710400},{"title":"\u91d1\u6c99\u9547","code":"710507","parent_code":710500},{"title":"\u91d1\u6e56\u9547","code":"710508","parent_code":710500},{"title":"\u91d1\u5b81\u4e61","code":"710509","parent_code":710500},{"title":"\u91d1\u57ce\u9547","code":"710510","parent_code":710500},{"title":"\u70c8\u5c7f\u4e61","code":"710511","parent_code":710500},{"title":"\u4e4c\u5775\u4e61","code":"710512","parent_code":710500},{"title":"\u5357\u6295\u5e02","code":"710614","parent_code":710600},{"title":"\u4e2d\u5bee\u4e61","code":"710615","parent_code":710600},{"title":"\u8349\u5c6f\u9547","code":"710616","parent_code":710600},{"title":"\u56fd\u59d3\u4e61","code":"710617","parent_code":710600},{"title":"\u57d4\u91cc\u9547","code":"710618","parent_code":710600},{"title":"\u4ec1\u7231\u4e61","code":"710619","parent_code":710600},{"title":"\u540d\u95f4\u4e61","code":"710620","parent_code":710600},{"title":"\u96c6\u96c6\u9547","code":"710621","parent_code":710600},{"title":"\u6c34\u91cc\u4e61","code":"710622","parent_code":710600},{"title":"\u9c7c\u6c60\u4e61","code":"710623","parent_code":710600},{"title":"\u4fe1\u4e49\u4e61","code":"710624","parent_code":710600},{"title":"\u7af9\u5c71\u9547","code":"710625","parent_code":710600},{"title":"\u9e7f\u8c37\u4e61","code":"710626","parent_code":710600},{"title":"\u4ec1\u7231\u533a","code":"710701","parent_code":710700},{"title":"\u4fe1\u4e49\u533a","code":"710702","parent_code":710700},{"title":"\u4e2d\u6b63\u533a","code":"710703","parent_code":710700},{"title":"\u4e2d\u5c71\u533a","code":"710704","parent_code":710700},{"title":"\u5b89\u4e50\u533a","code":"710705","parent_code":710700},{"title":"\u6696\u6696\u533a","code":"710706","parent_code":710700},{"title":"\u4e03\u5835\u533a","code":"710707","parent_code":710700},{"title":"\u5176\u5b83\u533a","code":"710799","parent_code":710700},{"title":"\u4e1c\u533a","code":"710801","parent_code":710800},{"title":"\u5317\u533a","code":"710802","parent_code":710800},{"title":"\u9999\u5c71\u533a","code":"710803","parent_code":710800},{"title":"\u5176\u5b83\u533a","code":"710899","parent_code":710800},{"title":"\u4e1c\u533a","code":"710901","parent_code":710900},{"title":"\u897f\u533a","code":"710902","parent_code":710900},{"title":"\u5176\u5b83\u533a","code":"710999","parent_code":710900},{"title":"\u4e07\u91cc\u533a","code":"711130","parent_code":711100},{"title":"\u677f\u6865\u533a","code":"711132","parent_code":711100},{"title":"\u6c50\u6b62\u533a","code":"711133","parent_code":711100},{"title":"\u6df1\u5751\u533a","code":"711134","parent_code":711100},{"title":"\u77f3\u7887\u533a","code":"711135","parent_code":711100},{"title":"\u745e\u82b3\u533a","code":"711136","parent_code":711100},{"title":"\u5e73\u6eaa\u533a","code":"711137","parent_code":711100},{"title":"\u53cc\u6eaa\u533a","code":"711138","parent_code":711100},{"title":"\u8d21\u5bee\u533a","code":"711139","parent_code":711100},{"title":"\u65b0\u5e97\u533a","code":"711140","parent_code":711100},{"title":"\u576a\u6797\u533a","code":"711141","parent_code":711100},{"title":"\u4e4c\u6765\u533a","code":"711142","parent_code":711100},{"title":"\u6c38\u548c\u533a","code":"711143","parent_code":711100},{"title":"\u4e2d\u548c\u533a","code":"711144","parent_code":711100},{"title":"\u571f\u57ce\u533a","code":"711145","parent_code":711100},{"title":"\u4e09\u5ce1\u533a","code":"711146","parent_code":711100},{"title":"\u6811\u6797\u533a","code":"711147","parent_code":711100},{"title":"\u83ba\u6b4c\u533a","code":"711148","parent_code":711100},{"title":"\u4e09\u91cd\u533a","code":"711149","parent_code":711100},{"title":"\u65b0\u5e84\u533a","code":"711150","parent_code":711100},{"title":"\u6cf0\u5c71\u533a","code":"711151","parent_code":711100},{"title":"\u6797\u53e3\u533a","code":"711152","parent_code":711100},{"title":"\u82a6\u6d32\u533a","code":"711153","parent_code":711100},{"title":"\u4e94\u80a1\u533a","code":"711154","parent_code":711100},{"title":"\u516b\u91cc\u533a","code":"711155","parent_code":711100},{"title":"\u6de1\u6c34\u533a","code":"711156","parent_code":711100},{"title":"\u4e09\u829d\u533a","code":"711157","parent_code":711100},{"title":"\u77f3\u95e8\u533a","code":"711158","parent_code":711100},{"title":"\u5b9c\u5170\u5e02","code":"711287","parent_code":711200},{"title":"\u5934\u57ce\u9547","code":"711288","parent_code":711200},{"title":"\u7901\u6eaa\u4e61","code":"711289","parent_code":711200},{"title":"\u58ee\u56f4\u4e61","code":"711290","parent_code":711200},{"title":"\u5458\u5c71\u4e61","code":"711291","parent_code":711200},{"title":"\u7f57\u4e1c\u9547","code":"711292","parent_code":711200},{"title":"\u4e09\u661f\u4e61","code":"711293","parent_code":711200},{"title":"\u5927\u540c\u4e61","code":"711294","parent_code":711200},{"title":"\u4e94\u7ed3\u4e61","code":"711295","parent_code":711200},{"title":"\u51ac\u5c71\u4e61","code":"711296","parent_code":711200},{"title":"\u82cf\u6fb3\u9547","code":"711297","parent_code":711200},{"title":"\u5357\u6fb3\u4e61","code":"711298","parent_code":711200},{"title":"\u9493\u9c7c\u53f0","code":"711299","parent_code":711200},{"title":"\u7af9\u5317\u5e02","code":"711387","parent_code":711300},{"title":"\u6e56\u53e3\u4e61","code":"711388","parent_code":711300},{"title":"\u65b0\u4e30\u4e61","code":"711389","parent_code":711300},{"title":"\u65b0\u57d4\u9547","code":"711390","parent_code":711300},{"title":"\u5173\u897f\u9547","code":"711391","parent_code":711300},{"title":"\u828e\u6797\u4e61","code":"711392","parent_code":711300},{"title":"\u5b9d\u5c71\u4e61","code":"711393","parent_code":711300},{"title":"\u7af9\u4e1c\u9547","code":"711394","parent_code":711300},{"title":"\u4e94\u5cf0\u4e61","code":"711395","parent_code":711300},{"title":"\u6a2a\u5c71\u4e61","code":"711396","parent_code":711300},{"title":"\u5c16\u77f3\u4e61","code":"711397","parent_code":711300},{"title":"\u5317\u57d4\u4e61","code":"711398","parent_code":711300},{"title":"\u5ce8\u7709\u4e61","code":"711399","parent_code":711300},{"title":"\u4e2d\u575c\u533a","code":"711414","parent_code":711400},{"title":"\u5e73\u9547\u533a","code":"711415","parent_code":711400},{"title":"\u6768\u6885\u533a","code":"711417","parent_code":711400},{"title":"\u65b0\u5c4b\u533a","code":"711418","parent_code":711400},{"title":"\u89c2\u97f3\u533a","code":"711419","parent_code":711400},{"title":"\u6843\u56ed\u533a","code":"711420","parent_code":711400},{"title":"\u9f9f\u5c71\u533a","code":"711421","parent_code":711400},{"title":"\u516b\u5fb7\u533a","code":"711422","parent_code":711400},{"title":"\u5927\u6eaa\u533a","code":"711423","parent_code":711400},{"title":"\u5927\u56ed\u533a","code":"711425","parent_code":711400},{"title":"\u82a6\u7af9\u533a","code":"711426","parent_code":711400},{"title":"\u4e2d\u575c\u5e02","code":"711487","parent_code":711400},{"title":"\u5e73\u9547\u5e02","code":"711488","parent_code":711400},{"title":"\u9f99\u6f6d\u4e61","code":"711489","parent_code":711400},{"title":"\u6768\u6885\u5e02","code":"711490","parent_code":711400},{"title":"\u65b0\u5c4b\u4e61","code":"711491","parent_code":711400},{"title":"\u89c2\u97f3\u4e61","code":"711492","parent_code":711400},{"title":"\u6843\u56ed\u5e02","code":"711493","parent_code":711400},{"title":"\u9f9f\u5c71\u4e61","code":"711494","parent_code":711400},{"title":"\u516b\u5fb7\u5e02","code":"711495","parent_code":711400},{"title":"\u5927\u6eaa\u9547","code":"711496","parent_code":711400},{"title":"\u590d\u5174\u4e61","code":"711497","parent_code":711400},{"title":"\u5927\u56ed\u4e61","code":"711498","parent_code":711400},{"title":"\u82a6\u7af9\u4e61","code":"711499","parent_code":711400},{"title":"\u5934\u4efd\u5e02","code":"711520","parent_code":711500},{"title":"\u7af9\u5357\u9547","code":"711582","parent_code":711500},{"title":"\u5934\u4efd\u9547","code":"711583","parent_code":711500},{"title":"\u4e09\u6e7e\u4e61","code":"711584","parent_code":711500},{"title":"\u5357\u5e84\u4e61","code":"711585","parent_code":711500},{"title":"\u72ee\u6f6d\u4e61","code":"711586","parent_code":711500},{"title":"\u540e\u9f99\u9547","code":"711587","parent_code":711500},{"title":"\u901a\u9704\u9547","code":"711588","parent_code":711500},{"title":"\u82d1\u91cc\u9547","code":"711589","parent_code":711500},{"title":"\u82d7\u6817\u5e02","code":"711590","parent_code":711500},{"title":"\u9020\u6865\u4e61","code":"711591","parent_code":711500},{"title":"\u5934\u5c4b\u4e61","code":"711592","parent_code":711500},{"title":"\u516c\u9986\u4e61","code":"711593","parent_code":711500},{"title":"\u5927\u6e56\u4e61","code":"711594","parent_code":711500},{"title":"\u6cf0\u5b89\u4e61","code":"711595","parent_code":711500},{"title":"\u94dc\u9523\u4e61","code":"711596","parent_code":711500},{"title":"\u4e09\u4e49\u4e61","code":"711597","parent_code":711500},{"title":"\u897f\u6e56\u4e61","code":"711598","parent_code":711500},{"title":"\u5353\u5170\u9547","code":"711599","parent_code":711500},{"title":"\u5458\u6797\u5e02","code":"711736","parent_code":711700},{"title":"\u5f70\u5316\u5e02","code":"711774","parent_code":711700},{"title":"\u82ac\u56ed\u4e61","code":"711775","parent_code":711700},{"title":"\u82b1\u575b\u4e61","code":"711776","parent_code":711700},{"title":"\u79c0\u6c34\u4e61","code":"711777","parent_code":711700},{"title":"\u9e7f\u6e2f\u9547","code":"711778","parent_code":711700},{"title":"\u798f\u5174\u4e61","code":"711779","parent_code":711700},{"title":"\u7ebf\u897f\u4e61","code":"711780","parent_code":711700},{"title":"\u548c\u7f8e\u9547","code":"711781","parent_code":711700},{"title":"\u4f38\u6e2f\u4e61","code":"711782","parent_code":711700},{"title":"\u5458\u6797\u9547","code":"711783","parent_code":711700},{"title":"\u793e\u5934\u4e61","code":"711784","parent_code":711700},{"title":"\u6c38\u9756\u4e61","code":"711785","parent_code":711700},{"title":"\u57d4\u5fc3\u4e61","code":"711786","parent_code":711700},{"title":"\u6eaa\u6e56\u9547","code":"711787","parent_code":711700},{"title":"\u5927\u6751\u4e61","code":"711788","parent_code":711700},{"title":"\u57d4\u76d0\u4e61","code":"711789","parent_code":711700},{"title":"\u7530\u4e2d\u9547","code":"711790","parent_code":711700},{"title":"\u5317\u6597\u9547","code":"711791","parent_code":711700},{"title":"\u7530\u5c3e\u4e61","code":"711792","parent_code":711700},{"title":"\u57e4\u5934\u4e61","code":"711793","parent_code":711700},{"title":"\u6eaa\u5dde\u4e61","code":"711794","parent_code":711700},{"title":"\u7af9\u5858\u4e61","code":"711795","parent_code":711700},{"title":"\u4e8c\u6797\u9547","code":"711796","parent_code":711700},{"title":"\u5927\u57ce\u4e61","code":"711797","parent_code":711700},{"title":"\u82b3\u82d1\u4e61","code":"711798","parent_code":711700},{"title":"\u4e8c\u6c34\u4e61","code":"711799","parent_code":711700},{"title":"\u756a\u8def\u4e61","code":"711982","parent_code":711900},{"title":"\u6885\u5c71\u4e61","code":"711983","parent_code":711900},{"title":"\u7af9\u5d0e\u4e61","code":"711984","parent_code":711900},{"title":"\u963f\u91cc\u5c71\u4e61","code":"711985","parent_code":711900},{"title":"\u4e2d\u57d4\u4e61","code":"711986","parent_code":711900},{"title":"\u5927\u57d4\u4e61","code":"711987","parent_code":711900},{"title":"\u6c34\u4e0a\u4e61","code":"711988","parent_code":711900},{"title":"\u9e7f\u8349\u4e61","code":"711989","parent_code":711900},{"title":"\u592a\u4fdd\u5e02","code":"711990","parent_code":711900},{"title":"\u6734\u5b50\u5e02","code":"711991","parent_code":711900},{"title":"\u4e1c\u77f3\u4e61","code":"711992","parent_code":711900},{"title":"\u516d\u811a\u4e61","code":"711993","parent_code":711900},{"title":"\u65b0\u6e2f\u4e61","code":"711994","parent_code":711900},{"title":"\u6c11\u96c4\u4e61","code":"711995","parent_code":711900},{"title":"\u5927\u6797\u9547","code":"711996","parent_code":711900},{"title":"\u6eaa\u53e3\u4e61","code":"711997","parent_code":711900},{"title":"\u4e49\u7af9\u4e61","code":"711998","parent_code":711900},{"title":"\u5e03\u888b\u9547","code":"711999","parent_code":711900},{"title":"\u6597\u5357\u9547","code":"712180","parent_code":712100},{"title":"\u5927\u57e4\u4e61","code":"712181","parent_code":712100},{"title":"\u864e\u5c3e\u9547","code":"712182","parent_code":712100},{"title":"\u571f\u5e93\u9547","code":"712183","parent_code":712100},{"title":"\u8912\u5fe0\u4e61","code":"712184","parent_code":712100},{"title":"\u4e1c\u52bf\u4e61","code":"712185","parent_code":712100},{"title":"\u53f0\u897f\u4e61","code":"712186","parent_code":712100},{"title":"\u4ed1\u80cc\u4e61","code":"712187","parent_code":712100},{"title":"\u9ea6\u5bee\u4e61","code":"712188","parent_code":712100},{"title":"\u6597\u516d\u5e02","code":"712189","parent_code":712100},{"title":"\u6797\u5185\u4e61","code":"712190","parent_code":712100},{"title":"\u53e4\u5751\u4e61","code":"712191","parent_code":712100},{"title":"\u83bf\u6850\u4e61","code":"712192","parent_code":712100},{"title":"\u897f\u87ba\u9547","code":"712193","parent_code":712100},{"title":"\u4e8c\u4ed1\u4e61","code":"712194","parent_code":712100},{"title":"\u5317\u6e2f\u9547","code":"712195","parent_code":712100},{"title":"\u6c34\u6797\u4e61","code":"712196","parent_code":712100},{"title":"\u53e3\u6e56\u4e61","code":"712197","parent_code":712100},{"title":"\u56db\u6e56\u4e61","code":"712198","parent_code":712100},{"title":"\u5143\u957f\u4e61","code":"712199","parent_code":712100},{"title":"\u5d01\u9876\u4e61","code":"712451","parent_code":712400},{"title":"\u5c4f\u4e1c\u5e02","code":"712467","parent_code":712400},{"title":"\u4e09\u5730\u95e8\u4e61","code":"712468","parent_code":712400},{"title":"\u96fe\u53f0\u4e61","code":"712469","parent_code":712400},{"title":"\u739b\u5bb6\u4e61","code":"712470","parent_code":712400},{"title":"\u4e5d\u5982\u4e61","code":"712471","parent_code":712400},{"title":"\u91cc\u6e2f\u4e61","code":"712472","parent_code":712400},{"title":"\u9ad8\u6811\u4e61","code":"712473","parent_code":712400},{"title":"\u76d0\u57d4\u4e61","code":"712474","parent_code":712400},{"title":"\u957f\u6cbb\u4e61","code":"712475","parent_code":712400},{"title":"\u9e9f\u6d1b\u4e61","code":"712476","parent_code":712400},{"title":"\u7af9\u7530\u4e61","code":"712477","parent_code":712400},{"title":"\u5185\u57d4\u4e61","code":"712478","parent_code":712400},{"title":"\u4e07\u4e39\u4e61","code":"712479","parent_code":712400},{"title":"\u6f6e\u5dde\u9547","code":"712480","parent_code":712400},{"title":"\u6cf0\u6b66\u4e61","code":"712481","parent_code":712400},{"title":"\u6765\u4e49\u4e61","code":"712482","parent_code":712400},{"title":"\u4e07\u5ce6\u4e61","code":"712483","parent_code":712400},{"title":"\u83b0\u9876\u4e61","code":"712484","parent_code":712400},{"title":"\u65b0\u57e4\u4e61","code":"712485","parent_code":712400},{"title":"\u5357\u5dde\u4e61","code":"712486","parent_code":712400},{"title":"\u6797\u8fb9\u4e61","code":"712487","parent_code":712400},{"title":"\u4e1c\u6e2f\u9547","code":"712488","parent_code":712400},{"title":"\u7409\u7403\u4e61","code":"712489","parent_code":712400},{"title":"\u4f73\u51ac\u4e61","code":"712490","parent_code":712400},{"title":"\u65b0\u56ed\u4e61","code":"712491","parent_code":712400},{"title":"\u678b\u5bee\u4e61","code":"712492","parent_code":712400},{"title":"\u678b\u5c71\u4e61","code":"712493","parent_code":712400},{"title":"\u6625\u65e5\u4e61","code":"712494","parent_code":712400},{"title":"\u72ee\u5b50\u4e61","code":"712495","parent_code":712400},{"title":"\u8f66\u57ce\u4e61","code":"712496","parent_code":712400},{"title":"\u7261\u4e39\u4e61","code":"712497","parent_code":712400},{"title":"\u6052\u6625\u9547","code":"712498","parent_code":712400},{"title":"\u6ee1\u5dde\u4e61","code":"712499","parent_code":712400},{"title":"\u53f0\u4e1c\u5e02","code":"712584","parent_code":712500},{"title":"\u7eff\u5c9b\u4e61","code":"712585","parent_code":712500},{"title":"\u5170\u5c7f\u4e61","code":"712586","parent_code":712500},{"title":"\u5ef6\u5e73\u4e61","code":"712587","parent_code":712500},{"title":"\u5351\u5357\u4e61","code":"712588","parent_code":712500},{"title":"\u9e7f\u91ce\u4e61","code":"712589","parent_code":712500},{"title":"\u5173\u5c71\u9547","code":"712590","parent_code":712500},{"title":"\u6d77\u7aef\u4e61","code":"712591","parent_code":712500},{"title":"\u6c60\u4e0a\u4e61","code":"712592","parent_code":712500},{"title":"\u4e1c\u6cb3\u4e61","code":"712593","parent_code":712500},{"title":"\u6210\u529f\u9547","code":"712594","parent_code":712500},{"title":"\u957f\u6ee8\u4e61","code":"712595","parent_code":712500},{"title":"\u91d1\u5cf0\u4e61","code":"712596","parent_code":712500},{"title":"\u5927\u6b66\u4e61","code":"712597","parent_code":712500},{"title":"\u8fbe\u4ec1\u4e61","code":"712598","parent_code":712500},{"title":"\u592a\u9ebb\u91cc\u4e61","code":"712599","parent_code":712500},{"title":"\u82b1\u83b2\u5e02","code":"712686","parent_code":712600},{"title":"\u65b0\u57ce\u4e61","code":"712687","parent_code":712600},{"title":"\u592a\u9c81\u9601","code":"712688","parent_code":712600},{"title":"\u79c0\u6797\u4e61","code":"712689","parent_code":712600},{"title":"\u5409\u5b89\u4e61","code":"712690","parent_code":712600},{"title":"\u5bff\u4e30\u4e61","code":"712691","parent_code":712600},{"title":"\u51e4\u6797\u9547","code":"712692","parent_code":712600},{"title":"\u5149\u590d\u4e61","code":"712693","parent_code":712600},{"title":"\u4e30\u6ee8\u4e61","code":"712694","parent_code":712600},{"title":"\u745e\u7a57\u4e61","code":"712695","parent_code":712600},{"title":"\u4e07\u8363\u4e61","code":"712696","parent_code":712600},{"title":"\u7389\u91cc\u9547","code":"712697","parent_code":712600},{"title":"\u5353\u6eaa\u4e61","code":"712698","parent_code":712600},{"title":"\u5bcc\u91cc\u4e61","code":"712699","parent_code":712600},{"title":"\u9a6c\u516c\u5e02","code":"712794","parent_code":712700},{"title":"\u897f\u5c7f\u4e61","code":"712795","parent_code":712700},{"title":"\u671b\u5b89\u4e61","code":"712796","parent_code":712700},{"title":"\u4e03\u7f8e\u4e61","code":"712797","parent_code":712700},{"title":"\u767d\u6c99\u4e61","code":"712798","parent_code":712700},{"title":"\u6e56\u897f\u4e61","code":"712799","parent_code":712700},{"title":"\u5357\u7aff\u4e61","code":"712896","parent_code":712800},{"title":"\u5317\u7aff\u4e61","code":"712897","parent_code":712800},{"title":"\u4e1c\u5f15\u4e61","code":"712898","parent_code":712800},{"title":"\u8392\u5149\u4e61","code":"712899","parent_code":712800},{"title":"\u4e2d\u897f\u533a","code":"810101","parent_code":810100},{"title":"\u6e7e\u4ed4\u533a","code":"810102","parent_code":810100},{"title":"\u4e1c\u533a","code":"810103","parent_code":810100},{"title":"\u5357\u533a","code":"810104","parent_code":810100},{"title":"\u4e5d\u9f99\u57ce\u533a","code":"810201","parent_code":810200},{"title":"\u6cb9\u5c16\u65fa\u533a","code":"810202","parent_code":810200},{"title":"\u6df1\u6c34\u57d7\u533a","code":"810203","parent_code":810200},{"title":"\u9ec4\u5927\u4ed9\u533a","code":"810204","parent_code":810200},{"title":"\u89c2\u5858\u533a","code":"810205","parent_code":810200},{"title":"\u5317\u533a","code":"810301","parent_code":810300},{"title":"\u5927\u57d4\u533a","code":"810302","parent_code":810300},{"title":"\u6c99\u7530\u533a","code":"810303","parent_code":810300},{"title":"\u897f\u8d21\u533a","code":"810304","parent_code":810300},{"title":"\u5143\u6717\u533a","code":"810305","parent_code":810300},{"title":"\u5c6f\u95e8\u533a","code":"810306","parent_code":810300},{"title":"\u8343\u6e7e\u533a","code":"810307","parent_code":810300},{"title":"\u8475\u9752\u533a","code":"810308","parent_code":810300},{"title":"\u79bb\u5c9b\u533a","code":"810309","parent_code":810300},{"title":"\u82b1\u5730\u739b\u5802\u533a","code":"820102","parent_code":820100},{"title":"\u82b1\u738b\u5802\u533a","code":"820103","parent_code":820100},{"title":"\u671b\u5fb7\u5802\u533a","code":"820104","parent_code":820100},{"title":"\u5927\u5802\u533a","code":"820105","parent_code":820100},{"title":"\u98ce\u987a\u5802\u533a","code":"820106","parent_code":820100},{"title":"\u5609\u6a21\u5802\u533a","code":"820202","parent_code":820200},{"title":"\u8def\u6c39\u586b\u6d77\u533a","code":"820203","parent_code":820200},{"title":"\u5723\u65b9\u6d4e\u5404\u5802\u533a","code":"820204","parent_code":820200}] diff --git a/docs/ACCOUNT_MODULE.md b/docs/ACCOUNT_MODULE.md new file mode 100644 index 0000000..a0b0e9b --- /dev/null +++ b/docs/ACCOUNT_MODULE.md @@ -0,0 +1,960 @@ +# 记账功能模块文档 + +## 概述 + +记账功能模块提供了完整的多用户多家庭记账功能,支持: +- 多家庭管理(每个用户可以加入多个家庭) +- 家庭成员管理(owner/admin/member角色) +- 收支记录管理 +- 账户管理 +- 数据权限控制 + +## 模块架构 + +本模块采用Laravel模块化架构,所有代码位于 `modules/Account` 目录下: + +``` +modules/Account/ +├── app/ +│ ├── Controllers/ +│ │ ├── Api/ # 前端API控制器 +│ │ └── Admin/ # 后台管理控制器 +│ ├── Models/ # 数据模型 +│ ├── Providers/ # 服务提供者 +│ └── Services/ # 业务逻辑服务层 +├── database/ +│ └── migrations/ # 数据库迁移文件 +├── routes/ +│ ├── api.php # 前端API路由 +│ └── admin.php # 后台管理路由 +└── module.json # 模块配置文件 +``` + +### 命名空间 + +- 模型:`Modules\Account\Models\` +- 控制器:`Modules\Account\Controllers\Api\` 或 `Modules\Account\Controllers\Admin\` +- 服务:`Modules\Account\Services\` +- 路由:自动加载,无需手动配置 + +### 路由说明 + +模块路由已自动注册,无需在主路由文件中配置: + +- 前端API路由位于 `modules/Account/routes/api.php` +- 后台管理路由位于 `modules/Account/routes/admin.php` + +所有路由由模块的 RouteServiceProvider 自动加载。 + +## 数据库表结构 + +### 1. account_families(家庭表) +| 字段 | 类型 | 说明 | +|------|------|------| +| id | bigint | 主键ID | +| name | varchar(50) | 家庭名称 | +| description | text | 家庭描述 | +| avatar | varchar(255) | 家庭头像 | +| created_at | datetime | 创建时间 | +| updated_at | datetime | 更新时间 | +| deleted_at | datetime | 删除时间(软删除) | + +### 2. account_family_members(家庭成员关系表) +| 字段 | 类型 | 说明 | +|------|------|------| +| id | bigint | 主键ID | +| family_id | bigint | 家庭ID | +| user_id | bigint | 用户ID | +| role | varchar(20) | 角色:owner-拥有者,admin-管理员,member-成员 | +| created_at | datetime | 创建时间 | +| updated_at | datetime | 更新时间 | +| deleted_at | datetime | 删除时间(软删除) | + +**唯一索引**: `family_id` + `user_id`(防止重复加入) + +### 3. account_records(记账记录表) +| 字段 | 类型 | 说明 | +|------|------|------| +| id | bigint | 主键ID | +| user_id | bigint | 用户ID | +| family_id | bigint | 家庭ID | +| member_id | bigint | 成员ID | +| account_id | bigint | 账户ID | +| type | varchar(20) | 类型:income-收入,expense-支出 | +| amount | decimal(10,2) | 金额 | +| category | varchar(50) | 分类 | +| date | date | 日期 | +| time | varchar(10) | 时间 | +| remark | text | 备注 | +| created_at | datetime | 创建时间 | +| updated_at | datetime | 更新时间 | +| deleted_at | datetime | 删除时间(软删除) | + +### 2. account_members(家庭成员表) +| 字段 | 类型 | 说明 | +|------|------|------| +| id | bigint | 主键ID | +| user_id | bigint | 用户ID | +| family_id | bigint | 家庭ID | +| name | varchar(50) | 成员名称 | +| avatar | varchar(255) | 头像 | +| created_at | datetime | 创建时间 | +| updated_at | datetime | 更新时间 | +| deleted_at | datetime | 删除时间(软删除) | + +### 3. accounts(账户表) +| 字段 | 类型 | 说明 | +|------|------|------| +| id | bigint | 主键ID | +| user_id | bigint | 用户ID | +| family_id | bigint | 家庭ID | +| name | varchar(50) | 账户名称 | +| type | varchar(20) | 账户类型:cash-现金,bank-银行卡,alipay-支付宝,wechat-微信 | +| balance | decimal(10,2) | 余额 | +| icon | varchar(255) | 图标 | +| created_at | datetime | 创建时间 | +| updated_at | datetime | 更新时间 | +| deleted_at | datetime | 删除时间(软删除) | + +## 前端API接口 + +### 基础URL +`/api/account` + +### 记账记录接口 + +#### 1. 获取记账记录列表 +- **路径**: `GET /api/account/records` +- **认证**: 需要 +- **参数**: + - `family_id` (可选): 家庭ID,不传则返回当前用户所有家庭的记录 + - `member_id` (可选): 成员ID + - `account_id` (可选): 账户ID + - `type` (可选): 类型(income/expense) + - `start_date` (可选): 开始日期 + - `end_date` (可选): 结束日期 + - `page` (可选): 页码 + - `limit` (可选): 每页数量,默认30 +- **响应**: +```json +{ + "code": 1, + "message": "success", + "data": { + "list": [...], + "total": 100 + } +} +``` + +#### 2. 获取记账记录详情 +- **路径**: `GET /api/account/records/{id}` +- **认证**: 需要 +- **响应**: 单条记录详情 + +#### 3. 创建记账记录 +- **路径**: `POST /api/account/records` +- **认证**: 需要 +- **参数**: + - `member_id` (必需): 成员ID + - `account_id` (必需): 账户ID + - `type` (必需): 类型(income/expense) + - `amount` (必需): 金额 + - `category` (必需): 分类 + - `date` (必需): 日期 + - `time` (必需): 时间 + - `remark` (可选): 备注 +- **响应**: 创建的记录 + +#### 4. 更新记账记录 +- **路径**: `PUT /api/account/records/{id}` +- **认证**: 需要 +- **参数**: 同创建接口(所有字段可选) +- **响应**: 更新后的记录 + +#### 5. 删除记账记录 +- **路径**: `DELETE /api/account/records/{id}` +- **认证**: 需要 +- **响应**: 删除成功 + +#### 6. 获取统计数据 +- **路径**: `GET /api/account/records/statistics` +- **认证**: 需要 +- **参数**: + - `start_date` (可选): 开始日期 + - `end_date` (可选): 结束日期 +- **响应**: +```json +{ + "code": 1, + "message": "success", + "data": { + "total_income": 10000.00, + "total_expense": 5000.00, + "balance": 5000.00, + "category_stats": [...], + "date_stats": [...] + } +} +``` + +### 家庭管理接口 + +#### 1. 获取用户的家庭列表 +- **路径**: `GET /api/account/families` +- **认证**: 需要 +- **响应**: +```json +{ + "code": 1, + "message": "success", + "data": { + "list": [ + { + "id": 1, + "name": "我的家", + "description": "温馨的家", + "avatar": "avatar.jpg", + "user_role": "owner", + "is_owner": true, + "can_manage": true, + "family_members": [...], + "created_at": "2025-01-01 00:00:00" + } + ], + "total": 1 + } +} +``` + +#### 2. 获取家庭详情 +- **路径**: `GET /api/account/families/{id}` +- **认证**: 需要 +- **权限**: 用户必须属于该家庭 +- **响应**: 家庭详情(包含成员、账户、记录信息) + +#### 3. 创建家庭 +- **路径**: `POST /api/account/families` +- **认证**: 需要 +- **参数**: + - `name` (必需): 家庭名称(最多50字符) + - `description` (可选): 家庭描述(最多255字符) + - `avatar` (可选): 家庭头像 +- **响应**: 创建的家庭(创建者自动成为owner) + +#### 4. 更新家庭 +- **路径**: `PUT /api/account/families/{id}` +- **认证**: 需要 +- **权限**: owner或admin +- **参数**: 同创建接口(所有字段可选) +- **响应**: 更新后的家庭 + +#### 5. 删除家庭 +- **路径**: `DELETE /api/account/families/{id}` +- **认证**: 需要 +- **权限**: 仅owner +- **限制**: 家庭下不能有关联账户 +- **响应**: 删除成功 + +#### 6. 邀请成员加入家庭 +- **路径**: `POST /api/account/families/{familyId}/invite` +- **认证**: 需要 +- **权限**: owner或admin +- **参数**: + - `user_id` (必需): 被邀请用户ID +- **限制**: 用户不能已经在家庭中 +- **响应**: 邀请成功 + +#### 7. 移除家庭成员 +- **路径**: `DELETE /api/account/families/{familyId}/members/{memberId}` +- **认证**: 需要 +- **权限**: owner或admin +- **限制**: 不能移除owner +- **响应**: 移除成功 + +#### 8. 更新成员角色 +- **路径**: `PUT /api/account/families/{familyId}/members/{memberId}/role` +- **认证**: 需要 +- **权限**: 仅owner +- **参数**: + - `role` (必需): 新角色(owner/admin/member) +- **限制**: 不能修改owner的角色 +- **响应**: 更新成功 + +#### 9. 退出家庭 +- **路径**: `POST /api/account/families/{id}/leave` +- **认证**: 需要 +- **限制**: owner不能退出,只能删除家庭 +- **响应**: 退出成功 + +### 家庭成员接口 + +#### 1. 获取成员列表 +- **路径**: `GET /api/account/members` +- **认证**: 需要 +- **参数**: + - `family_id` (可选): 家庭ID,不传则返回当前用户所有家庭的成员 +- **响应**: +```json +{ + "code": 1, + "message": "success", + "data": { + "list": [...], + "total": 10 + } +} +``` + +#### 2. 获取成员详情 +- **路径**: `GET /api/account/members/{id}` +- **认证**: 需要 +- **响应**: 成员详情 + +#### 3. 创建成员 +- **路径**: `POST /api/account/members` +- **认证**: 需要 +- **参数**: + - `name` (必需): 成员名称 + - `avatar` (可选): 头像 +- **响应**: 创建的成员 + +#### 4. 更新成员 +- **路径**: `PUT /api/account/members/{id}` +- **认证**: 需要 +- **参数**: 同创建接口(所有字段可选) +- **响应**: 更新后的成员 + +#### 5. 删除成员 +- **路径**: `DELETE /api/account/members/{id}` +- **认证**: 需要 +- **响应**: 删除成功 + +### 账户管理接口 + +#### 1. 获取账户列表 +- **路径**: `GET /api/account/accounts` +- **认证**: 需要 +- **参数**: + - `family_id` (可选): 家庭ID,不传则返回当前用户所有家庭的账户 +- **响应**: +```json +{ + "code": 1, + "message": "success", + "data": { + "list": [...], + "total": 5, + "total_balance": 10000.00 + } +} +``` + +#### 2. 获取账户详情 +- **路径**: `GET /api/account/accounts/{id}` +- **认证**: 需要 +- **响应**: 账户详情 + +#### 3. 创建账户 +- **路径**: `POST /api/account/accounts` +- **认证**: 需要 +- **参数**: + - `name` (必需): 账户名称 + - `type` (必需): 账户类型(cash/bank/alipay/wechat) + - `balance` (可选): 初始余额 + - `icon` (可选): 图标 +- **响应**: 创建的账户 + +#### 4. 更新账户 +- **路径**: `PUT /api/account/accounts/{id}` +- **认证**: 需要 +- **参数**: 同创建接口(所有字段可选) +- **响应**: 更新后的账户 + +#### 5. 删除账户 +- **路径**: `DELETE /api/account/accounts/{id}` +- **认证**: 需要 +- **响应**: 删除成功 + +#### 6. 重新计算账户余额 +- **路径**: `POST /api/account/accounts/{id}/recalculate` +- **认证**: 需要 +- **响应**: 更新后的账户 + +## 后台管理接口 + +### 基础URL +`/admin/account` + +### 记账记录管理 + +#### 1. 获取记账记录列表 +- **路径**: `GET /admin/account/records/index` +- **认证**: 需要 +- **参数**: 同前端API,额外支持 + - `user_id` (可选): 用户ID +- **响应**: 记录列表(包含用户、成员、账户信息) + +#### 2. 获取记账记录详情 +- **路径**: `GET /admin/account/records/show` +- **认证**: 需要 +- **响应**: 记录详情 + +#### 3. 删除记账记录 +- **路径**: `DELETE /admin/account/records/delete` +- **认证**: 需要 +- **参数**: `id` (必需): 记录ID +- **响应**: 删除成功(软删除) + +#### 4. 恢复记账记录 +- **路径**: `PUT /admin/account/records/restore` +- **认证**: 需要 +- **参数**: `id` (必需): 记录ID +- **响应**: 恢复成功 + +#### 5. 获取统计数据 +- **路径**: `GET /admin/account/records/statistics` +- **认证**: 需要 +- **参数**: + - `user_id` (可选): 用户ID + - `start_date` (可选): 开始日期 + - `end_date` (可选): 结束日期 +- **响应**: +```json +{ + "code": 1, + "message": "success", + "data": { + "total_income": 10000.00, + "total_expense": 5000.00, + "balance": 5000.00, + "user_stats": [...], + "category_stats": [...], + "total_records": 100 + } +} +``` + +### 家庭成员管理 + +#### 1. 获取成员列表 +- **路径**: `GET /admin/account/members/index` +- **认证**: 需要 +- **参数**: + - `user_id` (可选): 用户ID +- **响应**: 成员列表(包含统计信息) + +#### 2. 获取成员详情 +- **路径**: `GET /admin/account/members/show` +- **认证**: 需要 +- **参数**: `id` (必需): 成员ID +- **响应**: 成员详情(包含关联记录) + +#### 3. 创建成员 +- **路径**: `POST /admin/account/members/add` +- **认证**: 需要 +- **参数**: + - `user_id` (必需): 用户ID + - `name` (必需): 成员名称 + - `avatar` (可选): 头像 +- **响应**: 创建的成员 + +#### 4. 更新成员 +- **路径**: `PUT /admin/account/members/edit` +- **认证**: 需要 +- **参数**: + - `id` (必需): 成员ID + - 其他字段同创建接口 +- **响应**: 更新后的成员 + +#### 5. 删除成员 +- **路径**: `DELETE /admin/account/members/delete` +- **认证**: 需要 +- **参数**: `id` (必需): 成员ID +- **响应**: 删除成功(软删除) + +#### 6. 恢复成员 +- **路径**: `PUT /admin/account/members/restore` +- **认证**: 需要 +- **参数**: `id` (必需): 成员ID +- **响应**: 恢复成功 + +### 账户管理 + +#### 1. 获取账户列表 +- **路径**: `GET /admin/account/accounts/index` +- **认证**: 需要 +- **参数**: + - `user_id` (可选): 用户ID + - `type` (可选): 账户类型 +- **响应**: 账户列表(包含统计信息) + +#### 2. 获取账户详情 +- **路径**: `GET /admin/account/accounts/show` +- **认证**: 需要 +- **参数**: `id` (必需): 账户ID +- **响应**: 账户详情(包含关联记录) + +#### 3. 创建账户 +- **路径**: `POST /admin/account/accounts/add` +- **认证**: 需要 +- **参数**: + - `user_id` (必需): 用户ID + - `name` (必需): 账户名称 + - `type` (必需): 账户类型 + - `balance` (可选): 初始余额 + - `icon` (可选): 图标 +- **响应**: 创建的账户 + +#### 4. 更新账户 +- **路径**: `PUT /admin/account/accounts/edit` +- **认证**: 需要 +- **参数**: + - `id` (必需): 账户ID + - 其他字段同创建接口 +- **响应**: 更新后的账户 + +#### 5. 删除账户 +- **路径**: `DELETE /admin/account/accounts/delete` +- **认证**: 需要 +- **参数**: `id` (必需): 账户ID +- **响应**: 删除成功(软删除) + +#### 6. 恢复账户 +- **路径**: `PUT /admin/account/accounts/restore` +- **认证**: 需要 +- **参数**: `id` (必需): 账户ID +- **响应**: 恢复成功 + +#### 7. 重新计算账户余额 +- **路径**: `POST /admin/account/accounts/recalculate` +- **认证**: 需要 +- **参数**: `id` (必需): 账户ID +- **响应**: 更新后的账户 + +#### 8. 获取账户类型列表 +- **路径**: `GET /admin/account/accounts/types` +- **认证**: 需要 +- **响应**: +```json +{ + "code": 1, + "message": "success", + "data": { + "cash": "现金", + "bank": "银行卡", + "alipay": "支付宝", + "wechat": "微信" + } +} +``` + +## 后台管理系统 + +### 概述 + +后台管理系统提供了完整的管理功能,位于 `resources/admin` 目录下,使用 Vue 3 + Element Plus + Vite 技术栈。 + +### 目录结构 + +``` +resources/admin/src/ +├── api/ +│ ├── module/ +│ │ └── account.js # 记账模块API接口 +│ └── index.js # API自动导入 +├── pages/ +│ └── account/ # 记账模块页面 +│ ├── families/ # 家庭管理 +│ ├── accounts/ # 账户管理 +│ ├── members/ # 成员管理 +│ └── records/ # 记录管理 +└── router/ + ├── accountRouter.js # 记账模块路由 + └── index.js # 路由主文件 +``` + +### 功能模块 + +#### 1. 家庭管理 (`/account/families`) + +**功能特性**: +- 家庭列表展示(支持搜索) +- 查看家庭详情 +- 创建/编辑家庭 +- 删除家庭(含确认提示) +- 显示成员数量和账户数量统计 + +**页面组件**: +- `index.vue`: 家庭列表页 +- `save.vue`: 家庭添加/编辑/查看页 + +**搜索条件**: +- 家庭名称 + +**操作权限**: +- 添加家庭 +- 编辑家庭信息 +- 查看家庭详情 +- 删除家庭 + +#### 2. 账户管理 (`/account/accounts`) + +**功能特性**: +- 账户列表展示(支持搜索和筛选) +- 查看账户详情 +- 创建/编辑账户 +- 删除账户(含确认提示) +- 显示账户余额(正数绿色,负数红色) +- 支持多种账户类型(现金、银行卡、信用卡、支付宝、微信等) + +**页面组件**: +- `index.vue`: 账户列表页 +- `save.vue`: 账户添加/编辑/查看页 + +**搜索条件**: +- 账户名称 +- 账户类型 + +**表单字段**: +- 账户名称(必填,2-50字符) +- 账户类型(必填) +- 所属家庭(必填,下拉选择) +- 初始余额(可选,数字) +- 备注(可选,最多200字符) + +#### 3. 成员管理 (`/account/members`) + +**功能特性**: +- 成员列表展示(支持搜索和筛选) +- 查看成员详情 +- 添加/编辑成员 +- 移除成员(含确认提示) +- 显示成员角色(owner/admin/member) +- 显示成员所属家庭 + +**页面组件**: +- `index.vue`: 成员列表页 +- `save.vue`: 成员添加/编辑/查看页 + +**搜索条件**: +- 成员昵称 +- 成员角色 + +**表单字段**: +- 所属家庭(必填,下拉选择) +- 用户手机号(必填,用于查找用户) +- 角色(必填,下拉选择) +- 自动显示用户详细信息 + +**角色说明**: +- owner(家庭主):最高权限,红色标签 +- admin(管理员):管理权限,橙色标签 +- member(成员):普通权限,蓝色标签 + +#### 4. 记录管理 (`/account/records`) + +**功能特性**: +- 记录列表展示(支持搜索和筛选) +- 查看记录详情 +- 创建/编辑记录 +- 删除记录(含确认提示) +- 显示金额(收入绿色+号,支出红色-号) +- 显示记录类型(收入/支出标签) +- 支持日期范围筛选 + +**页面组件**: +- `index.vue`: 记录列表页 +- `save.vue`: 记录添加/编辑/查看页 + +**搜索条件**: +- 关键词 +- 记录类型(收入/支出) +- 日期范围 + +**表单字段**: +- 记录类型(必填,单选:收入/支出) +- 所属家庭(必填,下拉选择) +- 账户(必填,下拉选择,根据家庭筛选) +- 分类(必填,下拉选择,根据记录类型筛选) +- 金额(必填,数字) +- 日期(必填,日期选择器) +- 备注(可选,最多200字符) + +**联动功能**: +- 选择家庭后,自动加载该家庭的账户列表 +- 切换记录类型后,自动筛选对应的分类列表 + +### API接口封装 + +所有API接口统一封装在 `resources/admin/src/api/module/account.js` 中: + +```javascript +// 家庭管理 +this.$API.account.family.list.get(params) +this.$API.account.family.detail.get(params) +this.$API.account.family.add.post(params) +this.$API.account.family.edit.post(params) +this.$API.account.family.delete.post(params) + +// 账户管理 +this.$API.account.accounts.list.get(params) +this.$API.account.accounts.detail.get(params) +this.$API.account.accounts.add.post(params) +this.$API.account.accounts.edit.post(params) +this.$API.account.accounts.delete.post(params) + +// 成员管理 +this.$API.account.members.list.get(params) +this.$API.account.members.detail.get(params) +this.$API.account.members.add.post(params) +this.$API.account.members.edit.post(params) +this.$API.account.members.delete.post(params) + +// 记录管理 +this.$API.account.records.list.get(params) +this.$API.account.records.detail.get(params) +this.$API.account.records.add.post(params) +this.$API.account.records.edit.post(params) +this.$API.account.records.delete.post(params) +``` + +### 路由配置 + +记账模块路由已配置在 `resources/admin/src/router/accountRouter.js`: + +```javascript +{ + path: "/account", + meta: { + title: "记账管理", + icon: "el-icon-wallet" + }, + children: [ + { + path: "/account/families", + meta: { title: "家庭管理", icon: "el-icon-house" } + }, + { + path: "/account/accounts", + meta: { title: "账户管理", icon: "el-icon-bank-card" } + }, + { + path: "/account/members", + meta: { title: "成员管理", icon: "el-icon-user" } + }, + { + path: "/account/records", + meta: { title: "记录管理", icon: "el-icon-notebook-2" } + } + ] +} +``` + +路由已自动注册到主路由文件 `index.js`,无需额外配置。 + +### 通用功能 + +#### 表格组件 + +使用 `scTable` 组件实现统一的表格功能: +- 自动分页 +- 自动加载 +- 参数搜索 +- 行选择 +- 固定列 + +#### 抽屉组件 + +使用 `el-drawer` 组件实现表单弹窗: +- 三种模式:add(添加)、edit(编辑)、show(查看) +- 表单验证 +- 关闭时自动重置 +- 成功回调刷新列表 + +#### 权限控制 + +所有删除操作都使用 `el-popconfirm` 进行二次确认: +- 防止误删除 +- 支持取消操作 +- 显示确认提示文字 + +#### 数据展示 + +- 金额显示:保留2位小数,根据正负值显示不同颜色 +- 日期显示:统一格式化 +- 角色标签:使用不同颜色区分 +- 头像显示:支持头像上传和展示 + +### 使用说明 + +#### 1. 启动开发服务器 + +```bash +cd resources/admin +npm install +npm run dev +``` + +#### 2. 访问后台 + +启动后访问:`http://localhost:5173` + +#### 3. 登录系统 + +使用管理员账号登录后台系统。 + +#### 4. 使用记账功能 + +1. 点击左侧菜单"记账管理" +2. 选择子菜单进行操作: + - 家庭管理:管理所有家庭信息 + - 账户管理:管理所有账户信息 + - 成员管理:管理所有家庭成员 + - 记录管理:管理所有收支记录 + +#### 5. 数据流转 + +``` +创建家庭 → 添加成员 → 创建账户 → 记录收支 +``` + +**注意事项**: +- 必须先创建家庭,才能添加成员和账户 +- 创建账户时必须选择所属家庭 +- 创建记录时必须选择家庭、账户和成员 +- 所有删除操作都有确认提示 + +## 使用说明 + +### 数据库迁移 + +运行以下命令创建数据库表: + +```bash +php artisan migrate +``` + +### 多家庭架构 + +本模块支持多用户多家庭架构: + +#### 数据隔离 +- 每个用户可以加入多个家庭 +- 每个家庭有独立的成员、账户和记录 +- 所有数据通过`family_id`字段关联到具体家庭 +- 用户只能访问自己所属家庭的数据 + +#### 家庭角色权限 + +| 角色 | 创建/编辑家庭 | 邀请成员 | 移除成员 | 修改角色 | 删除家庭 | 退出家庭 | +|------|--------------|---------|---------|---------|---------|---------| +| owner | ✅ | ✅ | ✅(不能移除owner) | ✅(不能修改owner) | ✅ | ❌(只能删除) | +| admin | ✅ | ✅ | ✅(不能移除owner) | ❌ | ❌ | ✅ | +| member | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | + +#### 权限判断方法 + +AccountFamilyMember模型提供以下权限判断方法: + +```php +// 判断是否是拥有者 +$member->isOwner(); + +// 判断是否是管理员 +$member->isAdmin(); + +// 判断是否有管理权限(owner或admin) +$member->hasManagePermission(); +``` + +### 账户余额自动更新 + +系统会自动维护账户余额: +- 创建收入记录时,账户余额增加 +- 创建支出记录时,账户余额减少 +- 更新记录时,自动调整相关账户余额 +- 删除记录时,自动回滚账户余额 + +### 软删除 + +所有数据表都支持软删除: +- 删除操作不会物理删除数据,只是标记为已删除 +- 后台管理可以查看和恢复已删除的数据 +- 永久删除需要额外操作 + +### 数据验证 + +所有接口都有严格的数据验证: +- 必填字段检查 +- 数据类型验证 +- 业务逻辑验证(如删除成员前检查是否有关联记录) + +## 注意事项 + +1. 所有接口都需要认证(除登录接口外) +2. 前端API只能操作当前登录用户及其所属家庭的数据 +3. 后台API可以操作所有用户的数据 +4. 每个请求都应该指定`family_id`来操作特定家庭的数据 +5. 账户余额建议使用重新计算接口进行校准 +6. 删除成员或账户前,系统会检查是否有关联记录 +7. 家庭拥有者不能退出家庭,只能删除家庭 +8. 删除家庭前,必须先删除或转移该家庭下的所有账户 +9. 修改成员角色需要谨慎操作,可能影响其他成员的权限 + +## 多家庭使用示例 + +### 创建家庭并邀请成员 + +```javascript +// 1. 创建家庭 +const family = await familyApi.createFamily({ + name: '温馨小家', + description: '我们的家庭记账', + avatar: 'avatar.jpg' +}) + +// 2. 邀请成员(需要知道对方的用户ID) +await familyApi.inviteMember(family.id, 123) // 123是被邀请用户的ID +``` + +### 管理家庭成员 + +```javascript +// 1. 获取家庭详情 +const family = await familyApi.getFamilyDetail(familyId) + +// 2. 修改成员角色(仅owner可以) +await familyApi.updateMemberRole(familyId, memberId, 'admin') + +// 3. 移除成员(需要管理权限) +await familyApi.removeMember(familyId, memberId) +``` + +### 在记账时使用家庭 + +```javascript +// 创建记账记录时指定家庭ID +await recordApi.createRecord({ + family_id: 1, // 家庭ID + member_id: 2, // 家庭成员ID + account_id: 3, // 家庭账户ID + type: 'expense', + amount: 100, + category: '餐饮', + date: '2025-01-01', + time: '12:00' +}) +``` + +### 切换家庭 + +```javascript +// 获取用户的所有家庭 +const families = await familyApi.getFamilies() + +// 在应用中保存当前选择的家庭ID +const currentFamilyId = families.list[0].id + +// 后续操作都使用这个family_id +``` diff --git a/lang/en/auth.php b/lang/en/auth.php new file mode 100644 index 0000000..6598e2c --- /dev/null +++ b/lang/en/auth.php @@ -0,0 +1,20 @@ + 'These credentials do not match our records.', + 'password' => 'The provided password is incorrect.', + 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.', + +]; diff --git a/lang/en/pagination.php b/lang/en/pagination.php new file mode 100644 index 0000000..d481411 --- /dev/null +++ b/lang/en/pagination.php @@ -0,0 +1,19 @@ + '« Previous', + 'next' => 'Next »', + +]; diff --git a/lang/en/passwords.php b/lang/en/passwords.php new file mode 100644 index 0000000..fad3a7d --- /dev/null +++ b/lang/en/passwords.php @@ -0,0 +1,22 @@ + 'Your password has been reset.', + 'sent' => 'We have emailed your password reset link.', + 'throttled' => 'Please wait before retrying.', + 'token' => 'This password reset token is invalid.', + 'user' => "We can't find a user with that email address.", + +]; diff --git a/lang/en/validation.php b/lang/en/validation.php new file mode 100644 index 0000000..c3e94e1 --- /dev/null +++ b/lang/en/validation.php @@ -0,0 +1,193 @@ + 'The :attribute field must be accepted.', + 'accepted_if' => 'The :attribute field must be accepted when :other is :value.', + 'active_url' => 'The :attribute field must be a valid URL.', + 'after' => 'The :attribute field must be a date after :date.', + 'after_or_equal' => 'The :attribute field must be a date after or equal to :date.', + 'alpha' => 'The :attribute field must only contain letters.', + 'alpha_dash' => 'The :attribute field must only contain letters, numbers, dashes, and underscores.', + 'alpha_num' => 'The :attribute field must only contain letters and numbers.', + 'array' => 'The :attribute field must be an array.', + 'ascii' => 'The :attribute field must only contain single-byte alphanumeric characters and symbols.', + 'before' => 'The :attribute field must be a date before :date.', + 'before_or_equal' => 'The :attribute field must be a date before or equal to :date.', + 'between' => [ + 'array' => 'The :attribute field must have between :min and :max items.', + 'file' => 'The :attribute field must be between :min and :max kilobytes.', + 'numeric' => 'The :attribute field must be between :min and :max.', + 'string' => 'The :attribute field must be between :min and :max characters.', + ], + 'boolean' => 'The :attribute field must be true or false.', + 'can' => 'The :attribute field contains an unauthorized value.', + 'confirmed' => 'The :attribute field confirmation does not match.', + 'current_password' => 'The password is incorrect.', + 'date' => 'The :attribute field must be a valid date.', + 'date_equals' => 'The :attribute field must be a date equal to :date.', + 'date_format' => 'The :attribute field must match the format :format.', + 'decimal' => 'The :attribute field must have :decimal decimal places.', + 'declined' => 'The :attribute field must be declined.', + 'declined_if' => 'The :attribute field must be declined when :other is :value.', + 'different' => 'The :attribute field and :other must be different.', + 'digits' => 'The :attribute field must be :digits digits.', + 'digits_between' => 'The :attribute field must be between :min and :max digits.', + 'dimensions' => 'The :attribute field has invalid image dimensions.', + 'distinct' => 'The :attribute field has a duplicate value.', + 'doesnt_end_with' => 'The :attribute field must not end with one of the following: :values.', + 'doesnt_start_with' => 'The :attribute field must not start with one of the following: :values.', + 'email' => 'The :attribute field must be a valid email address.', + 'ends_with' => 'The :attribute field must end with one of the following: :values.', + 'enum' => 'The selected :attribute is invalid.', + 'exists' => 'The selected :attribute is invalid.', + 'extensions' => 'The :attribute field must have one of the following extensions: :values.', + 'file' => 'The :attribute field must be a file.', + 'filled' => 'The :attribute field must have a value.', + 'gt' => [ + 'array' => 'The :attribute field must have more than :value items.', + 'file' => 'The :attribute field must be greater than :value kilobytes.', + 'numeric' => 'The :attribute field must be greater than :value.', + 'string' => 'The :attribute field must be greater than :value characters.', + ], + 'gte' => [ + 'array' => 'The :attribute field must have :value items or more.', + 'file' => 'The :attribute field must be greater than or equal to :value kilobytes.', + 'numeric' => 'The :attribute field must be greater than or equal to :value.', + 'string' => 'The :attribute field must be greater than or equal to :value characters.', + ], + 'hex_color' => 'The :attribute field must be a valid hexadecimal color.', + 'image' => 'The :attribute field must be an image.', + 'in' => 'The selected :attribute is invalid.', + 'in_array' => 'The :attribute field must exist in :other.', + 'integer' => 'The :attribute field must be an integer.', + 'ip' => 'The :attribute field must be a valid IP address.', + 'ipv4' => 'The :attribute field must be a valid IPv4 address.', + 'ipv6' => 'The :attribute field must be a valid IPv6 address.', + 'json' => 'The :attribute field must be a valid JSON string.', + 'list' => 'The :attribute field must be a list.', + 'lowercase' => 'The :attribute field must be lowercase.', + 'lt' => [ + 'array' => 'The :attribute field must have less than :value items.', + 'file' => 'The :attribute field must be less than :value kilobytes.', + 'numeric' => 'The :attribute field must be less than :value.', + 'string' => 'The :attribute field must be less than :value characters.', + ], + 'lte' => [ + 'array' => 'The :attribute field must not have more than :value items.', + 'file' => 'The :attribute field must be less than or equal to :value kilobytes.', + 'numeric' => 'The :attribute field must be less than or equal to :value.', + 'string' => 'The :attribute field must be less than or equal to :value characters.', + ], + 'mac_address' => 'The :attribute field must be a valid MAC address.', + 'max' => [ + 'array' => 'The :attribute field must not have more than :max items.', + 'file' => 'The :attribute field must not be greater than :max kilobytes.', + 'numeric' => 'The :attribute field must not be greater than :max.', + 'string' => 'The :attribute field must not be greater than :max characters.', + ], + 'max_digits' => 'The :attribute field must not have more than :max digits.', + 'mimes' => 'The :attribute field must be a file of type: :values.', + 'mimetypes' => 'The :attribute field must be a file of type: :values.', + 'min' => [ + 'array' => 'The :attribute field must have at least :min items.', + 'file' => 'The :attribute field must be at least :min kilobytes.', + 'numeric' => 'The :attribute field must be at least :min.', + 'string' => 'The :attribute field must be at least :min characters.', + ], + 'min_digits' => 'The :attribute field must have at least :min digits.', + 'missing' => 'The :attribute field must be missing.', + 'missing_if' => 'The :attribute field must be missing when :other is :value.', + 'missing_unless' => 'The :attribute field must be missing unless :other is :value.', + 'missing_with' => 'The :attribute field must be missing when :values is present.', + 'missing_with_all' => 'The :attribute field must be missing when :values are present.', + 'multiple_of' => 'The :attribute field must be a multiple of :value.', + 'not_in' => 'The selected :attribute is invalid.', + 'not_regex' => 'The :attribute field format is invalid.', + 'numeric' => 'The :attribute field must be a number.', + 'password' => [ + 'letters' => 'The :attribute field must contain at least one letter.', + 'mixed' => 'The :attribute field must contain at least one uppercase and one lowercase letter.', + 'numbers' => 'The :attribute field must contain at least one number.', + 'symbols' => 'The :attribute field must contain at least one symbol.', + 'uncompromised' => 'The given :attribute has appeared in a data leak. Please choose a different :attribute.', + ], + 'present' => 'The :attribute field must be present.', + 'present_if' => 'The :attribute field must be present when :other is :value.', + 'present_unless' => 'The :attribute field must be present unless :other is :value.', + 'present_with' => 'The :attribute field must be present when :values is present.', + 'present_with_all' => 'The :attribute field must be present when :values are present.', + 'prohibited' => 'The :attribute field is prohibited.', + 'prohibited_if' => 'The :attribute field is prohibited when :other is :value.', + 'prohibited_unless' => 'The :attribute field is prohibited unless :other is in :values.', + 'prohibits' => 'The :attribute field prohibits :other from being present.', + 'regex' => 'The :attribute field format is invalid.', + 'required' => 'The :attribute field is required.', + 'required_array_keys' => 'The :attribute field must contain entries for: :values.', + 'required_if' => 'The :attribute field is required when :other is :value.', + 'required_if_accepted' => 'The :attribute field is required when :other is accepted.', + 'required_if_declined' => 'The :attribute field is required when :other is declined.', + 'required_unless' => 'The :attribute field is required unless :other is in :values.', + 'required_with' => 'The :attribute field is required when :values is present.', + 'required_with_all' => 'The :attribute field is required when :values are present.', + 'required_without' => 'The :attribute field is required when :values is not present.', + 'required_without_all' => 'The :attribute field is required when none of :values are present.', + 'same' => 'The :attribute field must match :other.', + 'size' => [ + 'array' => 'The :attribute field must contain :size items.', + 'file' => 'The :attribute field must be :size kilobytes.', + 'numeric' => 'The :attribute field must be :size.', + 'string' => 'The :attribute field must be :size characters.', + ], + 'starts_with' => 'The :attribute field must start with one of the following: :values.', + 'string' => 'The :attribute field must be a string.', + 'timezone' => 'The :attribute field must be a valid timezone.', + 'unique' => 'The :attribute has already been taken.', + 'uploaded' => 'The :attribute failed to upload.', + 'uppercase' => 'The :attribute field must be uppercase.', + 'url' => 'The :attribute field must be a valid URL.', + 'ulid' => 'The :attribute field must be a valid ULID.', + 'uuid' => 'The :attribute field must be a valid UUID.', + + /* + |-------------------------------------------------------------------------- + | Custom Validation Language Lines + |-------------------------------------------------------------------------- + | + | Here you may specify custom validation messages for attributes using the + | convention "attribute.rule" to name the lines. This makes it quick to + | specify a specific custom language line for a given attribute rule. + | + */ + + 'custom' => [ + 'attribute-name' => [ + 'rule-name' => 'custom-message', + ], + ], + + /* + |-------------------------------------------------------------------------- + | Custom Validation Attributes + |-------------------------------------------------------------------------- + | + | The following language lines are used to swap our attribute placeholder + | with something more reader friendly such as "E-Mail Address" instead + | of "email". This simply helps us make our message more expressive. + | + */ + + 'attributes' => [], + +]; diff --git a/lang/zh_CN/auth.php b/lang/zh_CN/auth.php new file mode 100644 index 0000000..071b7b3 --- /dev/null +++ b/lang/zh_CN/auth.php @@ -0,0 +1,20 @@ + '这些凭据与我们的记录不匹配。', + 'password' => '提供的密码不正确。', + 'throttle' => '登录尝试次数过多。请在:seconds秒后重试。', + +]; diff --git a/lang/zh_CN/pagination.php b/lang/zh_CN/pagination.php new file mode 100644 index 0000000..4e8dea7 --- /dev/null +++ b/lang/zh_CN/pagination.php @@ -0,0 +1,19 @@ + '« 上一页', + 'next' => '下一页 »', + +]; diff --git a/lang/zh_CN/passwords.php b/lang/zh_CN/passwords.php new file mode 100644 index 0000000..d0404d2 --- /dev/null +++ b/lang/zh_CN/passwords.php @@ -0,0 +1,22 @@ + '您的密码已经被重置', + 'sent' => '我们已通过电子邮件发送您的密码重置链接。', + 'throttled' => '请稍候,然后重试。', + 'token' => '此密码重置令牌无效。', + 'user' => "我们找不到具有该电子邮件地址的用户。", + +]; diff --git a/lang/zh_CN/validation.php b/lang/zh_CN/validation.php new file mode 100644 index 0000000..ef3cc02 --- /dev/null +++ b/lang/zh_CN/validation.php @@ -0,0 +1,200 @@ + "您必须接受 :attribute。", + "accepted_if" => "当 :other 为 :value 时,必须接受 :attribute。", + "active_url" => ":attribute 不是一个有效的网址。", + "after" => ":attribute 必须要晚于 :date。", + "after_or_equal" => ":attribute 必须要等于 :date 或更晚。", + "alpha" => ":attribute 只能由字母组成。", + "alpha_dash" => ":attribute 只能由字母、数字、短划线(-)和下划线(_)组成。", + "alpha_num" => ":attribute 只能由字母和数字组成。", + "array" => ":attribute 必须是一个数组。", + "ascii" => ":attribute 必须仅包含单字节字母数字字符和符号。", + "attached" => "这个 :attribute 已经连接。", + "before" => ":attribute 必须要早于 :date。", + "before_or_equal" => ":attribute 必须要等于 :date 或更早。", + "between.array" => ":attribute 必须只有 :min - :max 个单元。", + "between.file" => ":attribute 必须介于 :min - :max KB 之间。", + "between.numeric" => ":attribute 必须介于 :min - :max 之间。", + "between.string" => ":attribute 必须介于 :min - :max 个字符之间。", + "boolean" => ":attribute 必须为布尔值。", + "can" => ":attribute 字段包含未经授权的值。", + "confirmed" => ":attribute 两次输入不一致。", + "contains" => "The :attribute field is missing a required value.", + "current_password" => "密码错误。", + "date" => ":attribute 不是一个有效的日期。", + "date_equals" => ":attribute 必须要等于 :date。", + "date_format" => ":attribute 的格式必须为 :format。", + "decimal" => ":attribute 必须有 :decimal 位小数。", + "declined" => ":attribute 必须是拒绝的。", + "declined_if" => "当 :other 为 :value 时字段 :attribute 必须是拒绝的。", + "different" => ":attribute 和 :other 必须不同。", + "digits" => ":attribute 必须是 :digits 位数字。", + "digits_between" => ":attribute 必须是介于 :min 和 :max 位的数字。", + "dimensions" => ":attribute 图片尺寸不正确。", + "distinct" => ":attribute 已经存在。", + "doesnt_end_with" => ":attribute 不能以以下之一结尾: :values。", + "doesnt_start_with" => ":attribute 不能以下列之一开头: :values。", + "email" => ":attribute 不是一个合法的邮箱。", + "ends_with" => ":attribute 必须以 :values 为结尾。", + "enum" => ":attribute 值不正确。", + "exists" => ":attribute 不存在。", + "extensions" => ":attribute 字段必须具有以下扩展名之一::values。", + "failed" => "用户名或密码错误。", + "file" => ":attribute 必须是文件。", + "filled" => ":attribute 不能为空。", + "gt.array" => ":attribute 必须多于 :value 个元素。", + "gt.file" => ":attribute 必须大于 :value KB。", + "gt.numeric" => ":attribute 必须大于 :value。", + "gt.string" => ":attribute 必须多于 :value 个字符。", + "gte.array" => ":attribute 必须多于或等于 :value 个元素。", + "gte.file" => ":attribute 必须大于或等于 :value KB。", + "gte.numeric" => ":attribute 必须大于或等于 :value。", + "gte.string" => ":attribute 必须多于或等于 :value 个字符。", + "hex_color" => ":attribute 字段必须是有效的十六进制颜色。", + "image" => ":attribute 必须是图片。", + "in" => "已选的属性 :attribute 无效。", + "in_array" => ":attribute 必须在 :other 中。", + "integer" => ":attribute 必须是整数。", + "ip" => ":attribute 必须是有效的 IP 地址。", + "ipv4" => ":attribute 必须是有效的 IPv4 地址。", + "ipv6" => ":attribute 必须是有效的 IPv6 地址。", + "json" => ":attribute 必须是正确的 JSON 格式。", + "list" => ":attribute 字段必须是一个列表。", + "lowercase" => ":attribute 必须小写。", + "lt.array" => ":attribute 必须少于 :value 个元素。", + "lt.file" => ":attribute 必须小于 :value KB。", + "lt.numeric" => ":attribute 必须小于 :value。", + "lt.string" => ":attribute 必须少于 :value 个字符。", + "lte.array" => ":attribute 必须少于或等于 :value 个元素。", + "lte.file" => ":attribute 必须小于或等于 :value KB。", + "lte.numeric" => ":attribute 必须小于或等于 :value。", + "lte.string" => ":attribute 必须少于或等于 :value 个字符。", + "mac_address" => ":attribute 必须是一个有效的 MAC 地址。", + "max.array" => ":attribute 最多只有 :max 个单元。", + "max.file" => ":attribute 不能大于 :max KB。", + "max.numeric" => ":attribute 不能大于 :max。", + "max.string" => ":attribute 不能大于 :max 个字符。", + "max_digits" => ":attribute 不能超过 :max 位数。", + "mimes" => ":attribute 必须是一个 :values 类型的文件。", + "mimetypes" => ":attribute 必须是一个 :values 类型的文件。", + "min.array" => ":attribute 至少有 :min 个单元。", + "min.file" => ":attribute 大小不能小于 :min KB。", + "min.numeric" => ":attribute 必须大于等于 :min。", + "min.string" => ":attribute 至少为 :min 个字符。", + "min_digits" => ":attribute 必须至少有 :min 位数。", + "missing" => "必须缺少 :attribute 字段。", + "missing_if" => "当 :other 为 :value 时,必须缺少 :attribute 字段。", + "missing_unless" => "必须缺少 :attribute 字段,除非 :other 是 :value。", + "missing_with" => "存在 :values 时,必须缺少 :attribute 字段。", + "missing_with_all" => "存在 :values 时,必须缺少 :attribute 字段。", + "multiple_of" => ":attribute 必须是 :value 中的多个值。", + "next" => "下一页 »", + "not_in" => "已选的属性 :attribute 非法。", + "not_regex" => ":attribute 的格式错误。", + "numeric" => ":attribute 必须是一个数字。", + "password" => [ + "letters" => ":attribute 必须至少包含一个字母。", + "mixed" => ":attribute 必须至少包含一个大写字母和一个小写字母。", + "numbers" => ":attribute 必须至少包含一个数字。", + "symbols" => ":attribute 必须至少包含一个符号。", + "uncompromised" => "给定的 :attribute 出现在已经泄漏的密码中。请选择不同的 :attribute。", + ], + "present" => ":attribute 必须存在。", + "present_if" => "当 :other 等于 :value 时,必须存在 :attribute 字段。", + "present_unless" => "除非 :other 等于 :value,否则 :attribute 字段必须存在。", + "present_with" => "当 :values 存在时,:attribute 字段必须存在。", + "present_with_all" => "当存在 :values 时,必须存在 :attribute 字段。", + "previous" => "« 上一页", + "prohibited" => ":attribute 字段被禁止。", + "prohibited_if" => "当 :other 为 :value 时,禁止 :attribute 字段。", + "prohibited_unless" => ":attribute 字段被禁止,除非 :other 位于 :values 中。", + "prohibits" => ":attribute 字段禁止出现 :other。", + "regex" => ":attribute 格式不正确。", + "relatable" => "此 :attribute 可能与此资源不相关联。", + "required" => ":attribute 不能为空。", + "required_array_keys" => ":attribute 至少包含指定的键::values.", + "required_if" => "当 :other 为 :value 时 :attribute 不能为空。", + "required_if_accepted" => "当 :other 存在时,:attribute 不能为空。", + "required_if_declined" => "The :attribute field is required when :other is declined.", + "required_unless" => "当 :other 不为 :values 时 :attribute 不能为空。", + "required_with" => "当 :values 存在时 :attribute 不能为空。", + "required_with_all" => "当 :values 存在时 :attribute 不能为空。", + "required_without" => "当 :values 不存在时 :attribute 不能为空。", + "required_without_all" => "当 :values 都不存在时 :attribute 不能为空。", + "reset" => "密码重置成功!", + "same" => ":attribute 和 :other 必须相同。", + "sent" => "密码重置邮件已发送!", + "size" => [ + "array" => ":attribute 必须为 :size 个单元。", + "file" => ":attribute 大小必须为 :size KB。", + "numeric" => ":attribute 必须为 :size。", + "string" => ":attribute 必须是 :size 个字符。", + ], + "starts_with" => ":attribute 必须以 :values 为开头。", + "string" => ":attribute 必须是一个字符串。", + "throttle" => "您尝试的登录次数过多,请 :seconds 秒后再试。", + "throttled" => "请稍候再试。", + "timezone" => ":attribute 必须是一个合法的时区值。", + "token" => "密码重置令牌无效。", + "ulid" => ":attribute 必须是有效的 ULID。", + "unique" => ":attribute 已经存在。", + "uploaded" => ":attribute 上传失败。", + "uppercase" => ":attribute 必须大写", + "url" => ":attribute 格式不正确。", + "user" => "找不到该邮箱对应的用户。", + "uuid" => ":attribute 必须是有效的 UUID。", + + /* + |-------------------------------------------------------------------------- + | Custom Validation Language Lines + |-------------------------------------------------------------------------- + | + | Here you may specify custom validation messages for attributes using the + | convention "attribute.rule" to name the lines. This makes it quick to + | specify a specific custom language line for a given attribute rule. + | + */ + + 'custom' => [ + 'attribute-name' => [ + 'rule-name' => 'custom-message', + ], + ], + + /* + |-------------------------------------------------------------------------- + | Custom Validation Attributes + |-------------------------------------------------------------------------- + | + | The following language lines are used to swap our attribute placeholder + | with something more reader friendly such as "E-Mail Address" instead + | of "email". This simply helps us make our message more expressive. + | + */ + + 'attributes' => [ + 'title' => '标题', + 'name' => '名称', + 'mobile' => '手机号', + 'email' => '邮箱', + 'password' => '密码', + 'password_confirmation' => '确认密码', + 'old_password' => '旧密码', + 'new_password' => '新密码', + 'new_password_confirmation' => '确认新密码', + 'content' => '内容', + 'image' => '图片', + 'description' => '描述', + 'keywords' => '关键字', + 'type' => '类型', + 'url' => '链接', + 'parent_id' => '上级ID', + 'status' => '状态', + 'sort' => '排序', + 'id' => 'ID', + 'username' => '用户名', + ], + +]; diff --git a/modules/Ads/app/Controllers/Admin/Ads.php b/modules/Ads/app/Controllers/Admin/Ads.php new file mode 100644 index 0000000..cf333c0 --- /dev/null +++ b/modules/Ads/app/Controllers/Admin/Ads.php @@ -0,0 +1,84 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Ads\Controllers\Admin; + +use Illuminate\Http\Request; +use App\Http\Controllers\BaseController; +use Modules\Ads\Services\AdsService; + +class Ads extends BaseController { + + /** + * @title 广告列表 + * + * @param Request $request + * @param AdsService $service + * @return void + */ + public function index(Request $request, AdsService $service) { + try { + $this->data['data'] = $service->getDataList($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + return response()->json($this->data); + } + + /** + * @title 添加广告 + * + * @param Request $request + * @param AdsService $service + * @return void + */ + public function add(Request $request, AdsService $service) { + try { + $this->data['data'] = $service->create($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + return response()->json($this->data); + } + + /** + * @title 编辑广告 + * + * @param Request $request + * @param AdsService $service + * @return void + */ + public function edit(Request $request, AdsService $service) { + try { + $this->data['data'] = $service->update($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + return response()->json($this->data); + } + + /** + * @title 删除广告 + * + * @param Request $request + * @param AdsService $service + * @return void + */ + public function delete(Request $request, AdsService $service) { + try { + $this->data['data'] = $service->delete($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + return response()->json($this->data); + } +} diff --git a/modules/Ads/app/Controllers/Api/Ads.php b/modules/Ads/app/Controllers/Api/Ads.php new file mode 100644 index 0000000..a392c77 --- /dev/null +++ b/modules/Ads/app/Controllers/Api/Ads.php @@ -0,0 +1,35 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Ads\Controllers\Api; + +use Illuminate\Http\Request; +use App\Http\Controllers\BaseController; +use Modules\Ads\Services\AdsService; + +class Ads extends BaseController { + + /** + * @title 广告列表 + * + * @param Request $request + * @param AdsService $service + * @return void + */ + public function index(Request $request, AdsService $service){ + try { + $request->mergeIfMissing(['status' => 1]); + $this->data['data'] = $service->getDataList($request); + } catch (\Throwable $th) { + $this->data['code'] = 0; + $this->data['message'] = $th->getMessage(); + } + + return response()->json($this->data); + } +} diff --git a/modules/Ads/app/Models/Ads.php b/modules/Ads/app/Models/Ads.php new file mode 100644 index 0000000..58c7f07 --- /dev/null +++ b/modules/Ads/app/Models/Ads.php @@ -0,0 +1,28 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Ads\Models; + +use Illuminate\Database\Eloquent\Builder; +use App\Models\BaseModel; + +class Ads extends BaseModel { + + protected $table = 'ads'; + protected $fillable = ['title', 'description', 'logo', 'sort','status']; + // protected $hidden = ['deleted_at']; + + protected function casts(): array { + return [ + 'sort' => 'integer', + 'status' => 'integer', + 'created_at' => 'datetime:Y-m-d H:i:s', + 'updated_at' => 'datetime:Y-m-d H:i:s', + ]; + } +} diff --git a/modules/Ads/app/Providers/.gitkeep b/modules/Ads/app/Providers/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/modules/Ads/app/Providers/AdsServiceProvider.php b/modules/Ads/app/Providers/AdsServiceProvider.php new file mode 100644 index 0000000..fc33041 --- /dev/null +++ b/modules/Ads/app/Providers/AdsServiceProvider.php @@ -0,0 +1,120 @@ +registerCommands(); + $this->registerCommandSchedules(); + $this->registerTranslations(); + $this->registerConfig(); + $this->registerViews(); + $this->loadMigrationsFrom(module_path($this->moduleName, 'database/migrations')); + } + + /** + * Register the service provider. + */ + public function register(): void + { + $this->app->register(EventServiceProvider::class); + $this->app->register(RouteServiceProvider::class); + } + + /** + * Register commands in the format of Command::class + */ + protected function registerCommands(): void + { + // $this->commands([]); + } + + /** + * Register command Schedules. + */ + protected function registerCommandSchedules(): void + { + // $this->app->booted(function () { + // $schedule = $this->app->make(Schedule::class); + // $schedule->command('inspire')->hourly(); + // }); + } + + /** + * Register translations. + */ + public function registerTranslations(): void + { + $langPath = resource_path('lang/modules/'.$this->moduleNameLower); + + if (is_dir($langPath)) { + $this->loadTranslationsFrom($langPath, $this->moduleNameLower); + $this->loadJsonTranslationsFrom($langPath); + } else { + $this->loadTranslationsFrom(module_path($this->moduleName, 'lang'), $this->moduleNameLower); + $this->loadJsonTranslationsFrom(module_path($this->moduleName, 'lang')); + } + } + + /** + * Register config. + */ + protected function registerConfig(): void + { + $this->publishes([module_path($this->moduleName, 'config/config.php') => config_path($this->moduleNameLower.'.php')], 'config'); + $this->mergeConfigFrom(module_path($this->moduleName, 'config/config.php'), $this->moduleNameLower); + } + + /** + * Register views. + */ + public function registerViews(): void + { + $viewPath = resource_path('views/modules/'.$this->moduleNameLower); + $sourcePath = module_path($this->moduleName, 'resources/views'); + + $this->publishes([$sourcePath => $viewPath], ['views', $this->moduleNameLower.'-module-views']); + + $this->loadViewsFrom(array_merge($this->getPublishableViewPaths(), [$sourcePath]), $this->moduleNameLower); + + $componentNamespace = str_replace('/', '\\', config('modules.namespace').'\\'.$this->moduleName.'\\'.ltrim(config('modules.paths.generator.component-class.path'), config('modules.paths.app_folder', ''))); + Blade::componentNamespace($componentNamespace, $this->moduleNameLower); + } + + /** + * Get the services provided by the provider. + * + * @return array + */ + public function provides(): array + { + return []; + } + + /** + * @return array + */ + private function getPublishableViewPaths(): array + { + $paths = []; + foreach (config('view.paths') as $path) { + if (is_dir($path.'/modules/'.$this->moduleNameLower)) { + $paths[] = $path.'/modules/'.$this->moduleNameLower; + } + } + + return $paths; + } +} diff --git a/modules/Ads/app/Providers/EventServiceProvider.php b/modules/Ads/app/Providers/EventServiceProvider.php new file mode 100644 index 0000000..41eed85 --- /dev/null +++ b/modules/Ads/app/Providers/EventServiceProvider.php @@ -0,0 +1,38 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Ads\Providers; + +use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; + +class EventServiceProvider extends ServiceProvider +{ + /** + * The event handler mappings for the application. + * + * @var array> + */ + protected $listen = []; + + /** + * Indicates if events should be discovered. + * + * @var bool + */ + protected static $shouldDiscoverEvents = true; + + /** + * Configure the proper event listeners for email verification. + * + * @return void + */ + protected function configureEmailVerification(): void + { + + } +} diff --git a/modules/Ads/app/Providers/RouteServiceProvider.php b/modules/Ads/app/Providers/RouteServiceProvider.php new file mode 100644 index 0000000..5f7101d --- /dev/null +++ b/modules/Ads/app/Providers/RouteServiceProvider.php @@ -0,0 +1,67 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Ads\Providers; + +use Illuminate\Support\Facades\Route; +use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider; + +class RouteServiceProvider extends ServiceProvider +{ + /** + * Called before routes are registered. + * + * Register any model bindings or pattern based filters. + */ + public function boot(): void + { + parent::boot(); + } + + /** + * Define the routes for the application. + */ + public function map(): void + { + $this->mapApiRoutes(); + + $this->mapWebRoutes(); + + $this->mapAdminRoutes(); + } + + /** + * Define the "web" routes for the application. + * + * These routes all receive session state, CSRF protection, etc. + */ + protected function mapWebRoutes(): void + { + Route::middleware('web')->group(module_path('Ads', '/routes/web.php')); + } + + /** + * Define the "api" routes for the application. + * + * These routes are typically stateless. + */ + protected function mapApiRoutes(): void + { + Route::middleware('api')->prefix('api')->name('api.')->group(module_path('Ads', '/routes/api.php')); + } + + /** + * Define the "api" routes for the application. + * + * These routes are typically stateless. + */ + protected function mapAdminRoutes(): void + { + Route::middleware('api')->prefix('admin')->name('admin.')->group(module_path('Ads', '/routes/admin.php')); + } +} diff --git a/modules/Ads/app/Services/AdsService.php b/modules/Ads/app/Services/AdsService.php new file mode 100644 index 0000000..ed93b8b --- /dev/null +++ b/modules/Ads/app/Services/AdsService.php @@ -0,0 +1,124 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Ads\Services; + +use Modules\Ads\Models\Ads; + +class AdsService { + + /** + * @title 广告列表 + * + * @param [type] $request + * @return void + */ + public function getDataList($request){ + $map = []; + + if($request->filled('title')){ + $map[] = ['title', 'like', '%'.$request->input('title').'%']; + } + if($request->filled('client_id')){ + $map[] = ['client_id', '=', $request->input('client_id')]; + } + if($request->filled('code')){ + $map[] = ['code', '=', $request->input('code')]; + } + if($request->filled('group')){ + $map[] = ['group', '=', $request->input('group')]; + } + if($request->filled('status')){ + $map[] = ['status', '=', $request->input('status')]; + } + + $query = Ads::where($map); + + $query->orderBy($request->input('order', 'sort'), $request->input('sort', 'desc')); + if($request->filled('page')){ + $data = [ + 'total' => $query->count(), + 'page' => $request->input('page', 1), + 'data' => $query->offset($request->input('offset', 0))->limit($request->input('limit', 10))->get(), + ]; + }else{ + $data = $query->get(); + } + return $data; + } + + /** + * @title 添加广告 + * + * @param [type] $request + * @return void + */ + public function create($request){ + $request->validate([ + 'title' => 'required', + 'group' => 'required' + ], [ + 'title.required' => '请输入广告名称', + 'group.required' => '请输入广告位标识' + ]); + + $ads = new Ads; + foreach ($ads->setFilterFields($request->all()) as $key => $value) { + $ads->$key = $value; + } + + $ads->save(); + return $ads; + } + + /** + * @title 更新广告 + * + * @param [type] $request + * @return void + */ + public function update($request){ + $request->validate([ + 'title' => 'required', + 'group' => 'required' + ], [ + 'title.required' => '请输入广告名称', + 'group.required' => '请输入广告位标识' + ]); + + $ads = Ads::findOrFail($request->input('id')); + + foreach ($ads->setFilterFields($request->all()) as $key => $value) { + $ads->$key = $value; + } + + $ads->save(); + return $ads; + } + + public function delete($request){ + if($request->filled('id')){ + try { + $ads = Ads::findOrFail($request->input('id')); + } catch (\Throwable $th) { + throw new \Exception("广告不存在!", 1); + } + $ads->delete(); + } + if($request->filled('ids')){ + try { + $ads = Ads::whereIn('id', $request->input('ids')); + $ads->delete(); + } catch (\Throwable $th) { + throw new \Exception($th->getMessage(), 1); + } + } + + return $ads; + } +} diff --git a/modules/Ads/composer.json b/modules/Ads/composer.json new file mode 100644 index 0000000..b2420c6 --- /dev/null +++ b/modules/Ads/composer.json @@ -0,0 +1,30 @@ +{ + "name": "tensent/ads", + "description": "", + "authors": [ + { + "name": "molong", + "email": "molong@tensent.cn" + } + ], + "extra": { + "laravel": { + "providers": [], + "aliases": { + + } + } + }, + "autoload": { + "psr-4": { + "Modules\\Ads\\": "app/", + "Modules\\Ads\\Database\\Factories\\": "database/factories/", + "Modules\\Ads\\Database\\Seeders\\": "database/seeders/" + } + }, + "autoload-dev": { + "psr-4": { + "Modules\\Ads\\Tests\\": "tests/" + } + } +} diff --git a/modules/Ads/config/.gitkeep b/modules/Ads/config/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/modules/Ads/config/config.php b/modules/Ads/config/config.php new file mode 100644 index 0000000..6b8cf24 --- /dev/null +++ b/modules/Ads/config/config.php @@ -0,0 +1,5 @@ + 'Ads', +]; diff --git a/modules/Ads/database/factories/.gitkeep b/modules/Ads/database/factories/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/modules/Ads/database/migrations/.gitkeep b/modules/Ads/database/migrations/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/modules/Ads/database/migrations/2024_08_19_231401_ads_table.php b/modules/Ads/database/migrations/2024_08_19_231401_ads_table.php new file mode 100644 index 0000000..9969a80 --- /dev/null +++ b/modules/Ads/database/migrations/2024_08_19_231401_ads_table.php @@ -0,0 +1,39 @@ +id()->uniqid()->comment('主键id'); + $table->unsignedBigInteger('client_id')->default(0)->comment('站点ID'); + $table->string('title')->comment('广告标题'); + $table->string('name')->nullable()->comment('广告标识'); + $table->string('group')->nullable()->comment('广告所属板块标识'); + $table->string('description')->nullable()->comment('广告描述'); + $table->string('cover')->nullable()->comment('广告封面'); + $table->string('link')->nullable()->comment('广告链接'); + $table->string('sort')->default(0)->comment('排序'); + $table->tinyInteger('status')->default(1)->comment('状态 1正常 2禁用'); + $table->timestamp('created_at')->nullable()->comment('创建时间'); + $table->timestamp('updated_at')->nullable()->comment('更新时间'); + + $table->engine = 'InnoDB'; + $table->charset = 'utf8mb4'; + $table->collation = 'utf8mb4_unicode_ci'; + $table->comment('广告表'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void { + Schema::dropIfExists('ads'); + } +}; diff --git a/modules/Ads/database/seeders/.gitkeep b/modules/Ads/database/seeders/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/modules/Ads/database/seeders/AdsDatabaseSeeder.php b/modules/Ads/database/seeders/AdsDatabaseSeeder.php new file mode 100644 index 0000000..17c6f76 --- /dev/null +++ b/modules/Ads/database/seeders/AdsDatabaseSeeder.php @@ -0,0 +1,42 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Ads\Database\Seeders; + +use Illuminate\Database\Seeder; +use Illuminate\Support\Facades\DB; +use App\Services\Auth\MenuService; + +class AdsDatabaseSeeder extends Seeder +{ + /** + * Run the database seeds. + */ + public function run(): void + { + $this->initMenu(); + } + + public function initMenu(){ + $menuService = app(MenuService::class); + + $operate = DB::table('auth_permissions')->where('name', 'operate')->first(); + if (!$operate) { + $menu = [ + ['title' => '营销', 'name' => 'operate', 'path' => '/operate', 'component' => '', 'type' => 'menu', 'sort' => 5, 'children' => [ + ['title' => '广告管理', 'name' => 'operate.ads', 'path' => '/operate/ads', 'component' => 'operate/ads', 'type' => 'menu'] + ]] + ]; + $menuService->importMenu($menu, 0); + }else{ + $menuService->importMenu([ + ['title' => '广告管理', 'name' => 'operate.ads', 'path' => '/operate/ads', 'component' => 'operate/ads', 'type' => 'menu'] + ], $operate->id); + } + } +} diff --git a/modules/Ads/module.json b/modules/Ads/module.json new file mode 100644 index 0000000..9cda0f3 --- /dev/null +++ b/modules/Ads/module.json @@ -0,0 +1,11 @@ +{ + "name": "Ads", + "alias": "ads", + "description": "", + "keywords": [], + "priority": 0, + "providers": [ + "Modules\\Ads\\Providers\\AdsServiceProvider" + ], + "files": [] +} diff --git a/modules/Ads/package.json b/modules/Ads/package.json new file mode 100644 index 0000000..d6fbfc8 --- /dev/null +++ b/modules/Ads/package.json @@ -0,0 +1,15 @@ +{ + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build" + }, + "devDependencies": { + "axios": "^1.1.2", + "laravel-vite-plugin": "^0.7.5", + "sass": "^1.69.5", + "postcss": "^8.3.7", + "vite": "^4.0.0" + } +} diff --git a/modules/Ads/resources/assets/js/app.js b/modules/Ads/resources/assets/js/app.js new file mode 100644 index 0000000..e69de29 diff --git a/modules/Ads/resources/assets/sass/app.scss b/modules/Ads/resources/assets/sass/app.scss new file mode 100644 index 0000000..e69de29 diff --git a/modules/Ads/resources/views/index.blade.php b/modules/Ads/resources/views/index.blade.php new file mode 100644 index 0000000..6ad78a4 --- /dev/null +++ b/modules/Ads/resources/views/index.blade.php @@ -0,0 +1,7 @@ +@extends('ads::layouts.master') + +@section('content') +

Hello World

+ +

Module: {!! config('ads.name') !!}

+@endsection diff --git a/modules/Ads/resources/views/layouts/master.blade.php b/modules/Ads/resources/views/layouts/master.blade.php new file mode 100644 index 0000000..914cc13 --- /dev/null +++ b/modules/Ads/resources/views/layouts/master.blade.php @@ -0,0 +1,29 @@ + + + + + + + + + + Ads Module - {{ config('app.name', 'Laravel') }} + + + + + + + + + + {{-- Vite CSS --}} + {{-- {{ module_vite('build-ads', 'resources/assets/sass/app.scss') }} --}} + + + + @yield('content') + + {{-- Vite JS --}} + {{-- {{ module_vite('build-ads', 'resources/assets/js/app.js') }} --}} + diff --git a/modules/Ads/routes/.gitkeep b/modules/Ads/routes/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/modules/Ads/routes/admin.php b/modules/Ads/routes/admin.php new file mode 100644 index 0000000..ae6361b --- /dev/null +++ b/modules/Ads/routes/admin.php @@ -0,0 +1,16 @@ + +// +---------------------------------------------------------------------- +use Illuminate\Support\Facades\Route; + +Route::controller(Modules\Ads\Controllers\Admin\Ads::class)->prefix('ads')->name('ads.')->group(function () { + Route::get('/index', 'index')->name('index'); + Route::post('/add', 'add')->name('add'); + Route::put('/edit', 'edit')->name('edit'); + Route::delete('/delete', 'delete')->name('delete'); +}); diff --git a/modules/Ads/routes/api.php b/modules/Ads/routes/api.php new file mode 100644 index 0000000..6bc26b8 --- /dev/null +++ b/modules/Ads/routes/api.php @@ -0,0 +1,13 @@ + +// +---------------------------------------------------------------------- +use Illuminate\Support\Facades\Route; + +Route::controller(Modules\Ads\Controllers\Api\Ads::class)->prefix('ads')->name('ads.')->group(function () { + Route::get('/index', 'index')->name('index'); +}); diff --git a/modules/Ads/routes/web.php b/modules/Ads/routes/web.php new file mode 100644 index 0000000..1dea33c --- /dev/null +++ b/modules/Ads/routes/web.php @@ -0,0 +1,9 @@ + +// +---------------------------------------------------------------------- +use Illuminate\Support\Facades\Route; diff --git a/modules/Ads/vite.config.js b/modules/Ads/vite.config.js new file mode 100644 index 0000000..84e7174 --- /dev/null +++ b/modules/Ads/vite.config.js @@ -0,0 +1,26 @@ +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; + +export default defineConfig({ + build: { + outDir: '../../public/build-ads', + emptyOutDir: true, + manifest: true, + }, + plugins: [ + laravel({ + publicDirectory: '../../public', + buildDirectory: 'build-ads', + input: [ + __dirname + '/resources/assets/sass/app.scss', + __dirname + '/resources/assets/js/app.js' + ], + refresh: true, + }), + ], +}); + +//export const paths = [ +// 'Modules/Ads/resources/assets/sass/app.scss', +// 'Modules/Ads/resources/assets/js/app.js', +//]; \ No newline at end of file diff --git a/modules/Member/app/Controllers/.gitkeep b/modules/Member/app/Controllers/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/modules/Member/app/Controllers/Admin/Extend.php b/modules/Member/app/Controllers/Admin/Extend.php new file mode 100644 index 0000000..2b93c49 --- /dev/null +++ b/modules/Member/app/Controllers/Admin/Extend.php @@ -0,0 +1,39 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Member\Controllers\Admin; + +use Illuminate\Http\Request; +use App\Http\Controllers\BaseController; +use Modules\Member\Services\ExtendService; + +class Extend extends BaseController { + + public function index(Request $request, ExtendService $service){ + try { + $this->data['data'] = $service->getDataList($request); + } catch (\Throwable $th) { + $this->data['code'] = 0; + $this->data['message'] = $th->getMessage(); + } + + return $this->data; + } + + public function audit(Request $request, ExtendService $service){ + try { + $this->data['data'] = $service->audit($request); + $this->data['message'] = '审核完成!'; + } catch (\Throwable $th) { + $this->data['code'] = 0; + $this->data['message'] = $th->getMessage(); + } + + return $this->data; + } +} \ No newline at end of file diff --git a/modules/Member/app/Controllers/Admin/Field.php b/modules/Member/app/Controllers/Admin/Field.php new file mode 100644 index 0000000..fe2cab1 --- /dev/null +++ b/modules/Member/app/Controllers/Admin/Field.php @@ -0,0 +1,130 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Member\Controllers\Admin; + +use Illuminate\Http\Request; +use App\Http\Controllers\BaseController; +use Modules\Member\Services\FieldsService; + +class Field extends BaseController { + + /** + * @title 获取字段列表 + * + * @param Request $request + * @param FieldsService $service + * @return void + */ + public function index(Request $request, FieldsService $service) { + try { + $this->data['data'] = $service->getDataList($request); + $this->data['message'] = '获取成功'; + } catch (\Throwable $th) { + $this->data['code'] = 0; + $this->data['message'] = $th->getMessage(); + } + + return $this->data; + } + + /** + * @title 添加字段 + * + * @param Request $request + * @param FieldsService $service + * @return void + */ + public function add(Request $request, FieldsService $service) { + try { + $this->data['data'] = $service->create($request); + $this->data['message'] = '添加成功'; + } catch (\Throwable $th) { + $this->data['code'] = 0; + $this->data['message'] = $th->getMessage(); + } + + return $this->data; + } + + /** + * @title 编辑字段 + * + * @param Request $request + * @param FieldsService $service + * @return void + */ + public function edit(Request $request, FieldsService $service) { + try { + $this->data['data'] = $service->update($request); + $this->data['message'] = '编辑成功'; + } catch (\Throwable $th) { + $this->data['code'] = 0; + $this->data['message'] = $th->getMessage(); + } + + return $this->data; + } + + /** + * @title 删除字段 + * + * @param Request $request + * @param FieldsService $service + * @return void + */ + public function delete(Request $request, FieldsService $service) { + try { + $this->data['data'] = $service->delete($request); + $this->data['message'] = '删除成功'; + } catch (\Throwable $th) { + $this->data['code'] = 0; + $this->data['message'] = $th->getMessage(); + } + + return $this->data; + } + + /** + * @title 字段列表 + * + * @param Request $request + * @param FieldsService $service + * @return void + */ + public function lists(Request $request, FieldsService $service){ + try { + $this->data['data'] = $service->getFieldList($request); + $this->data['message'] = '获取成功'; + } catch (\Throwable $th) { + $this->data['code'] = 0; + $this->data['message'] = $th->getMessage(); + } + + return $this->data; + } + + /** + * @title 列表字段配置 + * + * @param Request $request + * @param FieldsService $service + * @return void + */ + public function setting(Request $request, FieldsService $service){ + try { + $this->data['data'] = $service->getFieldSetting($request); + $this->data['message'] = '获取成功'; + } catch (\Throwable $th) { + $this->data['code'] = 0; + $this->data['message'] = $th->getMessage(); + } + + return $this->data; + } +} diff --git a/modules/Member/app/Controllers/Admin/Index.php b/modules/Member/app/Controllers/Admin/Index.php new file mode 100644 index 0000000..3825e2b --- /dev/null +++ b/modules/Member/app/Controllers/Admin/Index.php @@ -0,0 +1,124 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Member\Controllers\Admin; + +use Illuminate\Http\Request; +use App\Http\Controllers\BaseController; +use Modules\Member\Services\MemberService; + +class Index extends BaseController{ + + /** + * @title 会员列表 + * + * @param Request $request + * @param MemberService $service + * @return void + */ + public function index(Request $request, MemberService $service){ + try { + $this->data['data'] = $service->getDataList($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 添加会员 + * + * @param Request $request + * @param MemberService $service + * @return void + */ + public function add(Request $request, MemberService $service){ + try { + $this->data['data'] = $service->create($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 修改会员 + * + * @param Request $request + * @param MemberService $service + * @return void + */ + public function edit(Request $request, MemberService $service){ + try { + $this->data['data'] = $service->update($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 删除会员 + * + * @param Request $request + * @param MemberService $service + * @return void + */ + public function delete(Request $request, MemberService $service){ + try { + $this->data['data'] = $service->delete($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 导入会员 + * + * @param Request $request + * @param MemberService $service + * @return void + */ + public function import(Request $request, MemberService $service){ + try { + $this->data['data'] = $service->import($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 导出会员 + * + * @param Request $request + * @param MemberService $service + * @return void + */ + public function export(Request $request, MemberService $service){ + try { + $this->data['data'] = $service->export($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } +} diff --git a/modules/Member/app/Controllers/Admin/Level.php b/modules/Member/app/Controllers/Admin/Level.php new file mode 100644 index 0000000..ba912a5 --- /dev/null +++ b/modules/Member/app/Controllers/Admin/Level.php @@ -0,0 +1,88 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Member\Controllers\Admin; + +use Illuminate\Http\Request; +use App\Http\Controllers\BaseController; +use Modules\Member\Services\LevelService; + +class Level extends BaseController{ + + /** + * @title 会员列表 + * + * @param Request $request + * @param LevelService $service + * @return void + */ + public function index(Request $request, LevelService $service){ + try { + $this->data['data'] = $service->getDataList($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 添加会员 + * + * @param Request $request + * @param LevelService $service + * @return void + */ + public function add(Request $request, LevelService $service){ + try { + $this->data['data'] = $service->create($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 修改会员 + * + * @param Request $request + * @param LevelService $service + * @return void + */ + public function edit(Request $request, LevelService $service){ + try { + $this->data['data'] = $service->update($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 删除会员 + * + * @param Request $request + * @param LevelService $service + * @return void + */ + public function delete(Request $request, LevelService $service){ + try { + $this->data['data'] = $service->delete($request); + } catch (\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + + return response()->json($this->data); + } +} diff --git a/modules/Member/app/Controllers/Api/Extend.php b/modules/Member/app/Controllers/Api/Extend.php new file mode 100644 index 0000000..d0ef28f --- /dev/null +++ b/modules/Member/app/Controllers/Api/Extend.php @@ -0,0 +1,82 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Member\Controllers\Api; + +use Illuminate\Http\Request; +use App\Http\Controllers\BaseController; +use Modules\Member\Services\LevelService; +use Modules\Member\Services\FieldsService; +use Modules\Member\Services\ExtendService; + +class Extend extends BaseController { + + /** + * title 会员扩展列表 + * + * @param Request $request + * @param ExtendService $service + * @return void + */ + public function index(Request $request, ExtendService $service){ + try { + $request->mergeIfMissing(['status' => 1]); + $this->data['data'] = $service->getDataList($request); + } catch (\Throwable $th) { + $this->data['code'] = 0; + $this->data['message'] = $th->getMessage(); + } + + return response()->json($this->data); + } + + public function detail(Request $request, ExtendService $service){ + try { + $this->data['data'] = $service->getDetail($request); + } catch (\Throwable $th) { + $this->data['code'] = 0; + $this->data['message'] = $th->getMessage(); + } + + return response()->json($this->data); + } + + public function field(Request $request, FieldsService $service){ + try { + $request->mergeIfMissing(['status' => 1]); + $this->data['data'] = $service->getDataList($request); + } catch (\Throwable $th) { + $this->data['code'] = 0; + $this->data['message'] = $th->getMessage(); + } + + return response()->json($this->data); + } + + public function level(Request $request, LevelService $service){ + try { + $this->data['data'] = $service->getDataList($request); + } catch (\Throwable $th) { + $this->data['code'] = 0; + $this->data['message'] = $th->getMessage(); + } + + return response()->json($this->data); + } + + public function save(Request $request, ExtendService $service){ + try { + $this->data['data'] = $service->update($request); + } catch (\Throwable $th) { + $this->data['code'] = 0; + $this->data['message'] = $th->getMessage(); + } + + return response()->json($this->data); + } +} \ No newline at end of file diff --git a/modules/Member/app/Controllers/Api/Index.php b/modules/Member/app/Controllers/Api/Index.php new file mode 100644 index 0000000..56e9d30 --- /dev/null +++ b/modules/Member/app/Controllers/Api/Index.php @@ -0,0 +1,100 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Member\Controllers\Api; + +use Illuminate\Http\Request; +use App\Http\Controllers\BaseController; +use Modules\Member\Services\MemberService; + +class Index extends BaseController { + + /** + * @title 用户列表 + * + * @param Request $request + * @param MemberService $service + * @return void + */ + public function index(Request $request, MemberService $service){ + try { + $this->data['data'] = $service->getDataList($request); + } catch (\Throwable $th) { + $this->data['code'] = 0; + $this->data['message'] = $th->getMessage(); + } + + return response()->json($this->data); + } + /** + * @title 手机号绑定 + * + * @param MemberService $service + * @return void + */ + public function mobile(MemberService $service){ + try { + $this->data['data'] = $service->bindMobile($this->request->param()); + } catch (\think\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + return $this->data; + } + + /** + * @title 微信绑定 + * + * @param MemberService $service + * @return void + */ + public function upwechat(MemberService $service){ + try { + $this->data['data'] = $service->updateWechatInfo($this->request->param()); + } catch (\think\Exception $e) { + $this->data['code'] = 0; + $this->data['message'] = $e->getMessage(); + } + return $this->data; + } + + public function edit(Request $request, MemberService $service){ + try { + $request->mergeIfMissing(['uid' => auth('api')->user()['uid']]); + $this->data['data'] = $service->update($request); + } catch (\Throwable $th) { + $this->data['code'] = 0; + $this->data['message'] = $th->getMessage(); + } + + return $this->data; + } + + public function editpasswd(Request $request, MemberService $service){ + try { + $request->mergeIfMissing(['uid' => auth('api')->user()['uid']]); + $this->data['data'] = $service->updatePasswd($request); + } catch (\Throwable $th) { + $this->data['code'] = 0; + $this->data['message'] = $th->getMessage(); + } + + return $this->data; + } + + public function card(Request $request, MemberService $service){ + try { + $this->data['data'] = $service->getDetail($request); + } catch (\Throwable $th) { + $this->data['code'] = 0; + $this->data['message'] = $th->getMessage(); + } + + return $this->data; + } +} diff --git a/modules/Member/app/Controllers/Api/Login.php b/modules/Member/app/Controllers/Api/Login.php new file mode 100644 index 0000000..b4ca63f --- /dev/null +++ b/modules/Member/app/Controllers/Api/Login.php @@ -0,0 +1,87 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Member\Controllers\Api; + +use Illuminate\Http\Request; +use App\Http\Controllers\BaseController; +use Modules\Member\Services\AuthService; + +class Login extends BaseController { + + /** + * @title 登录 + * + * @param Request $request + * @param AuthService $service + * @return void + */ + public function index(Request $request, AuthService $service){ + try { + $this->data['data'] = $service->userLogin($request); + } catch (\Throwable $th) { + $this->data['code'] = 0; + $this->data['message'] = $th->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 退出 + * + * @param Request $request + * @param AuthService $service + * @return void + */ + public function logout(Request $request, AuthService $service){ + try { + $this->data['data'] = $service->userLogout($request); + } catch (\Throwable $th) { + $this->data['code'] = 0; + $this->data['message'] = $th->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 注册 + * + * @param Request $request + * @param AuthService $service + * @return void + */ + public function register(Request $request, AuthService $service){ + try { + $this->data['data'] = $service->userRegister($request); + } catch (\Throwable $th) { + $this->data['code'] = 0; + $this->data['message'] = $th->getMessage(); + } + + return response()->json($this->data); + } + + /** + * @title 获取当前登录用户信息 + * + * @return void + */ + public function user(){ + $type = request('type', 'wechat'); + $user = auth('api')->user(); + + $user->level = $user->level()->first(); + $user->social = $user->social()->where('type', $type)->first(); + $user->store = $user->store()->first(); + $user->pm = $user->pm()->where('status', 1)->first(); + $this->data['data'] = $user; + return response()->json($this->data); + } +} diff --git a/modules/Member/app/Controllers/Api/Social.php b/modules/Member/app/Controllers/Api/Social.php new file mode 100644 index 0000000..ff5aeb1 --- /dev/null +++ b/modules/Member/app/Controllers/Api/Social.php @@ -0,0 +1,60 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Member\Controllers\Api; + +use Illuminate\Http\Request; +use App\Services\Social\Wechat\OauthService; +use Modules\Member\Services\AuthService; +use App\Http\Controllers\BaseController; + +class Social extends BaseController{ + + public function wechat(Request $request, OauthService $service){ + if($request->filled('code')){ + if($request->filled('url')){ + return redirect($request->input('url') . '?code=' . $request->input('code', '')); + } + }else{ + $res = $service->oauth($request); + return redirect($res); + } + } + + public function login(Request $request, OauthService $service){ + $type = $request->input('type', 'wechat'); + if($request->filled('code')){ + switch ($type) { + case 'wechat': + try { + $wechatInfo = $service->wechatLogin($request->input('code', '')); + $request->merge([ + 'username' => $wechatInfo['openid'], + 'nickname' => $wechatInfo['nickname'], + 'password' => uniqid() + ]); + $member = app(AuthService::class)->userRegister($request, $wechatInfo, 'wechat'); + $token = auth('api')->login($member); + $this->data['data'] = [ + 'access_token' => $token, + 'token_type' => 'bearer', + 'expires_in' => auth('api')->factory()->getTTL() * 60 + ]; + } catch (\Throwable $th) { + $this->data['message'] = $th->getMessage(); + $this->data['code'] = 0; + } + break; + } + }else{ + $this->data['message'] = "非法操作!"; + $this->data['code'] = 0; + } + return response()->json($this->data); + } +} diff --git a/modules/Member/app/Events/LoginBefore.php b/modules/Member/app/Events/LoginBefore.php new file mode 100644 index 0000000..6c6e0f7 --- /dev/null +++ b/modules/Member/app/Events/LoginBefore.php @@ -0,0 +1,33 @@ + + */ + public function broadcastOn(): array { + return [ + new PrivateChannel('channel-name'), + ]; + } +} diff --git a/modules/Member/app/Events/LoginEvent.php b/modules/Member/app/Events/LoginEvent.php new file mode 100644 index 0000000..85ef89b --- /dev/null +++ b/modules/Member/app/Events/LoginEvent.php @@ -0,0 +1,34 @@ + + */ + public function broadcastOn(): array { + return [ + new PrivateChannel('channel-name'), + ]; + } +} diff --git a/modules/Member/app/Events/MemberUpdate.php b/modules/Member/app/Events/MemberUpdate.php new file mode 100644 index 0000000..7521e4a --- /dev/null +++ b/modules/Member/app/Events/MemberUpdate.php @@ -0,0 +1,40 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Member\Events; + +use Illuminate\Broadcasting\Channel; +use Illuminate\Broadcasting\InteractsWithSockets; +use Illuminate\Broadcasting\PresenceChannel; +use Illuminate\Broadcasting\PrivateChannel; +use Illuminate\Contracts\Broadcasting\ShouldBroadcast; +use Illuminate\Foundation\Events\Dispatchable; +use Illuminate\Queue\SerializesModels; +use Modules\Member\Models\Member; + +class MemberUpdate { + use Dispatchable, InteractsWithSockets, SerializesModels; + + /** + * Create a new event instance. + */ + public function __construct(public Member $member){ + // + } + + /** + * Get the channels the event should broadcast on. + * + * @return array + */ + public function broadcastOn(): array{ + return [ + new PrivateChannel('channel-name'), + ]; + } +} diff --git a/modules/Member/app/Events/Registered.php b/modules/Member/app/Events/Registered.php new file mode 100644 index 0000000..074cdb9 --- /dev/null +++ b/modules/Member/app/Events/Registered.php @@ -0,0 +1,40 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Member\Events; + +use Illuminate\Broadcasting\Channel; +use Illuminate\Broadcasting\InteractsWithSockets; +use Illuminate\Broadcasting\PresenceChannel; +use Illuminate\Broadcasting\PrivateChannel; +use Illuminate\Contracts\Broadcasting\ShouldBroadcast; +use Illuminate\Foundation\Events\Dispatchable; +use Illuminate\Queue\SerializesModels; +use Modules\Member\Models\Member; + +class Registered { + use Dispatchable, InteractsWithSockets, SerializesModels; + + /** + * Create a new event instance. + */ + public function __construct(public Member $member, public $openid = '', public $type = ''){ + // + } + + /** + * Get the channels the event should broadcast on. + * + * @return array + */ + public function broadcastOn(): array{ + return [ + new PrivateChannel('channel-name'), + ]; + } +} diff --git a/modules/Member/app/Jobs/MemberExport.php b/modules/Member/app/Jobs/MemberExport.php new file mode 100644 index 0000000..a64fa83 --- /dev/null +++ b/modules/Member/app/Jobs/MemberExport.php @@ -0,0 +1,77 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Member\Jobs; + +use Illuminate\Bus\Queueable; +use Illuminate\Queue\SerializesModels; +use Illuminate\Queue\InteractsWithQueue; +use Illuminate\Contracts\Queue\ShouldQueue; +use Illuminate\Foundation\Bus\Dispatchable; +use Modules\Member\Models\Member; +use Modules\Member\Models\Fields; +use Modules\System\Models\Tasks; +use Illuminate\Support\Facades\Storage; +use Dcat\EasyExcel\Excel; +use Dcat\EasyExcel\Contracts\Sheet as SheetInterface; +use Dcat\EasyExcel\Support\SheetCollection; + +class MemberExport implements ShouldQueue { + use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; + + protected $params; + protected $tasks; + + /** + * Create a new job instance. + */ + public function __construct($params, Tasks $tasks) { + $this->params = $params; + $this->tasks = $tasks; + } + + /** + * Execute the job. + */ + public function handle(): void { + $file = $this->params['fileName'] ? 'export/' . $this->params['fileName'] : 'member_' . date('YmdHis') . '.xlsx'; + + //扩展字段 + $fields = Fields::all(); + Excel::export()->chunk(function(int $page) use($fields){ + $chunkSize = 1000; + $query = Member::with(['extend', 'level'])->forPage($page, $chunkSize); + logger($query->toSql()); + if ($query->count() == 0) { + $this->tasks->status = 1; + $this->tasks->save(); + return; + } + $data = $query->get(); + $exportData = []; + foreach ($data as $key => $value) { + $item = [ + '用户名' => $value->username, + '昵称' => $value->nickname, + '手机号' => $value->mobile, + '邮箱' => $value->email, + ]; + foreach ($fields as $field) { + $item[$field->title] = $value->extend ? $value->extend->{$field->name} : ''; + } + $item['等级'] = $value->level ? $value->level->title : '未设置等级'; + $item['等级到期时间'] = $value->level_expire_time; + $item['登录次数'] = $value->login_count; + $item['注册时间'] = $value->created_at; + + $exportData[] = $item; + } + return $exportData; + })->disk(Storage::disk('public'))->store($file); + } +} \ No newline at end of file diff --git a/modules/Member/app/Jobs/MemberImport.php b/modules/Member/app/Jobs/MemberImport.php new file mode 100644 index 0000000..72a529f --- /dev/null +++ b/modules/Member/app/Jobs/MemberImport.php @@ -0,0 +1,79 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Member\Jobs; + +use Illuminate\Bus\Queueable; +use Illuminate\Queue\SerializesModels; +use Illuminate\Queue\InteractsWithQueue; +use Illuminate\Contracts\Queue\ShouldQueue; +use Illuminate\Foundation\Bus\Dispatchable; +use Modules\Member\Models\Member; +use Modules\Member\Models\Fields; +use Modules\Member\Models\MemberExtend; +use Modules\System\Models\Tasks; +use Illuminate\Support\Facades\Storage; +use Dcat\EasyExcel\Excel; +use Dcat\EasyExcel\Contracts\Sheet as SheetInterface; +use Dcat\EasyExcel\Support\SheetCollection; + +class MemberImport implements ShouldQueue { + use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; + + protected $file; + protected $tasks; + + /** + * Create a new job instance. + */ + public function __construct($file, Tasks $tasks) { + $this->file = Storage::disk('public')->path($file); + $this->tasks = $tasks; + } + + /** + * Execute the job. + */ + public function handle(): void { + //扩展字段 + $fields = Fields::all(); + Excel::import($this->file)->each(function (SheetInterface $sheet) use($fields) { + $chunkSize = 100; + $sheet->chunk($chunkSize, function (SheetCollection $collection) { + $collection->each(function ($row) { + if (empty($row)){ + $this->tasks->status = 1; + $this->tasks->save(); + return true; + } + + $member = Member::updateOrCreate([ + 'username' => $row['用户名'], + 'mobile' => $row['手机号'], + 'email' => $row['邮箱'] + ], [ + 'nickname' => $row['昵称'], + 'password' => $row['手机号'], + ]); + + if ($member->id) { + if($member->extend()->exists()){ + $extend = $member->extend()->first(); + }else{ + $extend = new MemberExtend(); + $extend->member_id = $member->id; + } + foreach ($fields as $field) { + $extend->{$field->name} = $row[$field->title]; + } + } + }); + }); + }); + } +} \ No newline at end of file diff --git a/modules/Member/app/Listeners/UpMemberPaswd.php b/modules/Member/app/Listeners/UpMemberPaswd.php new file mode 100644 index 0000000..b209551 --- /dev/null +++ b/modules/Member/app/Listeners/UpMemberPaswd.php @@ -0,0 +1,36 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Member\Listeners; + +use Modules\Member\Events\LoginBefore; +use Modules\Member\Models\Member; + +class UpMemberPaswd { + + /** + * @title 会员登录后更新用户信息 + * + * @param LoginEvent $event + * @return void + */ + public function handle(LoginBefore $event) { + $username = $event->username; + $password = $event->password; + + $member = Member::where('username', '=', $username)->whereNotNull('old_password')->first(); + if($member){ + if(md5($password . $member->salt) === $member->old_password){ + $member->password = $password; + $member->old_password = ''; + $member->salt = ''; + $member->save(); + } + } + } +} diff --git a/modules/Member/app/Listeners/UpdateMember.php b/modules/Member/app/Listeners/UpdateMember.php new file mode 100644 index 0000000..809f962 --- /dev/null +++ b/modules/Member/app/Listeners/UpdateMember.php @@ -0,0 +1,27 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Member\Listeners; + +use Modules\Member\Events\Registered; +use Modules\Member\Models\Member; + +class UpdateMember { + + /** + * @title 会员登录后更新用户信息 + * + * @param LoginEvent $event + * @return void + */ + public function handle(Registered $event) { + $member = $event->member; + $openid = $event->openid; + $type = $event->type; + } +} diff --git a/modules/Member/app/Models/Fields.php b/modules/Member/app/Models/Fields.php new file mode 100644 index 0000000..7e66353 --- /dev/null +++ b/modules/Member/app/Models/Fields.php @@ -0,0 +1,21 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Member\Models; + +use App\Models\BaseModel; +use Illuminate\Database\Eloquent\Attributes\ObservedBy; +use Modules\Member\Observers\FieldObserver; + +#[ObservedBy([FieldObserver::class])] +class Fields extends BaseModel { + + protected $table = 'member_extends_fields'; + protected $fillable = []; + // protected $hidden = ['deleted_at']; +} diff --git a/modules/Member/app/Models/Member.php b/modules/Member/app/Models/Member.php new file mode 100644 index 0000000..1f7e013 --- /dev/null +++ b/modules/Member/app/Models/Member.php @@ -0,0 +1,97 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Member\Models; + +use Tymon\JWTAuth\Contracts\JWTSubject; +use Illuminate\Notifications\Notifiable; +use Illuminate\Notifications\Notification; +use Illuminate\Foundation\Auth\User as Authenticatable; +use Illuminate\Database\Eloquent\Casts\Attribute; +use Illuminate\Support\Facades\Hash; + +class Member extends Authenticatable implements JWTSubject { + use Notifiable; + + protected $table = 'member'; + protected $primaryKey = 'uid'; + protected $fillable = ['username', 'nickname', 'email', 'mobile', 'password', 'status']; + protected $hidden = ['password', 'deleted_at']; + protected $dateFormat = 'Y-m-d H:i:s'; + protected $with = ['invite:uid,nickname,username,level_id', 'extend', 'level', 'store', 'social', 'pm']; + + /** + * Get the identifier that will be stored in the subject claim of the JWT. + * + * @return mixed + */ + public function getJWTIdentifier() { + return $this->getKey(); + } + + /** + * Return a key value array, containing any custom claims to be added to the JWT. + * + * @return array + */ + public function getJWTCustomClaims() { + return []; + } + + protected function casts(): array { + return [ + 'created_at' => 'datetime:Y-m-d H:i:s', + 'updated_at' => 'datetime:Y-m-d H:i:s', + 'deleted_at' => 'datetime:Y-m-d H:i:s', + ]; + } + + public function password() : Attribute { + return new Attribute( + set: fn ($value) => Hash::make($value), + ); + } + + public function avatar(): Attribute { + return new Attribute( + get: fn ($value) => $value ?? request()->root() . '/storage/avatar/default_avatar.jpg', + ); + } + + public function routeNotificationForMail(Notification $notification): array|string{ + return [$this->email => $this->nickname]; + } + + public function invite(){ + return $this->hasOne(Member::class, 'uid', 'invite_uid'); + } + + public function extend(){ + return $this->hasOne(MemberExtends::class, 'member_id', 'uid'); + } + + public function social(){ + return $this->hasMany(\Modules\Wechat\Models\MemberSocial::class, 'member_id', 'uid'); + } + + public function level(){ + return $this->hasOne(MemberLevel::class, 'id', 'level_id'); + } + + public function address(){ + return $this->hasMany(MemberAddress::class, 'member_id', 'id'); + } + + public function pm(){ + return $this->hasOne(Pm::class, 'member_id', 'pm_uid'); + } + + public function store(){ + return $this->hasOne(Store::class, 'member_id', 'store_uid'); + } +} diff --git a/modules/Member/app/Models/MemberAccount.php b/modules/Member/app/Models/MemberAccount.php new file mode 100644 index 0000000..a6f77c9 --- /dev/null +++ b/modules/Member/app/Models/MemberAccount.php @@ -0,0 +1,18 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Member\Models; + +use App\Models\BaseModel; + +class MemberAccount extends BaseModel { + + protected $table = 'member_account'; + protected $fillable = []; + // protected $hidden = ['deleted_at']; +} diff --git a/modules/Member/app/Models/MemberAddress.php b/modules/Member/app/Models/MemberAddress.php new file mode 100644 index 0000000..580e3ef --- /dev/null +++ b/modules/Member/app/Models/MemberAddress.php @@ -0,0 +1,18 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Member\Models; + +use App\Models\BaseModel; + +class MemberAddress extends BaseModel { + + protected $table = 'member_address'; + protected $fillable = []; + // protected $hidden = ['deleted_at']; +} diff --git a/modules/Member/app/Models/MemberBank.php b/modules/Member/app/Models/MemberBank.php new file mode 100644 index 0000000..ac59fa1 --- /dev/null +++ b/modules/Member/app/Models/MemberBank.php @@ -0,0 +1,18 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Member\Models; + +use App\Models\BaseModel; + +class MemberBank extends BaseModel { + + protected $table = 'member_bank'; + protected $fillable = []; + // protected $hidden = ['deleted_at']; +} diff --git a/modules/Member/app/Models/MemberCoupon.php b/modules/Member/app/Models/MemberCoupon.php new file mode 100644 index 0000000..aeabd9e --- /dev/null +++ b/modules/Member/app/Models/MemberCoupon.php @@ -0,0 +1,18 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Member\Models; + +use App\Models\BaseModel; + +class MemberCoupon extends BaseModel { + + protected $table = 'member_coupon'; + protected $fillable = []; + // protected $hidden = ['deleted_at']; +} diff --git a/modules/Member/app/Models/MemberExtends.php b/modules/Member/app/Models/MemberExtends.php new file mode 100644 index 0000000..e7c7548 --- /dev/null +++ b/modules/Member/app/Models/MemberExtends.php @@ -0,0 +1,22 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Member\Models; + +use App\Models\BaseModel; + +class MemberExtends extends BaseModel { + + protected $table = 'member_extends'; + protected $fillable = []; + // protected $hidden = ['deleted_at']; + + public function member(){ + return $this->belongsTo(Member::class, 'member_id', 'uid'); + } +} diff --git a/modules/Member/app/Models/MemberLevel.php b/modules/Member/app/Models/MemberLevel.php new file mode 100644 index 0000000..bd6a928 --- /dev/null +++ b/modules/Member/app/Models/MemberLevel.php @@ -0,0 +1,22 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Member\Models; + +use App\Models\BaseModel; + +class MemberLevel extends BaseModel { + + protected $table = 'member_level'; + protected $fillable = ['title', 'name', 'icon', 'sort', 'status', 'remark', 'created_at', 'updated_at']; + // protected $hidden = ['deleted_at']; + + public function members(){ + return $this->belongsToMany(Member::class, 'member_has_level', 'level_id', 'member_id'); + } +} diff --git a/modules/Member/app/Models/MemberScore.php b/modules/Member/app/Models/MemberScore.php new file mode 100644 index 0000000..4a664e2 --- /dev/null +++ b/modules/Member/app/Models/MemberScore.php @@ -0,0 +1,18 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Member\Models; + +use App\Models\BaseModel; + +class MemberScore extends BaseModel { + + protected $table = 'member_score'; + protected $fillable = []; + // protected $hidden = ['deleted_at']; +} diff --git a/modules/Member/app/Models/MemberStore.php b/modules/Member/app/Models/MemberStore.php new file mode 100644 index 0000000..91825a6 --- /dev/null +++ b/modules/Member/app/Models/MemberStore.php @@ -0,0 +1,18 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Member\Models; + +use App\Models\BaseModel; + +class MemberStore extends BaseModel { + + protected $table = 'member_store'; + protected $fillable = []; + // protected $hidden = ['deleted_at']; +} diff --git a/modules/Member/app/Models/MemberWallet.php b/modules/Member/app/Models/MemberWallet.php new file mode 100644 index 0000000..ee920b1 --- /dev/null +++ b/modules/Member/app/Models/MemberWallet.php @@ -0,0 +1,18 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Member\Models; + +use App\Models\BaseModel; + +class MemberWallet extends BaseModel { + + protected $table = 'member_wallet'; + protected $fillable = []; + // protected $hidden = ['deleted_at']; +} diff --git a/modules/Member/app/Models/Pm.php b/modules/Member/app/Models/Pm.php new file mode 100644 index 0000000..7c80e06 --- /dev/null +++ b/modules/Member/app/Models/Pm.php @@ -0,0 +1,22 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Member\Models; + +use App\Models\BaseModel; + +class Pm extends BaseModel { + + protected $table = 'member_pm'; + protected $fillable = []; + // protected $hidden = ['deleted_at']; + + public function members(){ + return $this->belongsTo(Member::class, 'member_id', 'uid'); + } +} diff --git a/modules/Member/app/Models/Store.php b/modules/Member/app/Models/Store.php new file mode 100644 index 0000000..530440e --- /dev/null +++ b/modules/Member/app/Models/Store.php @@ -0,0 +1,22 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Member\Models; + +use App\Models\BaseModel; + +class Store extends BaseModel { + + protected $table = 'member_store'; + protected $fillable = []; + // protected $hidden = ['deleted_at']; + + public function members(){ + return $this->belongsTo(Member::class, 'member_id', 'uid'); + } +} diff --git a/modules/Member/app/Notifications/Member.php b/modules/Member/app/Notifications/Member.php new file mode 100644 index 0000000..9889136 --- /dev/null +++ b/modules/Member/app/Notifications/Member.php @@ -0,0 +1,57 @@ +email) { + $via[] = 'mail'; + } + if ($notifiable->mobile) { + $via[] = 'sms'; + } + return $via; + } + + /** + * Get the mail representation of the notification. + */ + public function toMail($notifiable): MailMessage + { + return (new MailMessage) + ->subject('通知主题') + ->greeting('你好!') + ->line('The introduction to the notification.') + ->action('Notification Action', 'https://laravel.com') + ->line('Thank you for using our application!'); + } + + /** + * Get the array representation of the notification. + */ + public function toArray($notifiable): array + { + return []; + } +} diff --git a/modules/Member/app/Observers/FieldObserver.php b/modules/Member/app/Observers/FieldObserver.php new file mode 100644 index 0000000..c4e4693 --- /dev/null +++ b/modules/Member/app/Observers/FieldObserver.php @@ -0,0 +1,107 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Member\Observers; + +use Modules\Member\Models\Fields; +use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Schema; +use Illuminate\Support\Facades\DB; +use Illuminate\Support\Str; + +class FieldObserver { + + public $type = [ + 'string' => 'string', + 'audio' => 'string', + 'video' => 'string', + 'editor' => 'longText', + 'text' => 'text', + 'image' => 'string', + 'images' => 'text', + 'file' => 'string', + 'files' => 'text', + 'number' => 'integer', + 'bool' => 'boolean', + 'date' => 'date', + 'datetime' => 'dateTime', + 'select' => 'string', + 'radio' => 'string', + 'checkbox' => 'text' + ]; + /** + * 处理 "created" 事件。 + */ + public function created(Fields $fields): void { + $table = 'member_extends'; + if(Schema::hasTable($table)){ + Schema::table($table, function (Blueprint $table) use ($fields) { + $type = $this->type[$fields->type] ? $this->type[$fields->type] : 'string'; + if($fields->length){ + $field = $table->{$type}($fields->name, length: $fields->length); + }else{ + $field = $table->{$type}($fields->name); + } + if($fields->default_value){ + $field = $field->default($fields->default_value); + } + if($fields->type == 'required'){ + $field = $field->nullable(); + } + $field = $field->comment($fields->title)->after($fields->after); + }); + } + } + + /** + * 处理 "updated" 事件。 + */ + public function updated(Fields $fields): void { + $table = 'member_extends'; + if (!Schema::hasTable($table) && !Schema::hasColumn($table, $fields->name)) { + return; + } + if($fields->isDirty('name') && Schema::hasColumn($table, $fields->getOriginal('name'))){ + Schema::table($table, function (Blueprint $table) use ($fields) { + $table->renameColumn($fields->getOriginal('name'), $fields->name); + }); + } + + if($fields->isDirty()){ + Schema::table($table, function (Blueprint $table) use ($fields) { + $type = $this->type[$fields->type] ? $this->type[$fields->type] : 'string'; + if($fields->length){ + $field = $table->{$type}($fields->name, length: $fields->length); + }else{ + $field = $table->{$type}($fields->name); + } + if($fields->default_value){ + $field = $table->default($fields->default_value); + }else{ + if($fields->type == 'required'){ + $field = $field->nullable(false); + }else{ + $field = $field->nullable(); + } + } + $field->after($fields->after)->comment($fields->title)->change(); + }); + } + } + + public function deleted(Fields $fields): void { + if($fields->status == 1){ + $table = 'member_extends'; + if (Schema::hasTable($table) && Schema::hasColumn($table, $fields->name)) { + Schema::table($table, function (Blueprint $table) use ($fields) { + $table->dropColumn($fields->name); + }); + } + } + } +} diff --git a/modules/Member/app/Providers/.gitkeep b/modules/Member/app/Providers/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/modules/Member/app/Providers/EventServiceProvider.php b/modules/Member/app/Providers/EventServiceProvider.php new file mode 100644 index 0000000..be5e0f0 --- /dev/null +++ b/modules/Member/app/Providers/EventServiceProvider.php @@ -0,0 +1,42 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Member\Providers; + +use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; + +class EventServiceProvider extends ServiceProvider +{ + /** + * The event handler mappings for the application. + * + * @var array> + */ + protected $listen = [ + \Modules\Member\Events\LoginBefore::class => [ + \Modules\Member\Listeners\UpMemberPaswd::class, + ], + ]; + + /** + * Indicates if events should be discovered. + * + * @var bool + */ + protected static $shouldDiscoverEvents = true; + + /** + * Configure the proper event listeners for email verification. + * + * @return void + */ + protected function configureEmailVerification(): void + { + + } +} diff --git a/modules/Member/app/Providers/MemberServiceProvider.php b/modules/Member/app/Providers/MemberServiceProvider.php new file mode 100644 index 0000000..7dfc5ee --- /dev/null +++ b/modules/Member/app/Providers/MemberServiceProvider.php @@ -0,0 +1,120 @@ +registerCommands(); + $this->registerCommandSchedules(); + $this->registerTranslations(); + $this->registerConfig(); + $this->registerViews(); + $this->loadMigrationsFrom(module_path($this->moduleName, 'database/migrations')); + } + + /** + * Register the service provider. + */ + public function register(): void + { + $this->app->register(EventServiceProvider::class); + $this->app->register(RouteServiceProvider::class); + } + + /** + * Register commands in the format of Command::class + */ + protected function registerCommands(): void + { + // $this->commands([]); + } + + /** + * Register command Schedules. + */ + protected function registerCommandSchedules(): void + { + // $this->app->booted(function () { + // $schedule = $this->app->make(Schedule::class); + // $schedule->command('inspire')->hourly(); + // }); + } + + /** + * Register translations. + */ + public function registerTranslations(): void + { + $langPath = resource_path('lang/modules/'.$this->moduleNameLower); + + if (is_dir($langPath)) { + $this->loadTranslationsFrom($langPath, $this->moduleNameLower); + $this->loadJsonTranslationsFrom($langPath); + } else { + $this->loadTranslationsFrom(module_path($this->moduleName, 'lang'), $this->moduleNameLower); + $this->loadJsonTranslationsFrom(module_path($this->moduleName, 'lang')); + } + } + + /** + * Register config. + */ + protected function registerConfig(): void + { + $this->publishes([module_path($this->moduleName, 'config/config.php') => config_path($this->moduleNameLower.'.php')], 'config'); + $this->mergeConfigFrom(module_path($this->moduleName, 'config/config.php'), $this->moduleNameLower); + } + + /** + * Register views. + */ + public function registerViews(): void + { + $viewPath = resource_path('views/modules/'.$this->moduleNameLower); + $sourcePath = module_path($this->moduleName, 'resources/views'); + + $this->publishes([$sourcePath => $viewPath], ['views', $this->moduleNameLower.'-module-views']); + + $this->loadViewsFrom(array_merge($this->getPublishableViewPaths(), [$sourcePath]), $this->moduleNameLower); + + $componentNamespace = str_replace('/', '\\', config('modules.namespace').'\\'.$this->moduleName.'\\'.ltrim(config('modules.paths.generator.component-class.path'), config('modules.paths.app_folder', ''))); + Blade::componentNamespace($componentNamespace, $this->moduleNameLower); + } + + /** + * Get the services provided by the provider. + * + * @return array + */ + public function provides(): array + { + return []; + } + + /** + * @return array + */ + private function getPublishableViewPaths(): array + { + $paths = []; + foreach (config('view.paths') as $path) { + if (is_dir($path.'/modules/'.$this->moduleNameLower)) { + $paths[] = $path.'/modules/'.$this->moduleNameLower; + } + } + + return $paths; + } +} diff --git a/modules/Member/app/Providers/RouteServiceProvider.php b/modules/Member/app/Providers/RouteServiceProvider.php new file mode 100644 index 0000000..2557baa --- /dev/null +++ b/modules/Member/app/Providers/RouteServiceProvider.php @@ -0,0 +1,67 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Member\Providers; + +use Illuminate\Support\Facades\Route; +use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider; + +class RouteServiceProvider extends ServiceProvider +{ + /** + * Called before routes are registered. + * + * Register any model bindings or pattern based filters. + */ + public function boot(): void + { + parent::boot(); + } + + /** + * Define the routes for the application. + */ + public function map(): void + { + $this->mapApiRoutes(); + + $this->mapWebRoutes(); + + $this->mapAdminRoutes(); + } + + /** + * Define the "web" routes for the application. + * + * These routes all receive session state, CSRF protection, etc. + */ + protected function mapWebRoutes(): void + { + Route::middleware('web')->group(module_path('Member', '/routes/web.php')); + } + + /** + * Define the "api" routes for the application. + * + * These routes are typically stateless. + */ + protected function mapApiRoutes(): void + { + Route::middleware('api')->prefix('api')->name('api.')->group(module_path('Member', '/routes/api.php')); + } + + /** + * Define the "api" routes for the application. + * + * These routes are typically stateless. + */ + protected function mapAdminRoutes(): void + { + Route::middleware('api')->prefix('admin')->name('admin.')->group(module_path('Member', '/routes/admin.php')); + } +} diff --git a/modules/Member/app/Services/AuthService.php b/modules/Member/app/Services/AuthService.php new file mode 100644 index 0000000..56ec0be --- /dev/null +++ b/modules/Member/app/Services/AuthService.php @@ -0,0 +1,134 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Member\Services; + +use Modules\Member\Models\Member; +use Modules\Member\Events\LoginBefore; +use Modules\Member\Events\LoginEvent; +use Modules\Member\Events\Registered; + +class AuthService { + + /** + * @title 会员登录 + * + * @param [type] $request + * @return void + */ + public function userLogin($request){ + $request->validate([ + 'username' => 'required', + 'password' => 'required', + ]); + + $username = $request->input('username'); + $password = $request->input('password'); + LoginBefore::dispatch($username, $password); + + $token = auth('api')->attempt(['mobile' => $username, 'password' => $password]); + + if (!$token) { + throw new \Exception("登錄失敗!", 1000); + }else{ + LoginEvent::dispatch(auth('api')->user(), $request->input('openid', ''), $request->input('type')); + // 判断是否到期 + $user = auth('api')->user(); + if(isset($user['level_expire_time']) && $user['level_expire_time'] != ''){ + $level_expire_time = strtotime($user['level_expire_time']); + if($level_expire_time < time()){ + throw new \Exception("您的會員已到期,請聯系管理員!", 1004); + } + } + + return [ + 'access_token' => $token, + 'token_type' => 'bearer', + 'expires_in' => auth('api')->factory()->getTTL() * 60 + ]; + } + } + + /** + * @title UID登录 + * @description 提供给第三方登录接口 + * + * @param [type] $uid + * @param array $params 第三方登录参数 + * @param string $type 第三方登录类型 + * @return void + */ + public function userLoginByUid($uid, $params = [], $type = 'wechat'){ + $member = Member::find($uid); + if(!$member){ + throw new \Exception("用户不存在!", 1000); + } + + $token = auth('api')->login($member); + + if (!$token) { + throw new \Exception("登录失败!", 1000); + }else{ + LoginEvent::dispatch(auth('api')->user(), $params, $type); + return [ + 'access_token' => $token, + 'token_type' => 'bearer', + 'expires_in' => auth('api')->factory()->getTTL() * 60 + ]; + } + } + + /** + * @title 会员注册 + * + * @param [type] $request + * @return void + */ + public function userRegister($request){ + $request->validate([ + 'username' => 'required|unique:member,username', + 'password' => 'required', + 'mobile' => 'nullable|unique:member,mobile', + ], [ + 'username.unique' => '用户名已存在!', + 'mobile.unique' => '手机号已存在!', + 'username.required' => '用户名不能为空!', + 'password.required' => '密码不能为空!' + ]); + + $member = Member::where('username', '=', $request->input('username'))->first(); + if(!$member){ + $data = [ + 'username' => $request->input('username'), + 'password' => $request->input('password'), + 'nickname' => $request->input('nickname', ''), + 'avatar' => $request->input('avatar', ''), + 'mobile' => $request->input('mobile', 0), + ]; + + $member = Member::updateOrCreate($data); + } + Registered::dispatch($member, $request->input('openid', ''), $request->input('type', '')); + $token = auth('api')->login($member); + return [ + 'access_token' => $token, + 'token_type' => 'bearer', + 'expires_in' => auth('api')->factory()->getTTL() * 60 + ]; + } + + /** + * @title 会员退出 + * + * @param [type] $request + * @return void + */ + public function userLogout($request){ + auth('api')->logout(); + } +} diff --git a/modules/Member/app/Services/ExtendService.php b/modules/Member/app/Services/ExtendService.php new file mode 100644 index 0000000..87c5e54 --- /dev/null +++ b/modules/Member/app/Services/ExtendService.php @@ -0,0 +1,146 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Member\Services; + +use Modules\Member\Models\Fields; +use Modules\Member\Models\MemberExtends; + +class ExtendService { + + public function getDataList($request){ + $map = []; + + $query = MemberExtends::with('member:uid,username,nickname,avatar,level_id,level_expire_time'); + + if ($request->filled('member_id')){ + $query->where('member_id', $request->input('member_id')); + } + + if ($request->filled('keyword')){ + $map[] = ['name', 'like', '%' . $request->input('keyword') . '%']; + } + if ($request->filled('name')){ + $map[] = ['name', 'like', '%' . $request->input('name') . '%']; + } + + if ($request->filled('status')){ + $map[] = ['status', '=', $request->input('status')]; + } + + $query->where($map); + + $query->orderBy($request->input('order', 'id'), $request->input('sort', 'desc')); + if ($request->filled('page')){ + $data = [ + 'total' => $query->count(), + 'page' => (int) $request->input('page', 1), + 'data' => $query->offset($request->input('offset', 0))->limit($request->input('limit', 30))->get(), + ]; + }else{ + $data = $query->limit($request->input('limit', 10))->get(); + } + + return $data; + } + + public function getDetail($request){ + if ($request->filled('member_id')){ + $member_id = $request->input('member_id'); + } else { + $member_id = auth('api')->user()['uid']; + } + + $extends = MemberExtends::with('member:uid,username,nickname,avatar,level_id,level_expire_time', 'member.level')->where('member_id', $member_id)->first(); + + if (!$extends){ + throw new \Exception('用户数据不存在'); + } + if ($extends->status == 0){ + throw new \Exception('用户数据审核中'); + } + if ($extends->status == 2){ + throw new \Exception('用户数据审核未通过'); + } + + return $extends ?? []; + } + + public function update($request){ + $field = Fields::all(); + + if ($request->filled('member_id')){ + $member_id = $request->input('member_id'); + } else { + $member_id = auth('api')->user()['uid']; + } + + $extends = MemberExtends::where('member_id', $member_id)->first(); + if (!$extends){ + $extends = new MemberExtends(); + $extends->member_id = $member_id; + } + + foreach ($field as $key => $value) { + if ($request->filled($value['name']) && $request->input($value['name']) != ''){ + $extends->{$value['name']} = $request->input($value['name']); + } + } + + $extends->status = 0; + $extends->save(); + return $extends; + } + + public function audit($request){ + $request->validate([ + 'id' => 'required', + 'member_id' => 'required', + 'level_id' => 'required', + 'status' => 'required|integer|in:0,1,2', + 'level_expire_time' => 'required|date', + ], [ + 'id.required' => 'ID不能为空', + 'member_id.required' => '用户ID不能为空', + 'level_id.required' => '等级ID不能为空', + 'status.required' => '审核状态不能为空', + 'status.integer' => '审核状态必须为数字', + 'status.in' => '审核状态值错误', + 'level_expire_time.required' => '等级过期时间不能为空', + 'level_expire_time.date' => '等级过期时间格式错误', + ]); + + $extends = MemberExtends::where('id', $request->input('id'))->first(); + + if (!$extends){ + throw new \Exception('审核数据不存在'); + } + + $extends->status = $request->input('status'); + $extends->card_number = 'NF9' . str_pad($extends->member_id, 6, '0', STR_PAD_LEFT); + + $extends->save(); + + if ($request->input('status') == 1){ + $member = $extends->member()->first(); + $member->level_id = $request->input('level_id'); + $member->level_expire_time = $request->input('level_expire_time'); + if ($extends->name){ + $member->nickname = $extends->name; + } + if ($extends->mobile){ + $member->mobile = $extends->mobile; + } + if ($extends->email){ + $member->email = $extends->email; + } + $member->save(); + } + return $extends; + } +} \ No newline at end of file diff --git a/modules/Member/app/Services/FieldsService.php b/modules/Member/app/Services/FieldsService.php new file mode 100644 index 0000000..fb55d07 --- /dev/null +++ b/modules/Member/app/Services/FieldsService.php @@ -0,0 +1,140 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Member\Services; + +use Modules\Member\Models\Fields; +use Illuminate\Support\Facades\Schema; +use Illuminate\Support\Str; + +class FieldsService { + + /** + * @title 字段列表 + * + * @param [type] $request + * @return void + */ + public function getDataList($request) { + $map = []; + + if ($request->filled('status')) { + $map[] = ['status', '=', $request->input('status')]; + } + + $query = Fields::where($map); + + if($request->filled('page')){ + $data = [ + 'total' => $query->count(), + 'page' => $request->input('page', 1), + 'data' => $query->offset($request->input('offset', 0))->limit($request->input('limit', 10))->get(), + ]; + }else{ + $data = $query->limit($request->input('limit', 10))->get(); + } + + return $data; + } + + /** + * @title 创建字段 + * + * @param [type] $request + * @return void + */ + public function create($request) { + $request->validate([ + 'name' => 'required|unique:cms_model_field', + 'title' => 'required', + 'type' => 'required', + ]); + $field = new Fields(); + + foreach ($field->setFilterFields($request->all()) as $key => $value) { + $field->$key = $value; + } + + $field->save(); + return $field; + } + + /** + * @title 更新字段 + * + * @param [type] $request + * @return void + */ + public function update($request) { + $request->validate([ + 'name' => 'required|unique:cms_model_field,name,' . $request->input('id'), + 'title' => 'required', + 'type' => 'required', + ]); + $field = Fields::find($request->input('id')); + + foreach ($field->setFilterFields($request->all()) as $key => $value){ + $field->$key = $value; + } + + $field->save(); + return $field; + } + + /** + * @title 删除字段 + * + * @param [type] $request + * @return void + */ + public function delete($request) { + if($request->filled('id')){ + try { + $field = Fields::findOrFail($request->input('id')); + } catch (\Throwable $th) { + throw new \Exception("模型不存在!", 1); + } + $field->delete(); + } + if($request->filled('ids')){ + try { + $field = Fields::whereIn('id', $request->input('ids')); + $field->delete(); + } catch (\Throwable $th) { + throw new \Exception($th->getMessage(), 1); + } + } + return $field; + } + + public function getFieldList($request){ + $fields = Schema::getColumnListing('member_extends'); + + $data = []; + foreach ($fields as $value) { + if ($value !== $request->input('name')) + $data[] = ['title' => $value, 'values' => $value]; + } + return $data; + } + + public function getFieldSetting($request){ + $map = []; + + $query = Fields::with('model:id,title,name,icon'); + + if ($request->filled('model')) { + $query->whereHas('model', function($q) use($request) { + $q->where('name', $request->input('model')); + }); + } + + $data = $query->get(); + return $data; + } +} diff --git a/modules/Member/app/Services/LevelService.php b/modules/Member/app/Services/LevelService.php new file mode 100644 index 0000000..90a05c9 --- /dev/null +++ b/modules/Member/app/Services/LevelService.php @@ -0,0 +1,124 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Member\Services; + +use Modules\Member\Models\MemberLevel; + +class LevelService { + + /** + * @title 获取会员列表 + * + * @param [type] $request + * @return void + */ + public function getDataList($request){ + $map = []; + + if ($request->filled('title')) { + $map[] = ['title', 'like', '%' . $request->input('title') . '%']; + } + if ($request->filled('name')) { + $map[] = ['name', 'like', '%' . $request->input('name') . '%']; + } + $query = MemberLevel::where($map)->orderBy('id', 'desc'); + + if ($request->filled('page')) { + $data = [ + 'total' => $query->count(), + 'page' => $request->input('page', 1), + 'data' => $query->offset($request->input('offset', 0))->limit($request->input('limit', 30))->get(), + ]; + }else{ + $data = $query->limit($request->input('limit', 30))->get(); + } + return $data; + } + + /** + * @title 添加会员 + * + * @param Request $request + * @param MemberService $service + * @return void + */ + public function create($request){ + $request->validate([ + 'title' => 'required|max:255', + 'name' => 'required|max:255|alpha_dash:ascii|unique:member_level,name', + ]); + + $data = [ + 'title' => $request->input('title', ''), + 'name' => $request->input('name', ''), + 'icon' => $request->input('icon', ''), + 'sort' => $request->input('sort', 0), + 'status' => $request->input('status', 1), + 'remark' => $request->input('remark', ''), + ]; + + $level = MemberLevel::create($data); + } + + /** + * @title 修改会员 + * + * @param Request $request + * @param MemberService $service + * @return void + */ + public function update($request){ + $request->validate([ + 'title' => 'required|max:255', + 'name' => 'required|max:255|alpha_dash:ascii|unique:member_level,name,' . $request->input('id'), + ]); + + try { + $level = MemberLevel::findOrFail($request->input('id')); + } catch (\Throwable $th) { + throw new \Exception("会员不存在!", 1); + } + + $data = [ + 'title'=> $request->input('title', ''), + 'name' => $request->input('name', ''), + 'icon' => $request->input('icon', ''), + 'sort' => $request->input('sort', 0), + 'status' => $request->input('status', 1), + 'remark' => $request->input('remark', ''), + ]; + $level->update($data); + return $level; + } + + public function delete($request){ + if($request->filled('id')){ + try { + $level = MemberLevel::findOrFail($request->input('id')); + } catch (\Throwable $th) { + throw new \Exception("会员等级不存在!", 1); + } + $level->members()->detach(); + $level->delete(); + } + if($request->filled('ids')){ + try { + $level = MemberLevel::whereIn('id', $request->input('ids')); + foreach ($level->get() as $item) { + $item->members()->detach(); //删除关联 + } + $level->delete(); + } catch (\Throwable $th) { + throw new \Exception($th->getMessage(), 1); + } + } + + return $level; + } +} diff --git a/modules/Member/app/Services/MemberService.php b/modules/Member/app/Services/MemberService.php new file mode 100644 index 0000000..6642ca1 --- /dev/null +++ b/modules/Member/app/Services/MemberService.php @@ -0,0 +1,299 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Member\Services; + +use Modules\Member\Models\Member; +use Modules\Member\Events\MemberUpdate; + +class MemberService { + + /** + * @title 获取会员列表 + * + * @param [type] $request + * @return void + */ + public function getDataList($request){ + $map = []; + + if ($request->filled('username')) { + $map[] = ['username', 'like', '%' . $request->input('username') . '%']; + } + if ($request->filled('mobile')) { + $map[] = ['mobile', 'like', '%' . $request->input('mobile') . '%']; + } + if ($request->filled('email')) { + $map[] = ['email', 'like', '%' . $request->input('email') . '%']; + } + + $query = Member::with(['social', 'level'])->where($map)->orderBy('uid', 'desc'); + + if ($request->filled('title')) { + $query->whereAny(['nickname', 'username'], 'like', '%' . $request->input('title') . '%'); + } + $data = [ + 'total' => $query->count(), + 'page' => $request->input('page', 1), + 'data' => $query->offset($request->input('offset', 0))->limit($request->input('limit', 10))->get(), + ]; + return $data; + } + + public function getDetail($request){ + $request->validate([ + 'id_card' => 'required', + ], [ + 'id_card.required' => '身份证号不能为空!', + ]); + + $query = Member::with(['level', 'extend']); + if ($request->filled('id_card')) { + $query->whereHas('extend', function ($query) use ($request) { + $query->where('id_card', $request->input('id_card')); + }); + } + + if ($request->is('api/*')) { + $query->select(['nickname', 'uid', 'level_id']); + } + + if ($query->exists()) { + return $query->first(); + } else { + throw new \Exception("会员不存在!", 1); + } + } + + /** + * @title 创建用户 + * + * @param [type] $request + * @return void + */ + public function create($request){ + $request->validate([ + 'username' => 'required|unique:member', + 'nickname' => 'required', + 'mobile' => 'required|unique:member', + 'password' => 'required|min:6|max:16' + ], [ + 'username.required' => '用户名不能为空!', + 'username.unique' => '用户名已存在!', + 'nickname.required' => '昵称不能为空!', + 'mobile.required' => '手机号不能为空!', + 'mobile.unique' => '手机号已存在!', + 'password.required' => '密码不能为空!', + 'password.min' => '密码长度不能小于6位!', + 'password.max' => '密码长度不能大于16位!' + ]); + + $member = new Member; + + if ($request->filled('username')) { + $member->username = $request->input('username'); + } + if ($request->filled('nickname')) { + $member->nickname = $request->input('nickname'); + } + if ($request->filled('avatar')) { + $member->avatar = $request->input('avatar'); + } + if ($request->filled('email')) { + $member->email = $request->input('email'); + } + if ($request->filled('gender')) { + $member->gender = $request->input('gender'); + } + if ($request->filled('birthday')) { + $member->birthday = $request->input('birthday'); + } + if ($request->filled('date_type')) { + $member->date_type = $request->input('date_type'); + } + if ($request->filled('mobile')) { + $member->mobile = $request->input('mobile'); + } + if ($request->filled('level_expire_time')) { + $member->level_expire_time = $request->input('level_expire_time'); + } + if ($request->filled('password')) { + $member->password = $request->input('password'); + } + if ($request->filled('status')) { + $member->status = $request->input('status'); + } + $member->invite_uid = $request->input('invite_uid', '1890000001'); + $member->pm_uid = $request->input('pm_uid', '1890000001'); + $member->store_uid = $request->input('store_uid', '1890000001'); + $member->level_id = $request->input('level_id', '1'); + + $member->save(); + + return $member; + } + + /** + * @title 更新用户 + * + * @param [type] $request + * @return void + */ + public function update($request){ + $request->validate([ + 'username' => 'unique:member,username,'.$request->input('uid').',uid', + 'mobile' => 'unique:member,mobile,'.$request->input('uid').',uid', + ],[ + 'username.unique' => '用户名已存在!', + 'mobile.unique' => '手机号已存在!', + ]); + if ($request->filled('uid')) { + $members = Member::where('uid', $request->input('uid'))->first(); + } + if ($request->is('api/*') && auth('api')->user()) { + $members = Member::where('uid', auth('api')->user()['uid'])->first(); + } + + if ($request->filled('username')) { + $members->username = $request->input('username'); + } + if ($request->filled('nickname')) { + $members->nickname = $request->input('nickname'); + } + if ($request->filled('avatar')) { + $members->avatar = $request->input('avatar'); + } + if ($request->filled('email')) { + $members->email = $request->input('email'); + } + if ($request->filled('gender')) { + $members->gender = $request->input('gender'); + } + if ($request->filled('birthday')) { + $members->birthday = $request->input('birthday'); + } + if ($request->filled('date_type')) { + $members->date_type = $request->input('date_type'); + } + if ($request->filled('mobile')) { + $members->mobile = $request->input('mobile'); + } + if ($request->filled('password')) { + $members->password = $request->input('password'); + } + if ($request->filled('level_expire_time')) { + $members->level_expire_time = $request->input('level_expire_time'); + } + + $members->save(); + + MemberUpdate::dispatch($members); + return $members; + } + + public function delete($request){ + if(!$request->filled('uid')){ + throw new \Exception("非法操作!", 1); + } + + if (is_array($request->input('uid'))) { + // 批量删除 + $mebers = Member::whereIn('uid', $request->input('uid'))->get(); + if ($mebers->isEmpty()) { + throw new \Exception("会员不存在!", 1); + } + $member = Member::destroy(request()->input('uid')); + + return $member; + }else{ + // 单个删除 + try { + $mebers = Member::findOrFail($request->input('uid')); + } catch (\Throwable $th) { + throw new \Exception("会员不存在!", 1); + } + $mebers->delete(); + return $mebers; + } + } + + public function updatePasswd($request){ + $request->validate([ + 'password' => 'required|min:6|max:16' + ], [ + 'password.required' => '密码不能为空!', + 'password.min' => '密码长度不能小于6位!', + 'password.max' => '密码长度不能大于16位!' + ]); + if ($request->is('api/*')) { + $request->validate([ + 'old_password' => 'required', + 'password' => 'confirmed' + ], [ + 'old_password.required' => '旧密码不能为空!', + 'password.confirmed' => '两次密码不一致!' + ]); + } + if(!$request->filled('uid')){ + throw new \Exception("非法操作!", 1); + } + + try { + $mebers = Member::findOrFail($request->input('uid')); + } catch (\Throwable $th) { + throw new \Exception("会员不存在!", 1); + } + + if(!$request->filled('password')){ + throw new \Exception("密码不能为空!", 1); + } + + $mebers->password = $request->input('password'); + + $mebers->save(); + return $mebers; + } + + public function import($request){ + $file = $request->file('file'); + + if ($file->isValid()) { + $ext = $file->extension(); + + if (in_array($ext, ['csv', 'xlsx']) === false) { + throw new \Exception("请上传csv文件"); + } + + $path = $file->store('import/'.date('Ymd'), 'public'); + + $tasks = app()->tasks->create([ + 'title' => '会员数据导入', + 'type' => 'import', + 'file' => $path, + 'params' => $request->except('file'), + 'status' => 0, + ]); + if ($tasks->id) { + \Modules\Member\Jobs\MemberImport::dispatch($path, $tasks); + } + } + } + + public function export($request){ + $tasks = app()->tasks->create([ + 'title' => '会员数据导出', + 'type' => 'export', + 'file' => 'export/' . $request->input('fileName', time().'xlsx'), + 'status' => 0, + ]); + + if ($tasks->id) { + \Modules\Member\Jobs\MemberExport::dispatch($request->all(), $tasks); + } + } +} diff --git a/modules/Member/composer.json b/modules/Member/composer.json new file mode 100644 index 0000000..0b00901 --- /dev/null +++ b/modules/Member/composer.json @@ -0,0 +1,30 @@ +{ + "name": "tensent/member", + "description": "", + "authors": [ + { + "name": "molong", + "email": "molong@tensent.cn" + } + ], + "extra": { + "laravel": { + "providers": [], + "aliases": { + + } + } + }, + "autoload": { + "psr-4": { + "Modules\\Member\\": "app/", + "Modules\\Member\\Database\\Factories\\": "database/factories/", + "Modules\\Member\\Database\\Seeders\\": "database/seeders/" + } + }, + "autoload-dev": { + "psr-4": { + "Modules\\Member\\Tests\\": "tests/" + } + } +} diff --git a/modules/Member/config/.gitkeep b/modules/Member/config/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/modules/Member/config/config.php b/modules/Member/config/config.php new file mode 100644 index 0000000..a496c19 --- /dev/null +++ b/modules/Member/config/config.php @@ -0,0 +1,5 @@ + 'Member', +]; diff --git a/modules/Member/database/factories/.gitkeep b/modules/Member/database/factories/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/modules/Member/database/migrations/.gitkeep b/modules/Member/database/migrations/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/modules/Member/database/migrations/2024_05_29_220055_member_base_table.php b/modules/Member/database/migrations/2024_05_29_220055_member_base_table.php new file mode 100644 index 0000000..d15fba5 --- /dev/null +++ b/modules/Member/database/migrations/2024_05_29_220055_member_base_table.php @@ -0,0 +1,72 @@ +id('uid')->unique()->comment('用户ID'); + $table->string('username')->comment('用户名'); + $table->string('password')->comment('密码'); + $table->string('nickname')->comment('昵称'); + $table->string('mobile')->nullable()->comment('手机号'); + $table->string('email')->nullable()->comment('邮箱'); + $table->string('avatar')->nullable()->comment('头像'); + $table->tinyInteger('gender')->default(0)->comment('性别'); + $table->date('birthday')->nullable()->comment('生日'); + $table->unsignedBigInteger('invite_uid')->default(0)->comment('邀请人UID'); + $table->unsignedBigInteger('level_id')->default(0)->comment('会员等级ID'); + $table->string('last_login_ip')->nullable()->comment('最后登录IP'); + $table->timestamp('last_login_time')->nullable()->comment('最后登录时间'); + $table->integer('login_count')->default(0)->comment('登录次数'); + $table->tinyInteger('status')->default(1)->comment('状态 1正常 2禁用'); + $table->timestamp('created_at')->nullable()->comment('创建时间'); + $table->timestamp('updated_at')->nullable()->comment('更新时间'); + + $table->engine = 'InnoDB'; + $table->charset = 'utf8mb4'; + $table->collation = 'utf8mb4_unicode_ci'; + $table->comment('会员表'); + }); + Schema::create('member_level', function (Blueprint $table) { + $table->id()->uniqid()->comment('主键id'); + $table->string('title')->comment('会员等级名称'); + $table->string('name')->comment('会员等级标识'); + $table->string('icon')->nullable()->comment('会员等级图标'); + $table->string('remark')->nullable()->comment('会员等级备注'); + $table->string('sort')->default(0)->comment('排序'); + $table->tinyInteger('status')->default(1)->comment('状态'); + $table->timestamp('created_at')->nullable()->comment('创建时间'); + $table->timestamp('updated_at')->nullable()->comment('更新时间'); + + $table->engine = 'InnoDB'; + $table->charset = 'utf8mb4'; + $table->collation = 'utf8mb4_unicode_ci'; + $table->comment('会员等级表'); + }); + Schema::create('member_has_level', function (Blueprint $table) { + $table->unsignedBigInteger('level_id')->comment('等级id'); + $table->unsignedBigInteger('member_id')->comment('用户id'); + $table->primary(['level_id', 'member_id']); + + $table->engine = 'InnoDB'; + $table->charset = 'utf8mb4'; + $table->collation = 'utf8mb4_unicode_ci'; + $table->comment('会员等级关联表'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void { + Schema::dropIfExists('member'); + Schema::dropIfExists('member_level'); + Schema::dropIfExists('member_has_level'); + } +}; diff --git a/modules/Member/database/migrations/2024_10_05_035325_memeber_extends.php b/modules/Member/database/migrations/2024_10_05_035325_memeber_extends.php new file mode 100644 index 0000000..9ca7177 --- /dev/null +++ b/modules/Member/database/migrations/2024_10_05_035325_memeber_extends.php @@ -0,0 +1,71 @@ +id()->uniqid()->comment('主键id'); + $table->unsignedBigInteger('member_id')->comment('用户id'); + $table->tinyInteger('status')->default(1)->comment('状态'); + $table->timestamp('created_at')->nullable()->comment('创建时间'); + $table->timestamp('updated_at')->nullable()->comment('更新时间'); + + $table->engine = 'InnoDB'; + $table->charset = 'utf8mb4'; + $table->collation = 'utf8mb4_unicode_ci'; + $table->comment('会员扩展表'); + }); + + Schema::create('member_extends_fields', function (Blueprint $table) { + $table->id()->uniqid()->comment('主键id'); + $table->string('title')->comment('字段名称'); + $table->string('name')->comment('字段标识'); + $table->string('type')->comment('字段类型'); + $table->string('length')->nullable()->comment('字段长度'); + $table->string('default_value')->nullable()->comment('默认值'); + $table->string('after')->nullable()->comment('在字段后'); + $table->string('remark')->nullable()->comment('字段备注'); + $table->tinyInteger('required')->default(0)->comment('是否必填'); + $table->string('sort')->default(0)->comment('排序'); + $table->tinyInteger('status')->default(1)->comment('状态 1正常 2禁用'); + $table->timestamp('created_at')->nullable()->comment('创建时间'); + $table->timestamp('updated_at')->nullable()->comment('更新时间'); + + $table->engine = 'InnoDB'; + $table->charset = 'utf8mb4'; + $table->collation = 'utf8mb4_unicode_ci'; + $table->comment('会员扩展字段表'); + }); + + Schema::create('member_extends_log', function (Blueprint $table) { + $table->id()->uniqid()->comment('主键id'); + $table->unsignedBigInteger('member_id')->comment('用户id'); + $table->string('field')->comment('修改的字段'); + $table->string('old_value')->nullable()->comment('旧值'); + $table->string('new_value')->nullable()->comment('新值'); + $table->timestamp('created_at')->nullable()->comment('创建时间'); + $table->timestamp('updated_at')->nullable()->comment('更新时间'); + + $table->engine = 'InnoDB'; + $table->charset = 'utf8mb4'; + $table->collation = 'utf8mb4_unicode_ci'; + $table->comment('会员扩展日志表'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void { + Schema::dropIfExists('member_extends'); + Schema::dropIfExists('member_extends_fields'); + Schema::dropIfExists('member_extends_log'); + } +}; diff --git a/modules/Member/database/migrations/2024_10_05_035325_memeber_table_up.php b/modules/Member/database/migrations/2024_10_05_035325_memeber_table_up.php new file mode 100644 index 0000000..d2bf563 --- /dev/null +++ b/modules/Member/database/migrations/2024_10_05_035325_memeber_table_up.php @@ -0,0 +1,23 @@ +timestamp('level_expire_time')->nullable()->after('level_id')->comment('等级到期时间'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void { + } +}; diff --git a/modules/Member/database/migrations/2025_08_31_210818_member_health.php b/modules/Member/database/migrations/2025_08_31_210818_member_health.php new file mode 100644 index 0000000..7e9405d --- /dev/null +++ b/modules/Member/database/migrations/2025_08_31_210818_member_health.php @@ -0,0 +1,72 @@ +string('invite_code')->nullable()->after('login_count')->comment('推荐码'); + $table->string('old_password')->nullable()->after('password')->comment('旧密码'); + $table->string('salt')->nullable()->after('old_password')->comment('密码盐值'); + $table->unsignedBigInteger('pm_uid')->nullable()->after('invite_code')->comment('健康管理师id'); + $table->unsignedBigInteger('store_id')->nullable()->after('pm_uid')->comment('所属综合服务体'); + }); + + Schema::create('member_pm', function (Blueprint $table) { + $table->id()->uniqid()->comment('主键id'); + $table->unsignedBigInteger('member_id')->comment('用户id'); + $table->tinyInteger('level')->default(1)->comment('等级'); + $table->string('name')->nullable()->comment('名称'); + $table->integer('sex')->default(0)->comment('内容'); + $table->string('mobile')->nullable()->comment('电话'); + $table->string('area')->nullable()->comment('区域'); + $table->tinyInteger('store_id')->default(0)->comment('所属综合服务体'); + $table->tinyInteger('status')->default(1)->comment('状态'); + $table->bigInteger('end_time')->nullable()->comment('到期时间'); + $table->timestamp('created_at')->nullable()->comment('创建时间'); + $table->timestamp('updated_at')->nullable()->comment('更新时间'); + + $table->engine = 'InnoDB'; + $table->charset = 'utf8mb4'; + $table->collation = 'utf8mb4_unicode_ci'; + $table->comment('会员工作室表'); + }); + Schema::create('member_store', function (Blueprint $table) { + $table->id()->uniqid()->comment('主键id'); + $table->unsignedBigInteger('member_id')->comment('用户id'); + $table->string('name')->nullable()->comment('名称'); + $table->string('logo')->nullable()->comment('logo'); + $table->string('mobile')->nullable()->comment('电话'); + $table->string('area')->nullable()->comment('区域'); + $table->string('address')->nullable()->comment('地址'); + $table->string('map')->nullable()->comment('地图坐标'); + $table->string('license')->nullable()->comment('营业执照'); + $table->tinyInteger('status')->default(1)->comment('状态'); + $table->timestamp('created_at')->nullable()->comment('创建时间'); + $table->timestamp('updated_at')->nullable()->comment('更新时间'); + + $table->engine = 'InnoDB'; + $table->charset = 'utf8mb4'; + $table->collation = 'utf8mb4_unicode_ci'; + $table->comment('会员综合服务体表'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void { + Schema::table('member', function (Blueprint $table) { + $table->dropColumn('invite_code'); + $table->dropColumn('old_password'); + $table->dropColumn('salt'); + }); + Schema::dropIfExists('member_pm'); + Schema::dropIfExists('member_store'); + } +}; diff --git a/modules/Member/database/migrations/2025_12_04_200250_member_up_date.php b/modules/Member/database/migrations/2025_12_04_200250_member_up_date.php new file mode 100644 index 0000000..e2ff6ec --- /dev/null +++ b/modules/Member/database/migrations/2025_12_04_200250_member_up_date.php @@ -0,0 +1,26 @@ +string('date_type', 10)->default('lunar')->comment('会员生日类型')->after('birthday'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + // + } +}; diff --git a/modules/Member/database/seeders/.gitkeep b/modules/Member/database/seeders/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/modules/Member/database/seeders/MemberDatabaseSeeder.php b/modules/Member/database/seeders/MemberDatabaseSeeder.php new file mode 100644 index 0000000..daa335a --- /dev/null +++ b/modules/Member/database/seeders/MemberDatabaseSeeder.php @@ -0,0 +1,24 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Member\Database\Seeders; + +use Illuminate\Database\Seeder; + +class MemberDatabaseSeeder extends Seeder +{ + /** + * Run the database seeds. + */ + public function run(): void + { + $this->call([ + MemberSeeder::class + ]); + } +} diff --git a/modules/Member/database/seeders/MemberSeeder.php b/modules/Member/database/seeders/MemberSeeder.php new file mode 100644 index 0000000..d27014b --- /dev/null +++ b/modules/Member/database/seeders/MemberSeeder.php @@ -0,0 +1,33 @@ +addMemberMenu(); + } + + /** + * @title 商品菜单导入 + * + * @return void + */ + public function addMemberMenu(){ + $permissions = [ + ['title' => '会员', 'name' => 'member', 'path' => '/member', 'component' => '', 'type' => 'menu', 'sort' => 1, 'children' => [ + ['title' => '会员列表', 'name' => 'member.lists', 'path' => '/member/lists', 'component' => 'member/lists', 'type' => 'menu'], + ['title' => '会员等级', 'name' => 'member.level', 'path' => '/member/level', 'component' => 'member/level', 'type' => 'menu'], + ['title' => '会员字段', 'name' => 'member.field', 'path' => '/member/field', 'component' => 'member/field', 'type' => 'menu'], + ]], + ]; + return app(MenuService::class)->importMenu($permissions, 0); + } +} diff --git a/modules/Member/module.json b/modules/Member/module.json new file mode 100644 index 0000000..cbd5db8 --- /dev/null +++ b/modules/Member/module.json @@ -0,0 +1,11 @@ +{ + "name": "Member", + "alias": "member", + "description": "", + "keywords": [], + "priority": 0, + "providers": [ + "Modules\\Member\\Providers\\MemberServiceProvider" + ], + "files": [] +} diff --git a/modules/Member/package.json b/modules/Member/package.json new file mode 100644 index 0000000..d6fbfc8 --- /dev/null +++ b/modules/Member/package.json @@ -0,0 +1,15 @@ +{ + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build" + }, + "devDependencies": { + "axios": "^1.1.2", + "laravel-vite-plugin": "^0.7.5", + "sass": "^1.69.5", + "postcss": "^8.3.7", + "vite": "^4.0.0" + } +} diff --git a/modules/Member/resources/assets/.gitkeep b/modules/Member/resources/assets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/modules/Member/resources/assets/js/app.js b/modules/Member/resources/assets/js/app.js new file mode 100644 index 0000000..e69de29 diff --git a/modules/Member/resources/assets/sass/app.scss b/modules/Member/resources/assets/sass/app.scss new file mode 100644 index 0000000..e69de29 diff --git a/modules/Member/resources/views/.gitkeep b/modules/Member/resources/views/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/modules/Member/resources/views/index.blade.php b/modules/Member/resources/views/index.blade.php new file mode 100644 index 0000000..68ad060 --- /dev/null +++ b/modules/Member/resources/views/index.blade.php @@ -0,0 +1,7 @@ +@extends('member::layouts.master') + +@section('content') +

Hello World

+ +

Module: {!! config('member.name') !!}

+@endsection diff --git a/modules/Member/resources/views/layouts/master.blade.php b/modules/Member/resources/views/layouts/master.blade.php new file mode 100644 index 0000000..f597d33 --- /dev/null +++ b/modules/Member/resources/views/layouts/master.blade.php @@ -0,0 +1,29 @@ + + + + + + + + + + Member Module - {{ config('app.name', 'Laravel') }} + + + + + + + + + + {{-- Vite CSS --}} + {{-- {{ module_vite('build-member', 'resources/assets/sass/app.scss') }} --}} + + + + @yield('content') + + {{-- Vite JS --}} + {{-- {{ module_vite('build-member', 'resources/assets/js/app.js') }} --}} + diff --git a/modules/Member/routes/.gitkeep b/modules/Member/routes/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/modules/Member/routes/admin.php b/modules/Member/routes/admin.php new file mode 100644 index 0000000..2b3fdb1 --- /dev/null +++ b/modules/Member/routes/admin.php @@ -0,0 +1,49 @@ + +// +---------------------------------------------------------------------- +use Illuminate\Support\Facades\Route; +use Modules\Member\Controllers\MemberController; + +// 会员管理路由配置 +Route::name('member.')->prefix('member')->middleware(['auth.check:admin'])->group(function () { + Route::controller(Modules\Member\Controllers\Admin\Index::class)->prefix('index')->name('index.')->group(function () { + Route::get('/index', 'index')->name('index'); + Route::post('/add', 'add')->name('add'); + Route::put('/edit', 'edit')->name('edit'); + Route::delete('/delete', 'delete')->name('delete'); + Route::get('/export', 'export')->name('export'); + Route::post('/import', 'import')->name('import'); + }); + Route::controller(Modules\Member\Controllers\Admin\Level::class)->prefix('level')->name('level.')->group(function () { + Route::get('/index', 'index')->name('index'); + Route::post('/add', 'add')->name('add'); + Route::put('/edit', 'edit')->name('edit'); + Route::delete('/delete', 'delete')->name('delete'); + }); + Route::controller(Modules\Member\Controllers\Admin\Account::class)->prefix('account')->name('account.')->group(function () { + Route::get('/index', 'index')->name('index'); + Route::post('/add', 'add')->name('add'); + Route::put('/edit', 'edit')->name('edit'); + Route::delete('/delete', 'delete')->name('delete'); + }); + + Route::controller(Modules\Member\Controllers\Admin\Field::class)->prefix('field')->name('field.')->group(function () { + Route::get('/index', 'index')->name('index'); + Route::post('/add', 'add')->name('add'); + Route::put('/edit', 'edit')->name('edit'); + Route::delete('/delete', 'delete')->name('delete'); + Route::get('/fields', 'lists')->name('fields'); + Route::get('/setting', 'setting')->name('setting'); + }); + + Route::controller(Modules\Member\Controllers\Admin\Extend::class)->prefix('extend')->name('extend.')->group(function () { + Route::get('/index', 'index')->name('index'); + Route::put('/audit', 'audit')->name('audit'); + Route::delete('/delete', 'delete')->name('delete'); + }); +}); diff --git a/modules/Member/routes/api.php b/modules/Member/routes/api.php new file mode 100644 index 0000000..e55c61f --- /dev/null +++ b/modules/Member/routes/api.php @@ -0,0 +1,32 @@ + +// +---------------------------------------------------------------------- +use Illuminate\Support\Facades\Route; +use Modules\Member\Controllers\MemberController; + +Route::post('/member/login', [Modules\Member\Controllers\Api\Login::class, 'index'])->name('member.login'); +Route::post('/member/register', [Modules\Member\Controllers\Api\Login::class, 'register'])->name('member.register'); + +Route::name('member.')->prefix('member')->middleware(['auth.check:api'])->group(function () { + Route::get('/user', [Modules\Member\Controllers\Api\Login::class, 'user'])->name('user'); + Route::post('/logout', [Modules\Member\Controllers\Api\Login::class, 'logout'])->name('logout'); + + Route::controller(Modules\Member\Controllers\Api\Index::class)->prefix('member')->name('member.')->group(function () { + Route::get('/index', 'index')->name('index'); + Route::put('/edit', 'edit')->name('edit'); + Route::put('/editpasswd', 'editpasswd')->name('editpasswd'); + }); + + Route::controller(Modules\Member\Controllers\Api\Extend::class)->prefix('extend')->name('extend.')->group(function () { + Route::get('/index', 'index')->name('index'); + Route::get('/detail', 'detail')->name('detail'); + Route::get('/field', 'field')->name('field'); + Route::get('/level', 'level')->name('level'); + Route::post('/save', 'save')->name('save'); + }); +}); diff --git a/modules/Member/routes/web.php b/modules/Member/routes/web.php new file mode 100644 index 0000000..1dea33c --- /dev/null +++ b/modules/Member/routes/web.php @@ -0,0 +1,9 @@ + +// +---------------------------------------------------------------------- +use Illuminate\Support\Facades\Route; diff --git a/modules/Member/tests/Feature/.gitkeep b/modules/Member/tests/Feature/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/modules/Member/tests/Unit/.gitkeep b/modules/Member/tests/Unit/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/modules/Member/vite.config.js b/modules/Member/vite.config.js new file mode 100644 index 0000000..c3a5586 --- /dev/null +++ b/modules/Member/vite.config.js @@ -0,0 +1,26 @@ +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; + +export default defineConfig({ + build: { + outDir: '../../public/build-member', + emptyOutDir: true, + manifest: true, + }, + plugins: [ + laravel({ + publicDirectory: '../../public', + buildDirectory: 'build-member', + input: [ + __dirname + '/resources/assets/sass/app.scss', + __dirname + '/resources/assets/js/app.js' + ], + refresh: true, + }), + ], +}); + +//export const paths = [ +// 'Modules/Member/resources/assets/sass/app.scss', +// 'Modules/Member/resources/assets/js/app.js', +//]; \ No newline at end of file diff --git a/modules/Wechat/app/Controllers/Api/Index.php b/modules/Wechat/app/Controllers/Api/Index.php new file mode 100644 index 0000000..886222f --- /dev/null +++ b/modules/Wechat/app/Controllers/Api/Index.php @@ -0,0 +1,150 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Wechat\Controllers\Api; + +use Illuminate\Http\Request; +use App\Http\Controllers\BaseController; +use Modules\Wechat\Services\OauthService; +use Modules\Wechat\Services\WechatService; + +class Index extends BaseController { + + /** + * 获取微信用户授权跳转 + */ + public function oauth(Request $request, OauthService $service){ + if($request->filled('code')){ + if($request->filled('url')){ + return redirect($request->input('url') . '?code=' . $request->input('code', '')); + } + }else{ + $res = $service->oauth($request); + return redirect($res); + } + } + + /** + * @title 微信登录 + * + * @param Request $request + * @param OauthService $service + * @return void + */ + public function login(Request $request, OauthService $service){ + $type = $request->input('type', 'wechat'); + if($request->filled('code')){ + $info = []; + switch ($type) { + case 'wechat': + try { + $info = $service->wechatLogin($request->input('code', '')); + } catch (\Throwable $th) { + $this->data['message'] = $th->getMessage(); + $this->data['code'] = 0; + return $this->data; + } + break; + case 'miniapp': + try { + $info = $service->miniappLogin($request); + } catch (\Throwable $th) { + $this->data['message'] = $th->getMessage(); + $this->data['code'] = 0; + return $this->data; + } + break; + default: + $this->data['message'] = "非法操作!"; + $this->data['code'] = 0; + break; + } + + if(isset($info['member_id']) && $info['member_id']){ + $token = auth('api')->tokenById($info['member_id']); + if($token){ + $this->data['data'] = [ + 'access_token' => $token, + 'token_type' => 'bearer', + 'expires_in' => auth('api')->factory()->getTTL() * 60 + ]; + }elseif(isset($info['openid']) && $info['openid']){ + $this->data['data'] = $info; + $this->data['message'] = "初次登录未绑定用户,请先绑定用户,或注册新用户绑定!"; + $this->data['code'] = 100; + }else{ + $this->data['message'] = "登录失败!"; + $this->data['code'] = 0; + } + }else{ + if(isset($info['openid']) && $info['openid']){ + $this->data['data'] = $info; + $this->data['message'] = "初次登录未绑定用户,请先绑定用户,或注册新用户绑定!"; + $this->data['code'] = 100; + }else{ + $this->data['message'] = "登录失败!"; + $this->data['code'] = 0; + } + } + }else{ + $this->data['message'] = "非法操作!"; + $this->data['code'] = 0; + } + return response()->json($this->data); + } + /** + * @title 微信公众号验证 + * + * @param OauthService $service + * @return void + */ + public function serve(OauthService $service){ + return $service->WechatServe(); + } + + /** + * @title 获取微信jssdk配置 + * + * @param WechatService $service + * @return void + */ + public function jssdk(OauthService $service){ + try { + $this->data['data'] = $service->getJsSdk($this->request); + } catch (\think\Exception $e) { + $this->data['message'] = $e->getMessage(); + $this->data['code'] = 0; + } + return $this->data; + } + + public function invitecode(Request $request, WechatService $service){ + try { + $this->data['data'] = $service->getInviteCode($request); + } catch (\think\Exception $e) { + $this->data['message'] = $e->getMessage(); + $this->data['code'] = 0; + } + + return $this->data; + } + + public function qrcode(Request $request, WechatService $service){ + try { + $request->mergeIfMissing([ + 'doctor_id' => auth('doctor')->id(), + ]); + $this->data['data'] = $service->getSingleQrcode($request); + } catch (\think\Exception $e) { + $this->data['message'] = $e->getMessage(); + $this->data['code'] = 0; + } + + return $this->data; + } +} diff --git a/modules/Wechat/app/Listeners/LoginBind.php b/modules/Wechat/app/Listeners/LoginBind.php new file mode 100644 index 0000000..4304bc4 --- /dev/null +++ b/modules/Wechat/app/Listeners/LoginBind.php @@ -0,0 +1,33 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Wechat\Listeners; + +use Modules\Member\Events\LoginEvent; +use Modules\Wechat\Models\MemberSocial; + +class LoginBind { + + /** + * @title 会员登录后更新用户信息 + * + * @param LoginEvent $event + * @return void + */ + public function handle(LoginEvent $event) { + $member = $event->member; + $openid = $event->openid; + $type = $event->type; + + $social = MemberSocial::where('openid', $openid)->where('type', $type)->first(); + if ($social && $social->member_id == 0) { + $social->member_id = $member->uid; + $social->save(); + } + } +} \ No newline at end of file diff --git a/modules/Wechat/app/Listeners/RegisterBind.php b/modules/Wechat/app/Listeners/RegisterBind.php new file mode 100644 index 0000000..80d417e --- /dev/null +++ b/modules/Wechat/app/Listeners/RegisterBind.php @@ -0,0 +1,33 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Wechat\Listeners; + +use Modules\Member\Events\Registered; +use Modules\Wechat\Models\MemberSocial; + +class RegisterBind { + + /** + * @title 会员登录后更新用户信息 + * + * @param LoginEvent $event + * @return void + */ + public function handle(Registered $event) { + $member = $event->member; + $openid = $event->openid; + $type = $event->type; + + $social = MemberSocial::where('openid', $openid)->where('type', $type)->first(); + if ($social && $social->member_id == 0) { + $social->member_id = $member->uid; + $social->save(); + } + } +} \ No newline at end of file diff --git a/modules/Wechat/app/Models/MemberSocial.php b/modules/Wechat/app/Models/MemberSocial.php new file mode 100644 index 0000000..f999a45 --- /dev/null +++ b/modules/Wechat/app/Models/MemberSocial.php @@ -0,0 +1,18 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Wechat\Models; + +use App\Models\BaseModel; + +class MemberSocial extends BaseModel { + + protected $table = 'member_social'; + protected $fillable = ['nickname', 'member_id', 'type', 'gender', 'openid', 'avatar', 'county', 'province', 'city', 'language', 'unionid']; + // protected $hidden = ['deleted_at']; +} diff --git a/modules/Wechat/app/Providers/.gitkeep b/modules/Wechat/app/Providers/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/modules/Wechat/app/Providers/EventServiceProvider.php b/modules/Wechat/app/Providers/EventServiceProvider.php new file mode 100644 index 0000000..a9c737d --- /dev/null +++ b/modules/Wechat/app/Providers/EventServiceProvider.php @@ -0,0 +1,45 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Wechat\Providers; + +use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; + +class EventServiceProvider extends ServiceProvider +{ + /** + * The event handler mappings for the application. + * + * @var array> + */ + protected $listen = [ + 'Modules\Member\Events\LoginEvent' => [ + 'Modules\Wechat\Listeners\LoginBind', + ], + 'Modules\Member\Events\Registered' => [ + 'Modules\Wechat\Listeners\RegisterBind', + ], + ]; + + /** + * Indicates if events should be discovered. + * + * @var bool + */ + protected static $shouldDiscoverEvents = true; + + /** + * Configure the proper event listeners for email verification. + * + * @return void + */ + protected function configureEmailVerification(): void + { + + } +} diff --git a/modules/Wechat/app/Providers/RouteServiceProvider.php b/modules/Wechat/app/Providers/RouteServiceProvider.php new file mode 100644 index 0000000..7ac92e5 --- /dev/null +++ b/modules/Wechat/app/Providers/RouteServiceProvider.php @@ -0,0 +1,67 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Wechat\Providers; + +use Illuminate\Support\Facades\Route; +use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider; + +class RouteServiceProvider extends ServiceProvider +{ + /** + * Called before routes are registered. + * + * Register any model bindings or pattern based filters. + */ + public function boot(): void + { + parent::boot(); + } + + /** + * Define the routes for the application. + */ + public function map(): void + { + $this->mapApiRoutes(); + + $this->mapWebRoutes(); + + $this->mapAdminRoutes(); + } + + /** + * Define the "web" routes for the application. + * + * These routes all receive session state, CSRF protection, etc. + */ + protected function mapWebRoutes(): void + { + Route::middleware('web')->group(module_path('Wechat', '/routes/web.php')); + } + + /** + * Define the "api" routes for the application. + * + * These routes are typically stateless. + */ + protected function mapApiRoutes(): void + { + Route::middleware('api')->prefix('api')->name('api.')->group(module_path('Wechat', '/routes/api.php')); + } + + /** + * Define the "api" routes for the application. + * + * These routes are typically stateless. + */ + protected function mapAdminRoutes(): void + { + Route::middleware('api')->prefix('admin')->name('admin.')->group(module_path('Wechat', '/routes/admin.php')); + } +} diff --git a/modules/Wechat/app/Providers/WechatServiceProvider.php b/modules/Wechat/app/Providers/WechatServiceProvider.php new file mode 100644 index 0000000..be06d2a --- /dev/null +++ b/modules/Wechat/app/Providers/WechatServiceProvider.php @@ -0,0 +1,120 @@ +registerCommands(); + $this->registerCommandSchedules(); + $this->registerTranslations(); + $this->registerConfig(); + $this->registerViews(); + $this->loadMigrationsFrom(module_path($this->moduleName, 'database/migrations')); + } + + /** + * Register the service provider. + */ + public function register(): void + { + $this->app->register(EventServiceProvider::class); + $this->app->register(RouteServiceProvider::class); + } + + /** + * Register commands in the format of Command::class + */ + protected function registerCommands(): void + { + // $this->commands([]); + } + + /** + * Register command Schedules. + */ + protected function registerCommandSchedules(): void + { + // $this->app->booted(function () { + // $schedule = $this->app->make(Schedule::class); + // $schedule->command('inspire')->hourly(); + // }); + } + + /** + * Register translations. + */ + public function registerTranslations(): void + { + $langPath = resource_path('lang/modules/'.$this->moduleNameLower); + + if (is_dir($langPath)) { + $this->loadTranslationsFrom($langPath, $this->moduleNameLower); + $this->loadJsonTranslationsFrom($langPath); + } else { + $this->loadTranslationsFrom(module_path($this->moduleName, 'lang'), $this->moduleNameLower); + $this->loadJsonTranslationsFrom(module_path($this->moduleName, 'lang')); + } + } + + /** + * Register config. + */ + protected function registerConfig(): void + { + $this->publishes([module_path($this->moduleName, 'config/config.php') => config_path($this->moduleNameLower.'.php')], 'config'); + $this->mergeConfigFrom(module_path($this->moduleName, 'config/config.php'), $this->moduleNameLower); + } + + /** + * Register views. + */ + public function registerViews(): void + { + $viewPath = resource_path('views/modules/'.$this->moduleNameLower); + $sourcePath = module_path($this->moduleName, 'resources/views'); + + $this->publishes([$sourcePath => $viewPath], ['views', $this->moduleNameLower.'-module-views']); + + $this->loadViewsFrom(array_merge($this->getPublishableViewPaths(), [$sourcePath]), $this->moduleNameLower); + + $componentNamespace = str_replace('/', '\\', config('modules.namespace').'\\'.$this->moduleName.'\\'.ltrim(config('modules.paths.generator.component-class.path'), config('modules.paths.app_folder', ''))); + Blade::componentNamespace($componentNamespace, $this->moduleNameLower); + } + + /** + * Get the services provided by the provider. + * + * @return array + */ + public function provides(): array + { + return []; + } + + /** + * @return array + */ + private function getPublishableViewPaths(): array + { + $paths = []; + foreach (config('view.paths') as $path) { + if (is_dir($path.'/modules/'.$this->moduleNameLower)) { + $paths[] = $path.'/modules/'.$this->moduleNameLower; + } + } + + return $paths; + } +} diff --git a/modules/Wechat/app/Services/MessageService.php b/modules/Wechat/app/Services/MessageService.php new file mode 100644 index 0000000..e8a5f62 --- /dev/null +++ b/modules/Wechat/app/Services/MessageService.php @@ -0,0 +1,38 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Wechat\Services; + +use Illuminate\Support\Facades\Config; +use EasyWeChat\OfficialAccount\Application; + +class MessageService { + /** + * 发送模板消息 + * @param $openid + * @param $template_id + * @param $url + * @param $data + * @return mixed + */ + public function sendMessage($openid, $template_id, $url, $data){ + $config = Config::get('wechat.wx'); + $app = new Application($config); + $client = $app->getClient(); + + $result = $client->postJson('/cgi-bin/message/template/send', [ + 'touser' => $openid, + 'template_id' => $template_id, + 'page' => $url, + 'data' => $data, + 'miniprogram_state' => 'formal', + 'lang' => 'zh_CN', + ]); + return $result; + } +} diff --git a/modules/Wechat/app/Services/OauthService.php b/modules/Wechat/app/Services/OauthService.php new file mode 100644 index 0000000..331ccaa --- /dev/null +++ b/modules/Wechat/app/Services/OauthService.php @@ -0,0 +1,179 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Wechat\Services; + +use Illuminate\Support\Facades\Config; +use EasyWeChat\OfficialAccount\Application; +use EasyWeChat\MiniApp\Application as MiniApp; +use Modules\Wechat\Models\MemberSocial; +use Illuminate\Support\Str; + +class OauthService { + + /** + * @title 微信授权登录 + * + * @param [type] $request + * @return void + */ + public function oauth($request){ + $config = Config::get('wechat.wx'); + $url = $request->fullUrl(); + $app = new Application($config); + try { + //获取openid + $oauth = $app->getOAuth(); + $redirect = $oauth->scopes(['snsapi_userinfo'])->redirect($url); + return $redirect; + } catch (\Exception $e) { + throw new \Exception($e->getMessage(), 100); + } + } + + /** + * @title 微信用户登录 + * + * @param [type] $code + * @return void + */ + public function wechatLogin($code){ + $config = Config::get('wechat.wx'); + + $app = new Application($config); + try { + //获取openid + $oauth = $app->getOAuth(); + $user = $oauth->userFromCode($code); + $userinfo = $user->toArray(); + + $social = MemberSocial::where('openid', '=', $userinfo['id'])->where('type', '=', 'wechat')->first(); + if(!$social){ + $data = [ + 'type' => 'wechat', + 'member_id' => 0, + 'openid' => isset($userinfo['id']) ? $userinfo['id'] : '', + 'nickname' => isset($userinfo['nickname']) ? $userinfo['nickname'] : '', + 'avatar' => isset($userinfo['avatar']) ? $userinfo['avatar'] : '', + 'gender' => isset($userinfo['gender']) ? $userinfo['gender'] : '', + ]; + $social = MemberSocial::create($data); + } + return $social; + } catch (\Exception $e) { + throw new \Exception($e->getMessage(), 100); + } + } + + public function miniappLogin($request){ + $config = Config::get('wechat.miniapp'); + + $app = new MiniApp($config); + try { + //获取openid + $utils = $app->getUtils(); + $session = $utils->codeToSession($request->input('code')); + + $social = MemberSocial::where('openid', '=', $session['openid'])->where('type', '=', 'miniapp')->first(); + if(!$social){ + if($request->filled('iv') && $request->filled('encryptedData')){ + $userinfo = $utils->decryptSession($session['session_key'], $request->input('iv'), $request->input('encryptedData')); + }else{ + $userinfo = ['nickName' => '微信用户' . Str::substr($session['openid'], -7), 'avatarUrl' => '', 'gender' => 1]; + } + $data = [ + 'type' => 'miniapp', + 'member_id' => 0, + 'openid' => isset($session['openid']) ? $session['openid'] : '', + 'unionid' => isset($session['unionid']) ? $session['unionid'] : '', + 'nickname' => $userinfo['nickName'] ? $userinfo['nickName'] : '', + 'avatar' => isset($userinfo['avatarUrl']) ? $userinfo['avatarUrl'] : '', + 'gender' => isset($userinfo['gender']) ? $userinfo['gender'] : '', + ]; + $social = MemberSocial::create($data); + } + return $social; + } catch (\Exception $e) { + throw new \Exception($e->getMessage(), 100); + } + } + + /** + * @title 获取微信JS-SDK配置 + * + * @param [type] $request + * @return void + */ + public function getJsSdk($request){ + $config = Config::get('wechat.wx'); + $url = $request->input('url', ''); + $url = $url ? urldecode($url) : $request->url(true); + $app = new Application($config); + try { + $utils = $app->getUtils(); + $config = $utils->buildJsSdkConfig( + url: $url, + jsApiList: ['updateAppMessageShareData', 'updateTimelineShareData', 'scanQRCode', 'closeWindow', 'hideAllNonBaseMenuItem', 'showAllNonBaseMenuItem', 'openAddress'], + openTagList: [], + debug: false, + ); + return $config; + } catch (\Exception $e) { + throw new \Exception($e->getMessage(), 100); + } + } + + /** + * @title 微信公众号服务 + * + * @return void + */ + public function WechatServe(){ + $config = Config::get('wechat.wx'); + $app = new Application($config); + $server = $app->getServer(); + + + $server->with(function($message, \Closure $next){ + if ($message->MsgType === 'text') { + return [ + 'MsgType' => 'text', + 'Content' => '暂未开通自动回复功能!' + ]; + } + return $next($message); + }); + $server->addEventListener('subscribe', function() { + return '欢迎!!'; + }); + $server->addEventListener('unsubscribe', function() { + return '再见~'; + }); + return $server->serve(); + } + + /** + * @title 获取微信用户信息 + * + * @param [type] $openid + * @return void + */ + public function getWechatInfo($openid){ + $config = Config::get('wechat.wx'); + $app = new Application($config); + $api = $app->getClient(); + + $userinfo = $api->post('cgi-bin/user/info', [ + 'json' => [ + 'openid' => $openid, + 'lang' => 'zh_CN', + ] + ]); + return $userinfo; + } +} diff --git a/modules/Wechat/app/Services/WechatService.php b/modules/Wechat/app/Services/WechatService.php new file mode 100644 index 0000000..9792aa3 --- /dev/null +++ b/modules/Wechat/app/Services/WechatService.php @@ -0,0 +1,74 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Wechat\Services; + +use Illuminate\Support\Facades\Storage; +use Illuminate\Support\Facades\Config; +use EasyWeChat\MiniApp\Application; + +class WechatService { + public function getSingleQrcode($request){ + $config = config('wechat.miniapp'); + + $doctor_id = $request->input('doctor_id'); + $refresh = $request->input('refresh', 0); + $pic_name = md5('doctor_' . $doctor_id); + $path = "qrcode/{$pic_name}.png"; + if (Storage::disk('public')->exists($path) && !$refresh) { + return Storage::disk('public')->url($path); + } + + $app = new Application($config); + $client = $app->getClient(); + + $response = $client->postJson('/wxa/getwxacodeunlimit', [ + 'scene' => $doctor_id, + 'page' => 'pages/health/patient/form', + 'width' => 430, + 'check_path' => false, + // 'env_version' => $config['env_version'], + ]); + + if ($response->isFailed()) { + throw new \Exception($response->getContent(), $response->getStatusCode()); + }else{ + Storage::disk('public')->put($path, $response->toStream()); + return Storage::disk('public')->url($path); + } + } + + public function getInviteCode($request){ + $config = Config::get('wechat.miniapp'); + + $app = new Application($config); + $refresh = $request->input('refresh', 0); + $pic_name = md5('invite_code_' . $request->input('uid', '')); + $path = "qrcode/{$pic_name}.png"; + if (Storage::disk('public')->exists($path) && !$refresh) { + return Storage::disk('public')->url($path); + } + + $client = $app->getClient(); + + $response = $client->postJson('/wxa/getwxacodeunlimit', [ + 'scene' => 'invite_uid=' . $request->input('uid', ''), + 'page' => 'pages/ucenter/login/index', + 'width' => 430, + 'check_path' => false, + // 'env_version' => $config['env_version'], + ]); + + if ($response->isFailed()) { + throw new \Exception($response->getContent(), $response->getStatusCode()); + }else{ + Storage::disk('public')->put($path, $response->toStream()); + return Storage::disk('public')->url($path); + } + } +} diff --git a/modules/Wechat/composer.json b/modules/Wechat/composer.json new file mode 100644 index 0000000..a3dd966 --- /dev/null +++ b/modules/Wechat/composer.json @@ -0,0 +1,30 @@ +{ + "name": "tensent/wechat", + "description": "", + "authors": [ + { + "name": "molong", + "email": "molong@tensent.cn" + } + ], + "extra": { + "laravel": { + "providers": [], + "aliases": { + + } + } + }, + "autoload": { + "psr-4": { + "Modules\\Wechat\\": "app/", + "Modules\\Wechat\\Database\\Factories\\": "database/factories/", + "Modules\\Wechat\\Database\\Seeders\\": "database/seeders/" + } + }, + "autoload-dev": { + "psr-4": { + "Modules\\Wechat\\Tests\\": "tests/" + } + } +} diff --git a/modules/Wechat/config/.gitkeep b/modules/Wechat/config/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/modules/Wechat/config/config.php b/modules/Wechat/config/config.php new file mode 100644 index 0000000..a7b9536 --- /dev/null +++ b/modules/Wechat/config/config.php @@ -0,0 +1,5 @@ + 'Wechat', +]; diff --git a/modules/Wechat/database/factories/.gitkeep b/modules/Wechat/database/factories/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/modules/Wechat/database/migrations/.gitkeep b/modules/Wechat/database/migrations/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/modules/Wechat/database/migrations/2024_05_29_220055_member_social_table.php b/modules/Wechat/database/migrations/2024_05_29_220055_member_social_table.php new file mode 100644 index 0000000..7e27cec --- /dev/null +++ b/modules/Wechat/database/migrations/2024_05_29_220055_member_social_table.php @@ -0,0 +1,41 @@ +id()->uniqid()->comment('主键id'); + $table->unsignedBigInteger('member_id')->comment('会员id'); + $table->string('type', 20)->comment('第三方类型'); + $table->string('openid', 50)->comment('第三方openid'); + $table->string('nickname')->comment('昵称'); + $table->string('avatar')->nullable()->comment('头像'); + $table->string('gender', 10)->nullable()->comment('性别'); + $table->string('country', 50)->nullable()->comment('国家'); + $table->string('province', 50)->nullable()->comment('省份'); + $table->string('city', 50)->nullable()->comment('城市'); + $table->string('language', 50)->nullable()->comment('语言'); + $table->string('unionid', 50)->nullable()->comment('第三方unionid'); + $table->timestamp('created_at')->nullable()->comment('创建时间'); + $table->timestamp('updated_at')->nullable()->comment('更新时间'); + + $table->engine = 'InnoDB'; + $table->charset = 'utf8mb4'; + $table->collation = 'utf8mb4_unicode_ci'; + $table->comment('会员第三方登录表'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void { + Schema::dropIfExists('member_social'); + } +}; diff --git a/modules/Wechat/database/seeders/.gitkeep b/modules/Wechat/database/seeders/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/modules/Wechat/database/seeders/WechatDatabaseSeeder.php b/modules/Wechat/database/seeders/WechatDatabaseSeeder.php new file mode 100644 index 0000000..af41de8 --- /dev/null +++ b/modules/Wechat/database/seeders/WechatDatabaseSeeder.php @@ -0,0 +1,22 @@ + +// +---------------------------------------------------------------------- +namespace Modules\Wechat\Database\Seeders; + +use Illuminate\Database\Seeder; + +class WechatDatabaseSeeder extends Seeder +{ + /** + * Run the database seeds. + */ + public function run(): void + { + // $this->call([]); + } +} diff --git a/modules/Wechat/module.json b/modules/Wechat/module.json new file mode 100644 index 0000000..adb57bc --- /dev/null +++ b/modules/Wechat/module.json @@ -0,0 +1,11 @@ +{ + "name": "Wechat", + "alias": "wechat", + "description": "", + "keywords": [], + "priority": 0, + "providers": [ + "Modules\\Wechat\\Providers\\WechatServiceProvider" + ], + "files": [] +} diff --git a/modules/Wechat/package.json b/modules/Wechat/package.json new file mode 100644 index 0000000..d6fbfc8 --- /dev/null +++ b/modules/Wechat/package.json @@ -0,0 +1,15 @@ +{ + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build" + }, + "devDependencies": { + "axios": "^1.1.2", + "laravel-vite-plugin": "^0.7.5", + "sass": "^1.69.5", + "postcss": "^8.3.7", + "vite": "^4.0.0" + } +} diff --git a/modules/Wechat/resources/assets/js/app.js b/modules/Wechat/resources/assets/js/app.js new file mode 100644 index 0000000..e69de29 diff --git a/modules/Wechat/resources/assets/sass/app.scss b/modules/Wechat/resources/assets/sass/app.scss new file mode 100644 index 0000000..e69de29 diff --git a/modules/Wechat/resources/views/index.blade.php b/modules/Wechat/resources/views/index.blade.php new file mode 100644 index 0000000..1427743 --- /dev/null +++ b/modules/Wechat/resources/views/index.blade.php @@ -0,0 +1,7 @@ +@extends('wechat::layouts.master') + +@section('content') +

Hello World

+ +

Module: {!! config('wechat.name') !!}

+@endsection diff --git a/modules/Wechat/resources/views/layouts/master.blade.php b/modules/Wechat/resources/views/layouts/master.blade.php new file mode 100644 index 0000000..008ba4b --- /dev/null +++ b/modules/Wechat/resources/views/layouts/master.blade.php @@ -0,0 +1,29 @@ + + + + + + + + + + Wechat Module - {{ config('app.name', 'Laravel') }} + + + + + + + + + + {{-- Vite CSS --}} + {{-- {{ module_vite('build-wechat', 'resources/assets/sass/app.scss') }} --}} + + + + @yield('content') + + {{-- Vite JS --}} + {{-- {{ module_vite('build-wechat', 'resources/assets/js/app.js') }} --}} + diff --git a/modules/Wechat/routes/.gitkeep b/modules/Wechat/routes/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/modules/Wechat/routes/admin.php b/modules/Wechat/routes/admin.php new file mode 100644 index 0000000..1dea33c --- /dev/null +++ b/modules/Wechat/routes/admin.php @@ -0,0 +1,9 @@ + +// +---------------------------------------------------------------------- +use Illuminate\Support\Facades\Route; diff --git a/modules/Wechat/routes/api.php b/modules/Wechat/routes/api.php new file mode 100644 index 0000000..a2cfde0 --- /dev/null +++ b/modules/Wechat/routes/api.php @@ -0,0 +1,31 @@ + +// +---------------------------------------------------------------------- +use Illuminate\Support\Facades\Route; +use Modules\Wechat\Controllers\Api\Index; + +Route::post('/wechat/login', [Index::class, 'login']); +Route::post('/wechat/oauth', [Index::class, 'oauth']); +Route::get('serve', [Index::class, 'serve']); + +Route::name('wechat.')->prefix('wechat')->middleware(['auth.check:api'])->group(function () { + Route::get('jssdk', [Index::class, 'jssdk']); + + Route::get('phone', [Index::class, 'phone']); + Route::get('invitecode', [Index::class, 'invitecode']); + + Route::controller(Modules\Wechat\Controllers\Api\Pay::class)->prefix('pay')->name('pay.')->group(function () { + Route::post('miniapp', 'miniapp')->name('miniapp'); + }); +}); + +Route::name('wechat.')->prefix('wechat')->middleware(['auth.check:doctor'])->group(function () { + Route::controller(Modules\Wechat\Controllers\Api\Index::class)->prefix('index')->name('index.')->group(function () { + Route::get('qrcode', 'qrcode')->name('qrcode'); + }); +}); diff --git a/modules/Wechat/routes/web.php b/modules/Wechat/routes/web.php new file mode 100644 index 0000000..1dea33c --- /dev/null +++ b/modules/Wechat/routes/web.php @@ -0,0 +1,9 @@ + +// +---------------------------------------------------------------------- +use Illuminate\Support\Facades\Route; diff --git a/modules/Wechat/vite.config.js b/modules/Wechat/vite.config.js new file mode 100644 index 0000000..8e8db44 --- /dev/null +++ b/modules/Wechat/vite.config.js @@ -0,0 +1,26 @@ +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; + +export default defineConfig({ + build: { + outDir: '../../public/build-wechat', + emptyOutDir: true, + manifest: true, + }, + plugins: [ + laravel({ + publicDirectory: '../../public', + buildDirectory: 'build-wechat', + input: [ + __dirname + '/resources/assets/sass/app.scss', + __dirname + '/resources/assets/js/app.js' + ], + refresh: true, + }), + ], +}); + +//export const paths = [ +// 'Modules/Wechat/resources/assets/sass/app.scss', +// 'Modules/Wechat/resources/assets/js/app.js', +//]; \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..c96c632 --- /dev/null +++ b/package.json @@ -0,0 +1,18 @@ +{ + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.2.1", + "laravel-vite-plugin": "^1.0", + "vite": "^5.0" + }, + "dependencies": { + "axios": "^1.7.9", + "vue": "^3.5.13", + "vuex": "^4.1.0" + } +} diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..506b9a3 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,33 @@ + + + + + tests/Unit + + + tests/Feature + + + + + app + + + + + + + + + + + + + + + + diff --git a/public/.htaccess b/public/.htaccess new file mode 100644 index 0000000..3aec5e2 --- /dev/null +++ b/public/.htaccess @@ -0,0 +1,21 @@ + + + Options -MultiViews -Indexes + + + RewriteEngine On + + # Handle Authorization Header + RewriteCond %{HTTP:Authorization} . + RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] + + # Redirect Trailing Slashes If Not A Folder... + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_URI} (.+)/$ + RewriteRule ^ %1 [L,R=301] + + # Send Requests To Front Controller... + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^ index.php [L] + diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..e69de29 diff --git a/public/index.php b/public/index.php new file mode 100644 index 0000000..947d989 --- /dev/null +++ b/public/index.php @@ -0,0 +1,17 @@ +handleRequest(Request::capture()); diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..eb05362 --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: diff --git a/resources/admin/.gitignore b/resources/admin/.gitignore new file mode 100644 index 0000000..f1a99aa --- /dev/null +++ b/resources/admin/.gitignore @@ -0,0 +1,26 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? +package-lock.json +yarn.lock diff --git a/resources/admin/LICENSE b/resources/admin/LICENSE new file mode 100644 index 0000000..f7c1e1f --- /dev/null +++ b/resources/admin/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 sakuya + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/resources/admin/README.md b/resources/admin/README.md new file mode 100644 index 0000000..cfa8008 --- /dev/null +++ b/resources/admin/README.md @@ -0,0 +1,19 @@ +### sentcms后台管理系统 + +1.安装依赖 + +~~~ +npm install +~~~ + +2.启动项目 + +~~~ +npm run dev +~~~ + +3.打包项目 + +~~~ +npm run build +~~~ diff --git a/resources/admin/index.html b/resources/admin/index.html new file mode 100644 index 0000000..d1777fb --- /dev/null +++ b/resources/admin/index.html @@ -0,0 +1,111 @@ + + + + + + + +Admin + + + + + +
+
+ +
+
Admin
+
+ +
+ + + + + + \ No newline at end of file diff --git a/resources/admin/package.json b/resources/admin/package.json new file mode 100644 index 0000000..c5fde77 --- /dev/null +++ b/resources/admin/package.json @@ -0,0 +1,41 @@ +{ + "name": "sentos-admin", + "private": true, + "version": "0.1.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "@ckeditor/ckeditor5-vue": "^7.2.0", + "@element-plus/icons-vue": "^2.3.1", + "aieditor": "^1.3.5", + "axios": "^1.7.2", + "ckeditor5": "^43.2.0", + "codemirror": "5.65.5", + "core-js": "3.29.0", + "cropperjs": "1.5.13", + "crypto-js": "4.2.0", + "echarts": "5.6.0", + "element-plus": "2.8.4", + "nprogress": "0.2.0", + "pinyin-match": "^1.2.4", + "qrcodejs2": "0.0.2", + "sortablejs": "1.15.0", + "vue": "^3.4.21", + "vue-i18n": "^9.13.1", + "vue-router": "^4.3.2", + "vuedraggable": "^4.0.3", + "vuex": "^4.1.0", + "xgplayer": "2.32.2", + "xgplayer-hls": "2.5.2" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.0.4", + "sass": "1.77.2", + "terser": "^5.31.0", + "vite": "^5.2.0" + } +} diff --git a/resources/admin/public/admin.js b/resources/admin/public/admin.js new file mode 100644 index 0000000..aa7ce71 --- /dev/null +++ b/resources/admin/public/admin.js @@ -0,0 +1,11 @@ + +// 此文件非必要,在生产环境下此文件配置可覆盖运行配置,开发环境下不起效 +// 详情见 src/config/index.js + +const APP_CONFIG = { + //标题 + APP_NAME: "后台管理系统", + + //接口地址,如遇跨域需使用nginx代理 + API_URL: "http://www.sentcms.com/admin/" +} diff --git a/resources/admin/public/static/images/404.png b/resources/admin/public/static/images/404.png new file mode 100644 index 0000000..47197ec Binary files /dev/null and b/resources/admin/public/static/images/404.png differ diff --git a/resources/admin/public/static/images/auth_banner.jpg b/resources/admin/public/static/images/auth_banner.jpg new file mode 100644 index 0000000..dda5466 Binary files /dev/null and b/resources/admin/public/static/images/auth_banner.jpg differ diff --git a/resources/admin/public/static/images/avatar.jpg b/resources/admin/public/static/images/avatar.jpg new file mode 100644 index 0000000..b8a3cf3 Binary files /dev/null and b/resources/admin/public/static/images/avatar.jpg differ diff --git a/resources/admin/public/static/images/avatar2.gif b/resources/admin/public/static/images/avatar2.gif new file mode 100644 index 0000000..6fade97 Binary files /dev/null and b/resources/admin/public/static/images/avatar2.gif differ diff --git a/resources/admin/public/static/images/avatar3.gif b/resources/admin/public/static/images/avatar3.gif new file mode 100644 index 0000000..495fe43 Binary files /dev/null and b/resources/admin/public/static/images/avatar3.gif differ diff --git a/resources/admin/public/static/images/loginbg.svg b/resources/admin/public/static/images/loginbg.svg new file mode 100644 index 0000000..5f81c5b --- /dev/null +++ b/resources/admin/public/static/images/loginbg.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/admin/public/static/images/logo-r.png b/resources/admin/public/static/images/logo-r.png new file mode 100644 index 0000000..9f8fba9 Binary files /dev/null and b/resources/admin/public/static/images/logo-r.png differ diff --git a/resources/admin/public/static/images/logo.png b/resources/admin/public/static/images/logo.png new file mode 100644 index 0000000..7a2fb28 Binary files /dev/null and b/resources/admin/public/static/images/logo.png differ diff --git a/resources/admin/public/static/images/no-widgets.svg b/resources/admin/public/static/images/no-widgets.svg new file mode 100644 index 0000000..ec8b3ae --- /dev/null +++ b/resources/admin/public/static/images/no-widgets.svg @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/admin/public/static/images/tasks-example.png b/resources/admin/public/static/images/tasks-example.png new file mode 100644 index 0000000..0f13591 Binary files /dev/null and b/resources/admin/public/static/images/tasks-example.png differ diff --git a/resources/admin/public/static/images/ver.svg b/resources/admin/public/static/images/ver.svg new file mode 100644 index 0000000..34fd73b --- /dev/null +++ b/resources/admin/public/static/images/ver.svg @@ -0,0 +1,236 @@ + + + + +升级中 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/admin/src/App.vue b/resources/admin/src/App.vue new file mode 100644 index 0000000..d469fa9 --- /dev/null +++ b/resources/admin/src/App.vue @@ -0,0 +1,64 @@ + + + + + diff --git a/resources/admin/src/api/index.js b/resources/admin/src/api/index.js new file mode 100644 index 0000000..8e7b064 --- /dev/null +++ b/resources/admin/src/api/index.js @@ -0,0 +1,11 @@ +/** + * @description 自动import导入所有 api 模块 + */ + +const files = import.meta.glob('./module/*.js', { eager: true }) +const modules = {} +Object.keys(files).forEach(key => { + modules[key.replace(/^\.\/module\/(.*)\.js$/g, '$1')] = files[key].default +}) + +export default modules diff --git a/resources/admin/src/api/module/account.js b/resources/admin/src/api/module/account.js new file mode 100644 index 0000000..56748b3 --- /dev/null +++ b/resources/admin/src/api/module/account.js @@ -0,0 +1,153 @@ +import config from "@/config" +import http from "@/utils/request" + +export default { + family: { + list: { + url: `${config.API_URL}account/families/index`, + name: "家庭列表", + get: async function(params){ + return await http.get(this.url, params); + } + }, + detail: { + url: `${config.API_URL}account/families/detail`, + name: "家庭详情", + get: async function(params){ + return await http.get(this.url, params); + } + }, + add: { + url: `${config.API_URL}account/families/add`, + name: "家庭添加", + post: async function(params){ + return await http.post(this.url, params); + } + }, + edit: { + url: `${config.API_URL}account/families/edit`, + name: "家庭编辑", + post: async function(params){ + return await http.put(this.url, params); + } + }, + delete: { + url: `${config.API_URL}account/families/delete`, + name: "家庭删除", + post: async function(params){ + return await http.delete(this.url, params); + } + } + }, + accounts: { + list: { + url: `${config.API_URL}account/accounts/index`, + name: "账户列表", + get: async function(params){ + return await http.get(this.url, params); + } + }, + detail: { + url: `${config.API_URL}account/accounts/detail`, + name: "账户详情", + get: async function(params){ + return await http.get(this.url, params); + } + }, + add: { + url: `${config.API_URL}account/accounts/add`, + name: "账户添加", + post: async function(params){ + return await http.post(this.url, params); + } + }, + edit: { + url: `${config.API_URL}account/accounts/edit`, + name: "账户编辑", + post: async function(params){ + return await http.put(this.url, params); + } + }, + delete: { + url: `${config.API_URL}account/accounts/delete`, + name: "账户删除", + post: async function(params){ + return await http.delete(this.url, params); + } + } + }, + members: { + list: { + url: `${config.API_URL}account/members/index`, + name: "成员列表", + get: async function(params){ + return await http.get(this.url, params); + } + }, + detail: { + url: `${config.API_URL}account/members/detail`, + name: "成员详情", + get: async function(params){ + return await http.get(this.url, params); + } + }, + add: { + url: `${config.API_URL}account/members/add`, + name: "成员添加", + post: async function(params){ + return await http.post(this.url, params); + } + }, + edit: { + url: `${config.API_URL}account/members/edit`, + name: "成员编辑", + post: async function(params){ + return await http.put(this.url, params); + } + }, + delete: { + url: `${config.API_URL}account/members/delete`, + name: "成员删除", + post: async function(params){ + return await http.delete(this.url, params); + } + } + }, + records: { + list: { + url: `${config.API_URL}account/records/index`, + name: "记录列表", + get: async function(params){ + return await http.get(this.url, params); + } + }, + detail: { + url: `${config.API_URL}account/records/detail`, + name: "记录详情", + get: async function(params){ + return await http.get(this.url, params); + } + }, + add: { + url: `${config.API_URL}account/records/add`, + name: "记录添加", + post: async function(params){ + return await http.post(this.url, params); + } + }, + edit: { + url: `${config.API_URL}account/records/edit`, + name: "记录编辑", + post: async function(params){ + return await http.put(this.url, params); + } + }, + delete: { + url: `${config.API_URL}account/records/delete`, + name: "记录删除", + post: async function(params){ + return await http.delete(this.url, params); + } + } + } +} diff --git a/resources/admin/src/api/module/ads.js b/resources/admin/src/api/module/ads.js new file mode 100644 index 0000000..d910530 --- /dev/null +++ b/resources/admin/src/api/module/ads.js @@ -0,0 +1,33 @@ +import config from "@/config"; +import http from "@/utils/request"; + +export default { + list: { + url: `${config.API_URL}ads/index`, + name: "获得广告列表", + get: async function (params) { + return await http.get(this.url, params); + }, + }, + add: { + url: `${config.API_URL}ads/add`, + name: "添加广告", + post: async function (params) { + return await http.post(this.url, params); + }, + }, + edit: { + url: `${config.API_URL}ads/edit`, + name: "编辑广告", + post: async function (params) { + return await http.put(this.url, params); + }, + }, + delete: { + url: `${config.API_URL}ads/delete`, + name: "删除广告", + post: async function (params) { + return await http.delete(this.url, params); + }, + }, +}; diff --git a/resources/admin/src/api/module/auth.js b/resources/admin/src/api/module/auth.js new file mode 100644 index 0000000..550b00d --- /dev/null +++ b/resources/admin/src/api/module/auth.js @@ -0,0 +1,174 @@ +import config from "@/config" +import http from "@/utils/request" + +export default { + login: { + url: `${config.API_URL}auth/login`, + name: "登录获取TOKEN", + post: async function(data={}){ + return await http.post(this.url, data); + } + }, + logout:{ + url: `${config.API_URL}auth/logout`, + name: "登出", + get: async function(data={}){ + return await http.get(this.url, data); + } + }, + user: { + url: `${config.API_URL}auth/user`, + name: "获取当前登录用户", + get: async function(data={}){ + return await http.get(this.url, data); + } + }, + users: { + list: { + url: `${config.API_URL}auth/users/index`, + name: "获得用户列表", + get: async function(params){ + return await http.get(this.url, params); + } + }, + add: { + url: `${config.API_URL}auth/users/add`, + name: "添加用户", + post: async function(params){ + return await http.post(this.url, params); + } + }, + edit: { + url: `${config.API_URL}auth/users/edit`, + name: "编辑用户", + post: async function(params){ + return await http.put(this.url, params); + } + }, + uppasswd:{ + url: `${config.API_URL}auth/users/passwd`, + name: "修改密码", + post: async function(params){ + return await http.put(this.url, params); + } + }, + uprole: { + url: `${config.API_URL}auth/users/uprole`, + name: "设置角色", + post: async function(params){ + return await http.put(this.url, params); + } + }, + delete: { + url: `${config.API_URL}auth/users/delete`, + name: "删除用户", + post: async function(params){ + return await http.delete(this.url, params); + } + } + }, + role: { + list: { + url: `${config.API_URL}auth/role/index`, + name: "获得角色列表", + get: async function(params){ + return await http.get(this.url, params); + } + }, + add: { + url: `${config.API_URL}auth/role/add`, + name: "添加角色", + post: async function(params){ + return await http.post(this.url, params); + } + }, + edit: { + url: `${config.API_URL}auth/role/edit`, + name: "编辑角色", + post: async function(params){ + return await http.put(this.url, params); + } + }, + auth: { + url: `${config.API_URL}auth/role/auth`, + name: "角色授权", + post: async function(params){ + return await http.put(this.url, params); + } + }, + delete: { + url: `${config.API_URL}auth/role/delete`, + name: "删除角色", + post: async function(params){ + return await http.delete(this.url, params); + } + } + }, + department: { + list: { + url: `${config.API_URL}auth/department/index`, + name: "获得部门列表", + get: async function(params){ + return await http.get(this.url, params); + } + }, + add: { + url: `${config.API_URL}auth/department/add`, + name: "添加部门", + post: async function(params){ + return await http.post(this.url, params); + } + }, + edit: { + url: `${config.API_URL}auth/department/edit`, + name: "编辑部门", + post: async function(params){ + return await http.put(this.url, params); + } + }, + delete: { + url: `${config.API_URL}auth/department/delete`, + name: "删除部门", + post: async function(params){ + return await http.delete(this.url, params); + } + } + }, + menu: { + myMenus: { + url: `${config.API_URL}auth/menu/my`, + name: "获取我的菜单", + get: async function(){ + return await http.get(this.url); + } + }, + list: { + url: `${config.API_URL}auth/menu/index`, + name: "获取菜单", + get: async function(params){ + return await http.get(this.url, params); + } + }, + add: { + url: `${config.API_URL}auth/menu/add`, + name: "添加菜单", + post: async function(params){ + return await http.post(this.url, params); + } + }, + edit: { + url: `${config.API_URL}auth/menu/edit`, + name: "编辑菜单", + post: async function(params){ + return await http.put(this.url, params); + } + }, + delete: { + url: `${config.API_URL}auth/menu/delete`, + name: "删除菜单", + post: async function(params){ + return await http.delete(this.url, params); + } + } + }, +} diff --git a/resources/admin/src/api/module/common.js b/resources/admin/src/api/module/common.js new file mode 100644 index 0000000..b65a6da --- /dev/null +++ b/resources/admin/src/api/module/common.js @@ -0,0 +1,29 @@ +import config from "@/config" +import http from "@/utils/request" + +export default { + upload: { + url: `${config.API_URL}system/file/upload`, + name: "文件上传", + post: async function(data, config={}){ + return await http.post(this.url, data, config); + } + }, + ckeditor: `${config.API_URL}system/file/ckeditor`, + file: { + menu: { + url: `${config.API_URL}system/file/menu`, + name: "获取文件分类", + get: async function(){ + return await http.get(this.url); + } + }, + list: { + url: `${config.API_URL}system/file/list`, + name: "获取文件列表", + get: async function(params){ + return await http.get(this.url, params); + } + } + } +} diff --git a/resources/admin/src/api/module/member.js b/resources/admin/src/api/module/member.js new file mode 100644 index 0000000..aeac088 --- /dev/null +++ b/resources/admin/src/api/module/member.js @@ -0,0 +1,132 @@ +import config from "@/config"; +import http from "@/utils/request"; + +export default { + lists: { + list: { + url: `${config.API_URL}member/index/index`, + name: "获得会员列表", + get: async function (params) { + return await http.get(this.url, params); + }, + }, + add: { + url: `${config.API_URL}member/index/add`, + name: "添加会员", + post: async function (params) { + return await http.post(this.url, params); + }, + }, + edit: { + url: `${config.API_URL}member/index/edit`, + name: "编辑会员", + post: async function (params) { + return await http.put(this.url, params); + }, + }, + delete: { + url: `${config.API_URL}member/index/delete`, + name: "删除会员", + post: async function (params) { + return await http.delete(this.url, params); + }, + }, + import: { + url: `${config.API_URL}member/index/import`, + name: "导入会员", + post: async function (params) { + return await http.post(this.url, params); + }, + }, + export: { + url: `${config.API_URL}member/index/export`, + name: "导出会员", + get: async function (params) { + return await http.get(this.url, params); + }, + }, + }, + level: { + list: { + url: `${config.API_URL}member/level/index`, + name: "获得会员等级列表", + get: async function (params) { + return await http.get(this.url, params); + }, + }, + add: { + url: `${config.API_URL}member/level/add`, + name: "添加会员等级", + post: async function (params) { + return await http.post(this.url, params); + }, + }, + edit: { + url: `${config.API_URL}member/level/edit`, + name: "编辑会员等级", + post: async function (params) { + return await http.put(this.url, params); + }, + }, + delete: { + url: `${config.API_URL}member/level/delete`, + name: "删除会员等级", + post: async function (params) { + return await http.delete(this.url, params); + }, + }, + }, + field: { + list: { + url: `${config.API_URL}member/field/index`, + name: "获得会员字段列表", + get: async function (params) { + return await http.get(this.url, params); + }, + }, + add: { + url: `${config.API_URL}member/field/add`, + name: "添加会员字段", + post: async function (params) { + return await http.post(this.url, params); + }, + }, + edit: { + url: `${config.API_URL}member/field/edit`, + name: "编辑会员字段", + post: async function (params) { + return await http.put(this.url, params); + }, + }, + field: { + url: `${config.API_URL}member/field/fields`, + name: "获得会员字段", + get: async function (params) { + return await http.get(this.url, params); + }, + }, + delete: { + url: `${config.API_URL}member/field/delete`, + name: "删除会员字段", + post: async function (params) { + return await http.delete(this.url, params); + }, + }, + }, + extend: { + list: { + url: `${config.API_URL}member/extend/index`, + name: "获得会员扩展列表", + get: async function (params) { + return await http.get(this.url, params); + }, + }, + audit: { + url: `${config.API_URL}member/extend/audit`, + name: "申请审核", + post: async function (params) { + return await http.put(this.url, params); + }, + }, + }, +}; diff --git a/resources/admin/src/api/module/system.js b/resources/admin/src/api/module/system.js new file mode 100644 index 0000000..1be2a74 --- /dev/null +++ b/resources/admin/src/api/module/system.js @@ -0,0 +1,342 @@ +import config from "@/config" +import http from "@/utils/request" + +export default { + version:{ + url: `${config.API_URL}system/index/version`, + name: "获取最新版本号", + get: async function(){ + return await http.get(this.url); + } + }, + clearcache: { + url: `${config.API_URL}system/index/clearcache`, + name: "清除缓存", + post: async function(){ + return await http.post(this.url); + } + }, + info: { + url: `${config.API_URL}system/index/info`, + name: "系统信息", + get: function(data){ + return http.get(this.url, data); + } + }, + setting:{ + list: { + url: `${config.API_URL}system/setting/index`, + name: "获取配置信息", + get: function(params){ + return http.get(this.url, params); + } + }, + fields: { + url: `${config.API_URL}system/setting/fields`, + name: "获取配置字段", + get: async function(params){ + return await http.get(this.url, params); + } + }, + add: { + url: `${config.API_URL}system/setting/add`, + name: "保存配置信息", + post: function(data){ + return http.post(this.url, data); + } + }, + edit: { + url: `${config.API_URL}system/setting/edit`, + name: "编辑配置信息", + post: function(data){ + return http.put(this.url, data); + } + }, + save: { + url: `${config.API_URL}system/setting/save`, + name: "保存配置信息", + post: function(data){ + return http.put(this.url, data); + } + } + }, + dictionary: { + category: { + url: `${config.API_URL}system/dict/category`, + name: "获取字典树", + get: async function(params){ + return await http.get(this.url, params); + } + }, + editcate:{ + url: `${config.API_URL}system/dict/editcate`, + name: "编辑字典树", + post: async function(data = {}){ + return await http.put(this.url, data); + } + }, + addcate:{ + url: `${config.API_URL}system/dict/addcate`, + name: "添加字典树", + post: async function(data = {}){ + return await http.post(this.url, data); + } + }, + delCate:{ + url: `${config.API_URL}system/dict/deletecate`, + name: "删除字典树", + post: async function(data = {}){ + return await http.delete(this.url, data); + } + }, + list: { + url: `${config.API_URL}system/dict/lists`, + name: "字典明细", + get: async function(params){ + return await http.get(this.url, params); + } + }, + get: { + url: `${config.API_URL}system/dict/detail`, + name: "获取字典数据", + get: async function(params){ + return await http.get(this.url, params); + } + }, + edit:{ + url: `${config.API_URL}system/dict/edit`, + name: "编辑字典明细", + post: async function(data = {}){ + return await http.put(this.url, data); + } + }, + add:{ + url: `${config.API_URL}system/dict/add`, + name: "添加字典明细", + post: async function(data = {}){ + return await http.post(this.url, data); + } + }, + delete:{ + url: `${config.API_URL}system/dict/delete`, + name: "删除字典明细", + post: async function(data = {}){ + return await http.delete(this.url, data); + } + }, + detail: { + url: `${config.API_URL}system/dict/detail`, + name: "字典明细", + get: async function(params){ + return await http.get(this.url, params); + } + }, + alldic: { + url: `${config.API_URL}system/dict/all`, + name: "全部字典", + get: async function(params){ + return await http.get(this.url, params); + } + } + }, + area: { + list: { + url: `${config.API_URL}system/area/index`, + name: "地区列表", + get: async function(params){ + return await http.get(this.url, params); + } + }, + add: { + url: `${config.API_URL}system/area/add`, + name: "地区添加", + post: async function(params){ + return await http.post(this.url, params); + } + }, + edit: { + url: `${config.API_URL}system/area/edit`, + name: "地区编辑", + post: async function(params){ + return await http.put(this.url, params); + } + }, + }, + app: { + list: { + url: `${config.API_URL}system/app/list`, + name: "应用列表", + get: async function(){ + return await http.get(this.url); + } + } + }, + client: { + list: { + url: `${config.API_URL}system/client/index`, + name: "客户端列表", + get: async function(params){ + return await http.get(this.url, params); + } + }, + add: { + url: `${config.API_URL}system/client/add`, + name: "客户端添加", + post: async function(params){ + return await http.post(this.url, params); + } + }, + edit: { + url: `${config.API_URL}system/client/edit`, + name: "客户端编辑", + post: async function(params){ + return await http.put(this.url, params); + } + }, + delete: { + url: `${config.API_URL}system/client/delete`, + name: "客户端删除", + post: async function(params){ + return await http.delete(this.url, params); + } + }, + menu: { + list: { + url: `${config.API_URL}system/menu/index`, + name: "客户端菜单列表", + get: async function(params){ + return await http.get(this.url, params); + } + }, + add: { + url: `${config.API_URL}system/menu/add`, + name: "客户端菜单添加", + post: async function(params){ + return await http.post(this.url, params); + } + }, + edit: { + url: `${config.API_URL}system/menu/edit`, + name: "客户端菜单编辑", + post: async function(params){ + return await http.put(this.url, params); + } + }, + delete: { + url: `${config.API_URL}system/menu/delete`, + name: "客户端菜单删除", + post: async function(params){ + return await http.delete(this.url, params); + } + } + } + }, + log: { + list: { + url: `${config.API_URL}system/log/index`, + name: "日志列表", + get: async function(params){ + return await http.get(this.url, params); + } + }, + my: { + url: `${config.API_URL}system/log/my`, + name: "我的日志", + get: async function(params){ + return await http.get(this.url, params); + } + }, + delete: { + url: `${config.API_URL}system/log/delete`, + name: "日志删除", + post: async function(params){ + return await http.delete(this.url, params); + } + } + }, + tasks: { + list: { + url: `${config.API_URL}system/tasks/index`, + name: "任务列表", + get: async function(params){ + return await http.get(this.url, params); + } + }, + delete: { + url: `${config.API_URL}system/tasks/delete`, + name: "任务删除", + post: async function(params){ + return await http.delete(this.url, params); + } + }, + }, + crontab: { + list: { + url: `${config.API_URL}system/crontab/index`, + name: "定时任务列表", + get: async function(params){ + return await http.get(this.url, params); + } + }, + add: { + url: `${config.API_URL}system/crontab/add`, + name: "定时任务添加", + post: async function(params){ + return await http.post(this.url, params); + } + }, + edit: { + url: `${config.API_URL}system/crontab/edit`, + name: "定时任务编辑", + post: async function(params){ + return await http.put(this.url, params); + } + }, + delete: { + url: `${config.API_URL}system/crontab/delete`, + name: "定时任务删除", + post: async function(params){ + return await http.delete(this.url, params); + } + }, + log: { + url: `${config.API_URL}system/crontab/log`, + name: "定时任务日志", + get: async function(params){ + return await http.get(this.url, params); + } + }, + reload: { + url: `${config.API_URL}system/crontab/reload`, + name: "定时任务重载", + post: async function(params){ + return await http.put(this.url, params); + } + } + }, + modules:{ + list: { + url: `${config.API_URL}system/modules/index`, + name: "模块列表", + get: async function(params){ + return await http.get(this.url, params); + } + }, + update: { + url: `${config.API_URL}system/modules/update`, + name: "更新模块", + post: async function(params){ + return await http.post(this.url, params); + } + } + }, + sms: { + count: { + url: `${config.API_URL}system/sms/count`, + name: "短信发送统计", + get: async function(params){ + return await http.get(this.url, params); + } + } + } +} diff --git a/resources/admin/src/assets/icons/Back.vue b/resources/admin/src/assets/icons/Back.vue new file mode 100644 index 0000000..f2342ac --- /dev/null +++ b/resources/admin/src/assets/icons/Back.vue @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/resources/admin/src/assets/icons/BugFill.vue b/resources/admin/src/assets/icons/BugFill.vue new file mode 100644 index 0000000..35a6edc --- /dev/null +++ b/resources/admin/src/assets/icons/BugFill.vue @@ -0,0 +1,3 @@ + diff --git a/resources/admin/src/assets/icons/BugLine.vue b/resources/admin/src/assets/icons/BugLine.vue new file mode 100644 index 0000000..b1cb9ad --- /dev/null +++ b/resources/admin/src/assets/icons/BugLine.vue @@ -0,0 +1,3 @@ + diff --git a/resources/admin/src/assets/icons/Code.vue b/resources/admin/src/assets/icons/Code.vue new file mode 100644 index 0000000..9d1581c --- /dev/null +++ b/resources/admin/src/assets/icons/Code.vue @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/resources/admin/src/assets/icons/Download.vue b/resources/admin/src/assets/icons/Download.vue new file mode 100644 index 0000000..3b139da --- /dev/null +++ b/resources/admin/src/assets/icons/Download.vue @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/resources/admin/src/assets/icons/FileExcel.vue b/resources/admin/src/assets/icons/FileExcel.vue new file mode 100644 index 0000000..c6d5fdf --- /dev/null +++ b/resources/admin/src/assets/icons/FileExcel.vue @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/resources/admin/src/assets/icons/FilePpt.vue b/resources/admin/src/assets/icons/FilePpt.vue new file mode 100644 index 0000000..9fcf2a2 --- /dev/null +++ b/resources/admin/src/assets/icons/FilePpt.vue @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/resources/admin/src/assets/icons/FileWord.vue b/resources/admin/src/assets/icons/FileWord.vue new file mode 100644 index 0000000..cfd035a --- /dev/null +++ b/resources/admin/src/assets/icons/FileWord.vue @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/resources/admin/src/assets/icons/Organization.vue b/resources/admin/src/assets/icons/Organization.vue new file mode 100644 index 0000000..a666d76 --- /dev/null +++ b/resources/admin/src/assets/icons/Organization.vue @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/resources/admin/src/assets/icons/Upload.vue b/resources/admin/src/assets/icons/Upload.vue new file mode 100644 index 0000000..c0bf5e6 --- /dev/null +++ b/resources/admin/src/assets/icons/Upload.vue @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/resources/admin/src/assets/icons/Vue.vue b/resources/admin/src/assets/icons/Vue.vue new file mode 100644 index 0000000..5253d61 --- /dev/null +++ b/resources/admin/src/assets/icons/Vue.vue @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/resources/admin/src/assets/icons/Wechat.vue b/resources/admin/src/assets/icons/Wechat.vue new file mode 100644 index 0000000..a572f7b --- /dev/null +++ b/resources/admin/src/assets/icons/Wechat.vue @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/resources/admin/src/assets/icons/index.js b/resources/admin/src/assets/icons/index.js new file mode 100644 index 0000000..5992a04 --- /dev/null +++ b/resources/admin/src/assets/icons/index.js @@ -0,0 +1,12 @@ +export { default as Vue } from './Vue.vue' +export { default as Code } from './Code.vue' +export { default as Wechat } from './Wechat.vue' +export { default as BugFill } from './BugFill.vue' +export { default as BugLine } from './BugLine.vue' +export { default as FileWord } from './FileWord.vue' +export { default as FileExcel } from './FileExcel.vue' +export { default as FilePpt } from './FilePpt.vue' +export { default as Organization } from './Organization.vue' +export { default as Upload } from './Upload.vue' +export { default as Download } from './Download.vue' +export { default as Back } from './Back.vue' \ No newline at end of file diff --git a/resources/admin/src/assets/style/app.scss b/resources/admin/src/assets/style/app.scss new file mode 100644 index 0000000..04f8c65 --- /dev/null +++ b/resources/admin/src/assets/style/app.scss @@ -0,0 +1,103 @@ +/* 全局 */ +#app, body, html {width: 100%;height: 100%;background-color: #f6f8f9;font-size: 12px;} +a {color: #333;text-decoration: none;} +a:hover, a:focus {color: #000;text-decoration: none;} +a:link {text-decoration: none;} +a:-webkit-any-link {text-decoration: none;} +a,button,input,textarea{-webkit-tap-highlight-color:rgba(0,0,0,0);box-sizing: border-box;outline:none !important; -webkit-appearance: none;} +* {margin: 0;padding: 0;box-sizing: border-box;outline: none;} + +/* 大布局样式 */ +.aminui {display: flex;flex-flow: column;} +.aminui-wrapper {display: flex;flex:1;overflow: auto;} + +/* 全局滚动条样式 */ +.scrollable {-webkit-overflow-scrolling: touch;} +::-webkit-scrollbar {width: 5px;height: 5px;} +::-webkit-scrollbar-thumb {background-color: rgba(50, 50, 50, 0.3);} +::-webkit-scrollbar-thumb:hover {background-color: rgba(50, 50, 50, 0.6);} +::-webkit-scrollbar-track {background-color: rgba(50, 50, 50, 0.1);} +::-webkit-scrollbar-track:hover {background-color: rgba(50, 50, 50, 0.2);} + +/*布局设置*/ +.layout-setting {position: fixed;width: 40px;height: 40px;border-radius: 3px 0 0 3px;bottom: 100px;right: 0px;z-index: 100;background: #409EFF;display: flex;flex-direction: column;align-items: center;justify-content: center;cursor: pointer;} +.layout-setting i {font-size: 18px;color: #fff;} + +/* 头部 */ +.adminui-header {height: 58px;background: #fff;color: #222b45; border-bottom: 1px solid rgba(0, 0, 0, 0.05); display: flex;justify-content:space-between;} +.adminui-header-left {display: flex;align-items: center;padding-left:20px;} +.adminui-header-right {display: flex;align-items: center;} +.adminui-header .logo-bar {font-size: 20px;font-weight: bold;display: flex;align-items: center;} +.adminui-header .logo-bar .logo {margin-right: 10px;height: 35px;} +.adminui-header .nav {display: flex;height: 100%;margin-left: 40px;} +.adminui-header .nav li {padding:0 10px;margin: 0 10px 0 0;font-size: 14px;color: rgba(255, 255, 255, 0.6);list-style: none;height: 100%;display: flex;align-items: center;cursor: pointer;} +.adminui-header .nav li i {margin-right: 5px;} +.adminui-header .nav li:hover {color: #222b45;} +.adminui-header .nav li.active {background: rgba(255, 255, 255, 0.1);color: #222b45;} +.adminui-header .user-bar .panel-item:hover {background: rgba(255, 255, 255, 0.1)!important;} +.adminui-header .user-bar .user label{color: #222b45;} + +/* 左侧菜单 */ +.aminui-side-split {width:65px;flex-shrink:0;background: #222b45;display: flex;flex-flow: column;} +.aminui-side-split-top {height: 49px;} +.aminui-side-split-top a {display: inline-block;width: 100%;height: 100%;display: flex;align-items: center;justify-content: center;} +.aminui-side-split-top .logo {height:30px;vertical-align: bottom;} +.adminui-side-split-scroll {overflow: auto;overflow-x:hidden;height: 100%;flex: 1;} +.aminui-side-split li {cursor: pointer;width: 65px;height: 65px;color: #fff;text-align: center;display: flex;flex-direction: column;align-items: center;justify-content: center;} +.aminui-side-split li i {font-size: 18px;} +.aminui-side-split li p {margin-top:5px;} +.aminui-side-split li:hover {background: rgba(255, 255, 255, 0.1);} +.aminui-side-split li.active {background: #409EFF;} + +.adminui-side-split-scroll::-webkit-scrollbar-thumb {background-color: rgba(255, 255, 255, 0.4);border-radius:5px;} +.adminui-side-split-scroll::-webkit-scrollbar-thumb:hover {background-color: rgba(255, 255, 255, 0.5);} +.adminui-side-split-scroll::-webkit-scrollbar-track {background-color: rgba(255, 255, 255, 0);} +.adminui-side-split-scroll::-webkit-scrollbar-track:hover {background-color: rgba(255, 255, 255, 0);} + +.aminui-side {display: flex;flex-flow: column;flex-shrink:0;width:210px;background: #fff;box-shadow: 2px 0 8px 0 rgba(29,35,41,.05);border-right: 1px solid #e6e6e6;transition:width 0.3s;} +.adminui-side-top {border-bottom: 1px solid #ebeef5;height:50px;line-height: 50px;} +.adminui-side-top h2 {padding:0 20px;font-size: 17px;color: #3c4a54;} +.adminui-side-scroll {overflow: auto;overflow-x:hidden;flex: 1;} +.adminui-side-bottom {border-top: 1px solid #ebeef5;height:51px;cursor: pointer;display: flex;align-items: center;justify-content: center;} +.adminui-side-bottom i {font-size: 16px;} +.adminui-side-bottom:hover {color: var(--el-color-primary);} +.aminui-side.isCollapse {width: 65px;} +.el-menu .menu-tag {position: absolute;height: 18px;line-height: 18px;background: var(--el-color-danger);font-size: 12px;color: #fff;right: 20px;border-radius:18px;padding:0 6px;} +.el-menu .el-sub-menu__title .menu-tag {right: 40px;} +.el-menu--horizontal > li .menu-tag {display: none;} + +/* 右侧内容 */ +.aminui-body {flex: 1;display: flex;flex-flow: column;} + +.adminui-topbar {height: 50px;border-bottom: 1px solid #ebeef5;background: #fff;box-shadow: 0 1px 4px rgba(0,21,41,.08);display: flex;justify-content:space-between;} +.adminui-topbar .left-panel {display: flex;align-items: center;} +.adminui-topbar .right-panel {display: flex;align-items: center;} + +.right-panel-search {display: flex;align-items: center;} +.right-panel-search > * + * {margin-left:10px;} + +.adminui-tags {height:35px;background: #fff;border-bottom: 1px solid #e6e6e6;} +.adminui-tags ul {display: flex;overflow: hidden;} +.adminui-tags li {cursor: pointer;display: inline-block;float: left;height:34px;line-height: 34px;position: relative;flex-shrink: 0;} +.adminui-tags li::after {content: " ";width:1px;height:100%;position: absolute;right:0px;background-image: linear-gradient(#fff, #e6e6e6);} +.adminui-tags li a {display: inline-block;padding:0 10px;width:100%;height:100%;color: #999;text-decoration:none;display: flex;align-items: center;} +.adminui-tags li i {margin-left:10px;border-radius: 3px;width:18px;height:18px;display: flex;align-items: center;justify-content: center;} +.adminui-tags li i:hover {background: rgba(0,0,0,.2);color: #fff;} +.adminui-tags li:hover {background: #ecf5ff;} +.adminui-tags li.active {background: #409EFF;} +.adminui-tags li.active a {color: #fff;} +.adminui-tags li.sortable-ghost {opacity: 0;} + +.adminui-main {overflow: auto;background-color: #f6f8f9;flex: 1;} + +/*页面最大化*/ +.aminui.main-maximize { + .main-maximize-exit {display: block;} + .aminui-side-split, .aminui-side, .adminui-header, .adminui-topbar, .adminui-tags {display: none;} +} +.main-maximize-exit {display: none;position: fixed;z-index: 3000;top:-20px;left:50%;margin-left: -20px;border-radius: 50%;width: 40px;height: 40px;cursor: pointer;background: rgba(0,0,0,0.2);text-align: center;} +.main-maximize-exit i {font-size: 14px;margin-top: 22px;color: #fff;} +.main-maximize-exit:hover {background: rgba(0,0,0,0.4);} + +/*定宽页面*/ +.sc-page {width: 1230px;margin: 0 auto;} diff --git a/resources/admin/src/assets/style/dark.scss b/resources/admin/src/assets/style/dark.scss new file mode 100644 index 0000000..5147428 --- /dev/null +++ b/resources/admin/src/assets/style/dark.scss @@ -0,0 +1,37 @@ +@import 'element-plus/theme-chalk/src/dark/css-vars.scss'; + +html.dark { + //变量 + --el-text-color-primary: #d0d0d0; + --el-color-primary-dark-2: var(--el-color-primary-light-2) !important; + --el-color-primary-light-9: var(--el-color-primary-dark-8) !important; + --el-color-primary-light-8: var(--el-color-primary-dark-7) !important; + --el-color-primary-light-7: var(--el-color-primary-dark-6) !important; + --el-color-primary-light-5: var(--el-color-primary-dark-4) !important; + --el-color-primary-light-3: var(--el-color-primary-dark-3) !important; + + //背景 + #app {background: var(--el-bg-color);} + + //登录背景 + .login_bg {background: var(--el-bg-color);} + + //框架 + .adminui-header {background: var(--el-bg-color-overlay);border-bottom: 1px solid var(--el-border-color-light);height:59px;} + .aminui-side-split {background: var(--el-bg-color);} + .aminui-side-split li {color: var(--el-text-color-primary);} + .aminui-side {background: var(--el-bg-color-overlay);border-color: var(--el-border-color-light);} + .adminui-side-top, .adminui-side-bottom {border-color: var(--el-border-color-light);} + .adminui-side-top h2 {color: var(--el-text-color-primary);} + .adminui-topbar, .adminui-tags {background: var(--el-bg-color-overlay);border-color: var(--el-border-color-light);} + .adminui-main {background: var(--el-bg-color);} + .drawerBG {background: var(--el-bg-color);} + .adminui-header-menu .el-menu {--el-menu-bg-color:var(--el-bg-color-overlay) !important;--el-menu-hover-bg-color: #171819 !important;} + + //组件 + .el-header, .el-main.nopadding, .el-footer {background: var(--el-bg-color-overlay);border-color: var(--el-border-color-light);} + .el-main {background: var(--el-bg-color);} + .el-aside {background: var(--el-bg-color-overlay);border-color: var(--el-border-color-light);} + .el-table .el-table__body-wrapper {background: var(--el-bg-color);} + .el-table th.is-sortable:hover {background: #111;} +} diff --git a/resources/admin/src/assets/style/diy.scss b/resources/admin/src/assets/style/diy.scss new file mode 100644 index 0000000..efc0b65 --- /dev/null +++ b/resources/admin/src/assets/style/diy.scss @@ -0,0 +1,12 @@ +input::-webkit-outer-spin-button,input::-webkit-inner-spin-button { -webkit-appearance: none;} + +.action-icon{cursor: pointer; width: 1.2em; height: 1.2em; color: #409EFC; vertical-align: middle; margin-right: 5px;} +.el-tabs__new-tab{margin: 10px;} + +.el-drawer__header{margin-bottom: 10px;} + +.el-card__header{padding: 10px; padding-bottom: 0; font-size: 14px;} +.el-card__body{padding: 10px;} + +.el-dialog__body{padding: calc(var(--el-dialog-padding-primary)) var(--el-dialog-padding-primary);} +.el-upload-dragger, .el-upload-dragger.is-dragover{padding: 0; border: none;} diff --git a/resources/admin/src/assets/style/fix.scss b/resources/admin/src/assets/style/fix.scss new file mode 100644 index 0000000..1312696 --- /dev/null +++ b/resources/admin/src/assets/style/fix.scss @@ -0,0 +1,83 @@ +/* 覆盖element-plus样式 */ + +:root { + --el-color-primary: #409EFF; + --el-color-primary-light-1: #53a7ff; + --el-color-primary-light-2: #66b1ff; + --el-color-primary-light-3: #79bbff; + --el-color-primary-light-4: #8cc4ff; + --el-color-primary-light-5: #9fceff; + --el-color-primary-light-6: #b2d8ff; + --el-color-primary-light-7: #c5e1ff; + --el-color-primary-light-8: #d8ebff; + --el-color-primary-light-9: #ebf5ff; + --el-color-primary-dark-1: #398ee5; + --el-color-primary-dark-2: #337ecc; + --el-color-primary-dark-3: #2c6eb2; + --el-color-primary-dark-4: #265e99; + --el-color-primary-dark-5: #204f7f; + --el-color-primary-dark-6: #193f66; + --el-color-primary-dark-7: #132f4c; + --el-color-primary-dark-8: #0c1f32; + --el-color-primary-dark-9: #060f19; +} + +.el-menu {border: none!important;} +.el-menu .el-menu-item a {color: inherit;text-decoration: none;display: block;width:100%;height:100%;position: absolute;top:0px;left:0px;} +.el-form-item-msg {font-size: 12px;color: #999;clear: both;width: 100%;} +.el-container {height: 100%;} +.el-aside {border-right: 1px solid var(--el-border-color-light);} +.el-container + .el-aside {border-right: 0;border-left: 1px solid var(--el-border-color-light);} +.el-header {background: #fff;border-bottom: 1px solid var(--el-border-color-light);padding:13px 15px;display: flex;justify-content: space-between;align-items: center;} +.el-header .left-panel {display: flex;align-items: center;} +.el-header .right-panel {display: flex;align-items: center;} +.el-header .right-panel > * + * {margin-left:10px;} +.el-footer {background: #fff;border-top: 1px solid var(--el-border-color-light);padding:13px 15px;height: 51px;} +.el-main {padding:15px;} +.el-main.nopadding {padding:0;background: #fff;} +.el-drawer__body {overflow: auto;padding:0;} +.el-popconfirm__main {margin: 14px 0;} +.el-card__header {border-bottom: 0;font-size: 17px;font-weight: bold;padding:15px 20px 0px 20px;} +.el-dialog__title {font-size: 17px;font-weight: bold;} +.el-drawer__header>:first-child {font-size: 17px;font-weight: bold;} +.el-tree.menu .el-tree-node__content {height:36px;} +.el-tree.menu .el-tree-node__content .el-tree-node__label .icon {margin-right: 5px;} +.el-progress__text {font-size: 12px!important;} +.el-progress__text i {font-size: 14.4px!important;} +.el-step.is-horizontal .el-step__line {height:1px;} +.el-step__title {font-size: 14px;} +.drawerBG {background: #f6f8f9;} +.el-button+.el-dropdown {margin-left: 10px;} +.el-button-group+.el-dropdown {margin-left: 10px;} +.el-tag+.el-tag {margin-left: 10px;} +.el-button-group+.el-button-group {margin-left: 10px;} +.el-tabs__nav-wrap::after {height: 1px;} +.el-table th.is-sortable {transition: .1s;} +.el-table th.is-sortable:hover {background: #eee;} +.el-table .el-table__body-wrapper {background: #f6f8f9;} +.el-col .el-card {margin-bottom: 15px;} +.el-main {flex-basis: 100%;} +.el-main > .scTable .el-table--border::before {display: none;} +.el-main > .scTable .el-table--border::after {display: none;} +.el-main > .scTable .el-table--border .el-table__inner-wrapper::after {display: none;} +.el-main > .scTable .el-table__border-left-patch {display: none;} +.el-main > .scTable .el-table--border .el-table__inner-wrapper tr:first-child td:first-child {border-left: 0;} +.el-main > .scTable .el-table--border .el-table__inner-wrapper tr:first-child th:first-child {border-left: 0;} +.el-table.el-table--large {font-size: 14px;} +.el-table.el-table--small {font-size: 12px;} +.el-table {font-size: 12px;} +.el-radio-button__inner {font-size: 12px;} +.el-checkbox-button__inner {font-size: 12px;} +.el-sub-menu .el-icon {font-size: 17px;} +.el-sub-menu .el-sub-menu__icon-arrow {font-size: 12px;} + +.aminui-side-split li.active {background-color: var(--el-color-primary);} +.adminui-tags li:hover {background-color: var(--el-color-primary-light-9);} +.adminui-tags li.active {background-color: var(--el-color-primary)!important;} +.contextmenu li:hover {background-color: var(--el-color-primary-light-9)!important;color: var(--el-color-primary-light-2)!important;} +.data-box .item-background {background-color: var(--el-color-primary)!important;} +.layout-setting,.diy-grid-setting {background-color: var(--el-color-primary)!important;} + +/* 覆盖tinymce样式 */ +.sceditor .tox-tinymce {border: 1px solid #DCDFE6;border-radius: 0;} +body .tox-tinymce-aux {z-index: 5700;} diff --git a/resources/admin/src/assets/style/media.scss b/resources/admin/src/assets/style/media.scss new file mode 100644 index 0000000..01cd3e1 --- /dev/null +++ b/resources/admin/src/assets/style/media.scss @@ -0,0 +1,50 @@ +@media (max-width: 992px){ + // 移动端样式覆盖 + .el-form-item {display: block;} + .el-form-item__label {display: block;text-align: left;padding: 0 0 10px;} + .el-dialog {width: 90%!important;} + .el-dialog.is-fullscreen {width: 100%!important;} + .el-drawer.rtl {width: 90%!important;} + .el-form-item__content {margin-left: 0px!important;} + + .adminui-main { + >.el-container {display: block;height:auto;} + >.el-container > .el-aside {width: 100%!important;border: 0} + } + .scTable { + .el-table, + .el-table__body-wrapper {display: block!important;height:auto!important;} + .el-scrollbar__wrap {height:auto!important;} + .scTable-page {padding: 0 5px!important;} + .el-pagination__total, + .el-pagination__jump, + .el-pagination__sizes {display: none!important;} + } + + .headerPublic { + height: auto!important;display: block; + .left-panel {overflow: auto;} + .left-panel::-webkit-scrollbar{display: none;} + .right-panel {display: block;border-top: 1px solid var(--el-border-color-light);margin-top: 15px;} + .right-panel .right-panel-search {display: block;} + .right-panel .right-panel-search >* {width: 100%;margin: 0;margin-top: 15px;} + } + .adminui-main > .el-container >*:first-child:not(.el-aside):not(.el-header) {border: 0;margin-top: 0;} + .adminui-main > .el-container >*:first-child:not(.el-aside):not(.el-header) + .el-aside {margin-top: 0;} + .adminui-main > .el-container > .el-aside {border-bottom: 1px solid var(--el-border-color-light)!important;} + .adminui-main > .el-container > .el-container {border-top: 1px solid var(--el-border-color-light);border-bottom: 1px solid var(--el-border-color-light);margin-top: 15px;} + .adminui-main > .el-container > .el-container + .el-aside {border-top: 1px solid var(--el-border-color-light);margin-top: 15px;} + .adminui-main > .el-container > .el-header {@extend .headerPublic;} + .adminui-main > .el-container > .el-main.nopadding {border-top: 1px solid var(--el-border-color-light);border-bottom: 1px solid var(--el-border-color-light);margin-top: 15px;} + .adminui-main > .el-container > .el-main + .el-aside {border-left: 0!important;border-top: 1px solid var(--el-border-color-light);margin-top: 15px;} + .adminui-main > .el-container > .el-footer {margin-top: 15px;border-bottom: 1px solid var(--el-border-color-light);} + .adminui-main > .el-container > .el-container > .el-header {@extend .headerPublic} + .adminui-main > .el-container > .el-container > .el-header .left-panel {display: block;} + .adminui-main > .el-container > .el-container > .el-header .right-panel {display: block;margin-top: 15px;} + + .sc-page {width: 100%;margin: 0;} + + .common-main .el-form {width: 100% !important;} + .common-header-logo label {display: none;} + .common-header-title {display: none;} +} diff --git a/resources/admin/src/assets/style/pages.scss b/resources/admin/src/assets/style/pages.scss new file mode 100644 index 0000000..564c8ca --- /dev/null +++ b/resources/admin/src/assets/style/pages.scss @@ -0,0 +1,44 @@ +/* USERCENTER */ +.page-user { + .user-info-top {text-align: center;} + .user-info-top h2 {font-size: 18px;margin-top: 5px;} + .user-info-top p {margin: 8px 0 10px 0;} + .menu {background: none;} + .menu .el-menu-item {font-size: 12px;--el-menu-item-height:50px;} + .menu .el-menu-item-group {border-top: 1px solid var(--el-border-color-light);} + .menu .el-menu-item-group:first-child {border: 0;} +} + +/*static-table*/ +.static-table {border-collapse: collapse;width: 100%;font-size: 14px;margin-bottom: 45px;line-height: 1.5em;} +.static-table th {text-align: left;white-space: nowrap;color: #909399;font-weight: 400;border-bottom: 1px solid #dcdfe6;padding: 15px;max-width: 250px;} +.static-table td {border-bottom: 1px solid #dcdfe6;padding: 15px;max-width: 250px;color: #606266;} + +/*header-tabs*/ +.header-tabs {padding:10px 0 0 0;display:block;border:0!important;height:50px;background: none;} +.header-tabs .el-tabs__header {padding-left:10px;margin: 0;} +.header-tabs .el-tabs__content {display: none;} +.header-tabs .el-tabs__nav {border-radius: 0 !important;} +.header-tabs .el-tabs__item {font-size: 13px;} +.header-tabs .el-tabs__item.is-active {background-color: var(--el-bg-color-overlay);} + +/*common-page*/ +.common-page {} +.common-header-left {display: flex;align-items: center;} +.common-header-logo {display: flex;align-items: center;} +.common-header-logo img {height:30px;margin-right: 10px;vertical-align: bottom;} +.common-header-logo label {font-size: 20px;} +.common-header-title {font-size: 16px;border-left: 1px solid var(--el-border-color-light);margin-left: 15px;padding-left: 15px;} +.common-header-right {display: flex;align-items: center;} +.common-header-right a {font-size: 14px;color: var(--el-color-primary);cursor: pointer;} +.common-header-right a:hover {color: var(--el-color-primary-light-3);} +.common-container {max-width: 1240px;margin:30px auto 30px auto;} +.common-main {padding:20px;} +.common-title {font-size: 26px;margin-bottom: 20px;font-weight: normal;} +.common-main .el-form {width: 500px;margin:30px auto;} +.common-main .el-steps .el-step__title {font-size: 14px;} +.common-main .el-steps .el-step__icon {border: 1px solid;} +.common-main .yzm {display: flex;width: 100%;} +.common-main .yzm .el-button {margin-left: 10px;} +.common-main .link {color: var(--el-color-primary);cursor: pointer;} +.common-main .link:hover {color: var(--el-color-primary-light-3);} diff --git a/resources/admin/src/assets/style/style.scss b/resources/admin/src/assets/style/style.scss new file mode 100644 index 0000000..24a0a07 --- /dev/null +++ b/resources/admin/src/assets/style/style.scss @@ -0,0 +1,6 @@ +@import 'app.scss'; +@import 'fix.scss'; +@import 'pages.scss'; +@import 'media.scss'; +@import 'dark.scss'; +@import 'diy.scss'; diff --git a/resources/admin/src/components/scCodeEditor/index.vue b/resources/admin/src/components/scCodeEditor/index.vue new file mode 100644 index 0000000..4a9c29e --- /dev/null +++ b/resources/admin/src/components/scCodeEditor/index.vue @@ -0,0 +1,115 @@ + + + + + + + diff --git a/resources/admin/src/components/scContextmenu/index.vue b/resources/admin/src/components/scContextmenu/index.vue new file mode 100644 index 0000000..7f0a72e --- /dev/null +++ b/resources/admin/src/components/scContextmenu/index.vue @@ -0,0 +1,100 @@ + + + + + + + diff --git a/resources/admin/src/components/scContextmenu/item.vue b/resources/admin/src/components/scContextmenu/item.vue new file mode 100644 index 0000000..aa5a44e --- /dev/null +++ b/resources/admin/src/components/scContextmenu/item.vue @@ -0,0 +1,84 @@ + + + + + + + diff --git a/resources/admin/src/components/scCron/components/day.vue b/resources/admin/src/components/scCron/components/day.vue new file mode 100644 index 0000000..04aa393 --- /dev/null +++ b/resources/admin/src/components/scCron/components/day.vue @@ -0,0 +1,97 @@ + + + + + \ No newline at end of file diff --git a/resources/admin/src/components/scCron/components/hour.vue b/resources/admin/src/components/scCron/components/hour.vue new file mode 100644 index 0000000..cfdb923 --- /dev/null +++ b/resources/admin/src/components/scCron/components/hour.vue @@ -0,0 +1,97 @@ + + + + + \ No newline at end of file diff --git a/resources/admin/src/components/scCron/components/minute.vue b/resources/admin/src/components/scCron/components/minute.vue new file mode 100644 index 0000000..baaea86 --- /dev/null +++ b/resources/admin/src/components/scCron/components/minute.vue @@ -0,0 +1,97 @@ + + + + + \ No newline at end of file diff --git a/resources/admin/src/components/scCron/components/month.vue b/resources/admin/src/components/scCron/components/month.vue new file mode 100644 index 0000000..4933831 --- /dev/null +++ b/resources/admin/src/components/scCron/components/month.vue @@ -0,0 +1,99 @@ + + + + + \ No newline at end of file diff --git a/resources/admin/src/components/scCron/components/second.vue b/resources/admin/src/components/scCron/components/second.vue new file mode 100644 index 0000000..83af5ab --- /dev/null +++ b/resources/admin/src/components/scCron/components/second.vue @@ -0,0 +1,97 @@ + + + + + \ No newline at end of file diff --git a/resources/admin/src/components/scCron/index.vue b/resources/admin/src/components/scCron/index.vue new file mode 100644 index 0000000..d633f4a --- /dev/null +++ b/resources/admin/src/components/scCron/index.vue @@ -0,0 +1,125 @@ + + + + + diff --git a/resources/admin/src/components/scCropper/index.vue b/resources/admin/src/components/scCropper/index.vue new file mode 100644 index 0000000..96055d9 --- /dev/null +++ b/resources/admin/src/components/scCropper/index.vue @@ -0,0 +1,84 @@ + + + + + + + diff --git a/resources/admin/src/components/scDialog/index.vue b/resources/admin/src/components/scDialog/index.vue new file mode 100644 index 0000000..cfd065d --- /dev/null +++ b/resources/admin/src/components/scDialog/index.vue @@ -0,0 +1,84 @@ + + + + + + + diff --git a/resources/admin/src/components/scEcharts/echarts-theme-T.js b/resources/admin/src/components/scEcharts/echarts-theme-T.js new file mode 100644 index 0000000..4154161 --- /dev/null +++ b/resources/admin/src/components/scEcharts/echarts-theme-T.js @@ -0,0 +1,74 @@ +const T = { + "color": [ + "#409EFF", + "#36CE9E", + "#f56e6a", + "#626c91", + "#edb00d", + "#909399" + ], + 'grid': { + 'left': '3%', + 'right': '3%', + 'bottom': '10', + 'top': '40', + 'containLabel': true + }, + "legend": { + "textStyle": { + "color": "#999" + }, + "inactiveColor": "rgba(128,128,128,0.4)" + }, + "categoryAxis": { + "axisLine": { + "show": true, + "lineStyle": { + "color": "rgba(128,128,128,0.2)", + "width": 1 + } + }, + "axisTick": { + "show": false, + "lineStyle": { + "color": "#333" + } + }, + "axisLabel": { + "color": "#999" + }, + "splitLine": { + "show": false, + "lineStyle": { + "color": [ + "#eee" + ] + } + }, + "splitArea": { + "show": false, + "areaStyle": { + "color": [ + "rgba(255,255,255,0.01)", + "rgba(0,0,0,0.01)" + ] + } + } + }, + "valueAxis": { + "axisLine": { + "show": false, + "lineStyle": { + "color": "#999" + } + }, + "splitLine": { + "show": true, + "lineStyle": { + "color": "rgba(128,128,128,0.2)" + } + } + } +} + +export default T diff --git a/resources/admin/src/components/scEcharts/index.vue b/resources/admin/src/components/scEcharts/index.vue new file mode 100644 index 0000000..9e2112c --- /dev/null +++ b/resources/admin/src/components/scEcharts/index.vue @@ -0,0 +1,64 @@ + + + diff --git a/resources/admin/src/components/scEditor/UploadAdapter.js b/resources/admin/src/components/scEditor/UploadAdapter.js new file mode 100644 index 0000000..7eb7f36 --- /dev/null +++ b/resources/admin/src/components/scEditor/UploadAdapter.js @@ -0,0 +1,80 @@ +export default class UploadAdapter { + constructor( loader, options ) { + this.loader = loader; + this.options = options; + } + + upload() { + return this.loader.file + .then( file => new Promise( ( resolve, reject ) => { + this._initRequest(); + this._initListeners( resolve, reject, file ); + this._sendRequest( file ); + } ) ); + } + + abort() { + if ( this.xhr ) { + this.xhr.abort(); + } + } + + _initRequest() { + const xhr = this.xhr = new XMLHttpRequest(); + + xhr.open( 'POST', this.options.upload.uploadUrl, true ); + xhr.responseType = 'json'; + } + + _initListeners( resolve, reject, file ) { + const xhr = this.xhr; + const loader = this.loader; + const genericErrorText = `Couldn't upload file: ${ file.name }.`; + + xhr.addEventListener( 'error', () => reject( genericErrorText ) ); + xhr.addEventListener( 'abort', () => reject() ); + xhr.addEventListener( 'load', () => { + const response = xhr.response; + if ( !response || response.code == 0 ) { + return reject( response && response.code == 0 ? response.message : genericErrorText ); + } + resolve( { + default: response.data.url ? response.data.url : response.data.src + } ); + } ); + + if ( xhr.upload ) { + xhr.upload.addEventListener( 'progress', evt => { + if ( evt.lengthComputable ) { + loader.uploadTotal = evt.total; + loader.uploaded = evt.loaded; + } + } ); + } + } + + _sendRequest( file ) { + // Set headers if specified. + const headers = this.options.upload.headers || {}; + const extendData = this.options.upload.extendData || {}; + // Use the withCredentials flag if specified. + const withCredentials = this.options.upload.withCredentials || false; + const uploadName = this.options.upload.uploadName || 'file'; + for (const headerName of Object.keys(headers)) { + this.xhr.setRequestHeader(headerName, headers[headerName]); + } + this.xhr.withCredentials = withCredentials; + const data = new FormData(); + for (const key of Object.keys(extendData)) { + data.append(key, extendData[key]); + } + data.append( uploadName, file ); + this.xhr.send( data ); + } +} + +export function UploadAdapterPlugin( editor ) { + editor.plugins.get( 'FileRepository' ).createUploadAdapter = ( loader ) => { + return new UploadAdapter( loader, editor.config._config ); + }; +} \ No newline at end of file diff --git a/resources/admin/src/components/scEditor/ckeditor.vue b/resources/admin/src/components/scEditor/ckeditor.vue new file mode 100644 index 0000000..2e185ce --- /dev/null +++ b/resources/admin/src/components/scEditor/ckeditor.vue @@ -0,0 +1,180 @@ + + + + + diff --git a/resources/admin/src/components/scEditor/index.vue b/resources/admin/src/components/scEditor/index.vue new file mode 100644 index 0000000..e25cf12 --- /dev/null +++ b/resources/admin/src/components/scEditor/index.vue @@ -0,0 +1,210 @@ + + + diff --git a/resources/admin/src/components/scFileExport/column.vue b/resources/admin/src/components/scFileExport/column.vue new file mode 100644 index 0000000..9be6608 --- /dev/null +++ b/resources/admin/src/components/scFileExport/column.vue @@ -0,0 +1,53 @@ + + + + diff --git a/resources/admin/src/components/scFileExport/index.vue b/resources/admin/src/components/scFileExport/index.vue new file mode 100644 index 0000000..7dc2c5f --- /dev/null +++ b/resources/admin/src/components/scFileExport/index.vue @@ -0,0 +1,199 @@ + + + + + + + diff --git a/resources/admin/src/components/scFileImport/index.vue b/resources/admin/src/components/scFileImport/index.vue new file mode 100644 index 0000000..0cb6f3d --- /dev/null +++ b/resources/admin/src/components/scFileImport/index.vue @@ -0,0 +1,133 @@ + + + + + + + diff --git a/resources/admin/src/components/scFileSelect/index.vue b/resources/admin/src/components/scFileSelect/index.vue new file mode 100644 index 0000000..224baed --- /dev/null +++ b/resources/admin/src/components/scFileSelect/index.vue @@ -0,0 +1,283 @@ + + + + + + + diff --git a/resources/admin/src/components/scFilterBar/index.vue b/resources/admin/src/components/scFilterBar/index.vue new file mode 100644 index 0000000..4e3653b --- /dev/null +++ b/resources/admin/src/components/scFilterBar/index.vue @@ -0,0 +1,310 @@ + + + + + + + diff --git a/resources/admin/src/components/scFilterBar/my.vue b/resources/admin/src/components/scFilterBar/my.vue new file mode 100644 index 0000000..15a52c1 --- /dev/null +++ b/resources/admin/src/components/scFilterBar/my.vue @@ -0,0 +1,112 @@ + + + + + + + diff --git a/resources/admin/src/components/scFilterBar/pinyin.js b/resources/admin/src/components/scFilterBar/pinyin.js new file mode 100644 index 0000000..1308b4e --- /dev/null +++ b/resources/admin/src/components/scFilterBar/pinyin.js @@ -0,0 +1,2 @@ +/* eslint-disable */ +!function(n,a){"object"==typeof exports&&"undefined"!=typeof module?module.exports=a():"function"==typeof define&&define.amd?define(a):(n="undefined"!=typeof globalThis?globalThis:n||self).PinyinMatch=a()}(this,(function(){"use strict";var n=[],a={},i={};function u(n){for(var a=[],i=[],u=0;n.length>=u;u++)i.push(!0);return e(0,n,[],a,i),a}function e(a,i,u,o,g){if(a!==i.length)for(var h=function(h){var t=i.substring(a,h+1),r=!1;if(n.some((function(n){return 0===n.indexOf(t)}))&&!i[h+1]&&g[h+1]){if(1===t.length)u.push(t);else{var s=[];n.forEach((function(n){0===n.indexOf(t)&&s.push(n)})),u.push(s)}r=!0}else-1!==n.indexOf(t)&&g[h+1]&&(u.push(t),r=!0);if(r){var f=o.length;e(h+1,i,u,o,g),o.length===f&&(g[h+1]=!1),u.pop()}},t=a;i.length>t;t++)h(t);else o.push(u.join(" "))}function o(n){var a=[];return u(n).forEach((function(n){var i=n.split(" "),u=i.length-1;i[u].indexOf(",")?i[u].split(",").forEach((function(n){i.splice(u,1,n),a.push(JSON.parse(JSON.stringify(i)))})):a.push(i)})),0!==a.length&&a[0].length===n.length||a.push(n.split("")),i=function(n,a,i){return a in n?Object.defineProperty(n,a,{value:i,enumerable:!0,configurable:!0,writable:!0}):n[a]=i,n}({},n,a),a}function g(n,a,i,u){if(!n)return!1;var e=n.split(" ");return e.forEach((function(n){n.length>0&&u&&e.push(n.charAt(0))})),i?e.some((function(n){return 0===n.indexOf(a)})):-1!==e.indexOf(a)}function h(n,u){if(!n||!u)return!1;n=n.toLowerCase(),u=u.replace(/\s+/g,"").toLowerCase();var e=n.indexOf(u);if(-1!==e)return[e,e+u.length-1];var g=t(n.split(""),[u.split("")],u);return g||t(function(n){for(var i=[],u=0,e=n.length;e>u;u++){var o=n.charAt(u);i.push(a[o]||o)}return i}(n),i[u]||o(u),u)}function t(n,a,i){for(var u=0;n.length>u;u++)for(var e=0;a.length>e;e++){var o=a[e],h=o.length,t=h===i.length,r=!0,s=0,f=0,c=0;if(n.length>=h){for(;o.length>s;s++)if(0===s&&" "===n[u+s+f])f+=1,s-=1;else if(" "===n[u+s+c])c+=1,s-=1;else if(!g(n[u+s+c],o[s],!n[u+s+1]||!o[s+1],t)){r=!1;break}if(r)return[u+f,c+u+s-1]}}return!1}return{match:function(i){return n=Object.keys(i),a=function(n){var a={};for(var i in n)for(var u=n[i],e=0,o=u.length;o>e;e++)a[u[e]]=a[u[e]]?a[u[e]]+" "+i:i;return a}(i),h}({a:"阿啊呵腌嗄吖锕",e:"额阿俄恶鹅遏鄂厄饿峨扼娥鳄哦蛾噩愕讹锷垩婀鹗萼谔莪腭锇颚呃阏屙苊轭",ai:"爱埃艾碍癌哀挨矮隘蔼唉皑哎霭捱暧嫒嗳瑷嗌锿砹",ei:"诶",xi:"系西席息希习吸喜细析戏洗悉锡溪惜稀袭夕洒晰昔牺腊烯熙媳栖膝隙犀蹊硒兮熄曦禧嬉玺奚汐徙羲铣淅嘻歙熹矽蟋郗唏皙隰樨浠忾蜥檄郄翕阋鳃舾屣葸螅咭粞觋欷僖醯鼷裼穸饩舄禊诶菥蓰",yi:"一以已意议义益亿易医艺食依移衣异伊仪宜射遗疑毅谊亦疫役忆抑尾乙译翼蛇溢椅沂泄逸蚁夷邑怡绎彝裔姨熠贻矣屹颐倚诣胰奕翌疙弈轶蛾驿壹猗臆弋铱旖漪迤佚翊诒怿痍懿饴峄揖眙镒仡黟肄咿翳挹缢呓刈咦嶷羿钇殪荑薏蜴镱噫癔苡悒嗌瘗衤佾埸圯舣酏劓",an:"安案按岸暗鞍氨俺胺铵谙庵黯鹌桉埯犴揞厂广",han:"厂汉韩含旱寒汗涵函喊憾罕焊翰邯撼瀚憨捍酣悍鼾邗颔蚶晗菡旰顸犴焓撖",ang:"昂仰盎肮",ao:"奥澳傲熬凹鳌敖遨鏖袄坳翱嗷拗懊岙螯骜獒鏊艹媪廒聱",wa:"瓦挖娃洼袜蛙凹哇佤娲呙腽",yu:"于与育余预域予遇奥语誉玉鱼雨渔裕愈娱欲吁舆宇羽逾豫郁寓吾狱喻御浴愉禹俞邪榆愚渝尉淤虞屿峪粥驭瑜禺毓钰隅芋熨瘀迂煜昱汩於臾盂聿竽萸妪腴圄谕觎揄龉谀俣馀庾妤瘐鬻欤鹬阈嵛雩鹆圉蜮伛纡窬窳饫蓣狳肀舁蝓燠",niu:"牛纽扭钮拗妞忸狃",o:"哦噢喔",ba:"把八巴拔伯吧坝爸霸罢芭跋扒叭靶疤笆耙鲅粑岜灞钯捌菝魃茇",pa:"怕帕爬扒趴琶啪葩耙杷钯筢",pi:"被批副否皮坏辟啤匹披疲罢僻毗坯脾譬劈媲屁琵邳裨痞癖陂丕枇噼霹吡纰砒铍淠郫埤濞睥芘蚍圮鼙罴蜱疋貔仳庀擗甓陴",bi:"比必币笔毕秘避闭佛辟壁弊彼逼碧鼻臂蔽拂泌璧庇痹毙弼匕鄙陛裨贲敝蓖吡篦纰俾铋毖筚荸薜婢哔跸濞秕荜愎睥妣芘箅髀畀滗狴萆嬖襞舭",bai:"百白败摆伯拜柏佰掰呗擘捭稗",bo:"波博播勃拨薄佛伯玻搏柏泊舶剥渤卜驳簿脖膊簸菠礴箔铂亳钵帛擘饽跛钹趵檗啵鹁擗踣",bei:"北被备倍背杯勃贝辈悲碑臂卑悖惫蓓陂钡狈呗焙碚褙庳鞴孛鹎邶鐾",ban:"办版半班般板颁伴搬斑扮拌扳瓣坂阪绊钣瘢舨癍",pan:"判盘番潘攀盼拚畔胖叛拌蹒磐爿蟠泮袢襻丬",bin:"份宾频滨斌彬濒殡缤鬓槟摈膑玢镔豳髌傧",bang:"帮邦彭旁榜棒膀镑绑傍磅蚌谤梆浜蒡",pang:"旁庞乓磅螃彷滂逄耪",beng:"泵崩蚌蹦迸绷甭嘣甏堋",bao:"报保包宝暴胞薄爆炮饱抱堡剥鲍曝葆瀑豹刨褒雹孢苞煲褓趵鸨龅勹",bu:"不部步布补捕堡埔卜埠簿哺怖钚卟瓿逋晡醭钸",pu:"普暴铺浦朴堡葡谱埔扑仆蒲曝瀑溥莆圃璞濮菩蹼匍噗氆攵镨攴镤",mian:"面棉免绵缅勉眠冕娩腼渑湎沔黾宀眄",po:"破繁坡迫颇朴泊婆泼魄粕鄱珀陂叵笸泺皤钋钷",fan:"反范犯繁饭泛翻凡返番贩烦拚帆樊藩矾梵蕃钒幡畈蘩蹯燔",fu:"府服副负富复福夫妇幅付扶父符附腐赴佛浮覆辅傅伏抚赋辐腹弗肤阜袱缚甫氟斧孚敷俯拂俘咐腑孵芙涪釜脯茯馥宓绂讣呋罘麸蝠匐芾蜉跗凫滏蝮驸绋蚨砩桴赙菔呒趺苻拊阝鲋怫稃郛莩幞祓艴黻黼鳆",ben:"本体奔苯笨夯贲锛畚坌",feng:"风丰封峰奉凤锋冯逢缝蜂枫疯讽烽俸沣酆砜葑唪",bian:"变便边编遍辩鞭辨贬匾扁卞汴辫砭苄蝙鳊弁窆笾煸褊碥忭缏",pian:"便片篇偏骗翩扁骈胼蹁谝犏缏",zhen:"镇真针圳振震珍阵诊填侦臻贞枕桢赈祯帧甄斟缜箴疹砧榛鸩轸稹溱蓁胗椹朕畛浈",biao:"表标彪镖裱飚膘飙镳婊骠飑杓髟鳔灬瘭",piao:"票朴漂飘嫖瓢剽缥殍瞟骠嘌莩螵",huo:"和活或货获火伙惑霍祸豁嚯藿锪蠖钬耠镬夥灬劐攉",bie:"别鳖憋瘪蹩",min:"民敏闽闵皿泯岷悯珉抿黾缗玟愍苠鳘",fen:"分份纷奋粉氛芬愤粪坟汾焚酚吩忿棼玢鼢瀵偾鲼",bing:"并病兵冰屏饼炳秉丙摒柄槟禀枋邴冫",geng:"更耕颈庚耿梗埂羹哽赓绠鲠",fang:"方放房防访纺芳仿坊妨肪邡舫彷枋鲂匚钫",xian:"现先县见线限显险献鲜洗宪纤陷闲贤仙衔掀咸嫌掺羡弦腺痫娴舷馅酰铣冼涎暹籼锨苋蚬跹岘藓燹鹇氙莶霰跣猃彡祆筅",fou:"不否缶",ca:"拆擦嚓礤",cha:"查察差茶插叉刹茬楂岔诧碴嚓喳姹杈汊衩搽槎镲苴檫馇锸猹",cai:"才采财材菜彩裁蔡猜踩睬",can:"参残餐灿惨蚕掺璨惭粲孱骖黪",shen:"信深参身神什审申甚沈伸慎渗肾绅莘呻婶娠砷蜃哂椹葚吲糁渖诜谂矧胂",cen:"参岑涔",san:"三参散伞叁糁馓毵",cang:"藏仓苍沧舱臧伧",zang:"藏脏葬赃臧奘驵",chen:"称陈沈沉晨琛臣尘辰衬趁忱郴宸谌碜嗔抻榇伧谶龀肜",cao:"草操曹槽糙嘈漕螬艚屮",ce:"策测册侧厕栅恻",ze:"责则泽择侧咋啧仄箦赜笮舴昃迮帻",zhai:"债择齐宅寨侧摘窄斋祭翟砦瘵哜",dao:"到道导岛倒刀盗稻蹈悼捣叨祷焘氘纛刂帱忉",ceng:"层曾蹭噌",zha:"查扎炸诈闸渣咋乍榨楂札栅眨咤柞喳喋铡蚱吒怍砟揸痄哳齄",chai:"差拆柴钗豺侪虿瘥",ci:"次此差词辞刺瓷磁兹慈茨赐祠伺雌疵鹚糍呲粢",zi:"资自子字齐咨滋仔姿紫兹孜淄籽梓鲻渍姊吱秭恣甾孳訾滓锱辎趑龇赀眦缁呲笫谘嵫髭茈粢觜耔",cuo:"措错磋挫搓撮蹉锉厝嵯痤矬瘥脞鹾",chan:"产单阐崭缠掺禅颤铲蝉搀潺蟾馋忏婵孱觇廛谄谗澶骣羼躔蒇冁",shan:"山单善陕闪衫擅汕扇掺珊禅删膳缮赡鄯栅煽姗跚鳝嬗潸讪舢苫疝掸膻钐剡蟮芟埏彡骟",zhan:"展战占站崭粘湛沾瞻颤詹斩盏辗绽毡栈蘸旃谵搌",xin:"新心信辛欣薪馨鑫芯锌忻莘昕衅歆囟忄镡",lian:"联连练廉炼脸莲恋链帘怜涟敛琏镰濂楝鲢殓潋裢裣臁奁莶蠊蔹",chang:"场长厂常偿昌唱畅倡尝肠敞倘猖娼淌裳徜昶怅嫦菖鲳阊伥苌氅惝鬯",zhang:"长张章障涨掌帐胀彰丈仗漳樟账杖璋嶂仉瘴蟑獐幛鄣嫜",chao:"超朝潮炒钞抄巢吵剿绰嘲晁焯耖怊",zhao:"着照招找召朝赵兆昭肇罩钊沼嘲爪诏濯啁棹笊",zhou:"调州周洲舟骤轴昼宙粥皱肘咒帚胄绉纣妯啁诌繇碡籀酎荮",che:"车彻撤尺扯澈掣坼砗屮",ju:"车局据具举且居剧巨聚渠距句拒俱柜菊拘炬桔惧矩鞠驹锯踞咀瞿枸掬沮莒橘飓疽钜趄踽遽琚龃椐苣裾榘狙倨榉苴讵雎锔窭鞫犋屦醵",cheng:"成程城承称盛抢乘诚呈净惩撑澄秤橙骋逞瞠丞晟铛埕塍蛏柽铖酲裎枨",rong:"容荣融绒溶蓉熔戎榕茸冗嵘肜狨蝾",sheng:"生声升胜盛乘圣剩牲甸省绳笙甥嵊晟渑眚",deng:"等登邓灯澄凳瞪蹬噔磴嶝镫簦戥",zhi:"制之治质职只志至指织支值知识直致执置止植纸拓智殖秩旨址滞氏枝芝脂帜汁肢挚稚酯掷峙炙栉侄芷窒咫吱趾痔蜘郅桎雉祉郦陟痣蛭帙枳踯徵胝栀贽祗豸鸷摭轵卮轾彘觯絷跖埴夂黹忮骘膣踬",zheng:"政正证争整征郑丁症挣蒸睁铮筝拯峥怔诤狰徵钲",tang:"堂唐糖汤塘躺趟倘棠烫淌膛搪镗傥螳溏帑羰樘醣螗耥铴瑭",chi:"持吃池迟赤驰尺斥齿翅匙痴耻炽侈弛叱啻坻眙嗤墀哧茌豉敕笞饬踟蚩柢媸魑篪褫彳鸱螭瘛眵傺",shi:"是时实事市十使世施式势视识师史示石食始士失适试什泽室似诗饰殖释驶氏硕逝湿蚀狮誓拾尸匙仕柿矢峙侍噬嗜栅拭嘘屎恃轼虱耆舐莳铈谥炻豕鲥饣螫酾筮埘弑礻蓍鲺贳",qi:"企其起期气七器汽奇齐启旗棋妻弃揭枝歧欺骑契迄亟漆戚岂稽岐琦栖缉琪泣乞砌祁崎绮祺祈凄淇杞脐麒圻憩芪伎俟畦耆葺沏萋骐鳍綦讫蕲屺颀亓碛柒啐汔綮萁嘁蛴槭欹芑桤丌蜞",chuai:"揣踹啜搋膪",tuo:"托脱拓拖妥驼陀沱鸵驮唾椭坨佗砣跎庹柁橐乇铊沲酡鼍箨柝",duo:"多度夺朵躲铎隋咄堕舵垛惰哆踱跺掇剁柁缍沲裰哚隳",xue:"学血雪削薛穴靴谑噱鳕踅泶彐",chong:"重种充冲涌崇虫宠忡憧舂茺铳艟",chou:"筹抽绸酬愁丑臭仇畴稠瞅踌惆俦瘳雠帱",qiu:"求球秋丘邱仇酋裘龟囚遒鳅虬蚯泅楸湫犰逑巯艽俅蝤赇鼽糗",xiu:"修秀休宿袖绣臭朽锈羞嗅岫溴庥馐咻髹鸺貅",chu:"出处础初助除储畜触楚厨雏矗橱锄滁躇怵绌搐刍蜍黜杵蹰亍樗憷楮",tuan:"团揣湍疃抟彖",zhui:"追坠缀揣椎锥赘惴隹骓缒",chuan:"传川船穿串喘椽舛钏遄氚巛舡",zhuan:"专转传赚砖撰篆馔啭颛",yuan:"元员院原源远愿园援圆缘袁怨渊苑宛冤媛猿垣沅塬垸鸳辕鸢瑗圜爰芫鼋橼螈眢箢掾",cuan:"窜攒篡蹿撺爨汆镩",chuang:"创床窗闯幢疮怆",zhuang:"装状庄壮撞妆幢桩奘僮戆",chui:"吹垂锤炊椎陲槌捶棰",chun:"春纯醇淳唇椿蠢鹑朐莼肫蝽",zhun:"准屯淳谆肫窀",cu:"促趋趣粗簇醋卒蹴猝蹙蔟殂徂",dun:"吨顿盾敦蹲墩囤沌钝炖盹遁趸砘礅",qu:"区去取曲趋渠趣驱屈躯衢娶祛瞿岖龋觑朐蛐癯蛆苣阒诎劬蕖蘧氍黢蠼璩麴鸲磲",xu:"需许续须序徐休蓄畜虚吁绪叙旭邪恤墟栩絮圩婿戌胥嘘浒煦酗诩朐盱蓿溆洫顼勖糈砉醑",chuo:"辍绰戳淖啜龊踔辶",zu:"组族足祖租阻卒俎诅镞菹",ji:"济机其技基记计系期际及集级几给积极己纪即继击既激绩急奇吉季齐疾迹鸡剂辑籍寄挤圾冀亟寂暨脊跻肌稽忌饥祭缉棘矶汲畸姬藉瘠骥羁妓讥稷蓟悸嫉岌叽伎鲫诘楫荠戟箕霁嵇觊麂畿玑笈犄芨唧屐髻戢佶偈笄跽蒺乩咭赍嵴虮掎齑殛鲚剞洎丌墼蕺彐芰哜",cong:"从丛匆聪葱囱琮淙枞骢苁璁",zong:"总从综宗纵踪棕粽鬃偬枞腙",cou:"凑辏腠楱",cui:"衰催崔脆翠萃粹摧璀瘁悴淬啐隹毳榱",wei:"为位委未维卫围违威伟危味微唯谓伪慰尾魏韦胃畏帷喂巍萎蔚纬潍尉渭惟薇苇炜圩娓诿玮崴桅偎逶倭猥囗葳隗痿猬涠嵬韪煨艉隹帏闱洧沩隈鲔軎",cun:"村存寸忖皴",zuo:"作做座左坐昨佐琢撮祚柞唑嘬酢怍笮阼胙",zuan:"钻纂攥缵躜",da:"大达打答搭沓瘩惮嗒哒耷鞑靼褡笪怛妲",dai:"大代带待贷毒戴袋歹呆隶逮岱傣棣怠殆黛甙埭诒绐玳呔迨",tai:"台太态泰抬胎汰钛苔薹肽跆邰鲐酞骀炱",ta:"他它她拓塔踏塌榻沓漯獭嗒挞蹋趿遢铊鳎溻闼",dan:"但单石担丹胆旦弹蛋淡诞氮郸耽殚惮儋眈疸澹掸膻啖箪聃萏瘅赕",lu:"路六陆录绿露鲁卢炉鹿禄赂芦庐碌麓颅泸卤潞鹭辘虏璐漉噜戮鲈掳橹轳逯渌蓼撸鸬栌氇胪镥簏舻辂垆",tan:"谈探坦摊弹炭坛滩贪叹谭潭碳毯瘫檀痰袒坍覃忐昙郯澹钽锬",ren:"人任认仁忍韧刃纫饪妊荏稔壬仞轫亻衽",jie:"家结解价界接节她届介阶街借杰洁截姐揭捷劫戒皆竭桔诫楷秸睫藉拮芥诘碣嗟颉蚧孑婕疖桀讦疥偈羯袷哜喈卩鲒骱",yan:"研严验演言眼烟沿延盐炎燕岩宴艳颜殷彦掩淹阎衍铅雁咽厌焰堰砚唁焉晏檐蜒奄俨腌妍谚兖筵焱偃闫嫣鄢湮赝胭琰滟阉魇酽郾恹崦芫剡鼹菸餍埏谳讠厣罨",dang:"当党档荡挡宕砀铛裆凼菪谠",tao:"套讨跳陶涛逃桃萄淘掏滔韬叨洮啕绦饕鼗",tiao:"条调挑跳迢眺苕窕笤佻啁粜髫铫祧龆蜩鲦",te:"特忑忒铽慝",de:"的地得德底锝",dei:"得",di:"的地第提低底抵弟迪递帝敌堤蒂缔滴涤翟娣笛棣荻谛狄邸嘀砥坻诋嫡镝碲骶氐柢籴羝睇觌",ti:"体提题弟替梯踢惕剔蹄棣啼屉剃涕锑倜悌逖嚏荑醍绨鹈缇裼",tui:"推退弟腿褪颓蜕忒煺",you:"有由又优游油友右邮尤忧幼犹诱悠幽佑釉柚铀鱿囿酉攸黝莠猷蝣疣呦蚴莸莜铕宥繇卣牖鼬尢蚰侑",dian:"电点店典奠甸碘淀殿垫颠滇癫巅惦掂癜玷佃踮靛钿簟坫阽",tian:"天田添填甜甸恬腆佃舔钿阗忝殄畋栝掭",zhu:"主术住注助属逐宁著筑驻朱珠祝猪诸柱竹铸株瞩嘱贮煮烛苎褚蛛拄铢洙竺蛀渚伫杼侏澍诛茱箸炷躅翥潴邾槠舳橥丶瘃麈疰",nian:"年念酿辗碾廿捻撵拈蔫鲶埝鲇辇黏",diao:"调掉雕吊钓刁貂凋碉鲷叼铫铞",yao:"要么约药邀摇耀腰遥姚窑瑶咬尧钥谣肴夭侥吆疟妖幺杳舀窕窈曜鹞爻繇徭轺铫鳐崾珧",die:"跌叠蝶迭碟爹谍牒耋佚喋堞瓞鲽垤揲蹀",she:"设社摄涉射折舍蛇拾舌奢慑赦赊佘麝歙畲厍猞揲滠",ye:"业也夜叶射野液冶喝页爷耶邪咽椰烨掖拽曳晔谒腋噎揶靥邺铘揲",xie:"些解协写血叶谢械鞋胁斜携懈契卸谐泄蟹邪歇泻屑挟燮榭蝎撷偕亵楔颉缬邂鲑瀣勰榍薤绁渫廨獬躞",zhe:"这者着著浙折哲蔗遮辙辄柘锗褶蜇蛰鹧谪赭摺乇磔螫",ding:"定订顶丁鼎盯钉锭叮仃铤町酊啶碇腚疔玎耵",diu:"丢铥",ting:"听庭停厅廷挺亭艇婷汀铤烃霆町蜓葶梃莛",dong:"动东董冬洞懂冻栋侗咚峒氡恫胴硐垌鸫岽胨",tong:"同通统童痛铜桶桐筒彤侗佟潼捅酮砼瞳恸峒仝嗵僮垌茼",zhong:"中重种众终钟忠仲衷肿踵冢盅蚣忪锺舯螽夂",dou:"都斗读豆抖兜陡逗窦渎蚪痘蔸钭篼",du:"度都独督读毒渡杜堵赌睹肚镀渎笃竺嘟犊妒牍蠹椟黩芏髑",duan:"断段短端锻缎煅椴簖",dui:"对队追敦兑堆碓镦怼憝",rui:"瑞兑锐睿芮蕊蕤蚋枘",yue:"月说约越乐跃兑阅岳粤悦曰钥栎钺樾瀹龠哕刖",tun:"吞屯囤褪豚臀饨暾氽",hui:"会回挥汇惠辉恢徽绘毁慧灰贿卉悔秽溃荟晖彗讳诲珲堕诙蕙晦睢麾烩茴喙桧蛔洄浍虺恚蟪咴隳缋哕",wu:"务物无五武午吴舞伍污乌误亡恶屋晤悟吾雾芜梧勿巫侮坞毋诬呜钨邬捂鹜兀婺妩於戊鹉浯蜈唔骛仵焐芴鋈庑鼯牾怃圬忤痦迕杌寤阢",ya:"亚压雅牙押鸭呀轧涯崖邪芽哑讶鸦娅衙丫蚜碣垭伢氩桠琊揠吖睚痖疋迓岈砑",he:"和合河何核盖贺喝赫荷盒鹤吓呵苛禾菏壑褐涸阂阖劾诃颌嗬貉曷翮纥盍",wo:"我握窝沃卧挝涡斡渥幄蜗喔倭莴龌肟硪",en:"恩摁蒽",n:"嗯唔",er:"而二尔儿耳迩饵洱贰铒珥佴鸸鲕",fa:"发法罚乏伐阀筏砝垡珐",quan:"全权券泉圈拳劝犬铨痊诠荃醛蜷颧绻犭筌鬈悛辁畎",fei:"费非飞肥废菲肺啡沸匪斐蜚妃诽扉翡霏吠绯腓痱芾淝悱狒榧砩鲱篚镄",pei:"配培坏赔佩陪沛裴胚妃霈淠旆帔呸醅辔锫",ping:"平评凭瓶冯屏萍苹乒坪枰娉俜鲆",fo:"佛",hu:"和护户核湖互乎呼胡戏忽虎沪糊壶葫狐蝴弧瑚浒鹄琥扈唬滹惚祜囫斛笏芴醐猢怙唿戽槲觳煳鹕冱瓠虍岵鹱烀轷",ga:"夹咖嘎尬噶旮伽尕钆尜",ge:"个合各革格歌哥盖隔割阁戈葛鸽搁胳舸疙铬骼蛤咯圪镉颌仡硌嗝鬲膈纥袼搿塥哿虼",ha:"哈蛤铪",xia:"下夏峡厦辖霞夹虾狭吓侠暇遐瞎匣瑕唬呷黠硖罅狎瘕柙",gai:"改该盖概溉钙丐芥赅垓陔戤",hai:"海还害孩亥咳骸骇氦嗨胲醢",gan:"干感赶敢甘肝杆赣乾柑尴竿秆橄矸淦苷擀酐绀泔坩旰疳澉",gang:"港钢刚岗纲冈杠缸扛肛罡戆筻",jiang:"将强江港奖讲降疆蒋姜浆匠酱僵桨绛缰犟豇礓洚茳糨耩",hang:"行航杭巷夯吭桁沆绗颃",gong:"工公共供功红贡攻宫巩龚恭拱躬弓汞蚣珙觥肱廾",hong:"红宏洪轰虹鸿弘哄烘泓訇蕻闳讧荭黉薨",guang:"广光逛潢犷胱咣桄",qiong:"穷琼穹邛茕筇跫蛩銎",gao:"高告搞稿膏糕镐皋羔锆杲郜睾诰藁篙缟槁槔",hao:"好号毫豪耗浩郝皓昊皋蒿壕灏嚎濠蚝貉颢嗥薅嚆",li:"理力利立里李历例离励礼丽黎璃厉厘粒莉梨隶栗荔沥犁漓哩狸藜罹篱鲤砺吏澧俐骊溧砾莅锂笠蠡蛎痢雳俪傈醴栎郦俚枥喱逦娌鹂戾砬唳坜疠蜊黧猁鬲粝蓠呖跞疬缡鲡鳢嫠詈悝苈篥轹",jia:"家加价假佳架甲嘉贾驾嫁夹稼钾挟拮迦伽颊浃枷戛荚痂颉镓笳珈岬胛袈郏葭袷瘕铗跏蛱恝哿",luo:"落罗络洛逻螺锣骆萝裸漯烙摞骡咯箩珞捋荦硌雒椤镙跞瘰泺脶猡倮蠃",ke:"可科克客刻课颗渴壳柯棵呵坷恪苛咳磕珂稞瞌溘轲窠嗑疴蝌岢铪颏髁蚵缂氪骒钶锞",qia:"卡恰洽掐髂袷咭葜",gei:"给",gen:"根跟亘艮哏茛",hen:"很狠恨痕哏",gou:"构购够句沟狗钩拘勾苟垢枸篝佝媾诟岣彀缑笱鞲觏遘",kou:"口扣寇叩抠佝蔻芤眍筘",gu:"股古顾故固鼓骨估谷贾姑孤雇辜菇沽咕呱锢钴箍汩梏痼崮轱鸪牯蛊诂毂鹘菰罟嘏臌觚瞽蛄酤牿鲴",pai:"牌排派拍迫徘湃俳哌蒎",gua:"括挂瓜刮寡卦呱褂剐胍诖鸹栝呙",tou:"投头透偷愉骰亠",guai:"怪拐乖",kuai:"会快块筷脍蒯侩浍郐蒉狯哙",guan:"关管观馆官贯冠惯灌罐莞纶棺斡矜倌鹳鳏盥掼涫",wan:"万完晚湾玩碗顽挽弯蔓丸莞皖宛婉腕蜿惋烷琬畹豌剜纨绾脘菀芄箢",ne:"呢哪呐讷疒",gui:"规贵归轨桂柜圭鬼硅瑰跪龟匮闺诡癸鳜桧皈鲑刽晷傀眭妫炅庋簋刿宄匦",jun:"军均俊君峻菌竣钧骏龟浚隽郡筠皲麇捃",jiong:"窘炯迥炅冂扃",jue:"决绝角觉掘崛诀獗抉爵嚼倔厥蕨攫珏矍蹶谲镢鳜噱桷噘撅橛孓觖劂爝",gun:"滚棍辊衮磙鲧绲丨",hun:"婚混魂浑昏棍珲荤馄诨溷阍",guo:"国过果郭锅裹帼涡椁囗蝈虢聒埚掴猓崞蜾呙馘",hei:"黑嘿嗨",kan:"看刊勘堪坎砍侃嵌槛瞰阚龛戡凵莰",heng:"衡横恒亨哼珩桁蘅",mo:"万没么模末冒莫摩墨默磨摸漠脉膜魔沫陌抹寞蘑摹蓦馍茉嘿谟秣蟆貉嫫镆殁耱嬷麽瘼貊貘",peng:"鹏朋彭膨蓬碰苹棚捧亨烹篷澎抨硼怦砰嘭蟛堋",hou:"后候厚侯猴喉吼逅篌糇骺後鲎瘊堠",hua:"化华划话花画滑哗豁骅桦猾铧砉",huai:"怀坏淮徊槐踝",huan:"还环换欢患缓唤焕幻痪桓寰涣宦垸洹浣豢奂郇圜獾鲩鬟萑逭漶锾缳擐",xun:"讯训迅孙寻询循旬巡汛勋逊熏徇浚殉驯鲟薰荀浔洵峋埙巽郇醺恂荨窨蕈曛獯",huang:"黄荒煌皇凰慌晃潢谎惶簧璜恍幌湟蝗磺隍徨遑肓篁鳇蟥癀",nai:"能乃奶耐奈鼐萘氖柰佴艿",luan:"乱卵滦峦鸾栾銮挛孪脔娈",qie:"切且契窃茄砌锲怯伽惬妾趄挈郄箧慊",jian:"建间件见坚检健监减简艰践兼鉴键渐柬剑尖肩舰荐箭浅剪俭碱茧奸歼拣捡煎贱溅槛涧堑笺谏饯锏缄睑謇蹇腱菅翦戬毽笕犍硷鞯牮枧湔鲣囝裥踺搛缣鹣蒹谫僭戋趼楗",nan:"南难男楠喃囡赧腩囝蝻",qian:"前千钱签潜迁欠纤牵浅遣谦乾铅歉黔谴嵌倩钳茜虔堑钎骞阡掮钤扦芊犍荨仟芡悭缱佥愆褰凵肷岍搴箝慊椠",qiang:"强抢疆墙枪腔锵呛羌蔷襁羟跄樯戕嫱戗炝镪锖蜣",xiang:"向项相想乡象响香降像享箱羊祥湘详橡巷翔襄厢镶飨饷缃骧芗庠鲞葙蟓",jiao:"教交较校角觉叫脚缴胶轿郊焦骄浇椒礁佼蕉娇矫搅绞酵剿嚼饺窖跤蛟侥狡姣皎茭峤铰醮鲛湫徼鹪僬噍艽挢敫",zhuo:"着著缴桌卓捉琢灼浊酌拙茁涿镯淖啄濯焯倬擢斫棹诼浞禚",qiao:"桥乔侨巧悄敲俏壳雀瞧翘窍峭锹撬荞跷樵憔鞘橇峤诮谯愀鞒硗劁缲",xiao:"小效销消校晓笑肖削孝萧俏潇硝宵啸嚣霄淆哮筱逍姣箫骁枭哓绡蛸崤枵魈",si:"司四思斯食私死似丝饲寺肆撕泗伺嗣祀厮驷嘶锶俟巳蛳咝耜笥纟糸鸶缌澌姒汜厶兕",kai:"开凯慨岂楷恺揩锴铠忾垲剀锎蒈",jin:"进金今近仅紧尽津斤禁锦劲晋谨筋巾浸襟靳瑾烬缙钅矜觐堇馑荩噤廑妗槿赆衿卺",qin:"亲勤侵秦钦琴禽芹沁寝擒覃噙矜嗪揿溱芩衾廑锓吣檎螓",jing:"经京精境竞景警竟井惊径静劲敬净镜睛晶颈荆兢靖泾憬鲸茎腈菁胫阱旌粳靓痉箐儆迳婧肼刭弪獍",ying:"应营影英景迎映硬盈赢颖婴鹰荧莹樱瑛蝇萦莺颍膺缨瀛楹罂荥萤鹦滢蓥郢茔嘤璎嬴瘿媵撄潆",jiu:"就究九酒久救旧纠舅灸疚揪咎韭玖臼柩赳鸠鹫厩啾阄桕僦鬏",zui:"最罪嘴醉咀蕞觜",juan:"卷捐圈眷娟倦绢隽镌涓鹃鄄蠲狷锩桊",suan:"算酸蒜狻",yun:"员运云允孕蕴韵酝耘晕匀芸陨纭郧筠恽韫郓氲殒愠昀菀狁",qun:"群裙逡麇",ka:"卡喀咖咔咯佧胩",kang:"康抗扛慷炕亢糠伉钪闶",keng:"坑铿吭",kao:"考靠烤拷铐栲尻犒",ken:"肯垦恳啃龈裉",yin:"因引银印音饮阴隐姻殷淫尹荫吟瘾寅茵圻垠鄞湮蚓氤胤龈窨喑铟洇狺夤廴吲霪茚堙",kong:"空控孔恐倥崆箜",ku:"苦库哭酷裤枯窟挎骷堀绔刳喾",kua:"跨夸垮挎胯侉",kui:"亏奎愧魁馈溃匮葵窥盔逵睽馗聩喟夔篑岿喹揆隗傀暌跬蒉愦悝蝰",kuan:"款宽髋",kuang:"况矿框狂旷眶匡筐邝圹哐贶夼诳诓纩",que:"确却缺雀鹊阙瘸榷炔阕悫",kun:"困昆坤捆琨锟鲲醌髡悃阃",kuo:"扩括阔廓蛞",la:"拉落垃腊啦辣蜡喇剌旯砬邋瘌",lai:"来莱赖睐徕籁涞赉濑癞崃疠铼",lan:"兰览蓝篮栏岚烂滥缆揽澜拦懒榄斓婪阑褴罱啉谰镧漤",lin:"林临邻赁琳磷淋麟霖鳞凛拎遴蔺吝粼嶙躏廪檩啉辚膦瞵懔",lang:"浪朗郎廊狼琅榔螂阆锒莨啷蒗稂",liang:"量两粮良辆亮梁凉谅粱晾靓踉莨椋魉墚",lao:"老劳落络牢捞涝烙姥佬崂唠酪潦痨醪铑铹栳耢",mu:"目模木亩幕母牧莫穆姆墓慕牟牡募睦缪沐暮拇姥钼苜仫毪坶",le:"了乐勒肋叻鳓嘞仂泐",lei:"类累雷勒泪蕾垒磊擂镭肋羸耒儡嫘缧酹嘞诔檑",sui:"随岁虽碎尿隧遂髓穗绥隋邃睢祟濉燧谇眭荽",lie:"列烈劣裂猎冽咧趔洌鬣埒捩躐",leng:"冷愣棱楞塄",ling:"领令另零灵龄陵岭凌玲铃菱棱伶羚苓聆翎泠瓴囹绫呤棂蛉酃鲮柃",lia:"俩",liao:"了料疗辽廖聊寥缪僚燎缭撂撩嘹潦镣寮蓼獠钌尥鹩",liu:"流刘六留柳瘤硫溜碌浏榴琉馏遛鎏骝绺镏旒熘鹨锍",lun:"论轮伦仑纶沦抡囵",lv:"率律旅绿虑履吕铝屡氯缕滤侣驴榈闾偻褛捋膂稆",lou:"楼露漏陋娄搂篓喽镂偻瘘髅耧蝼嵝蒌",mao:"贸毛矛冒貌茂茅帽猫髦锚懋袤牦卯铆耄峁瑁蟊茆蝥旄泖昴瞀",long:"龙隆弄垄笼拢聋陇胧珑窿茏咙砻垅泷栊癃",nong:"农浓弄脓侬哝",shuang:"双爽霜孀泷",shu:"术书数属树输束述署熟殊蔬舒疏鼠淑叔暑枢墅俞曙抒竖蜀薯梳戍恕孰沭赎庶漱塾倏澍纾姝菽黍腧秫毹殳疋摅",shuai:"率衰帅摔甩蟀",lve:"略掠锊",ma:"么马吗摩麻码妈玛嘛骂抹蚂唛蟆犸杩",me:"么麽",mai:"买卖麦迈脉埋霾荬劢",man:"满慢曼漫埋蔓瞒蛮鳗馒幔谩螨熳缦镘颟墁鞔嫚",mi:"米密秘迷弥蜜谜觅靡泌眯麋猕谧咪糜宓汨醚嘧弭脒冖幂祢縻蘼芈糸敉",men:"们门闷瞒汶扪焖懑鞔钔",mang:"忙盲茫芒氓莽蟒邙硭漭",meng:"蒙盟梦猛孟萌氓朦锰檬勐懵蟒蜢虻黾蠓艨甍艋瞢礞",miao:"苗秒妙描庙瞄缪渺淼藐缈邈鹋杪眇喵",mou:"某谋牟缪眸哞鍪蛑侔厶",miu:"缪谬",mei:"美没每煤梅媒枚妹眉魅霉昧媚玫酶镁湄寐莓袂楣糜嵋镅浼猸鹛",wen:"文问闻稳温纹吻蚊雯紊瘟汶韫刎璺玟阌",mie:"灭蔑篾乜咩蠛",ming:"明名命鸣铭冥茗溟酩瞑螟暝",na:"内南那纳拿哪娜钠呐捺衲镎肭",nei:"内那哪馁",nuo:"难诺挪娜糯懦傩喏搦锘",ruo:"若弱偌箬",nang:"囊馕囔曩攮",nao:"脑闹恼挠瑙淖孬垴铙桡呶硇猱蛲",ni:"你尼呢泥疑拟逆倪妮腻匿霓溺旎昵坭铌鲵伲怩睨猊",nen:"嫩恁",neng:"能",nin:"您恁",niao:"鸟尿溺袅脲茑嬲",nie:"摄聂捏涅镍孽捻蘖啮蹑嗫臬镊颞乜陧",niang:"娘酿",ning:"宁凝拧泞柠咛狞佞聍甯",nu:"努怒奴弩驽帑孥胬",nv:"女钕衄恧",ru:"入如女乳儒辱汝茹褥孺濡蠕嚅缛溽铷洳薷襦颥蓐",nuan:"暖",nve:"虐疟",re:"热若惹喏",ou:"区欧偶殴呕禺藕讴鸥瓯沤耦怄",pao:"跑炮泡抛刨袍咆疱庖狍匏脬",pou:"剖掊裒",pen:"喷盆湓",pie:"瞥撇苤氕丿",pin:"品贫聘频拼拚颦姘嫔榀牝",se:"色塞瑟涩啬穑铯槭",qing:"情青清请亲轻庆倾顷卿晴氢擎氰罄磬蜻箐鲭綮苘黥圊檠謦",zan:"赞暂攒堑昝簪糌瓒錾趱拶",shao:"少绍召烧稍邵哨韶捎勺梢鞘芍苕劭艄筲杓潲",sao:"扫骚嫂梢缫搔瘙臊埽缲鳋",sha:"沙厦杀纱砂啥莎刹杉傻煞鲨霎嗄痧裟挲铩唼歃",xuan:"县选宣券旋悬轩喧玄绚渲璇炫萱癣漩眩暄煊铉楦泫谖痃碹揎镟儇",ran:"然染燃冉苒髯蚺",rang:"让壤攘嚷瓤穰禳",rao:"绕扰饶娆桡荛",reng:"仍扔",ri:"日",rou:"肉柔揉糅鞣蹂",ruan:"软阮朊",run:"润闰",sa:"萨洒撒飒卅仨脎",suo:"所些索缩锁莎梭琐嗦唆唢娑蓑羧挲桫嗍睃",sai:"思赛塞腮噻鳃",shui:"说水税谁睡氵",sang:"桑丧嗓搡颡磉",sen:"森",seng:"僧",shai:"筛晒",shang:"上商尚伤赏汤裳墒晌垧觞殇熵绱",xing:"行省星腥猩惺兴刑型形邢饧醒幸杏性姓陉荇荥擤悻硎",shou:"收手受首售授守寿瘦兽狩绶艏扌",shuo:"说数硕烁朔铄妁槊蒴搠",su:"速素苏诉缩塑肃俗宿粟溯酥夙愫簌稣僳谡涑蔌嗉觫",shua:"刷耍唰",shuan:"栓拴涮闩",shun:"顺瞬舜吮",song:"送松宋讼颂耸诵嵩淞怂悚崧凇忪竦菘",sou:"艘搜擞嗽嗖叟馊薮飕嗾溲锼螋瞍",sun:"损孙笋荪榫隼狲飧",teng:"腾疼藤滕誊",tie:"铁贴帖餮萜",tu:"土突图途徒涂吐屠兔秃凸荼钍菟堍酴",wai:"外歪崴",wang:"王望往网忘亡旺汪枉妄惘罔辋魍",weng:"翁嗡瓮蓊蕹",zhua:"抓挝爪",yang:"样养央阳洋扬杨羊详氧仰秧痒漾疡泱殃恙鸯徉佯怏炀烊鞅蛘",xiong:"雄兄熊胸凶匈汹芎",yo:"哟唷",yong:"用永拥勇涌泳庸俑踊佣咏雍甬镛臃邕蛹恿慵壅痈鳙墉饔喁",za:"杂扎咱砸咋匝咂拶",zai:"在再灾载栽仔宰哉崽甾",zao:"造早遭枣噪灶燥糟凿躁藻皂澡蚤唣",zei:"贼",zen:"怎谮",zeng:"增曾综赠憎锃甑罾缯",zhei:"这",zou:"走邹奏揍诹驺陬楱鄹鲰",zhuai:"转拽",zun:"尊遵鳟樽撙",dia:"嗲",nou:"耨"})}})); diff --git a/resources/admin/src/components/scFilterBar/pySelect.vue b/resources/admin/src/components/scFilterBar/pySelect.vue new file mode 100644 index 0000000..b0ba7fd --- /dev/null +++ b/resources/admin/src/components/scFilterBar/pySelect.vue @@ -0,0 +1,51 @@ + + + + + \ No newline at end of file diff --git a/resources/admin/src/components/scForm/index.vue b/resources/admin/src/components/scForm/index.vue new file mode 100644 index 0000000..f99fd3a --- /dev/null +++ b/resources/admin/src/components/scForm/index.vue @@ -0,0 +1,295 @@ + + + + + + + diff --git a/resources/admin/src/components/scForm/items/tableselect.vue b/resources/admin/src/components/scForm/items/tableselect.vue new file mode 100644 index 0000000..1a81ec4 --- /dev/null +++ b/resources/admin/src/components/scForm/items/tableselect.vue @@ -0,0 +1,37 @@ + + + + + diff --git a/resources/admin/src/components/scFormTable/index.vue b/resources/admin/src/components/scFormTable/index.vue new file mode 100644 index 0000000..cd921a4 --- /dev/null +++ b/resources/admin/src/components/scFormTable/index.vue @@ -0,0 +1,123 @@ + + + + + + + diff --git a/resources/admin/src/components/scIconSelect/index.vue b/resources/admin/src/components/scIconSelect/index.vue new file mode 100644 index 0000000..9d316a6 --- /dev/null +++ b/resources/admin/src/components/scIconSelect/index.vue @@ -0,0 +1,128 @@ + + + + + + + diff --git a/resources/admin/src/components/scMapSelect/index.vue b/resources/admin/src/components/scMapSelect/index.vue new file mode 100644 index 0000000..e244661 --- /dev/null +++ b/resources/admin/src/components/scMapSelect/index.vue @@ -0,0 +1,78 @@ + + + \ No newline at end of file diff --git a/resources/admin/src/components/scMini/scStatusIndicator.vue b/resources/admin/src/components/scMini/scStatusIndicator.vue new file mode 100644 index 0000000..4e51b40 --- /dev/null +++ b/resources/admin/src/components/scMini/scStatusIndicator.vue @@ -0,0 +1,47 @@ + + + + + + + diff --git a/resources/admin/src/components/scMini/scTrend.vue b/resources/admin/src/components/scMini/scTrend.vue new file mode 100644 index 0000000..304c44d --- /dev/null +++ b/resources/admin/src/components/scMini/scTrend.vue @@ -0,0 +1,66 @@ + + + + + + + diff --git a/resources/admin/src/components/scPageHeader/index.vue b/resources/admin/src/components/scPageHeader/index.vue new file mode 100644 index 0000000..9ca2d35 --- /dev/null +++ b/resources/admin/src/components/scPageHeader/index.vue @@ -0,0 +1,52 @@ + + + + + + + diff --git a/resources/admin/src/components/scPasswordStrength/index.vue b/resources/admin/src/components/scPasswordStrength/index.vue new file mode 100644 index 0000000..e80ef7a --- /dev/null +++ b/resources/admin/src/components/scPasswordStrength/index.vue @@ -0,0 +1,92 @@ + + + + + + + diff --git a/resources/admin/src/components/scQrCode/index.vue b/resources/admin/src/components/scQrCode/index.vue new file mode 100644 index 0000000..4d8de30 --- /dev/null +++ b/resources/admin/src/components/scQrCode/index.vue @@ -0,0 +1,88 @@ + + + + + + + diff --git a/resources/admin/src/components/scQrCode/qrcode.js b/resources/admin/src/components/scQrCode/qrcode.js new file mode 100644 index 0000000..8a46dfa --- /dev/null +++ b/resources/admin/src/components/scQrCode/qrcode.js @@ -0,0 +1,618 @@ +/** + * @fileoverview + * - Using the 'QRCode for Javascript library' + * - Fixed dataset of 'QRCode for Javascript library' for support full-spec. + * - this library has no dependencies. + * - source page: https://github.com/makevoid/qrcodejs + * + * + * @author davidshimjs + * @see http://www.d-project.com/ + * @see http://jeromeetienne.github.com/jquery-qrcode/ + */ +var QRCode; + +(function () { + //--------------------------------------------------------------------- + // QRCode for JavaScript + // + // Copyright (c) 2009 Kazuhiko Arase + // + // URL: http://www.d-project.com/ + // + // Licensed under the MIT license: + // http://www.opensource.org/licenses/mit-license.php + // + // The word "QR Code" is registered trademark of + // DENSO WAVE INCORPORATED + // http://www.denso-wave.com/qrcode/faqpatent-e.html + // + //--------------------------------------------------------------------- + function QR8bitByte(data) { + this.mode = QRMode.MODE_8BIT_BYTE; + this.data = data; + this.parsedData = []; + + // Added to support UTF-8 Characters + for (var i = 0, l = this.data.length; i < l; i++) { + var byteArray = []; + var code = this.data.charCodeAt(i); + + if (code > 0x10000) { + byteArray[0] = 0xF0 | ((code & 0x1C0000) >>> 18); + byteArray[1] = 0x80 | ((code & 0x3F000) >>> 12); + byteArray[2] = 0x80 | ((code & 0xFC0) >>> 6); + byteArray[3] = 0x80 | (code & 0x3F); + } else if (code > 0x800) { + byteArray[0] = 0xE0 | ((code & 0xF000) >>> 12); + byteArray[1] = 0x80 | ((code & 0xFC0) >>> 6); + byteArray[2] = 0x80 | (code & 0x3F); + } else if (code > 0x80) { + byteArray[0] = 0xC0 | ((code & 0x7C0) >>> 6); + byteArray[1] = 0x80 | (code & 0x3F); + } else { + byteArray[0] = code; + } + + this.parsedData.push(byteArray); + } + + this.parsedData = Array.prototype.concat.apply([], this.parsedData); + + if (this.parsedData.length != this.data.length) { + this.parsedData.unshift(191); + this.parsedData.unshift(187); + this.parsedData.unshift(239); + } + } + + QR8bitByte.prototype = { + getLength: function (buffer) { + return this.parsedData.length; + }, + write: function (buffer) { + for (var i = 0, l = this.parsedData.length; i < l; i++) { + buffer.put(this.parsedData[i], 8); + } + } + }; + + function QRCodeModel(typeNumber, errorCorrectLevel) { + this.typeNumber = typeNumber; + this.errorCorrectLevel = errorCorrectLevel; + this.modules = null; + this.moduleCount = 0; + this.dataCache = null; + this.dataList = []; + } + + QRCodeModel.prototype={addData:function(data){var newData=new QR8bitByte(data);this.dataList.push(newData);this.dataCache=null;},isDark:function(row,col){if(row<0||this.moduleCount<=row||col<0||this.moduleCount<=col){throw new Error(row+","+col);} + return this.modules[row][col];},getModuleCount:function(){return this.moduleCount;},make:function(){this.makeImpl(false,this.getBestMaskPattern());},makeImpl:function(test,maskPattern){this.moduleCount=this.typeNumber*4+17;this.modules=new Array(this.moduleCount);for(var row=0;row=7){this.setupTypeNumber(test);} + if(this.dataCache==null){this.dataCache=QRCodeModel.createData(this.typeNumber,this.errorCorrectLevel,this.dataList);} + this.mapData(this.dataCache,maskPattern);},setupPositionProbePattern:function(row,col){for(var r=-1;r<=7;r++){if(row+r<=-1||this.moduleCount<=row+r)continue;for(var c=-1;c<=7;c++){if(col+c<=-1||this.moduleCount<=col+c)continue;if((0<=r&&r<=6&&(c==0||c==6))||(0<=c&&c<=6&&(r==0||r==6))||(2<=r&&r<=4&&2<=c&&c<=4)){this.modules[row+r][col+c]=true;}else{this.modules[row+r][col+c]=false;}}}},getBestMaskPattern:function(){var minLostPoint=0;var pattern=0;for(var i=0;i<8;i++){this.makeImpl(true,i);var lostPoint=QRUtil.getLostPoint(this);if(i==0||minLostPoint>lostPoint){minLostPoint=lostPoint;pattern=i;}} + return pattern;},createMovieClip:function(target_mc,instance_name,depth){var qr_mc=target_mc.createEmptyMovieClip(instance_name,depth);var cs=1;this.make();for(var row=0;row>i)&1)==1);this.modules[Math.floor(i/3)][i%3+this.moduleCount-8-3]=mod;} + for(var i=0;i<18;i++){var mod=(!test&&((bits>>i)&1)==1);this.modules[i%3+this.moduleCount-8-3][Math.floor(i/3)]=mod;}},setupTypeInfo:function(test,maskPattern){var data=(this.errorCorrectLevel<<3)|maskPattern;var bits=QRUtil.getBCHTypeInfo(data);for(var i=0;i<15;i++){var mod=(!test&&((bits>>i)&1)==1);if(i<6){this.modules[i][8]=mod;}else if(i<8){this.modules[i+1][8]=mod;}else{this.modules[this.moduleCount-15+i][8]=mod;}} + for(var i=0;i<15;i++){var mod=(!test&&((bits>>i)&1)==1);if(i<8){this.modules[8][this.moduleCount-i-1]=mod;}else if(i<9){this.modules[8][15-i-1+1]=mod;}else{this.modules[8][15-i-1]=mod;}} + this.modules[this.moduleCount-8][8]=(!test);},mapData:function(data,maskPattern){var inc=-1;var row=this.moduleCount-1;var bitIndex=7;var byteIndex=0;for(var col=this.moduleCount-1;col>0;col-=2){if(col==6)col--;while(true){for(var c=0;c<2;c++){if(this.modules[row][col-c]==null){var dark=false;if(byteIndex>>bitIndex)&1)==1);} + var mask=QRUtil.getMask(maskPattern,row,col-c);if(mask){dark=!dark;} + this.modules[row][col-c]=dark;bitIndex--;if(bitIndex==-1){byteIndex++;bitIndex=7;}}} + row+=inc;if(row<0||this.moduleCount<=row){row-=inc;inc=-inc;break;}}}}};QRCodeModel.PAD0=0xEC;QRCodeModel.PAD1=0x11;QRCodeModel.createData=function(typeNumber,errorCorrectLevel,dataList){var rsBlocks=QRRSBlock.getRSBlocks(typeNumber,errorCorrectLevel);var buffer=new QRBitBuffer();for(var i=0;itotalDataCount*8){throw new Error("code length overflow. (" + +buffer.getLengthInBits() + +">" + +totalDataCount*8 + +")");} + if(buffer.getLengthInBits()+4<=totalDataCount*8){buffer.put(0,4);} + while(buffer.getLengthInBits()%8!=0){buffer.putBit(false);} + while(true){if(buffer.getLengthInBits()>=totalDataCount*8){break;} + buffer.put(QRCodeModel.PAD0,8);if(buffer.getLengthInBits()>=totalDataCount*8){break;} + buffer.put(QRCodeModel.PAD1,8);} + return QRCodeModel.createBytes(buffer,rsBlocks);};QRCodeModel.createBytes=function(buffer,rsBlocks){var offset=0;var maxDcCount=0;var maxEcCount=0;var dcdata=new Array(rsBlocks.length);var ecdata=new Array(rsBlocks.length);for(var r=0;r=0)?modPoly.get(modIndex):0;}} + var totalCodeCount=0;for(var i=0;i=0){d^=(QRUtil.G15<<(QRUtil.getBCHDigit(d)-QRUtil.getBCHDigit(QRUtil.G15)));} + return((data<<10)|d)^QRUtil.G15_MASK;},getBCHTypeNumber:function(data){var d=data<<12;while(QRUtil.getBCHDigit(d)-QRUtil.getBCHDigit(QRUtil.G18)>=0){d^=(QRUtil.G18<<(QRUtil.getBCHDigit(d)-QRUtil.getBCHDigit(QRUtil.G18)));} + return(data<<12)|d;},getBCHDigit:function(data){var digit=0;while(data!=0){digit++;data>>>=1;} + return digit;},getPatternPosition:function(typeNumber){return QRUtil.PATTERN_POSITION_TABLE[typeNumber-1];},getMask:function(maskPattern,i,j){switch(maskPattern){case QRMaskPattern.PATTERN000:return(i+j)%2==0;case QRMaskPattern.PATTERN001:return i%2==0;case QRMaskPattern.PATTERN010:return j%3==0;case QRMaskPattern.PATTERN011:return(i+j)%3==0;case QRMaskPattern.PATTERN100:return(Math.floor(i/2)+Math.floor(j/3))%2==0;case QRMaskPattern.PATTERN101:return(i*j)%2+(i*j)%3==0;case QRMaskPattern.PATTERN110:return((i*j)%2+(i*j)%3)%2==0;case QRMaskPattern.PATTERN111:return((i*j)%3+(i+j)%2)%2==0;default:throw new Error("bad maskPattern:"+maskPattern);}},getErrorCorrectPolynomial:function(errorCorrectLength){var a=new QRPolynomial([1],0);for(var i=0;i5){lostPoint+=(3+sameCount-5);}}} + for(var row=0;row=256){n-=255;} + return QRMath.EXP_TABLE[n];},EXP_TABLE:new Array(256),LOG_TABLE:new Array(256)};for(var i=0;i<8;i++){QRMath.EXP_TABLE[i]=1<>>(7-index%8))&1)==1;},put:function(num,length){for(var i=0;i>>(length-i-1))&1)==1);}},getLengthInBits:function(){return this.length;},putBit:function(bit){var bufIndex=Math.floor(this.length/8);if(this.buffer.length<=bufIndex){this.buffer.push(0);} + if(bit){this.buffer[bufIndex]|=(0x80>>>(this.length%8));} + this.length++;}};var QRCodeLimitLength=[[17,14,11,7],[32,26,20,14],[53,42,32,24],[78,62,46,34],[106,84,60,44],[134,106,74,58],[154,122,86,64],[192,152,108,84],[230,180,130,98],[271,213,151,119],[321,251,177,137],[367,287,203,155],[425,331,241,177],[458,362,258,194],[520,412,292,220],[586,450,322,250],[644,504,364,280],[718,560,394,310],[792,624,442,338],[858,666,482,382],[929,711,509,403],[1003,779,565,439],[1091,857,611,461],[1171,911,661,511],[1273,997,715,535],[1367,1059,751,593],[1465,1125,805,625],[1528,1190,868,658],[1628,1264,908,698],[1732,1370,982,742],[1840,1452,1030,790],[1952,1538,1112,842],[2068,1628,1168,898],[2188,1722,1228,958],[2303,1809,1283,983],[2431,1911,1351,1051],[2563,1989,1423,1093],[2699,2099,1499,1139],[2809,2213,1579,1219],[2953,2331,1663,1273]]; + + function _isSupportCanvas() { + return typeof CanvasRenderingContext2D != "undefined"; + } + + // android 2.x doesn't support Data-URI spec + function _getAndroid() { + var android = false; + var sAgent = navigator.userAgent; + + if (/android/i.test(sAgent)) { // android + android = true; + var aMat = sAgent.toString().match(/android ([0-9]\.[0-9])/i); + + if (aMat && aMat[1]) { + android = parseFloat(aMat[1]); + } + } + + return android; + } + + var svgDrawer = (function() { + + var Drawing = function (el, htOption) { + this._el = el; + this._htOption = htOption; + }; + + Drawing.prototype.draw = function (oQRCode) { + var _htOption = this._htOption; + var _el = this._el; + var nCount = oQRCode.getModuleCount(); + var nWidth = Math.floor(_htOption.width / nCount); + var nHeight = Math.floor(_htOption.height / nCount); + + this.clear(); + + function makeSVG(tag, attrs) { + var el = document.createElementNS('http://www.w3.org/2000/svg', tag); + for (var k in attrs) + if (attrs.hasOwnProperty(k)) el.setAttribute(k, attrs[k]); + return el; + } + + var svg = makeSVG("svg" , {'viewBox': '0 0 ' + String(nCount) + " " + String(nCount), 'width': '100%', 'height': '100%', 'fill': _htOption.colorLight}); + svg.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:xlink", "http://www.w3.org/1999/xlink"); + _el.appendChild(svg); + + svg.appendChild(makeSVG("rect", {"fill": _htOption.colorLight, "width": "100%", "height": "100%"})); + svg.appendChild(makeSVG("rect", {"fill": _htOption.colorDark, "width": "1", "height": "1", "id": "template"})); + + for (var row = 0; row < nCount; row++) { + for (var col = 0; col < nCount; col++) { + if (oQRCode.isDark(row, col)) { + var child = makeSVG("use", {"x": String(col), "y": String(row)}); + child.setAttributeNS("http://www.w3.org/1999/xlink", "href", "#template") + svg.appendChild(child); + } + } + } + }; + Drawing.prototype.clear = function () { + while (this._el.hasChildNodes()) + this._el.removeChild(this._el.lastChild); + }; + return Drawing; + })(); + + var useSVG = document.documentElement.tagName.toLowerCase() === "svg"; + + // Drawing in DOM by using Table tag + var Drawing = useSVG ? svgDrawer : !_isSupportCanvas() ? (function () { + var Drawing = function (el, htOption) { + this._el = el; + this._htOption = htOption; + }; + + /** + * Draw the QRCode + * + * @param {QRCode} oQRCode + */ + Drawing.prototype.draw = function (oQRCode) { + var _htOption = this._htOption; + var _el = this._el; + var nCount = oQRCode.getModuleCount(); + var nWidth = Math.floor(_htOption.width / nCount); + var nHeight = Math.floor(_htOption.height / nCount); + var aHTML = ['']; + + for (var row = 0; row < nCount; row++) { + aHTML.push(''); + + for (var col = 0; col < nCount; col++) { + aHTML.push(''); + } + + aHTML.push(''); + } + + aHTML.push('
'); + _el.innerHTML = aHTML.join(''); + + // Fix the margin values as real size. + var elTable = _el.childNodes[0]; + var nLeftMarginTable = (_htOption.width - elTable.offsetWidth) / 2; + var nTopMarginTable = (_htOption.height - elTable.offsetHeight) / 2; + + if (nLeftMarginTable > 0 && nTopMarginTable > 0) { + elTable.style.margin = nTopMarginTable + "px " + nLeftMarginTable + "px"; + } + }; + + /** + * Clear the QRCode + */ + Drawing.prototype.clear = function () { + this._el.innerHTML = ''; + }; + + return Drawing; + })() : (function () { // Drawing in Canvas + function _onMakeImage() { + this._elImage.src = this._elCanvas.toDataURL("image/png"); + this._elImage.style.display = "block"; + this._elCanvas.style.display = "none"; + } + + // Android 2.1 bug workaround + // http://code.google.com/p/android/issues/detail?id=5141 + if (this && this._android && this._android <= 2.1) { + var factor = 1 / window.devicePixelRatio; + var drawImage = CanvasRenderingContext2D.prototype.drawImage; + CanvasRenderingContext2D.prototype.drawImage = function (image, sx, sy, sw, sh, dx, dy, dw, dh) { + if (("nodeName" in image) && /img/i.test(image.nodeName)) { + for (var i = arguments.length - 1; i >= 1; i--) { + arguments[i] = arguments[i] * factor; + } + } else if (typeof dw == "undefined") { + arguments[1] *= factor; + arguments[2] *= factor; + arguments[3] *= factor; + arguments[4] *= factor; + } + + drawImage.apply(this, arguments); + }; + } + + /** + * Check whether the user's browser supports Data URI or not + * + * @private + * @param {Function} fSuccess Occurs if it supports Data URI + * @param {Function} fFail Occurs if it doesn't support Data URI + */ + function _safeSetDataURI(fSuccess, fFail) { + var self = this; + self._fFail = fFail; + self._fSuccess = fSuccess; + + // Check it just once + if (self._bSupportDataURI === null) { + var el = document.createElement("img"); + var fOnError = function() { + self._bSupportDataURI = false; + + if (self._fFail) { + self._fFail.call(self); + } + }; + var fOnSuccess = function() { + self._bSupportDataURI = true; + + if (self._fSuccess) { + self._fSuccess.call(self); + } + }; + + el.onabort = fOnError; + el.onerror = fOnError; + el.onload = fOnSuccess; + el.src = "data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="; // the Image contains 1px data. + return; + } else if (self._bSupportDataURI === true && self._fSuccess) { + self._fSuccess.call(self); + } else if (self._bSupportDataURI === false && self._fFail) { + self._fFail.call(self); + } + }; + + /** + * Drawing QRCode by using canvas + * + * @constructor + * @param {HTMLElement} el + * @param {Object} htOption QRCode Options + */ + var Drawing = function (el, htOption) { + this._bIsPainted = false; + this._android = _getAndroid(); + + this._htOption = htOption; + this._elCanvas = document.createElement("canvas"); + this._elCanvas.width = htOption.width; + this._elCanvas.height = htOption.height; + el.appendChild(this._elCanvas); + this._el = el; + this._oContext = this._elCanvas.getContext("2d"); + this._bIsPainted = false; + this._elImage = document.createElement("img"); + this._elImage.alt = "Scan me!"; + this._elImage.style.display = "none"; + this._el.appendChild(this._elImage); + this._bSupportDataURI = null; + }; + + /** + * Draw the QRCode + * + * @param {QRCode} oQRCode + */ + Drawing.prototype.draw = function (oQRCode) { + var _elImage = this._elImage; + var _oContext = this._oContext; + var _htOption = this._htOption; + + var nCount = oQRCode.getModuleCount(); + var nWidth = _htOption.width / nCount; + var nHeight = _htOption.height / nCount; + var nRoundedWidth = Math.round(nWidth); + var nRoundedHeight = Math.round(nHeight); + + _elImage.style.display = "none"; + this.clear(); + + for (var row = 0; row < nCount; row++) { + for (var col = 0; col < nCount; col++) { + var bIsDark = oQRCode.isDark(row, col); + var nLeft = col * nWidth; + var nTop = row * nHeight; + _oContext.strokeStyle = bIsDark ? _htOption.colorDark : _htOption.colorLight; + _oContext.lineWidth = 1; + _oContext.fillStyle = bIsDark ? _htOption.colorDark : _htOption.colorLight; + _oContext.fillRect(nLeft, nTop, nWidth, nHeight); + + // 안티 앨리어싱 방지 처리 + _oContext.strokeRect( + Math.floor(nLeft) + 0.5, + Math.floor(nTop) + 0.5, + nRoundedWidth, + nRoundedHeight + ); + + _oContext.strokeRect( + Math.ceil(nLeft) - 0.5, + Math.ceil(nTop) - 0.5, + nRoundedWidth, + nRoundedHeight + ); + } + } + + this._bIsPainted = true; + }; + + /** + * Make the image from Canvas if the browser supports Data URI. + */ + Drawing.prototype.makeImage = function () { + if (this._bIsPainted) { + _safeSetDataURI.call(this, _onMakeImage); + } + }; + + /** + * Return whether the QRCode is painted or not + * + * @return {Boolean} + */ + Drawing.prototype.isPainted = function () { + return this._bIsPainted; + }; + + /** + * Clear the QRCode + */ + Drawing.prototype.clear = function () { + this._oContext.clearRect(0, 0, this._elCanvas.width, this._elCanvas.height); + this._bIsPainted = false; + }; + + /** + * @private + * @param {Number} nNumber + */ + Drawing.prototype.round = function (nNumber) { + if (!nNumber) { + return nNumber; + } + + return Math.floor(nNumber * 1000) / 1000; + }; + + return Drawing; + })(); + + /** + * Get the type by string length + * + * @private + * @param {String} sText + * @param {Number} nCorrectLevel + * @return {Number} type + */ + function _getTypeNumber(sText, nCorrectLevel) { + var nType = 1; + var length = _getUTF8Length(sText); + + for (var i = 0, len = QRCodeLimitLength.length; i <= len; i++) { + var nLimit = 0; + + switch (nCorrectLevel) { + case QRErrorCorrectLevel.L : + nLimit = QRCodeLimitLength[i][0]; + break; + case QRErrorCorrectLevel.M : + nLimit = QRCodeLimitLength[i][1]; + break; + case QRErrorCorrectLevel.Q : + nLimit = QRCodeLimitLength[i][2]; + break; + case QRErrorCorrectLevel.H : + nLimit = QRCodeLimitLength[i][3]; + break; + } + + if (length <= nLimit) { + break; + } else { + nType++; + } + } + + if (nType > QRCodeLimitLength.length) { + throw new Error("Too long data"); + } + + return nType; + } + + function _getUTF8Length(sText) { + var replacedText = encodeURI(sText).toString().replace(/\%[0-9a-fA-F]{2}/g, 'a'); + return replacedText.length + (replacedText.length != sText ? 3 : 0); + } + + /** + * @class QRCode + * @constructor + * @example + * new QRCode(document.getElementById("test"), "http://jindo.dev.naver.com/collie"); + * + * @example + * var oQRCode = new QRCode("test", { + * text : "http://naver.com", + * width : 128, + * height : 128 + * }); + * + * oQRCode.clear(); // Clear the QRCode. + * oQRCode.makeCode("http://map.naver.com"); // Re-create the QRCode. + * + * @param {HTMLElement|String} el target element or 'id' attribute of element. + * @param {Object|String} vOption + * @param {String} vOption.text QRCode link data + * @param {Number} [vOption.width=256] + * @param {Number} [vOption.height=256] + * @param {String} [vOption.colorDark="#000000"] + * @param {String} [vOption.colorLight="#ffffff"] + * @param {QRCode.CorrectLevel} [vOption.correctLevel=QRCode.CorrectLevel.H] [L|M|Q|H] + */ + QRCode = function (el, vOption) { + this._htOption = { + width : 256, + height : 256, + typeNumber : 4, + colorDark : "#000000", + colorLight : "#ffffff", + correctLevel : QRErrorCorrectLevel.H + }; + + if (typeof vOption === 'string') { + vOption = { + text : vOption + }; + } + + // Overwrites options + if (vOption) { + for (var i in vOption) { + this._htOption[i] = vOption[i]; + } + } + + if (typeof el == "string") { + el = document.getElementById(el); + } + + if (this._htOption.useSVG) { + Drawing = svgDrawer; + } + + this._android = _getAndroid(); + this._el = el; + this._oQRCode = null; + this._oDrawing = new Drawing(this._el, this._htOption); + + if (this._htOption.text) { + this.makeCode(this._htOption.text); + } + }; + + /** + * Make the QRCode + * + * @param {String} sText link data + */ + QRCode.prototype.makeCode = function (sText) { + this._oQRCode = new QRCodeModel(_getTypeNumber(sText, this._htOption.correctLevel), this._htOption.correctLevel); + this._oQRCode.addData(sText); + this._oQRCode.make(); + this._el.title = sText; + this._oDrawing.draw(this._oQRCode); + this.makeImage(); + }; + + /** + * Make the Image from Canvas element + * - It occurs automatically + * - Android below 3 doesn't support Data-URI spec. + * + * @private + */ + QRCode.prototype.makeImage = function () { + if (typeof this._oDrawing.makeImage == "function" && (!this._android || this._android >= 3)) { + this._oDrawing.makeImage(); + } + }; + + /** + * Clear the QRCode + */ + QRCode.prototype.clear = function () { + this._oDrawing.clear(); + }; + + /** + * @name QRCode.CorrectLevel + */ + QRCode.CorrectLevel = QRErrorCorrectLevel; +})(); + +export default QRCode; \ No newline at end of file diff --git a/resources/admin/src/components/scSelect/index.vue b/resources/admin/src/components/scSelect/index.vue new file mode 100644 index 0000000..d4dffd8 --- /dev/null +++ b/resources/admin/src/components/scSelect/index.vue @@ -0,0 +1,94 @@ + + + + + + + \ No newline at end of file diff --git a/resources/admin/src/components/scSelectFilter/index.vue b/resources/admin/src/components/scSelectFilter/index.vue new file mode 100644 index 0000000..c62fd19 --- /dev/null +++ b/resources/admin/src/components/scSelectFilter/index.vue @@ -0,0 +1,127 @@ + + + + + + + diff --git a/resources/admin/src/components/scSelectRemote/index.vue b/resources/admin/src/components/scSelectRemote/index.vue new file mode 100644 index 0000000..bc015b3 --- /dev/null +++ b/resources/admin/src/components/scSelectRemote/index.vue @@ -0,0 +1,48 @@ + + + \ No newline at end of file diff --git a/resources/admin/src/components/scSelectTree/index.vue b/resources/admin/src/components/scSelectTree/index.vue new file mode 100644 index 0000000..c421b56 --- /dev/null +++ b/resources/admin/src/components/scSelectTree/index.vue @@ -0,0 +1,45 @@ + + + + + diff --git a/resources/admin/src/components/scStatistic/index.vue b/resources/admin/src/components/scStatistic/index.vue new file mode 100644 index 0000000..e32bbf2 --- /dev/null +++ b/resources/admin/src/components/scStatistic/index.vue @@ -0,0 +1,70 @@ + + + + + + + diff --git a/resources/admin/src/components/scTable/column.js b/resources/admin/src/components/scTable/column.js new file mode 100644 index 0000000..af5ab1f --- /dev/null +++ b/resources/admin/src/components/scTable/column.js @@ -0,0 +1,23 @@ +import { h, resolveComponent } from 'vue' + +export default { + render() { + return h ( + resolveComponent("el-table-column"), + { + index: this.index, + ...this.$attrs + }, + this.$slots + ) + }, + methods: { + index(index){ + if(this.$attrs.type=="index"){ + let page = this.$parent.$parent.currentPage + let pageSize = this.$parent.$parent.pageSize + return (page - 1) * pageSize + index + 1 + } + } + } +} diff --git a/resources/admin/src/components/scTable/columnSetting.vue b/resources/admin/src/components/scTable/columnSetting.vue new file mode 100644 index 0000000..f157ef2 --- /dev/null +++ b/resources/admin/src/components/scTable/columnSetting.vue @@ -0,0 +1,120 @@ + + + + + diff --git a/resources/admin/src/components/scTable/index.vue b/resources/admin/src/components/scTable/index.vue new file mode 100644 index 0000000..0d2ffed --- /dev/null +++ b/resources/admin/src/components/scTable/index.vue @@ -0,0 +1,409 @@ + + + + + + + diff --git a/resources/admin/src/components/scTableSelect/index.vue b/resources/admin/src/components/scTableSelect/index.vue new file mode 100644 index 0000000..8ae7d6a --- /dev/null +++ b/resources/admin/src/components/scTableSelect/index.vue @@ -0,0 +1,233 @@ + + + + + + + diff --git a/resources/admin/src/components/scTitle/index.vue b/resources/admin/src/components/scTitle/index.vue new file mode 100644 index 0000000..a01e4fa --- /dev/null +++ b/resources/admin/src/components/scTitle/index.vue @@ -0,0 +1,27 @@ + + + + + diff --git a/resources/admin/src/components/scUpload/file.vue b/resources/admin/src/components/scUpload/file.vue new file mode 100644 index 0000000..c8cb476 --- /dev/null +++ b/resources/admin/src/components/scUpload/file.vue @@ -0,0 +1,193 @@ + + + + + diff --git a/resources/admin/src/components/scUpload/index.vue b/resources/admin/src/components/scUpload/index.vue new file mode 100644 index 0000000..6af7640 --- /dev/null +++ b/resources/admin/src/components/scUpload/index.vue @@ -0,0 +1,282 @@ + + + + + diff --git a/resources/admin/src/components/scUpload/multiple.vue b/resources/admin/src/components/scUpload/multiple.vue new file mode 100644 index 0000000..ec30d33 --- /dev/null +++ b/resources/admin/src/components/scUpload/multiple.vue @@ -0,0 +1,249 @@ + + + + + diff --git a/resources/admin/src/components/scUserSelect/index.vue b/resources/admin/src/components/scUserSelect/index.vue new file mode 100644 index 0000000..cbacdcc --- /dev/null +++ b/resources/admin/src/components/scUserSelect/index.vue @@ -0,0 +1,265 @@ + + + + + + + \ No newline at end of file diff --git a/resources/admin/src/components/scVideo/index.vue b/resources/admin/src/components/scVideo/index.vue new file mode 100644 index 0000000..0777ce0 --- /dev/null +++ b/resources/admin/src/components/scVideo/index.vue @@ -0,0 +1,84 @@ + + + + + + + diff --git a/resources/admin/src/components/scWaterMark/index.vue b/resources/admin/src/components/scWaterMark/index.vue new file mode 100644 index 0000000..bb93890 --- /dev/null +++ b/resources/admin/src/components/scWaterMark/index.vue @@ -0,0 +1,66 @@ + + + + + + + diff --git a/resources/admin/src/components/scWorkflow/index.vue b/resources/admin/src/components/scWorkflow/index.vue new file mode 100644 index 0000000..d8b1766 --- /dev/null +++ b/resources/admin/src/components/scWorkflow/index.vue @@ -0,0 +1,154 @@ + + + + + + + diff --git a/resources/admin/src/components/scWorkflow/nodeWrap.vue b/resources/admin/src/components/scWorkflow/nodeWrap.vue new file mode 100644 index 0000000..b2ff83e --- /dev/null +++ b/resources/admin/src/components/scWorkflow/nodeWrap.vue @@ -0,0 +1,57 @@ + + + + + diff --git a/resources/admin/src/components/scWorkflow/nodes/addNode.vue b/resources/admin/src/components/scWorkflow/nodes/addNode.vue new file mode 100644 index 0000000..ff693cf --- /dev/null +++ b/resources/admin/src/components/scWorkflow/nodes/addNode.vue @@ -0,0 +1,102 @@ + + + + + diff --git a/resources/admin/src/components/scWorkflow/nodes/approver.vue b/resources/admin/src/components/scWorkflow/nodes/approver.vue new file mode 100644 index 0000000..4752c30 --- /dev/null +++ b/resources/admin/src/components/scWorkflow/nodes/approver.vue @@ -0,0 +1,193 @@ + + + + + diff --git a/resources/admin/src/components/scWorkflow/nodes/branch.vue b/resources/admin/src/components/scWorkflow/nodes/branch.vue new file mode 100644 index 0000000..9da0b4a --- /dev/null +++ b/resources/admin/src/components/scWorkflow/nodes/branch.vue @@ -0,0 +1,222 @@ + + + + + diff --git a/resources/admin/src/components/scWorkflow/nodes/promoter.vue b/resources/admin/src/components/scWorkflow/nodes/promoter.vue new file mode 100644 index 0000000..ebfa74f --- /dev/null +++ b/resources/admin/src/components/scWorkflow/nodes/promoter.vue @@ -0,0 +1,106 @@ + + + + + diff --git a/resources/admin/src/components/scWorkflow/nodes/send.vue b/resources/admin/src/components/scWorkflow/nodes/send.vue new file mode 100644 index 0000000..2bbd82e --- /dev/null +++ b/resources/admin/src/components/scWorkflow/nodes/send.vue @@ -0,0 +1,118 @@ + + + + + diff --git a/resources/admin/src/components/scWorkflow/select.vue b/resources/admin/src/components/scWorkflow/select.vue new file mode 100644 index 0000000..4e45b38 --- /dev/null +++ b/resources/admin/src/components/scWorkflow/select.vue @@ -0,0 +1,269 @@ + + + + + diff --git a/resources/admin/src/config/fileSelect.js b/resources/admin/src/config/fileSelect.js new file mode 100644 index 0000000..920a54a --- /dev/null +++ b/resources/admin/src/config/fileSelect.js @@ -0,0 +1,69 @@ +import API from "@/api"; + +//文件选择器配置 + +export default { + apiObj: API.common.upload, + menuApiObj: API.common.file.menu, + listApiObj: API.common.file.list, + successCode: 1, + maxSize: 30, + max: 99, + uploadParseData: function (res) { + return { + id: res.data.id, + fileName: res.data.fileName, + url: res.data.src + } + }, + listParseData: function (res) { + return { + rows: res.data.data, + total: res.data.total, + msg: res.message, + code: res.code + } + }, + request: { + page: 'page', + pageSize: 'pageSize', + keyword: 'keyword', + menuKey: 'groupId' + }, + menuProps: { + key: 'id', + label: 'title', + children: 'children' + }, + fileProps: { + key: 'id', + fileName: 'fileName', + url: 'url' + }, + files: { + doc: { + icon: 'sc-icon-file-word-2-fill', + color: '#409eff' + }, + docx: { + icon: 'sc-icon-file-word-2-fill', + color: '#409eff' + }, + xls: { + icon: 'sc-icon-file-excel-2-fill', + color: '#67C23A' + }, + xlsx: { + icon: 'sc-icon-file-excel-2-fill', + color: '#67C23A' + }, + ppt: { + icon: 'sc-icon-file-ppt-2-fill', + color: '#F56C6C' + }, + pptx: { + icon: 'sc-icon-file-ppt-2-fill', + color: '#F56C6C' + } + } +} diff --git a/resources/admin/src/config/filterBar.js b/resources/admin/src/config/filterBar.js new file mode 100644 index 0000000..15ed840 --- /dev/null +++ b/resources/admin/src/config/filterBar.js @@ -0,0 +1,74 @@ +export default { + //运算符 + operator: [ + { + label: '等于', + value: '=', + }, + { + label: '不等于', + value: '!=', + }, + { + label: '大于', + value: '>', + }, + { + label: '大于等于', + value: '>=', + }, + { + label: '小于', + value: '<', + }, + { + label: '小于等于', + value: '<=', + }, + { + label: '包含', + value: 'include', + }, + { + label: '不包含', + value: 'notinclude', + } + ], + //过滤结果运算符的分隔符 + separator: '|', + //获取我的常用 + getMy: function (name) { + return new Promise((resolve) => { + console.log(`这里可以根据${name}参数请求接口`) + var list = [] + setTimeout(()=>{ + resolve(list) + },500) + }) + }, + /** + * 常用保存处理 返回resolve后继续操作 + * @name scFilterBar组件的props->filterName + * @obj 过滤项整理好的对象 + */ + saveMy: function (name, obj) { + return new Promise((resolve) => { + console.log(name, obj) + setTimeout(()=>{ + resolve(true) + },500) + }) + }, + /** + * 常用删除处理 返回resolve后继续操作 + * @name scFilterBar组件的props->filterName + */ + delMy: function (name) { + return new Promise((resolve) => { + console.log(name) + setTimeout(()=>{ + resolve(true) + },500) + }) + } +} diff --git a/resources/admin/src/config/iconSelect.js b/resources/admin/src/config/iconSelect.js new file mode 100644 index 0000000..1ff2dac --- /dev/null +++ b/resources/admin/src/config/iconSelect.js @@ -0,0 +1,308 @@ +//图标选择器配置 +export default { + icons: [{ + name: '默认', + icons: [ + "el-icon-add-location", + "el-icon-aim", + "el-icon-alarm-clock", + "el-icon-apple", + "el-icon-arrow-down", + "el-icon-arrow-down-bold", + "el-icon-arrow-left", + "el-icon-arrow-left-bold", + "el-icon-arrow-right", + "el-icon-arrow-right-bold", + "el-icon-arrow-up", + "el-icon-arrow-up-bold", + "el-icon-avatar", + "el-icon-back", + "el-icon-baseball", + "el-icon-basketball", + "el-icon-bell", + "el-icon-bell-filled", + "el-icon-bicycle", + "el-icon-bottom", + "el-icon-bottom-left", + "el-icon-bottom-right", + "el-icon-bowl", + "el-icon-box", + "el-icon-briefcase", + "el-icon-brush", + "el-icon-brush-filled", + "el-icon-burger", + "el-icon-calendar", + "el-icon-camera", + "el-icon-camera-filled", + "el-icon-caret-bottom", + "el-icon-caret-left", + "el-icon-caret-right", + "el-icon-caret-top", + "el-icon-cellphone", + "el-icon-chat-dot-round", + "el-icon-chat-dot-square", + "el-icon-chat-line-round", + "el-icon-chat-line-square", + "el-icon-chat-round", + "el-icon-chat-square", + "el-icon-check", + "el-icon-checked", + "el-icon-cherry", + "el-icon-chicken", + "el-icon-circle-check", + "el-icon-circle-check-filled", + "el-icon-circle-close", + "el-icon-circle-close-filled", + "el-icon-circle-plus", + "el-icon-circle-plus-filled", + "el-icon-clock", + "el-icon-close", + "el-icon-close-bold", + "el-icon-cloudy", + "el-icon-coffee", + "el-icon-coffee-cup", + "el-icon-coin", + "el-icon-cold-drink", + "el-icon-collection", + "el-icon-collection-tag", + "el-icon-comment", + "el-icon-compass", + "el-icon-connection", + "el-icon-coordinate", + "el-icon-copy-document", + "el-icon-cpu", + "el-icon-credit-card", + "el-icon-crop", + "el-icon-d-arrow-left", + "el-icon-d-arrow-right", + "el-icon-d-caret", + "el-icon-data-analysis", + "el-icon-data-board", + "el-icon-data-line", + "el-icon-delete", + "el-icon-delete-filled", + "el-icon-delete-location", + "el-icon-dessert", + "el-icon-discount", + "el-icon-dish", + "el-icon-dish-dot", + "el-icon-document", + "el-icon-document-add", + "el-icon-document-checked", + "el-icon-document-copy", + "el-icon-document-delete", + "el-icon-document-remove", + "el-icon-download", + "el-icon-drizzling", + "el-icon-edit", + "el-icon-edit-pen", + "el-icon-eleme", + "el-icon-eleme-filled", + "el-icon-element-plus", + "el-icon-expand", + "el-icon-failed", + "el-icon-female", + "el-icon-files", + "el-icon-film", + "el-icon-filter", + "el-icon-finished", + "el-icon-first-aid-kit", + "el-icon-flag", + "el-icon-fold", + "el-icon-folder", + "el-icon-folder-add", + "el-icon-folder-checked", + "el-icon-folder-delete", + "el-icon-folder-opened", + "el-icon-folder-remove", + "el-icon-food", + "el-icon-football", + "el-icon-fork-spoon", + "el-icon-fries", + "el-icon-full-screen", + "el-icon-goblet", + "el-icon-goblet-full", + "el-icon-goblet-square", + "el-icon-goblet-square-full", + "el-icon-goods", + "el-icon-goods-filled", + "el-icon-grape", + "el-icon-grid", + "el-icon-guide", + "el-icon-headset", + "el-icon-help", + "el-icon-help-filled", + "el-icon-hide", + "el-icon-histogram", + "el-icon-home-filled", + "el-icon-hot-water", + "el-icon-house", + "el-icon-ice-cream", + "el-icon-ice-cream-round", + "el-icon-ice-cream-square", + "el-icon-ice-drink", + "el-icon-ice-tea", + "el-icon-info-filled", + "el-icon-iphone", + "el-icon-key", + "el-icon-knife-fork", + "el-icon-lightning", + "el-icon-link", + "el-icon-list", + "el-icon-loading", + "el-icon-location", + "el-icon-location-filled", + "el-icon-location-information", + "el-icon-lock", + "el-icon-lollipop", + "el-icon-magic-stick", + "el-icon-magnet", + "el-icon-male", + "el-icon-management", + "el-icon-map-location", + "el-icon-medal", + "el-icon-menu", + "el-icon-message", + "el-icon-message-box", + "el-icon-mic", + "el-icon-microphone", + "el-icon-milk-tea", + "el-icon-minus", + "el-icon-money", + "el-icon-monitor", + "el-icon-moon", + "el-icon-moon-night", + "el-icon-more", + "el-icon-more-filled", + "el-icon-mostly-cloudy", + "el-icon-mouse", + "el-icon-mug", + "el-icon-mute", + "el-icon-mute-notification", + "el-icon-no-smoking", + "el-icon-notebook", + "el-icon-notification", + "el-icon-odometer", + "el-icon-office-building", + "el-icon-open", + "el-icon-operation", + "el-icon-opportunity", + "el-icon-orange", + "el-icon-paperclip", + "el-icon-partly-cloudy", + "el-icon-pear", + "el-icon-phone", + "el-icon-phone-filled", + "el-icon-picture", + "el-icon-picture-filled", + "el-icon-picture-rounded", + "el-icon-pie-chart", + "el-icon-place", + "el-icon-platform", + "el-icon-plus", + "el-icon-pointer", + "el-icon-position", + "el-icon-postcard", + "el-icon-pouring", + "el-icon-present", + "el-icon-price-tag", + "el-icon-printer", + "el-icon-promotion", + "el-icon-question-filled", + "el-icon-rank", + "el-icon-reading", + "el-icon-reading-lamp", + "el-icon-refresh", + "el-icon-refresh-left", + "el-icon-refresh-right", + "el-icon-refrigerator", + "el-icon-remove", + "el-icon-remove-filled", + "el-icon-right", + "el-icon-scale-to-original", + "el-icon-school", + "el-icon-scissor", + "el-icon-search", + "el-icon-select", + "el-icon-sell", + "el-icon-semi-select", + "el-icon-service", + "el-icon-set-up", + "el-icon-setting", + "el-icon-share", + "el-icon-ship", + "el-icon-shop", + "el-icon-shopping-bag", + "el-icon-shopping-cart", + "el-icon-shopping-cart-full", + "el-icon-smoking", + "el-icon-soccer", + "el-icon-sold-out", + "el-icon-sort", + "el-icon-sort-down", + "el-icon-sort-up", + "el-icon-stamp", + "el-icon-star", + "el-icon-star-filled", + "el-icon-stopwatch", + "el-icon-success-filled", + "el-icon-sugar", + "el-icon-suitcase", + "el-icon-sunny", + "el-icon-sunrise", + "el-icon-sunset", + "el-icon-switch", + "el-icon-switch-button", + "el-icon-takeaway-box", + "el-icon-ticket", + "el-icon-tickets", + "el-icon-timer", + "el-icon-toilet-paper", + "el-icon-tools", + "el-icon-top", + "el-icon-top-left", + "el-icon-top-right", + "el-icon-trend-charts", + "el-icon-trophy", + "el-icon-turn-off", + "el-icon-umbrella", + "el-icon-unlock", + "el-icon-upload", + "el-icon-upload-filled", + "el-icon-user", + "el-icon-user-filled", + "el-icon-van", + "el-icon-video-camera", + "el-icon-video-camera-filled", + "el-icon-video-pause", + "el-icon-video-play", + "el-icon-view", + "el-icon-wallet", + "el-icon-wallet-filled", + "el-icon-warning", + "el-icon-warning-filled", + "el-icon-watch", + "el-icon-watermelon", + "el-icon-wind-power", + "el-icon-zoom-in", + "el-icon-zoom-out" + ] + }, + { + name: '扩展', + icons: [ + 'sc-icon-vue', + 'sc-icon-code', + 'sc-icon-wechat', + 'sc-icon-bug-fill', + 'sc-icon-bug-line', + 'sc-icon-file-word', + 'sc-icon-file-excel', + 'sc-icon-file-ppt', + 'sc-icon-organization', + 'sc-icon-upload', + 'sc-icon-download' + ] + } + ] +} diff --git a/resources/admin/src/config/index.js b/resources/admin/src/config/index.js new file mode 100644 index 0000000..4459882 --- /dev/null +++ b/resources/admin/src/config/index.js @@ -0,0 +1,79 @@ +const DEFAULT_CONFIG = { + //标题 + APP_NAME: 'SentOS', + + //首页地址 + DASHBOARD_URL: "/dashboard", + + //版本号 + APP_VER: "1.6.6", + + //内核版本号 + CORE_VER: "1.6.6", + + //接口地址 + API_URL: 'http://localhost:8000/admin/', + + //请求超时 + TIMEOUT: 50000, + + //TokenName + TOKEN_NAME: "authorization", + + //Token前缀,注意最后有个空格,如不需要需设置空字符串 + TOKEN_PREFIX: "Bearer ", + + //追加其他头 + HEADERS: {}, + + //请求是否开启缓存 + REQUEST_CACHE: false, + + //布局 默认:default | 通栏:header | 经典:menu | 功能坞:dock + //dock将关闭标签和面包屑栏 + LAYOUT: 'default', + + //菜单是否折叠 + MENU_IS_COLLAPSE: false, + + //菜单是否启用手风琴效果 + MENU_UNIQUE_OPENED: false, + + //是否开启多标签 + LAYOUT_TAGS: true, + + //语言 + LANG: 'zh-cn', + + //主题颜色 + COLOR: '', + + //是否加密localStorage, 为空不加密,可填写AES(模式ECB,移位Pkcs7)加密 + LS_ENCRYPTION: '', + + //localStorageAES加密秘钥,位数建议填写8的倍数 + LS_ENCRYPTION_key: '2XNN4K8LC0ELVWN4', + + //控制台首页默认布局 + DEFAULT_GRID: { + //默认分栏数量和宽度 例如 [24] [18,6] [8,8,8] [6,12,6] + layout: [24, 12, 12], + //小组件分布,com取值:pages/home/components 文件名 + copmsList: [ + ['welcome'], + ['info'], + ['ver'] + ] + } +} + +//合并业务配置 +import MY_CONFIG from "./myConfig" +Object.assign(DEFAULT_CONFIG, MY_CONFIG) + +// 如果生产模式,就合并动态的APP_CONFIG +// public/config.js +if(process.env.NODE_ENV === 'production'){ + Object.assign(DEFAULT_CONFIG, APP_CONFIG) +} +export default DEFAULT_CONFIG diff --git a/resources/admin/src/config/myConfig.js b/resources/admin/src/config/myConfig.js new file mode 100644 index 0000000..3aff39f --- /dev/null +++ b/resources/admin/src/config/myConfig.js @@ -0,0 +1,21 @@ +//业务配置 +//会合并至this.$CONFIG +//生产模式 public/config.js 同名key会覆盖这里的配置从而实现打包后的热更新 +//为避免和SCUI框架配置混淆建议添加前缀 MY_ +//全局可使用 this.$CONFIG.MY_KEY 访问 + +export default { + //是否显示第三方授权登录 + MY_SHOW_LOGIN_OAUTH: false, + + shopTemplateList: [ + { + value: 'default', + label: '默认模版', + }, + { + value: 'action', + label: '活动模版' + } + ] +} diff --git a/resources/admin/src/config/route.js b/resources/admin/src/config/route.js new file mode 100644 index 0000000..8084d85 --- /dev/null +++ b/resources/admin/src/config/route.js @@ -0,0 +1,31 @@ +// 静态路由配置 +// 书写格式与动态路由格式一致,全部经由框架统一转换 +// 比较动态路由在meta中多加入了role角色权限,为数组类型。一个菜单是否有权限显示,取决于它以及后代菜单是否有权限。 +// routes 显示在左侧菜单中的路由(显示顺序在动态路由之前) +// 示例如下 + +// const routes = [ +// { +// name: "demo", +// path: "/demo", +// meta: { +// icon: "el-icon-eleme-filled", +// title: "演示", +// role: ["SA"] +// }, +// children: [{ +// name: "demopage", +// path: "/demopage", +// component: "test/autocode/index", +// meta: { +// icon: "el-icon-menu", +// title: "演示页面", +// role: ["SA"] +// } +// }] +// } +// ] + +const routes = [] + +export default routes; diff --git a/resources/admin/src/config/select.js b/resources/admin/src/config/select.js new file mode 100644 index 0000000..5b97f12 --- /dev/null +++ b/resources/admin/src/config/select.js @@ -0,0 +1,21 @@ +import API from "@/api"; + +//字典选择器配置 + +export default { + dicApiObj: API.system.dictionary.list, //获取字典接口对象 + parseData: function (res) { + return { + data: res.data, //分析行数据字段结构 + msg: res.message, //分析描述字段结构 + code: res.code //分析状态字段结构 + } + }, + request: { + name: 'name' //规定搜索字段 + }, + props: { + label: 'title', //映射label显示字段 + value: 'values', //映射value值字段 + } +} diff --git a/resources/admin/src/config/table.js b/resources/admin/src/config/table.js new file mode 100644 index 0000000..7ee7842 --- /dev/null +++ b/resources/admin/src/config/table.js @@ -0,0 +1,70 @@ +//数据表格配置 + +import tool from '@/utils/tool' + +export default { + successCode: 1, //请求完成代码 + pageSize: 30, //表格每一页条数 + pageSizes: [30, 100, 200, 500], //表格可设置的一页条数 + paginationLayout: "total, sizes, prev, pager, next, jumper", //表格分页布局,可设置"total, sizes, prev, pager, next, jumper" + parseData: function (res) { //数据分析 + return { + data: res.data, //分析无分页的数据字段结构 + rows: res.data.data, //分析行数据字段结构 + total: res.data.total, //分析总数字段结构 + summary: res.data.summary, //分析合计行字段结构 + msg: res.message, //分析描述字段结构 + code: res.code //分析状态字段结构 + } + }, + request: { //请求规定字段 + page: 'page', //规定当前分页字段 + pageSize: 'limit', //规定一页条数字段 + prop: 'prop', //规定排序字段名字段 + order: 'order' //规定排序规格字段 + }, + /** + * 自定义列保存处理 + * @tableName scTable组件的props->tableName + * @column 用户配置好的列 + */ + columnSettingSave: function (tableName, column) { + return new Promise((resolve) => { + setTimeout(()=>{ + //这里为了演示使用了session和setTimeout演示,开发时应用数据请求 + tool.session.set(tableName, column) + resolve(true) + },1000) + }) + }, + /** + * 获取自定义列 + * @tableName scTable组件的props->tableName + * @column 组件接受到的props->column + */ + columnSettingGet: function (tableName, column) { + return new Promise((resolve) => { + //这里为了演示使用了session和setTimeout演示,开发时应用数据请求 + const userColumn = tool.session.get(tableName) + if(userColumn){ + resolve(userColumn) + }else{ + resolve(column) + } + }) + }, + /** + * 重置自定义列 + * @tableName scTable组件的props->tableName + * @column 组件接受到的props->column + */ + columnSettingReset: function (tableName, column) { + return new Promise((resolve) => { + //这里为了演示使用了session和setTimeout演示,开发时应用数据请求 + setTimeout(()=>{ + tool.session.remove(tableName) + resolve(column) + },1000) + }) + } +} diff --git a/resources/admin/src/config/tableSelect.js b/resources/admin/src/config/tableSelect.js new file mode 100644 index 0000000..fba6e81 --- /dev/null +++ b/resources/admin/src/config/tableSelect.js @@ -0,0 +1,23 @@ +//表格选择器配置 + +export default { + pageSize: 100, //表格每一页条数 + parseData: function (res) { + return { + data: res.data, + rows: res.data.data, //分析行数据字段结构 + total: res.data.total, //分析总数字段结构 + msg: res.message, //分析描述字段结构 + code: res.code //分析状态字段结构 + } + }, + request: { + page: 'page', //规定当前分页字段 + pageSize: 'pageSize', //规定一页条数字段 + keyword: 'keyword' //规定搜索字段 + }, + props: { + label: 'name', //映射label显示字段 + value: 'id', //映射value值字段 + } +} diff --git a/resources/admin/src/config/upload.js b/resources/admin/src/config/upload.js new file mode 100644 index 0000000..d2353a8 --- /dev/null +++ b/resources/admin/src/config/upload.js @@ -0,0 +1,20 @@ +import API from "@/api"; + +//上传配置 + +export default { + apiObj: API.common.upload, //上传请求API对象 + filename: "file", //form请求时文件的key + successCode: 1, //请求完成代码 + maxSize: 10, //最大文件大小 默认10MB + parseData: function (res) { + return { + code: res.code, //分析状态字段结构 + fileName: res.data.name,//分析文件名称 + src: res.data.url, //分析图片远程地址结构 + msg: res.message //分析描述字段结构 + } + }, + apiObjFile: API.common.upload, //附件上传请求API对象 + maxSizeFile: 10 //最大文件大小 默认10MB +} diff --git a/resources/admin/src/config/workflow.js b/resources/admin/src/config/workflow.js new file mode 100644 index 0000000..1024e16 --- /dev/null +++ b/resources/admin/src/config/workflow.js @@ -0,0 +1,69 @@ +import API from "@/api"; + +//审批工作流人员/组织选择器配置 + +export default { + //配置接口正常返回代码 + successCode: 200, + //配置组织 + group: { + //请求接口对象 + apiObj: API.system.dept.list, + //接受数据字段映射 + parseData: function (res) { + return { + rows: res.data, + msg: res.message, + code: res.code + } + }, + //显示数据字段映射 + props: { + key: 'id', + label: 'label', + children: 'children' + } + }, + //配置用户 + user: { + apiObj: API.demo.page, + pageSize: 20, + parseData: function (res) { + return { + rows: res.data.rows, + total: res.data.total, + msg: res.message, + code: res.code + } + }, + props: { + key: 'id', + label: 'user', + }, + request: { + page: 'page', + pageSize: 'pageSize', + groupId: 'groupId', + keyword: 'keyword' + } + }, + //配置角色 + role: { + //请求接口对象 + apiObj: API.system.dept.list, + //接受数据字段映射 + parseData: function (res) { + return { + rows: res.data, + msg: res.message, + code: res.code + } + }, + //显示数据字段映射 + props: { + key: 'id', + label: 'label', + children: 'children' + } + } +} diff --git a/resources/admin/src/directives/auth.js b/resources/admin/src/directives/auth.js new file mode 100644 index 0000000..75d74be --- /dev/null +++ b/resources/admin/src/directives/auth.js @@ -0,0 +1,22 @@ +import { permission } from '@/utils/permission' + +export default { + mounted(el, binding) { + const { value } = binding + if(Array.isArray(value)){ + let ishas = false; + value.forEach(item => { + if(permission(item)){ + ishas = true; + } + }) + if (!ishas){ + el.parentNode.removeChild(el) + } + }else{ + if(!permission(value)){ + el.parentNode.removeChild(el); + } + } + } +}; diff --git a/resources/admin/src/directives/copy.js b/resources/admin/src/directives/copy.js new file mode 100644 index 0000000..0efecbe --- /dev/null +++ b/resources/admin/src/directives/copy.js @@ -0,0 +1,29 @@ +import { ElMessage } from 'element-plus' + +export default { + mounted(el, binding) { + el.$value = binding.value + el.handler = () => { + const textarea = document.createElement('textarea') + textarea.readOnly = 'readonly' + textarea.style.position = 'absolute' + textarea.style.left = '-9999px' + textarea.value = el.$value + document.body.appendChild(textarea) + textarea.select() + textarea.setSelectionRange(0, textarea.value.length) + const result = document.execCommand('Copy') + if (result) { + ElMessage.success("复制成功") + } + document.body.removeChild(textarea) + } + el.addEventListener('click', el.handler) + }, + updated(el, binding){ + el.$value = binding.value + }, + unmounted(el){ + el.removeEventListener('click', el.handler) + } +} diff --git a/resources/admin/src/directives/role.js b/resources/admin/src/directives/role.js new file mode 100644 index 0000000..42655b0 --- /dev/null +++ b/resources/admin/src/directives/role.js @@ -0,0 +1,22 @@ +import { rolePermission } from '@/utils/permission' + +export default { + mounted(el, binding) { + const { value } = binding + if(Array.isArray(value)){ + let ishas = false; + value.forEach(item => { + if(rolePermission(item)){ + ishas = true; + } + }) + if (!ishas){ + el.parentNode.removeChild(el) + } + }else{ + if(!rolePermission(value)){ + el.parentNode.removeChild(el); + } + } + } +}; diff --git a/resources/admin/src/directives/time.js b/resources/admin/src/directives/time.js new file mode 100644 index 0000000..d0e7474 --- /dev/null +++ b/resources/admin/src/directives/time.js @@ -0,0 +1,79 @@ +import tool from '@/utils/tool' + +var Time = { + //获取当前时间戳 + getUnix: function() { + var date = new Date(); + return date.getTime(); + }, + //获取今天0点0分0秒的时间戳 + getTodayUnix: function() { + var date = new Date(); + date.setHours(0); + date.setMinutes(0); + date.setSeconds(0); + date.setMilliseconds(0); + return date.getTime(); + }, + //获取今年1月1日0点0秒的时间戳 + getYearUnix: function() { + var date = new Date(); + date.setMonth(0); + date.setDate(1); + date.setHours(0); + date.setMinutes(0); + date.setSeconds(0); + date.setMilliseconds(0); + return date.getTime(); + }, + //获取标准年月日 + getLastDate: function(time) { + var date = new Date(time); + var month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1; + var day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate(); + return date.getFullYear() + '-' + month + '-' + day; + }, + //转换时间 + getFormateTime: function(timestamp) { + timestamp = new Date(timestamp) + var now = this.getUnix(); + var today = this.getTodayUnix(); + //var year = this.getYearUnix(); + var timer = (now - timestamp) / 1000; + var tip = ''; + + if (timer <= 0) { + tip = '刚刚'; + } else if (Math.floor(timer / 60) <= 0) { + tip = '刚刚'; + } else if (timer < 3600) { + tip = Math.floor(timer / 60) + '分钟前'; + } else if (timer >= 3600 && (timestamp - today >= 0)) { + tip = Math.floor(timer / 3600) + '小时前'; + } else if (timer / 86400 <= 31) { + tip = Math.ceil(timer / 86400) + '天前'; + } else { + tip = this.getLastDate(timestamp); + } + return tip; + } +} + +export default (el, binding) => { + let { value, modifiers} = binding + if(!value){ + return false + } + if(value.toString().length == 10){ + value = value * 1000 + } + if (modifiers.tip) { + el.innerHTML = Time.getFormateTime(value) + el.__timeout__ = setInterval(() => { + el.innerHTML = Time.getFormateTime(value) + }, 60000) + } else { + const format = el.getAttribute('format') || undefined + el.innerHTML = tool.dateFormat(value, format) + } +} diff --git a/resources/admin/src/layout/components/NavMenu.vue b/resources/admin/src/layout/components/NavMenu.vue new file mode 100644 index 0000000..bedc552 --- /dev/null +++ b/resources/admin/src/layout/components/NavMenu.vue @@ -0,0 +1,38 @@ + + + diff --git a/resources/admin/src/layout/components/feedback.vue b/resources/admin/src/layout/components/feedback.vue new file mode 100644 index 0000000..9b3a0b3 --- /dev/null +++ b/resources/admin/src/layout/components/feedback.vue @@ -0,0 +1,60 @@ + + + + + diff --git a/resources/admin/src/layout/components/iframeView.vue b/resources/admin/src/layout/components/iframeView.vue new file mode 100644 index 0000000..3e64d54 --- /dev/null +++ b/resources/admin/src/layout/components/iframeView.vue @@ -0,0 +1,66 @@ + + + + + + + diff --git a/resources/admin/src/layout/components/search.vue b/resources/admin/src/layout/components/search.vue new file mode 100644 index 0000000..03df69c --- /dev/null +++ b/resources/admin/src/layout/components/search.vue @@ -0,0 +1,139 @@ + + + + + diff --git a/resources/admin/src/layout/components/setting.vue b/resources/admin/src/layout/components/setting.vue new file mode 100644 index 0000000..af0575c --- /dev/null +++ b/resources/admin/src/layout/components/setting.vue @@ -0,0 +1,89 @@ + + + + + diff --git a/resources/admin/src/layout/components/sideM.vue b/resources/admin/src/layout/components/sideM.vue new file mode 100644 index 0000000..138f29e --- /dev/null +++ b/resources/admin/src/layout/components/sideM.vue @@ -0,0 +1,136 @@ + + + + + diff --git a/resources/admin/src/layout/components/tags.vue b/resources/admin/src/layout/components/tags.vue new file mode 100644 index 0000000..29a986e --- /dev/null +++ b/resources/admin/src/layout/components/tags.vue @@ -0,0 +1,305 @@ + + + + + diff --git a/resources/admin/src/layout/components/tasks.vue b/resources/admin/src/layout/components/tasks.vue new file mode 100644 index 0000000..0752691 --- /dev/null +++ b/resources/admin/src/layout/components/tasks.vue @@ -0,0 +1,97 @@ + + + + + diff --git a/resources/admin/src/layout/components/topbar.vue b/resources/admin/src/layout/components/topbar.vue new file mode 100644 index 0000000..4d79c81 --- /dev/null +++ b/resources/admin/src/layout/components/topbar.vue @@ -0,0 +1,59 @@ + + + + + diff --git a/resources/admin/src/layout/components/userbar.vue b/resources/admin/src/layout/components/userbar.vue new file mode 100644 index 0000000..a372901 --- /dev/null +++ b/resources/admin/src/layout/components/userbar.vue @@ -0,0 +1,185 @@ + + + + + diff --git a/resources/admin/src/layout/components/winTool.vue b/resources/admin/src/layout/components/winTool.vue new file mode 100644 index 0000000..6431a83 --- /dev/null +++ b/resources/admin/src/layout/components/winTool.vue @@ -0,0 +1,38 @@ + + + + + + diff --git a/resources/admin/src/layout/index.vue b/resources/admin/src/layout/index.vue new file mode 100644 index 0000000..592a0ff --- /dev/null +++ b/resources/admin/src/layout/index.vue @@ -0,0 +1,370 @@ + + + + diff --git a/resources/admin/src/layout/other/404.vue b/resources/admin/src/layout/other/404.vue new file mode 100644 index 0000000..f1c110c --- /dev/null +++ b/resources/admin/src/layout/other/404.vue @@ -0,0 +1,44 @@ + + + + + diff --git a/resources/admin/src/layout/other/empty.vue b/resources/admin/src/layout/other/empty.vue new file mode 100644 index 0000000..9c72bc8 --- /dev/null +++ b/resources/admin/src/layout/other/empty.vue @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/resources/admin/src/locales/index.js b/resources/admin/src/locales/index.js new file mode 100644 index 0000000..97c53b7 --- /dev/null +++ b/resources/admin/src/locales/index.js @@ -0,0 +1,28 @@ +import sysConfig from "@/config" +import tool from '@/utils/tool' +import { createI18n } from 'vue-i18n' +import el_zh_cn from 'element-plus/es/locale/lang/zh-cn' +import el_en from 'element-plus/es/locale/lang/en' + +import zh_cn from './lang/zh-cn.js' +import en from './lang/en.js' + +const messages = { + 'zh-cn': { + el: el_zh_cn, + ...zh_cn + }, + 'en': { + el: el_en, + ...en + } +} + +const i18n = createI18n({ + locale: tool.data.get("APP_LANG") || sysConfig.LANG, + fallbackLocale: 'zh-cn', + globalInjection: true, + messages, +}) + +export default i18n; diff --git a/resources/admin/src/locales/lang/en.js b/resources/admin/src/locales/lang/en.js new file mode 100644 index 0000000..8731e33 --- /dev/null +++ b/resources/admin/src/locales/lang/en.js @@ -0,0 +1,39 @@ +export default { + login: { + slogan: 'High performance / delicate / grace', + describe: 'Vue3 + element plus based front-end solutions in the background.', + signInTitle: 'Sign in', + accountLogin: 'Account sign in', + mobileLogin: 'Mobile sign in', + rememberMe: 'Remember me', + forgetPassword: 'Forget password', + signIn: 'Sign in', + signInOther: 'Sign in with', + userPlaceholder: 'user / phone / email', + userError: 'Please input a user name', + PWPlaceholder: 'Please input a password', + PWError: 'Please input a password', + admin: 'Administrator', + user: 'User', + mobilePlaceholder: 'Mobile', + mobileError: 'Please input mobile', + smsPlaceholder: 'SMS Code', + smsError: 'Please input sms code', + smsGet: 'Get SMS Code', + smsSent: 'SMS sent to mobile number', + noAccount: 'No account?', + createAccount: 'Create a new account', + wechatLoginTitle: 'QR code sign in', + wechatLoginMsg: 'Please use wechat to scan and log in | Auto scan after 3 seconds of simulation', + wechatLoginResult: 'Scanned | Please click authorize login in the device' + }, + user: { + dynamic: 'Dynamic', + info: 'User Info', + settings: 'Settings', + nightmode: 'night mode', + nightmode_msg: 'Suitable for low light environment,The current night mode is beta', + language: 'language', + language_msg: 'Translation in progress,Temporarily translated the text of this view', + } +} diff --git a/resources/admin/src/locales/lang/zh-cn.js b/resources/admin/src/locales/lang/zh-cn.js new file mode 100644 index 0000000..87c0150 --- /dev/null +++ b/resources/admin/src/locales/lang/zh-cn.js @@ -0,0 +1,39 @@ +export default { + login: { + slogan: '高性能 / 精致 / 优雅', + describe: '基于Vue3 + Element-Plus 的中后台前端解决方案。', + signInTitle: '用户登录', + accountLogin: '账号登录', + mobileLogin: '手机号登录', + rememberMe: '24小时免登录', + forgetPassword: '忘记密码', + signIn: '登录', + signInOther: '其他登录方式', + userPlaceholder: '用户名 / 手机 / 邮箱', + userError: '请输入用户名', + PWPlaceholder: '请输入密码', + PWError: '请输入密码', + admin: '管理员', + user: '用户', + mobilePlaceholder: '手机号码', + mobileError: '请输入手机号码', + smsPlaceholder: '短信验证码', + smsError: '请输入短信验证码', + smsGet: '获取验证码', + smsSent: '已发送短信至手机号码', + noAccount: '还没有账号?', + createAccount: '创建新账号', + wechatLoginTitle: '二维码登录', + wechatLoginMsg: '请使用微信扫一扫登录 | 模拟3秒后自动扫描', + wechatLoginResult: '已扫描 | 请在设备中点击授权登录' + }, + user: { + dynamic: '近期动态', + info: '个人信息', + settings: '设置', + nightmode: '黑夜模式', + nightmode_msg: '适合光线较弱的环境,当前黑暗模式为beta版本', + language: '语言', + language_msg: '翻译进行中,暂翻译了本视图的文本', + } +} diff --git a/resources/admin/src/main.js b/resources/admin/src/main.js new file mode 100644 index 0000000..ce3864d --- /dev/null +++ b/resources/admin/src/main.js @@ -0,0 +1,20 @@ +import { createApp } from 'vue' +import ElementPlus from 'element-plus' +import 'element-plus/dist/index.css' +import 'element-plus/theme-chalk/display.css' +import sent from './sent' +import i18n from './locales' +import router from './router' +import store from './store' +import App from './App.vue' + +const app = createApp(App); + +app.use(i18n); +app.use(store); +app.use(router); +app.use(ElementPlus); +app.use(sent); + +//挂载app +app.mount('#app'); diff --git a/resources/admin/src/mixin/import.vue b/resources/admin/src/mixin/import.vue new file mode 100644 index 0000000..ac53642 --- /dev/null +++ b/resources/admin/src/mixin/import.vue @@ -0,0 +1,76 @@ + \ No newline at end of file diff --git a/resources/admin/src/pages/account/accounts/index.vue b/resources/admin/src/pages/account/accounts/index.vue new file mode 100644 index 0000000..e65f934 --- /dev/null +++ b/resources/admin/src/pages/account/accounts/index.vue @@ -0,0 +1,146 @@ + + + + + diff --git a/resources/admin/src/pages/account/accounts/save.vue b/resources/admin/src/pages/account/accounts/save.vue new file mode 100644 index 0000000..e4f5b12 --- /dev/null +++ b/resources/admin/src/pages/account/accounts/save.vue @@ -0,0 +1,142 @@ + + + + + diff --git a/resources/admin/src/pages/account/families/index.vue b/resources/admin/src/pages/account/families/index.vue new file mode 100644 index 0000000..e321cba --- /dev/null +++ b/resources/admin/src/pages/account/families/index.vue @@ -0,0 +1,120 @@ + + + + + diff --git a/resources/admin/src/pages/account/families/save.vue b/resources/admin/src/pages/account/families/save.vue new file mode 100644 index 0000000..584fab1 --- /dev/null +++ b/resources/admin/src/pages/account/families/save.vue @@ -0,0 +1,175 @@ + + + + + diff --git a/resources/admin/src/pages/account/members/index.vue b/resources/admin/src/pages/account/members/index.vue new file mode 100644 index 0000000..bd264f1 --- /dev/null +++ b/resources/admin/src/pages/account/members/index.vue @@ -0,0 +1,129 @@ + + + + + diff --git a/resources/admin/src/pages/account/members/save.vue b/resources/admin/src/pages/account/members/save.vue new file mode 100644 index 0000000..062e608 --- /dev/null +++ b/resources/admin/src/pages/account/members/save.vue @@ -0,0 +1,128 @@ + + + + + diff --git a/resources/admin/src/pages/account/records/index.vue b/resources/admin/src/pages/account/records/index.vue new file mode 100644 index 0000000..ac2ffad --- /dev/null +++ b/resources/admin/src/pages/account/records/index.vue @@ -0,0 +1,142 @@ + + + + + diff --git a/resources/admin/src/pages/account/records/save.vue b/resources/admin/src/pages/account/records/save.vue new file mode 100644 index 0000000..3c4c6fe --- /dev/null +++ b/resources/admin/src/pages/account/records/save.vue @@ -0,0 +1,216 @@ + + + + + diff --git a/resources/admin/src/pages/auth/department/index.vue b/resources/admin/src/pages/auth/department/index.vue new file mode 100644 index 0000000..f33e786 --- /dev/null +++ b/resources/admin/src/pages/auth/department/index.vue @@ -0,0 +1,155 @@ + + + diff --git a/resources/admin/src/pages/auth/department/save.vue b/resources/admin/src/pages/auth/department/save.vue new file mode 100644 index 0000000..2595d5c --- /dev/null +++ b/resources/admin/src/pages/auth/department/save.vue @@ -0,0 +1,103 @@ + + + diff --git a/resources/admin/src/pages/auth/permission/index.vue b/resources/admin/src/pages/auth/permission/index.vue new file mode 100644 index 0000000..acd266d --- /dev/null +++ b/resources/admin/src/pages/auth/permission/index.vue @@ -0,0 +1,170 @@ + + + + + diff --git a/resources/admin/src/pages/auth/permission/save.vue b/resources/admin/src/pages/auth/permission/save.vue new file mode 100644 index 0000000..9e4cd97 --- /dev/null +++ b/resources/admin/src/pages/auth/permission/save.vue @@ -0,0 +1,209 @@ + + + + + diff --git a/resources/admin/src/pages/auth/role/index.vue b/resources/admin/src/pages/auth/role/index.vue new file mode 100644 index 0000000..f384467 --- /dev/null +++ b/resources/admin/src/pages/auth/role/index.vue @@ -0,0 +1,173 @@ + + + diff --git a/resources/admin/src/pages/auth/role/permission.vue b/resources/admin/src/pages/auth/role/permission.vue new file mode 100644 index 0000000..f8d3c0e --- /dev/null +++ b/resources/admin/src/pages/auth/role/permission.vue @@ -0,0 +1,129 @@ + + + + + diff --git a/resources/admin/src/pages/auth/role/save.vue b/resources/admin/src/pages/auth/role/save.vue new file mode 100644 index 0000000..fa68871 --- /dev/null +++ b/resources/admin/src/pages/auth/role/save.vue @@ -0,0 +1,121 @@ + + + + + diff --git a/resources/admin/src/pages/auth/user/index.vue b/resources/admin/src/pages/auth/user/index.vue new file mode 100644 index 0000000..c9deb3b --- /dev/null +++ b/resources/admin/src/pages/auth/user/index.vue @@ -0,0 +1,218 @@ + + + + + diff --git a/resources/admin/src/pages/auth/user/role.vue b/resources/admin/src/pages/auth/user/role.vue new file mode 100644 index 0000000..5d2ee3a --- /dev/null +++ b/resources/admin/src/pages/auth/user/role.vue @@ -0,0 +1,78 @@ + + + + + diff --git a/resources/admin/src/pages/auth/user/save.vue b/resources/admin/src/pages/auth/user/save.vue new file mode 100644 index 0000000..362dca0 --- /dev/null +++ b/resources/admin/src/pages/auth/user/save.vue @@ -0,0 +1,156 @@ + + + + + diff --git a/resources/admin/src/pages/home/index.vue b/resources/admin/src/pages/home/index.vue new file mode 100644 index 0000000..dae58c7 --- /dev/null +++ b/resources/admin/src/pages/home/index.vue @@ -0,0 +1,48 @@ + + + + + diff --git a/resources/admin/src/pages/home/widgets/components/about.vue b/resources/admin/src/pages/home/widgets/components/about.vue new file mode 100644 index 0000000..efe806e --- /dev/null +++ b/resources/admin/src/pages/home/widgets/components/about.vue @@ -0,0 +1,27 @@ + + + + + diff --git a/resources/admin/src/pages/home/widgets/components/echarts.vue b/resources/admin/src/pages/home/widgets/components/echarts.vue new file mode 100644 index 0000000..e9fd5ae --- /dev/null +++ b/resources/admin/src/pages/home/widgets/components/echarts.vue @@ -0,0 +1,98 @@ + + + diff --git a/resources/admin/src/pages/home/widgets/components/index.js b/resources/admin/src/pages/home/widgets/components/index.js new file mode 100644 index 0000000..44be942 --- /dev/null +++ b/resources/admin/src/pages/home/widgets/components/index.js @@ -0,0 +1,8 @@ +import { markRaw } from 'vue' +const resultComps = {} +const files = import.meta.glob('./*.vue', { eager: true }) +Object.keys(files).forEach((fileName) => { + let comp = files[fileName] + resultComps[fileName.replace(/^\.\/(.*)\.\w+$/, '$1')] = comp.default +}) +export default markRaw(resultComps) diff --git a/resources/admin/src/pages/home/widgets/components/info.vue b/resources/admin/src/pages/home/widgets/components/info.vue new file mode 100644 index 0000000..8859455 --- /dev/null +++ b/resources/admin/src/pages/home/widgets/components/info.vue @@ -0,0 +1,33 @@ + + + + + diff --git a/resources/admin/src/pages/home/widgets/components/progress.vue b/resources/admin/src/pages/home/widgets/components/progress.vue new file mode 100644 index 0000000..994c082 --- /dev/null +++ b/resources/admin/src/pages/home/widgets/components/progress.vue @@ -0,0 +1,32 @@ + + + + + diff --git a/resources/admin/src/pages/home/widgets/components/sms.vue b/resources/admin/src/pages/home/widgets/components/sms.vue new file mode 100644 index 0000000..2e07e0f --- /dev/null +++ b/resources/admin/src/pages/home/widgets/components/sms.vue @@ -0,0 +1,63 @@ + + + diff --git a/resources/admin/src/pages/home/widgets/components/time.vue b/resources/admin/src/pages/home/widgets/components/time.vue new file mode 100644 index 0000000..c551cbc --- /dev/null +++ b/resources/admin/src/pages/home/widgets/components/time.vue @@ -0,0 +1,40 @@ + + + + + diff --git a/resources/admin/src/pages/home/widgets/components/ver.vue b/resources/admin/src/pages/home/widgets/components/ver.vue new file mode 100644 index 0000000..9b6fc9a --- /dev/null +++ b/resources/admin/src/pages/home/widgets/components/ver.vue @@ -0,0 +1,33 @@ + + + + + diff --git a/resources/admin/src/pages/home/widgets/components/welcome.vue b/resources/admin/src/pages/home/widgets/components/welcome.vue new file mode 100644 index 0000000..9e0972d --- /dev/null +++ b/resources/admin/src/pages/home/widgets/components/welcome.vue @@ -0,0 +1,56 @@ + + + + + diff --git a/resources/admin/src/pages/home/widgets/index.vue b/resources/admin/src/pages/home/widgets/index.vue new file mode 100644 index 0000000..2a34ce9 --- /dev/null +++ b/resources/admin/src/pages/home/widgets/index.vue @@ -0,0 +1,255 @@ + + + + + diff --git a/resources/admin/src/pages/home/work/components/myapp.vue b/resources/admin/src/pages/home/work/components/myapp.vue new file mode 100644 index 0000000..3a0ad72 --- /dev/null +++ b/resources/admin/src/pages/home/work/components/myapp.vue @@ -0,0 +1,131 @@ + + + + + diff --git a/resources/admin/src/pages/home/work/index.vue b/resources/admin/src/pages/home/work/index.vue new file mode 100644 index 0000000..49a01a5 --- /dev/null +++ b/resources/admin/src/pages/home/work/index.vue @@ -0,0 +1,37 @@ + + + + + diff --git a/resources/admin/src/pages/login/components/commonPage.vue b/resources/admin/src/pages/login/components/commonPage.vue new file mode 100644 index 0000000..a732ae4 --- /dev/null +++ b/resources/admin/src/pages/login/components/commonPage.vue @@ -0,0 +1,35 @@ + + + + + diff --git a/resources/admin/src/pages/login/components/passwordForm.vue b/resources/admin/src/pages/login/components/passwordForm.vue new file mode 100644 index 0000000..9ee9591 --- /dev/null +++ b/resources/admin/src/pages/login/components/passwordForm.vue @@ -0,0 +1,117 @@ + + + + + diff --git a/resources/admin/src/pages/login/components/phoneForm.vue b/resources/admin/src/pages/login/components/phoneForm.vue new file mode 100644 index 0000000..470d12a --- /dev/null +++ b/resources/admin/src/pages/login/components/phoneForm.vue @@ -0,0 +1,74 @@ + + + + + diff --git a/resources/admin/src/pages/login/index.vue b/resources/admin/src/pages/login/index.vue new file mode 100644 index 0000000..714927d --- /dev/null +++ b/resources/admin/src/pages/login/index.vue @@ -0,0 +1,188 @@ + + + + + diff --git a/resources/admin/src/pages/login/resetPassword.vue b/resources/admin/src/pages/login/resetPassword.vue new file mode 100644 index 0000000..3857a41 --- /dev/null +++ b/resources/admin/src/pages/login/resetPassword.vue @@ -0,0 +1,124 @@ + + + + + diff --git a/resources/admin/src/pages/login/userRegister.vue b/resources/admin/src/pages/login/userRegister.vue new file mode 100644 index 0000000..c41d8d5 --- /dev/null +++ b/resources/admin/src/pages/login/userRegister.vue @@ -0,0 +1,171 @@ + + + + + diff --git a/resources/admin/src/pages/member/apply/index.vue b/resources/admin/src/pages/member/apply/index.vue new file mode 100644 index 0000000..076e1c0 --- /dev/null +++ b/resources/admin/src/pages/member/apply/index.vue @@ -0,0 +1,216 @@ + + + diff --git a/resources/admin/src/pages/member/apply/save.vue b/resources/admin/src/pages/member/apply/save.vue new file mode 100644 index 0000000..497b36d --- /dev/null +++ b/resources/admin/src/pages/member/apply/save.vue @@ -0,0 +1,127 @@ + + + + + diff --git a/resources/admin/src/pages/member/field/index.vue b/resources/admin/src/pages/member/field/index.vue new file mode 100644 index 0000000..a24e284 --- /dev/null +++ b/resources/admin/src/pages/member/field/index.vue @@ -0,0 +1,213 @@ + + + diff --git a/resources/admin/src/pages/member/field/save.vue b/resources/admin/src/pages/member/field/save.vue new file mode 100644 index 0000000..ece55a0 --- /dev/null +++ b/resources/admin/src/pages/member/field/save.vue @@ -0,0 +1,200 @@ + + + diff --git a/resources/admin/src/pages/member/level/index.vue b/resources/admin/src/pages/member/level/index.vue new file mode 100644 index 0000000..bdc4760 --- /dev/null +++ b/resources/admin/src/pages/member/level/index.vue @@ -0,0 +1,152 @@ + + + + + diff --git a/resources/admin/src/pages/member/level/save.vue b/resources/admin/src/pages/member/level/save.vue new file mode 100644 index 0000000..082d800 --- /dev/null +++ b/resources/admin/src/pages/member/level/save.vue @@ -0,0 +1,148 @@ + + + + + diff --git a/resources/admin/src/pages/member/lists/index.vue b/resources/admin/src/pages/member/lists/index.vue new file mode 100644 index 0000000..69ddafe --- /dev/null +++ b/resources/admin/src/pages/member/lists/index.vue @@ -0,0 +1,163 @@ + + + + + diff --git a/resources/admin/src/pages/member/lists/save.vue b/resources/admin/src/pages/member/lists/save.vue new file mode 100644 index 0000000..63a00e6 --- /dev/null +++ b/resources/admin/src/pages/member/lists/save.vue @@ -0,0 +1,106 @@ + + + + + diff --git a/resources/admin/src/pages/operate/ads/index.vue b/resources/admin/src/pages/operate/ads/index.vue new file mode 100644 index 0000000..9acfce8 --- /dev/null +++ b/resources/admin/src/pages/operate/ads/index.vue @@ -0,0 +1,186 @@ + + + diff --git a/resources/admin/src/pages/operate/ads/save.vue b/resources/admin/src/pages/operate/ads/save.vue new file mode 100644 index 0000000..131d36f --- /dev/null +++ b/resources/admin/src/pages/operate/ads/save.vue @@ -0,0 +1,173 @@ + + + diff --git a/resources/admin/src/pages/system/area/index.vue b/resources/admin/src/pages/system/area/index.vue new file mode 100644 index 0000000..9e399bf --- /dev/null +++ b/resources/admin/src/pages/system/area/index.vue @@ -0,0 +1,113 @@ + + + + + diff --git a/resources/admin/src/pages/system/area/save.vue b/resources/admin/src/pages/system/area/save.vue new file mode 100644 index 0000000..16d3017 --- /dev/null +++ b/resources/admin/src/pages/system/area/save.vue @@ -0,0 +1,86 @@ + + + diff --git a/resources/admin/src/pages/system/client/index.vue b/resources/admin/src/pages/system/client/index.vue new file mode 100644 index 0000000..fe86f98 --- /dev/null +++ b/resources/admin/src/pages/system/client/index.vue @@ -0,0 +1,128 @@ + + + + + diff --git a/resources/admin/src/pages/system/client/menu.vue b/resources/admin/src/pages/system/client/menu.vue new file mode 100644 index 0000000..8cf6f37 --- /dev/null +++ b/resources/admin/src/pages/system/client/menu.vue @@ -0,0 +1,136 @@ + + + + + diff --git a/resources/admin/src/pages/system/client/menuform.vue b/resources/admin/src/pages/system/client/menuform.vue new file mode 100644 index 0000000..50f59d5 --- /dev/null +++ b/resources/admin/src/pages/system/client/menuform.vue @@ -0,0 +1,76 @@ + + + + + diff --git a/resources/admin/src/pages/system/client/save.vue b/resources/admin/src/pages/system/client/save.vue new file mode 100644 index 0000000..7612961 --- /dev/null +++ b/resources/admin/src/pages/system/client/save.vue @@ -0,0 +1,69 @@ + + + + + diff --git a/resources/admin/src/pages/system/crontab/index.vue b/resources/admin/src/pages/system/crontab/index.vue new file mode 100644 index 0000000..50933d2 --- /dev/null +++ b/resources/admin/src/pages/system/crontab/index.vue @@ -0,0 +1,154 @@ + + + + + diff --git a/resources/admin/src/pages/system/crontab/logs.vue b/resources/admin/src/pages/system/crontab/logs.vue new file mode 100644 index 0000000..d5396a7 --- /dev/null +++ b/resources/admin/src/pages/system/crontab/logs.vue @@ -0,0 +1,79 @@ + + + + + + + diff --git a/resources/admin/src/pages/system/crontab/save.vue b/resources/admin/src/pages/system/crontab/save.vue new file mode 100644 index 0000000..924328f --- /dev/null +++ b/resources/admin/src/pages/system/crontab/save.vue @@ -0,0 +1,113 @@ + + + + + diff --git a/resources/admin/src/pages/system/dic/dic.vue b/resources/admin/src/pages/system/dic/dic.vue new file mode 100644 index 0000000..0a1f059 --- /dev/null +++ b/resources/admin/src/pages/system/dic/dic.vue @@ -0,0 +1,107 @@ + + + + + diff --git a/resources/admin/src/pages/system/dic/index.vue b/resources/admin/src/pages/system/dic/index.vue new file mode 100644 index 0000000..15bb325 --- /dev/null +++ b/resources/admin/src/pages/system/dic/index.vue @@ -0,0 +1,321 @@ + + + + + diff --git a/resources/admin/src/pages/system/dic/list.vue b/resources/admin/src/pages/system/dic/list.vue new file mode 100644 index 0000000..dde31b6 --- /dev/null +++ b/resources/admin/src/pages/system/dic/list.vue @@ -0,0 +1,109 @@ + + + + + \ No newline at end of file diff --git a/resources/admin/src/pages/system/log/index.vue b/resources/admin/src/pages/system/log/index.vue new file mode 100644 index 0000000..edda528 --- /dev/null +++ b/resources/admin/src/pages/system/log/index.vue @@ -0,0 +1,114 @@ + + + diff --git a/resources/admin/src/pages/system/log/info.vue b/resources/admin/src/pages/system/log/info.vue new file mode 100644 index 0000000..7aa12da --- /dev/null +++ b/resources/admin/src/pages/system/log/info.vue @@ -0,0 +1,49 @@ + + + + + diff --git a/resources/admin/src/pages/system/modules/index.vue b/resources/admin/src/pages/system/modules/index.vue new file mode 100644 index 0000000..f482cfc --- /dev/null +++ b/resources/admin/src/pages/system/modules/index.vue @@ -0,0 +1,116 @@ + + + + + diff --git a/resources/admin/src/pages/system/setting/index.vue b/resources/admin/src/pages/system/setting/index.vue new file mode 100644 index 0000000..8e42200 --- /dev/null +++ b/resources/admin/src/pages/system/setting/index.vue @@ -0,0 +1,111 @@ + + + + + diff --git a/resources/admin/src/pages/system/setting/save.vue b/resources/admin/src/pages/system/setting/save.vue new file mode 100644 index 0000000..a56ce97 --- /dev/null +++ b/resources/admin/src/pages/system/setting/save.vue @@ -0,0 +1,99 @@ + + + diff --git a/resources/admin/src/pages/ucenter/index.vue b/resources/admin/src/pages/ucenter/index.vue new file mode 100644 index 0000000..1b5f91e --- /dev/null +++ b/resources/admin/src/pages/ucenter/index.vue @@ -0,0 +1,142 @@ + + + diff --git a/resources/admin/src/pages/ucenter/user/account.vue b/resources/admin/src/pages/ucenter/user/account.vue new file mode 100644 index 0000000..46da3de --- /dev/null +++ b/resources/admin/src/pages/ucenter/user/account.vue @@ -0,0 +1,86 @@ + + + + + diff --git a/resources/admin/src/pages/ucenter/user/logs.vue b/resources/admin/src/pages/ucenter/user/logs.vue new file mode 100644 index 0000000..78474a8 --- /dev/null +++ b/resources/admin/src/pages/ucenter/user/logs.vue @@ -0,0 +1,35 @@ + + + + + diff --git a/resources/admin/src/pages/ucenter/user/password.vue b/resources/admin/src/pages/ucenter/user/password.vue new file mode 100644 index 0000000..b4dee7f --- /dev/null +++ b/resources/admin/src/pages/ucenter/user/password.vue @@ -0,0 +1,95 @@ + + + + + diff --git a/resources/admin/src/pages/ucenter/user/pushSettings.vue b/resources/admin/src/pages/ucenter/user/pushSettings.vue new file mode 100644 index 0000000..0b5d4e6 --- /dev/null +++ b/resources/admin/src/pages/ucenter/user/pushSettings.vue @@ -0,0 +1,30 @@ + + + + + diff --git a/resources/admin/src/pages/ucenter/user/seting.vue b/resources/admin/src/pages/ucenter/user/seting.vue new file mode 100644 index 0000000..a93b21e --- /dev/null +++ b/resources/admin/src/pages/ucenter/user/seting.vue @@ -0,0 +1,65 @@ + + + + + diff --git a/resources/admin/src/pages/ucenter/user/space.vue b/resources/admin/src/pages/ucenter/user/space.vue new file mode 100644 index 0000000..a4f07a0 --- /dev/null +++ b/resources/admin/src/pages/ucenter/user/space.vue @@ -0,0 +1,55 @@ + + + + + diff --git a/resources/admin/src/pages/ucenter/user/upToEnterprise.vue b/resources/admin/src/pages/ucenter/user/upToEnterprise.vue new file mode 100644 index 0000000..e53614c --- /dev/null +++ b/resources/admin/src/pages/ucenter/user/upToEnterprise.vue @@ -0,0 +1,18 @@ + + + + + diff --git a/resources/admin/src/router/index.js b/resources/admin/src/router/index.js new file mode 100644 index 0000000..ff93d30 --- /dev/null +++ b/resources/admin/src/router/index.js @@ -0,0 +1,165 @@ +import {createRouter, createWebHashHistory} from 'vue-router'; +import { ElNotification } from 'element-plus'; +import config from "@/config" +import NProgress from 'nprogress' +import 'nprogress/nprogress.css' +import tool from '@/utils/tool' +import store from '@/store' +import systemRouter from './systemRouter' +import userRoutes from '@/config/route' +import {beforeEach, afterEach} from './scrollBehavior' + +// 匹配pages里面所有的.vue文件 +const modules = import.meta.glob('./../pages/**/*.vue') +const otherModules = { + '404': () => import('../layout/other/404.vue'), + 'empty': () => import('../layout/other/empty.vue'), +} + +//系统路由 +const routes = [...systemRouter] + +//系统特殊路由 +const routes_404 = { + path: "/:pathMatch(.*)*", + hidden: true, + component: otherModules['404'], +} +let routes_404_r = ()=>{} + +const router = createRouter({ + history: createWebHashHistory(), + routes: routes +}) + +//设置标题 +document.title = config.APP_NAME + +//判断是否已加载过动态/静态路由 +var isGetRouter = false; + +router.beforeEach(async (to, from, next) => { + + NProgress.start() + //动态标题 + document.title = to.meta?.title ? `${to.meta.title} - ${config.APP_NAME}` : `${config.APP_NAME}` + + let token = tool.data.get("TOKEN"); + + if(to.path === "/login"){ + //删除路由(替换当前layout路由) + router.addRoute(routes[0]) + //删除路由(404) + routes_404_r() + isGetRouter = false; + next(); + return false; + } + + if(routes.findIndex(r => r.path === to.path) >= 0){ + next(); + return false; + } + + if(!token){ + next({ + path: '/login' + }); + return false; + } + + //整页路由处理 + if(to.meta.fullpage){ + to.matched = [to.matched[to.matched.length-1]] + } + //加载动态/静态路由 + if(!isGetRouter){ + let apiMenu = tool.data.get('MENU') || [] + let userInfo = tool.data.get("USER_INFO") + let userMenu = treeFilter(userRoutes, node => { + return node.meta.role ? node.meta.role.filter(item=>userInfo.role.indexOf(item)>-1).length > 0 : true + }) + let menu = [...userMenu, ...apiMenu] + var menuRouter = filterAsyncRouter(menu) + menuRouter = tool.tree_to_list(menuRouter) + menuRouter.forEach(item => { + router.addRoute("layout", item) + }) + routes_404_r = router.addRoute(routes_404) + if (to.matched.length == 0) { + router.push(to.fullPath); + } + isGetRouter = true; + } + beforeEach(to, from) + next(); +}); + +router.afterEach((to, from) => { + afterEach(to, from) + NProgress.done() +}); + +router.onError((error) => { + NProgress.done(); + ElNotification.error({ + title: '路由错误', + message: error.message + }); +}); + +//入侵追加自定义方法、对象 +router.sc_getMenu = () => { + let apiMenu = tool.data.get('MENU') || [] + let userInfo = tool.data.get("USER_INFO") + let userMenu = treeFilter(userRoutes, node => { + return node.meta.role ? node.meta.role.filter(item=>userInfo.role.indexOf(item)>-1).length > 0 : true + }) + var menu = [...userMenu, ...apiMenu] + return menu +} + +//转换 +function filterAsyncRouter(routerMap) { + const accessedRouters = [] + routerMap.forEach(item => { + item.meta = item.meta ? item.meta : {}; + //处理外部链接特殊路由 + if(item.meta.type=='iframe'){ + item.meta.url = item.path; + item.path = `/i/${item.name}`; + } + //MAP转路由对象 + var route = { + path: item.path, + name: item.name, + meta: item.meta, + redirect: item.redirect, + children: item.children ? filterAsyncRouter(item.children) : null, + component: loadComponent(item.component) + } + accessedRouters.push(route) + }) + return accessedRouters +} +function loadComponent(component) { + if (component) { + for (const path in modules) { + const dir = path.split('pages/')[1].split('.vue')[0]; + if (dir === component || dir === component + '/index') { + return () => modules[path](); + } + } + } + return otherModules['empty'] +} + +//过滤树 +function treeFilter(tree, func) { + return tree.map(node => ({ ...node })).filter(node => { + node.children = node.children && treeFilter(node.children, func) + return func(node) || (node.children && node.children.length) + }) +} + +export default router diff --git a/resources/admin/src/router/scrollBehavior.js b/resources/admin/src/router/scrollBehavior.js new file mode 100644 index 0000000..ec30197 --- /dev/null +++ b/resources/admin/src/router/scrollBehavior.js @@ -0,0 +1,22 @@ +import store from '@/store' +import { nextTick } from 'vue' + +export function beforeEach(to, from){ + var adminMain = document.querySelector('#adminui-main') + if(!adminMain){return false} + store.commit("updateViewTags", { + fullPath: from.fullPath, + scrollTop: adminMain.scrollTop + }) +} + +export function afterEach(to){ + var adminMain = document.querySelector('#adminui-main') + if(!adminMain){return false} + nextTick(()=>{ + var beforeRoute = store.state.viewTags.viewTags.filter(v => v.fullPath == to.fullPath)[0] + if(beforeRoute){ + adminMain.scrollTop = beforeRoute.scrollTop || 0 + } + }) +} \ No newline at end of file diff --git a/resources/admin/src/router/systemRouter.js b/resources/admin/src/router/systemRouter.js new file mode 100644 index 0000000..65b02f3 --- /dev/null +++ b/resources/admin/src/router/systemRouter.js @@ -0,0 +1,35 @@ +import config from "@/config" + +//系统路由 +const routes = [ + { + name: "layout", + path: "/", + component: () => import(/* webpackChunkName: "layout" */ '@/layout'), + redirect: config.DASHBOARD_URL || '/dashboard', + children: [] + }, + { + path: "/login", + component: () => import(/* webpackChunkName: "login" */ '@/pages/login'), + meta: { + title: "登录" + } + }, + { + path: "/user_register", + component: () => import(/* webpackChunkName: "userRegister" */ '@/pages/login/userRegister'), + meta: { + title: "注册" + } + }, + { + path: "/reset_password", + component: () => import(/* webpackChunkName: "resetPassword" */ '@/pages/login/resetPassword'), + meta: { + title: "重置密码" + } + } +] + +export default routes; diff --git a/resources/admin/src/sent.js b/resources/admin/src/sent.js new file mode 100644 index 0000000..6b8679e --- /dev/null +++ b/resources/admin/src/sent.js @@ -0,0 +1,95 @@ +import config from "./config" +import api from './api' +import tool from './utils/tool' +import http from "./utils/request" +import { permission, rolePermission } from './utils/permission' + +import scTable from './components/scTable' +import scTableColumn from './components/scTable/column.js' +import scFilterBar from './components/scFilterBar' +import scUpload from './components/scUpload' +import scUploadMultiple from './components/scUpload/multiple' +import scUploadFile from './components/scUpload/file' +import scFormTable from './components/scFormTable' +import scTableSelect from './components/scTableSelect' +import scPageHeader from './components/scPageHeader' +import scSelect from './components/scSelect' +import scSelectRemote from './components/scSelectRemote' +import scSelectTree from './components/scSelectTree' +import scDialog from './components/scDialog' +import scForm from './components/scForm' +import scTitle from './components/scTitle' +import scWaterMark from './components/scWaterMark' +import scQrCode from './components/scQrCode' +import scFileImport from '@/components/scFileImport' +import scFileExport from '@/components/scFileExport' + +import scStatusIndicator from './components/scMini/scStatusIndicator' +import scTrend from './components/scMini/scTrend' +import scUserSelect from './components/scUserSelect' + +import auth from './directives/auth' +import role from './directives/role' +import time from './directives/time' +import copy from './directives/copy' +import errorHandler from './utils/errorHandler' + +import * as elIcons from '@element-plus/icons-vue' +import * as scIcons from './assets/icons' + +export default { + install(app) { + //挂载全局对象 + app.config.globalProperties.$CONFIG = config; + app.config.globalProperties.$TOOL = tool; + app.config.globalProperties.$HTTP = http; + app.config.globalProperties.$API = api; + app.config.globalProperties.$AUTH = permission; + app.config.globalProperties.$ROLE = rolePermission; + + //注册全局组件 + app.component('scTable', scTable); + app.component('scTableColumn', scTableColumn); + app.component('scFilterBar', scFilterBar); + app.component('scUpload', scUpload); + app.component('scUploadMultiple', scUploadMultiple); + app.component('scUploadFile', scUploadFile); + app.component('scFormTable', scFormTable); + app.component('scTableSelect', scTableSelect); + app.component('scPageHeader', scPageHeader); + app.component('scSelect', scSelect); + app.component('scSelectRemote', scSelectRemote); + app.component('scSelectTree', scSelectTree); + app.component('scDialog', scDialog); + app.component('scForm', scForm); + app.component('scTitle', scTitle); + app.component('scWaterMark', scWaterMark); + app.component('scQrCode', scQrCode); + app.component('scStatusIndicator', scStatusIndicator); + app.component('scTrend', scTrend); + app.component('scUserSelect', scUserSelect); + app.component('scFileImport', scFileImport); + app.component('scFileExport', scFileExport) + + //注册全局指令 + app.directive('auth', auth) + app.directive('role', role) + app.directive('time', time) + app.directive('copy', copy) + + //统一注册el-icon图标 + for(let icon in elIcons){ + app.component(`ElIcon${icon}`, elIcons[icon]) + } + //统一注册sc-icon图标 + for(let icon in scIcons){ + app.component(`ScIcon${icon}`, scIcons[icon]) + } + + //关闭async-validator全局控制台警告 + window.ASYNC_VALIDATOR_NO_WARNING = 1 + + //全局代码错误捕捉 + app.config.errorHandler = errorHandler + } +} diff --git a/resources/admin/src/store/index.js b/resources/admin/src/store/index.js new file mode 100644 index 0000000..6c0dee4 --- /dev/null +++ b/resources/admin/src/store/index.js @@ -0,0 +1,15 @@ +/** + * @description 自动import导入所有 vuex 模块 + */ + +import { createStore } from 'vuex'; + +const files = import.meta.glob('./modules/*.js', { eager: true }) +const modules = {} +Object.keys(files).forEach(key => { + modules[key.replace(/^\.\/modules\/(.*)\.js$/g, '$1')] = files[key].default +}) + +export default createStore({ + modules +}); diff --git a/resources/admin/src/store/modules/global.js b/resources/admin/src/store/modules/global.js new file mode 100644 index 0000000..6a1fd0e --- /dev/null +++ b/resources/admin/src/store/modules/global.js @@ -0,0 +1,51 @@ +import config from "@/config"; +import auth from "@/api/module/auth"; +import tool from "@/utils/tool"; + +export default { + state: { + //移动端布局 + ismobile: false, + //布局 + layout: config.LAYOUT, + //菜单是否折叠 toggle + menuIsCollapse: config.MENU_IS_COLLAPSE, + //多标签栏 + layoutTags: config.LAYOUT_TAGS, + //主题 + theme: config.THEME, + menu: tool.data.get('MENU') || [] + }, + mutations: { + SET_ismobile(state, key){ + state.ismobile = key + }, + SET_layout(state, key){ + state.layout = key + }, + SET_theme(state, key){ + state.theme = key + }, + TOGGLE_menuIsCollapse(state){ + state.menuIsCollapse = !state.menuIsCollapse + }, + TOGGLE_layoutTags(state){ + state.layoutTags = !state.layoutTags + }, + UPDATA_MENU(state){ + auth.menu.myMenus.get().then(res => { + state.menu = res.data.menu + if(res.data.menu.length==0){ + this.islogin = false + this.$alert("当前用户无任何菜单权限,请联系系统管理员", "无权限访问", { + type: 'error', + center: true + }) + return false + } + tool.data.set("MENU", res.data.menu) + tool.data.set("PERMISSIONS", res.data.permissions) + }) + } + } +} diff --git a/resources/admin/src/store/modules/iframe.js b/resources/admin/src/store/modules/iframe.js new file mode 100644 index 0000000..f139578 --- /dev/null +++ b/resources/admin/src/store/modules/iframe.js @@ -0,0 +1,38 @@ +export default { + state: { + iframeList: [] + }, + mutations: { + setIframeList(state, route){ + state.iframeList = [] + state.iframeList.push(route) + }, + pushIframeList(state, route){ + let target = state.iframeList.find((item) => item.path === route.path) + if(!target){ + state.iframeList.push(route) + } + }, + removeIframeList(state, route){ + state.iframeList.forEach((item, index) => { + if (item.path === route.path){ + state.iframeList.splice(index, 1) + } + }) + }, + refreshIframe(state, route){ + state.iframeList.forEach((item) => { + if (item.path == route.path){ + var url = route.meta.url; + item.meta.url = ''; + setTimeout(function() { + item.meta.url = url + }, 200); + } + }) + }, + clearIframeList(state){ + state.iframeList = [] + } + } +} diff --git a/resources/admin/src/store/modules/keepAlive.js b/resources/admin/src/store/modules/keepAlive.js new file mode 100644 index 0000000..c143cf5 --- /dev/null +++ b/resources/admin/src/store/modules/keepAlive.js @@ -0,0 +1,34 @@ +export default { + state: { + keepLiveRoute: [], + routeKey: null, + routeShow: true + }, + mutations: { + pushKeepLive(state, component){ + if(!state.keepLiveRoute.includes(component)){ + state.keepLiveRoute.push(component) + } + }, + removeKeepLive(state, component){ + var index = state.keepLiveRoute.indexOf(component); + if(index !== -1){ + state.keepLiveRoute.splice(index, 1); + } + }, + clearKeepLive(state){ + state.keepLiveRoute = [] + }, + setRouteKey(state, key){ + state.routeKey = key + }, + setRouteShow(state, key){ + state.routeShow = key + } + }, + actions: { + setRouteKey({ commit }, key) { + commit('setRouteKey', key); + } + } +} diff --git a/resources/admin/src/store/modules/viewTags.js b/resources/admin/src/store/modules/viewTags.js new file mode 100644 index 0000000..0edaaf7 --- /dev/null +++ b/resources/admin/src/store/modules/viewTags.js @@ -0,0 +1,52 @@ +import router from '@/router' +import tool from '@/utils/tool' + +export default { + state: { + viewTags: tool.data.get('viewTags') || [] + }, + mutations: { + pushViewTags(state, route){ + let backPathIndex = state.viewTags.findIndex(item => item.fullPath == router.options.history.state.back) + let target = state.viewTags.find((item) => item.fullPath === route.fullPath) + let isName = route.name + if(!target && isName){ + if(backPathIndex == -1){ + state.viewTags.push(tool.get_data_field(route, ['fullPath', 'hash', 'href', 'meta', 'name', 'params', 'query', 'scrollTop'])) + }else{ + state.viewTags.splice(backPathIndex+1, 0, tool.get_data_field(route, ['fullPath', 'hash', 'href', 'meta', 'name', 'params', 'query', 'scrollTop'])) + } + tool.data.set('viewTags', state.viewTags) + } + }, + removeViewTags(state, route){ + state.viewTags.forEach((item, index) => { + if (item.fullPath === route.fullPath){ + state.viewTags.splice(index, 1) + } + }) + tool.data.set('viewTags', state.viewTags) + }, + updateViewTags(state, route){ + state.viewTags.forEach((item) => { + if (item.fullPath == route.fullPath){ + item = Object.assign(item, route) + } + }) + tool.data.set('viewTags', state.viewTags) + }, + updateViewTagsTitle(state, title=''){ + const nowFullPath = location.hash.substring(1) + state.viewTags.forEach((item) => { + if (item.fullPath == nowFullPath){ + item.meta.title = title + } + }) + tool.data.set('viewTags', state.viewTags) + }, + clearViewTags(state){ + state.viewTags = [] + tool.data.set('viewTags', state.viewTags) + } + } +} diff --git a/resources/admin/src/utils/color.js b/resources/admin/src/utils/color.js new file mode 100644 index 0000000..295d011 --- /dev/null +++ b/resources/admin/src/utils/color.js @@ -0,0 +1,29 @@ +export default { + //hex颜色转rgb颜色 + HexToRgb(str) { + str = str.replace("#", "") + var hxs = str.match(/../g) + for (var i = 0; i < 3; i++) hxs[i] = parseInt(hxs[i], 16) + return hxs + }, + //rgb颜色转hex颜色 + RgbToHex(a, b, c) { + var hexs = [a.toString(16), b.toString(16), c.toString(16)] + for (var i = 0; i < 3; i++) { + if (hexs[i].length == 1) hexs[i] = "0" + hexs[i] + } + return "#" + hexs.join(""); + }, + //加深 + darken(color, level) { + var rgbc = this.HexToRgb(color) + for (var i = 0; i < 3; i++) rgbc[i] = Math.floor(rgbc[i] * (1 - level)) + return this.RgbToHex(rgbc[0], rgbc[1], rgbc[2]) + }, + //变淡 + lighten(color, level) { + var rgbc = this.HexToRgb(color) + for (var i = 0; i < 3; i++) rgbc[i] = Math.floor((255 - rgbc[i]) * level + rgbc[i]) + return this.RgbToHex(rgbc[0], rgbc[1], rgbc[2]) + } +} diff --git a/resources/admin/src/utils/db.js b/resources/admin/src/utils/db.js new file mode 100644 index 0000000..68e4683 --- /dev/null +++ b/resources/admin/src/utils/db.js @@ -0,0 +1,304 @@ +//初始数据库结构 +const dbData = [ + { + dbName: "masterDB", //数据库名称 + version: 1, //数据库版本号,当结构发生变化时 + tables: [ //表 + { + name: "SYS_favorites", //表名称 + keyPath: "uid", //主键 + autoIncrement: false, //主键是否自增 + index: [ //索引 + { + name: "name_index", //索引名称 + key: "name" //索引key + } + ] + }, + { + name: "SYS_keyword", + keyPath: "id" + } + ] + }, + { + dbName: "guestDB", + version: 1, + tables: [ + { + name: "MY_demo", + keyPath: "id" + } + ] + } +] + +// 使用示例 +// import DB from '@/utils/db' + +// 初始化创建数据库 一般在项目启动时就执行了 +// await DB.create() + +// 打开某个数据库,返回数据库实例 +// const database = await DB.open("dbName") + +// 在打开的数据库中添加数据到tablenName表 +// await database.add("tablenName", data) + +// 查询 +// await database.get("tablenName", key) + +// 查询 根据索引 +// await database.indexGet("tablenName", "indexName", indexVal) + +// 修改 +// await database.put("tablenName", data) + +// 删除 +// await database.delete("tablenName", key) + +// 获取所有 +// await database.getAll("tablenName") + +// 清空某个表数据 +// await database.clear("tablenName") + +// 获取某个表信息 +// database.getTable("tablenName") + +// 获取所有表 +// database.getTables() + +// 关闭数据库连接 +// database.close() + + +export default { + //建立数据库,表,初始数据 + create() { + var promiseArray = [] + const addDB = db => { + return new Promise((resolve, reject) => { + const request = indexedDB.open(db.dbName, db.version) + request.onupgradeneeded = e => { + const thisDB = e.target.result + db.tables.forEach(item => { + let table = null + if (thisDB.objectStoreNames.contains(item.name)) { + //已存在表,删除旧index + table = e.target.transaction.objectStore(item.name) + table.indexNames.length>0 && table.indexNames.forEach(indexName => { + table.deleteIndex(indexName) + }) + }else{ + //创建新的表 + table = thisDB.createObjectStore(item.name, { + keyPath: item.keyPath, + autoIncrement: item.autoIncrement + }) + } + //建立index + item.index && item.index.forEach(ind => { + table.createIndex(ind.name, ind.key, { unique: false }) + }) + }) + }, + request.onsuccess = e => { + return resolve(e.target.result) + } + request.onerror = e => { + return reject(e) + } + }) + } + dbData.forEach(db => { + promiseArray.push(addDB(db)) + }) + return new Promise((resolve, reject) => { + Promise.all(promiseArray).then((e) => { + resolve(e) + }).catch(e => { + reject(e) + }) + }) + }, + //所有数据库 + databases(){ + return indexedDB.databases() + }, + //打开数据库 + open(dbName){ + return new Promise((resolve, reject) => { + const request = indexedDB.open(dbName) + request.onsuccess = e => { + const database = new this.database(e.target.result) + resolve(database) + } + request.onerror = e => { + reject(e) + } + }) + }, + //删除数据库 + deleteDB(dbName){ + return indexedDB.deleteDatabase(dbName) + }, + //数据库类 + database: function (IDBDatabase) { + this.IDBDatabase = IDBDatabase + + /** + * 添加行数据 + * @param {string} tableName 表名 + * @param {object} data 数据 + * @returns {promise} + */ + this.add = (tableName, data) => { + return new Promise((resolve, reject) => { + const request = IDBDatabase.transaction([tableName], 'readwrite').objectStore(tableName).add(data) + request.onsuccess = e => { + resolve(e) + } + request.onerror = e => { + reject(e) + } + }) + } + + /** + * 修改行数据,未查询到就新增 + * @param {string} tableName 表名 + * @param {object} data 数据 + * @returns {promise} + */ + this.put = (tableName, data) => { + return new Promise((resolve, reject) => { + const request = IDBDatabase.transaction([tableName], 'readwrite').objectStore(tableName).put(data) + request.onsuccess = e => { + resolve(e) + } + request.onerror = e => { + reject(e) + } + }) + } + + /** + * 删除行 + * @param {string} tableName 表名 + * @param {string} key 主键 + * @returns {promise} + */ + this.delete = (tableName, key) => { + return new Promise((resolve, reject) => { + const request = IDBDatabase.transaction([tableName], 'readwrite').objectStore(tableName).delete(key) + request.onsuccess = e => { + resolve(e) + } + request.onerror = e => { + reject(e) + } + }) + } + + /** + * 根据主键获取行 + * @param {string} tableName 表名 + * @param {string} key 主键 + * @returns {promise} + */ + this.get = (tableName, key) => { + return new Promise((resolve, reject) => { + const request = IDBDatabase.transaction([tableName], 'readwrite').objectStore(tableName).get(key) + request.onsuccess = () => { + resolve(request.result || null) + } + request.onerror = e => { + reject(e) + } + }) + } + + /** + * 根据索引获取行 + * @param {string} tableName 表名 + * @param {string} indexName 索引库名称 + * @param {string} indexVal 索引值 + * @returns {promise} + */ + this.indexGet = (tableName, indexName, indexVal) => { + return new Promise((resolve, reject) => { + const request = IDBDatabase.transaction([tableName], 'readwrite').objectStore(tableName).index(indexName).get(indexVal) + request.onsuccess = () => { + resolve(request.result || null) + } + request.onerror = e => { + reject(e) + } + }) + } + + /** + * 获取所有行 + * @param {string} tableName 表名 + * @returns {promise} + */ + this.getAll = (tableName) => { + return new Promise((resolve, reject) => { + const request = IDBDatabase.transaction([tableName], 'readwrite').objectStore(tableName).getAll() + request.onsuccess = () => { + resolve(request.result || null) + } + request.onerror = e => { + reject(e) + } + }) + } + + /** + * 清空表 + * @param {string} tableName 表名 + * @returns {promise} + */ + this.clear = (tableName) => { + return new Promise((resolve, reject) => { + const request = IDBDatabase.transaction([tableName], 'readwrite').objectStore(tableName).clear() + request.onsuccess = e => { + resolve(e) + } + request.onerror = err => { + reject(err) + } + }) + } + + /** + * 获取表信息 + * @returns {IDBObjectStore} + */ + this.getTable = (tableName) => { + const request = IDBDatabase.transaction([tableName], 'readwrite').objectStore(tableName) + return request + } + + /** + * 获取所有的表 + * @returns {[IDBObjectStore]} + */ + this.getTables = () => { + const tables = [] + for (let item of IDBDatabase.objectStoreNames) { + tables.push(IDBDatabase.transaction([item], 'readwrite').objectStore(item)) + } + return tables + } + + /** + * 关闭数据库连接 + * @returns {} + */ + this.close = () => { + return IDBDatabase.close() + } + } +} diff --git a/resources/admin/src/utils/errorHandler.js b/resources/admin/src/utils/errorHandler.js new file mode 100644 index 0000000..88f4427 --- /dev/null +++ b/resources/admin/src/utils/errorHandler.js @@ -0,0 +1,33 @@ +/** + * 全局代码错误捕捉 + * 比如 null.length 就会被捕捉到 + */ + +export default (error, vm)=>{ + //过滤HTTP请求错误 + if(error.status || error.status==0){ + return false + } + + var errorMap = { + InternalError: "Javascript引擎内部错误", + ReferenceError: "未找到对象", + TypeError: "使用了错误的类型或对象", + RangeError: "使用内置对象时,参数超范围", + SyntaxError: "语法错误", + EvalError: "错误的使用了Eval", + URIError: "URI错误" + } + var errorName = errorMap[error.name] || "未知错误" + + console.warn(`[SCUI error]: ${error}`); + console.error(error); + //throw error; + + vm.$nextTick(() => { + vm.$notify.error({ + title: errorName, + message: error + }); + }) +} diff --git a/resources/admin/src/utils/load.js b/resources/admin/src/utils/load.js new file mode 100644 index 0000000..fa3ebc2 --- /dev/null +++ b/resources/admin/src/utils/load.js @@ -0,0 +1,60 @@ +/** + * loadJS 异步加载远程JS + * @constructor + * @param {string} src - 必填,需要加载的URL路径 + * @param {string} keyName - 必填,唯一key和JS返回的全局的对象名 + * @param {string} callbackName - 非必填,如果远程JS有callback,则可更有效的判断是否完成加载 + */ +export function loadJS (src, keyName, callbackName) { + return new Promise((resolve, reject) => { + let has = document.head.querySelector("script[loadKey="+keyName+"]") + if(has){ + return resolve(window[keyName]) + } + let script = document.createElement("script") + script.type = "text/javascript" + script.src = src + script.setAttribute("loadKey", keyName) + document.head.appendChild(script) + script.onload = () => { + if(callbackName){ + window[callbackName] = () => { + return resolve(window[keyName]) + } + }else{ + setTimeout(()=>{ + return resolve(window[keyName]) + },50) + } + } + script.onerror = (err) => { + return reject(err) + } + }) +} + +/** + * loadCSS 异步加载远程css + * @constructor + * @param {string} src - 必填,需要加载的URL路径 + * @param {string} keyName - 必填,唯一key + */ +export function loadCSS (src, keyName) { + return new Promise((resolve, reject) => { + let has = document.head.querySelector("link[loadKey="+keyName+"]") + if(has){ + return resolve() + } + let link = document.createElement('link') + link.rel = "stylesheet" + link.href = src + link.setAttribute("loadKey", keyName) + document.head.appendChild(link) + link.onload = () => { + return resolve() + } + link.onerror = (err) => { + return reject(err) + } + }) +} diff --git a/resources/admin/src/utils/permission.js b/resources/admin/src/utils/permission.js new file mode 100644 index 0000000..a8dd63e --- /dev/null +++ b/resources/admin/src/utils/permission.js @@ -0,0 +1,23 @@ +import tool from '@/utils/tool'; + +export function permission(data) { + let permissions = tool.data.get("PERMISSIONS"); + if(!permissions){ + return false; + } + let isHave = permissions.includes(data); + return isHave; +} + +export function rolePermission(data) { + let userInfo = tool.data.get("USER_INFO"); + if(!userInfo){ + return false; + } + let role = userInfo.role; + if(!role){ + return false; + } + let isHave = role.includes(data); + return isHave; +} diff --git a/resources/admin/src/utils/print.js b/resources/admin/src/utils/print.js new file mode 100644 index 0000000..fb6d422 --- /dev/null +++ b/resources/admin/src/utils/print.js @@ -0,0 +1,137 @@ +// 打印类属性、方法定义 +/* eslint-disable */ +const Print = function(dom, options) { + if (!(this instanceof Print)) return new Print(dom, options); + + this.options = this.extend({ + 'noPrint': '.no-print' + }, options); + + if ((typeof dom) === "string") { + try{ + this.dom = document.querySelector(dom); + }catch{ + var createDom = document.createElement("div") + createDom.innerHTML = dom + this.dom = createDom; + }; + } else { + this.isDOM(dom) + this.dom = this.isDOM(dom) ? dom : dom.$el; + } + + this.init(); +}; +Print.prototype = { + init: function() { + var content = this.getStyle() + this.getHtml(); + this.writeIframe(content); + }, + extend: function(obj, obj2) { + for (var k in obj2) { + obj[k] = obj2[k]; + } + return obj; + }, + + getStyle: function() { + var str = "", + styles = document.querySelectorAll('style,link'); + for (var i = 0; i < styles.length; i++) { + str += styles[i].outerHTML; + } + str += ""; + str += ""; + return str; + }, + + getHtml: function() { + var inputs = document.querySelectorAll('input'); + var textareas = document.querySelectorAll('textarea'); + var selects = document.querySelectorAll('select'); + + for (var k = 0; k < inputs.length; k++) { + if (inputs[k].type == "checkbox" || inputs[k].type == "radio") { + if (inputs[k].checked == true) { + inputs[k].setAttribute('checked', "checked") + } else { + inputs[k].removeAttribute('checked') + } + } else if (inputs[k].type == "text") { + inputs[k].setAttribute('value', inputs[k].value) + } else { + inputs[k].setAttribute('value', inputs[k].value) + } + } + + for (var k2 = 0; k2 < textareas.length; k2++) { + if (textareas[k2].type == 'textarea') { + textareas[k2].innerHTML = textareas[k2].value + } + } + + for (var k3 = 0; k3 < selects.length; k3++) { + if (selects[k3].type == 'select-one') { + var child = selects[k3].children; + for (var i in child) { + if (child[i].tagName == 'OPTION') { + if (child[i].selected == true) { + child[i].setAttribute('selected', "selected") + } else { + child[i].removeAttribute('selected') + } + } + } + } + } + + return this.dom.outerHTML; + }, + + writeIframe: function(content) { + var w, doc, iframe = document.createElement('iframe'), + f = document.body.appendChild(iframe); + iframe.id = "myIframe"; + //iframe.style = "position:absolute;width:0;height:0;top:-10px;left:-10px;"; + iframe.setAttribute('style', 'position:absolute;width:0;height:0;top:-10px;left:-10px;'); + w = f.contentWindow || f.contentDocument; + doc = f.contentDocument || f.contentWindow.document; + doc.open(); + doc.write(content); + doc.close(); + var _this = this + iframe.onload = function() { + _this.toPrint(w); + setTimeout(function() { + document.body.removeChild(iframe) + }, 100) + } + }, + + toPrint: function(frameWindow) { + try { + setTimeout(function() { + frameWindow.focus(); + try { + if (!frameWindow.document.execCommand('print', false, null)) { + frameWindow.print(); + } + } catch (e) { + frameWindow.print(); + } + frameWindow.close(); + }, 10); + } catch (err) { + console.log('err', err); + } + }, + isDOM: (typeof HTMLElement === 'object') ? + function(obj) { + return obj instanceof HTMLElement; + } : function(obj) { + return obj && typeof obj === 'object' && obj.nodeType === 1 && typeof obj.nodeName === 'string'; + } +}; + +export default Print diff --git a/resources/admin/src/utils/request.js b/resources/admin/src/utils/request.js new file mode 100644 index 0000000..c5a25df --- /dev/null +++ b/resources/admin/src/utils/request.js @@ -0,0 +1,219 @@ +import axios from 'axios'; +import { ElNotification, ElMessageBox } from 'element-plus'; +import sysConfig from "@/config"; +import tool from '@/utils/tool'; +import router from '@/router'; + +axios.defaults.baseURL = '' + +axios.defaults.timeout = sysConfig.TIMEOUT +let isMessage = false //是否提示重新登录 + +// HTTP request 拦截器 +axios.interceptors.request.use( + (config) => { + let token = tool.data.get("TOKEN"); + if(token){ + config.headers[sysConfig.TOKEN_NAME] = sysConfig.TOKEN_PREFIX + token + } + if(!sysConfig.REQUEST_CACHE && config.method == 'get'){ + config.params = config.params || {}; + config.params['_'] = new Date().getTime(); + } + Object.assign(config.headers, sysConfig.HEADERS) + return config; + }, + (error) => { + return Promise.reject(error); + } +); + +// HTTP response 拦截器 +axios.interceptors.response.use( + (response) => { + if(response.data.code == 2000 || response.data.code == 2001){ + if(!isMessage){ + ElMessageBox.confirm('当前用户已被登出或无权限访问当前资源,请尝试重新登录后再操作。', '无权限访问', { + type: 'error', + closeOnClickModal: false, + center: true, + confirmButtonText: '重新登录' + }).then(() => { + router.replace({path: '/login'}); + isMessage = false; + }).catch(() => {}) + isMessage = true; + } + } + return response; + }, + (error) => { + if (error.response) { + if (error.response.status == 404) { + ElNotification.error({ + title: '请求错误', + message: "Status:404,正在请求不存在的服务器记录!" + }); + } else if (error.response.status == 500) { + ElNotification.error({ + title: '请求错误', + message: error.response.data.message || "Status:500,服务器发生错误!" + }); + } else if (error.response.status == 401 || error.response.status == 2000) { + if(!isMessage){ + ElMessageBox.confirm('当前用户已被登出或无权限访问当前资源,请尝试重新登录后再操作。', '无权限访问', { + type: 'error', + closeOnClickModal: false, + center: true, + confirmButtonText: '重新登录' + }).then(() => { + router.replace({path: '/login'}); + isMessage = false; + }).catch(() => {}) + isMessage = true; + } + } else { + ElNotification.error({ + title: '请求错误', + message: error.message || `Status:${error.response.status},未知错误!` + }); + } + } else { + ElNotification.error({ + title: '请求错误', + message: "请求服务器无响应!" + }); + } + + return Promise.reject(error.response); + } +); + +var http = { + + /** get 请求 + * @param {接口地址} url + * @param {请求参数} params + * @param {参数} config + */ + get: function(url, params={}, config={}) { + return new Promise((resolve, reject) => { + axios({ + method: 'get', + url: url, + params: params, + ...config + }).then((response) => { + resolve(response.data); + }).catch((error) => { + reject(error); + }) + }) + }, + + /** post 请求 + * @param {接口地址} url + * @param {请求参数} data + * @param {参数} config + */ + post: function(url, data={}, config={}) { + return new Promise((resolve, reject) => { + axios({ + method: 'post', + url: url, + data: data, + ...config + }).then((response) => { + resolve(response.data); + }).catch((error) => { + reject(error); + }) + }) + }, + + /** put 请求 + * @param {接口地址} url + * @param {请求参数} data + * @param {参数} config + */ + put: function(url, data={}, config={}) { + return new Promise((resolve, reject) => { + axios({ + method: 'put', + url: url, + data: data, + ...config + }).then((response) => { + resolve(response.data); + }).catch((error) => { + reject(error); + }) + }) + }, + + /** patch 请求 + * @param {接口地址} url + * @param {请求参数} data + * @param {参数} config + */ + patch: function(url, data={}, config={}) { + return new Promise((resolve, reject) => { + axios({ + method: 'patch', + url: url, + data: data, + ...config + }).then((response) => { + resolve(response.data); + }).catch((error) => { + reject(error); + }) + }) + }, + + /** delete 请求 + * @param {接口地址} url + * @param {请求参数} data + * @param {参数} config + */ + delete: function(url, data={}, config={}) { + return new Promise((resolve, reject) => { + axios({ + method: 'delete', + url: url, + data: data, + ...config + }).then((response) => { + resolve(response.data); + }).catch((error) => { + reject(error); + }) + }) + }, + + /** jsonp 请求 + * @param {接口地址} url + * @param {JSONP回调函数名称} name + */ + jsonp: function(url, name='jsonp'){ + return new Promise((resolve) => { + var script = document.createElement('script') + var _id = `jsonp${Math.ceil(Math.random() * 1000000)}` + script.id = _id + script.type = 'text/javascript' + script.src = url + window[name] =(response) => { + resolve(response) + document.getElementsByTagName('head')[0].removeChild(script) + try { + delete window[name]; + }catch(e){ + window[name] = undefined; + } + } + document.getElementsByTagName('head')[0].appendChild(script) + }) + } +} + +export default http; diff --git a/resources/admin/src/utils/template.js b/resources/admin/src/utils/template.js new file mode 100644 index 0000000..e441bd6 --- /dev/null +++ b/resources/admin/src/utils/template.js @@ -0,0 +1,316 @@ +/*! + * template.js v0.7.1 (https://github.com/yanhaijing/template.js) + * API https://github.com/yanhaijing/template.js/blob/master/doc/api.md + * Copyright 2015 yanhaijing. All Rights Reserved + * Licensed under MIT (https://github.com/yanhaijing/template.js/blob/master/MIT-LICENSE.txt) + */ +/* eslint-disable */ +;(function(root, factory) { + var template = factory(root); + if (typeof define === 'function' && define.amd) { + // AMD + define('template', function() { + return template; + }); + } else if (typeof exports === 'object') { + // Node.js + module.exports = template; + } else { + // Browser globals + var _template = root.template; + + template.noConflict = function() { + if (root.template === template) { + root.template = _template; + } + + return template; + }; + root.template = template; + } +}(this, function(root) { + 'use strict'; + var o = { + sTag: '<%',//开始标签 + eTag: '%>',//结束标签 + compress: false,//是否压缩html + escape: true, //默认输出是否进行HTML转义 + error: function (e) {}//错误回调 + }; + var functionMap = {}; //内部函数对象 + //修饰器前缀 + var modifierMap = { + '': function (param) {return nothing(param)}, + 'h': function (param) {return encodeHTML(param)}, + 'u': function (param) {return encodeURI(param)} + }; + + var toString = {}.toString; + var slice = [].slice; + function type(x) { + if(x === null){ + return 'null'; + } + + var t= typeof x; + + if(t !== 'object'){ + return t; + } + + var c = toString.call(x).slice(8, -1).toLowerCase(); + if(c !== 'object'){ + return c; + } + + if(x.constructor==Object){ + return c; + } + + return 'unknown'; + } + + function isObject(obj) { + return type(obj) === 'object'; + } + function isFunction(fn) { + return type(fn) === 'function'; + } + function isString(str) { + return type(str) === 'string'; + } + function extend() { + var target = arguments[0] || {}; + var arrs = slice.call(arguments, 1); + var len = arrs.length; + + for (var i = 0; i < len; i++) { + var arr = arrs[i]; + for (var name in arr) { + target[name] = arr[name]; + } + + } + return target; + } + function clone() { + var args = slice.call(arguments); + return extend.apply(null, [{}].concat(args)); + } + function nothing(param) { + return param; + } + function encodeHTML(source) { + return String(source) + .replace(/&/g,'&') + .replace(//g,'>') + .replace(/\\/g,'\') + .replace(/"/g,'"') + .replace(/'/g,'''); + } + function compress(html) { + return html.replace(/\s+/g, ' ').replace(//g, ''); + } + function consoleAdapter(cmd, msg) { + typeof console !== 'undefined' && console[cmd] && console[cmd](msg); + } + function handelError(e) { + var message = 'template.js error\n\n'; + + for (var key in e) { + message += '<' + key + '>\n' + e[key] + '\n\n'; + } + message += '\n' + e.message + '\n\n'; + consoleAdapter('error', message); + + o.error(e); + function error() { + return 'template.js error'; + } + error.toString = function () { + return '__code__ = "template.js error"'; + } + return error; + } + function parse(tpl, opt) { + var code = ''; + var sTag = opt.sTag; + var eTag = opt.eTag; + var escape = opt.escape; + function parsehtml(line) { + // 单双引号转义,换行符替换为空格 + line = line.replace(/('|")/g, '\\$1'); + var lineList = line.split('\n'); + var code = ''; + for (var i = 0; i < lineList.length; i++) { + code += ';__code__ += ("' + lineList[i] + (i === lineList.length - 1 ? '")\n' : '\\n")\n'); + } + return code; + } + function parsejs(line) { + //var reg = /^(:?)(.*?)=(.*)$/; + var reg = /^(?:=|(:.*?)=)(.*)$/ + var html; + var arr; + var modifier; + + // = := :*= + // :h=123 [':h=123', 'h', '123'] + if (arr = reg.exec(line)) { + html = arr[2]; // 输出 + if (Boolean(arr[1])) { + // :开头 + modifier = arr[1].slice(1); + } else { + // = 开头 + modifier = escape ? 'h' : ''; + } + + return ';__code__ += __modifierMap__["' + modifier + '"](typeof (' + html + ') !== "undefined" ? (' + html + ') : "")\n'; + } + + //原生js + return ';' + line + '\n'; + } + + var tokens = tpl.split(sTag); + + for (var i = 0, len = tokens.length; i < len; i++) { + var token = tokens[i].split(eTag); + + if (token.length === 1) { + code += parsehtml(token[0]); + } else { + code += parsejs(token[0], true); + if (token[1]) { + code += parsehtml(token[1]); + } + } + } + return code; + } + function compiler(tpl, opt) { + var mainCode = parse(tpl, opt); + + var headerCode = '\n' + + ' var html = (function (__data__, __modifierMap__) {\n' + + ' var __str__ = "", __code__ = "";\n' + + ' for(var key in __data__) {\n' + + ' __str__+=("var " + key + "=__data__[\'" + key + "\'];");\n' + + ' }\n' + + ' eval(__str__);\n\n'; + + var footerCode = '\n' + + ' ;return __code__;\n' + + ' }(__data__, __modifierMap__));\n' + + ' return html;\n'; + + var code = headerCode + mainCode + footerCode; + code = code.replace(/[\r]/g, ' '); // ie 7 8 会报错,不知道为什么 + try { + var Render = new Function('__data__', '__modifierMap__', code); + Render.toString = function () { + return mainCode; + } + return Render; + } catch(e) { + e.temp = 'function anonymous(__data__, __modifierMap__) {' + code + '}'; + throw e; + } + } + function compile(tpl, opt) { + opt = clone(o, opt); + + try { + var Render = compiler(tpl, opt); + } catch(e) { + e.name = 'CompileError'; + e.tpl = tpl; + e.render = e.temp; + delete e.temp; + return handelError(e); + } + + function render(data) { + data = clone(functionMap, data); + try { + var html = Render(data, modifierMap); + html = opt.compress ? compress(html) : html; + return html; + } catch(e) { + e.name = 'RenderError'; + e.tpl = tpl; + e.render = Render.toString(); + return handelError(e)(); + } + } + + render.toString = function () { + return Render.toString(); + }; + return render; + } + function template(tpl, data) { + if (typeof tpl !== 'string') { + return ''; + } + + var fn = compile(tpl); + if (!isObject(data)) { + return fn; + } + + return fn(data); + } + + template.config = function (option) { + if (isObject(option)) { + o = extend(o, option); + } + return clone(o); + }; + + template.registerFunction = function(name, fn) { + if (!isString(name)) { + return clone(functionMap); + } + if (!isFunction(fn)) { + return functionMap[name]; + } + + return functionMap[name] = fn; + } + template.unregisterFunction = function (name) { + if (!isString(name)) { + return false; + } + delete functionMap[name]; + return true; + } + + template.registerModifier = function(name, fn) { + if (!isString(name)) { + return clone(modifierMap); + } + if (!isFunction(fn)) { + return modifierMap[name]; + } + + return modifierMap[name] = fn; + } + template.unregisterModifier = function (name) { + if (!isString(name)) { + return false; + } + delete modifierMap[name]; + return true; + } + + template.__encodeHTML = encodeHTML; + template.__compress = compress; + template.__handelError = handelError; + template.__compile = compile; + template.version = '0.7.1'; + return template; +})); \ No newline at end of file diff --git a/resources/admin/src/utils/tool.js b/resources/admin/src/utils/tool.js new file mode 100644 index 0000000..963da96 --- /dev/null +++ b/resources/admin/src/utils/tool.js @@ -0,0 +1,270 @@ +/* + * @Descripttion: 工具集 + * @version: 1.2 + * @LastEditors: sakuya + * @LastEditTime: 2022年5月24日00:28:56 + */ + +import CryptoJS from 'crypto-js'; +import sysConfig from "@/config"; + +const tool = {} + +/* localStorage */ +tool.data = { + set(key, data, datetime = 0) { + //加密 + if(sysConfig.LS_ENCRYPTION == "AES"){ + data = tool.crypto.AES.encrypt(JSON.stringify(data), sysConfig.LS_ENCRYPTION_key) + } + let cacheValue = { + content: data, + datetime: parseInt(datetime) === 0 ? 0 : new Date().getTime() + parseInt(datetime) * 1000 + } + return localStorage.setItem(key, JSON.stringify(cacheValue)) + }, + get(key) { + try { + const value = JSON.parse(localStorage.getItem(key)) + if (value) { + let nowTime = new Date().getTime() + if (nowTime > value.datetime && value.datetime != 0) { + localStorage.removeItem(key) + return null; + } + //解密 + if(sysConfig.LS_ENCRYPTION == "AES"){ + value.content = JSON.parse(tool.crypto.AES.decrypt(value.content, sysConfig.LS_ENCRYPTION_key)) + } + return value.content + } + return null + } catch (err) { + return null + } + }, + remove(key) { + return localStorage.removeItem(key) + }, + clear() { + return localStorage.clear() + } +} + +/*sessionStorage*/ +tool.session = { + set(table, settings) { + var _set = JSON.stringify(settings) + return sessionStorage.setItem(table, _set); + }, + get(table) { + var data = sessionStorage.getItem(table); + try { + data = JSON.parse(data) + } catch (err) { + return null + } + return data; + }, + remove(table) { + return sessionStorage.removeItem(table); + }, + clear() { + return sessionStorage.clear(); + } +} + +/*cookie*/ +tool.cookie = { + set(name, value, config={}) { + var cfg = { + expires: null, + path: null, + domain: null, + secure: false, + httpOnly: false, + ...config + } + var cookieStr = `${name}=${escape(value)}` + if(cfg.expires){ + var exp = new Date() + exp.setTime(exp.getTime() + parseInt(cfg.expires) * 1000) + cookieStr += `;expires=${exp.toGMTString()}` + } + if(cfg.path){ + cookieStr += `;path=${cfg.path}` + } + if(cfg.domain){ + cookieStr += `;domain=${cfg.domain}` + } + document.cookie = cookieStr + }, + get(name){ + var arr = document.cookie.match(new RegExp("(^| )"+name+"=([^;]*)(;|$)")) + if(arr != null){ + return unescape(arr[2]) + }else{ + return null + } + }, + remove(name){ + var exp = new Date() + exp.setTime(exp.getTime() - 1) + document.cookie = `${name}=;expires=${exp.toGMTString()}` + } +} + +/* Fullscreen */ +tool.screen = function (element) { + var isFull = !!(document.webkitIsFullScreen || document.mozFullScreen || document.msFullscreenElement || document.fullscreenElement); + if(isFull){ + if(document.exitFullscreen) { + document.exitFullscreen(); + }else if (document.msExitFullscreen) { + document.msExitFullscreen(); + }else if (document.mozCancelFullScreen) { + document.mozCancelFullScreen(); + }else if (document.webkitExitFullscreen) { + document.webkitExitFullscreen(); + } + }else{ + if(element.requestFullscreen) { + element.requestFullscreen(); + }else if(element.msRequestFullscreen) { + element.msRequestFullscreen(); + }else if(element.mozRequestFullScreen) { + element.mozRequestFullScreen(); + }else if(element.webkitRequestFullscreen) { + element.webkitRequestFullscreen(); + } + } +} + +/* 复制对象 */ +tool.objCopy = function (obj) { + return JSON.parse(JSON.stringify(obj)); +} + +/* 日期格式化 */ +tool.dateFormat = function (date, fmt='yyyy-MM-dd hh:mm:ss') { + date = new Date(date) + var o = { + "M+" : date.getMonth()+1, //月份 + "d+" : date.getDate(), //日 + "h+" : date.getHours(), //小时 + "m+" : date.getMinutes(), //分 + "s+" : date.getSeconds(), //秒 + "q+" : Math.floor((date.getMonth()+3)/3), //季度 + "S" : date.getMilliseconds() //毫秒 + }; + if(/(y+)/.test(fmt)) { + fmt=fmt.replace(RegExp.$1, (date.getFullYear()+"").substr(4 - RegExp.$1.length)); + } + for(var k in o) { + if(new RegExp("("+ k +")").test(fmt)){ + fmt = fmt.replace(RegExp.$1, (RegExp.$1.length==1) ? (o[k]) : (("00"+ o[k]).substr((""+ o[k]).length))); + } + } + return fmt; +} + +/* 千分符 */ +tool.groupSeparator = function (num) { + num = num + ''; + if(!num.includes('.')){ + num += '.' + } + return num.replace(/(\d)(?=(\d{3})+\.)/g, function ($0, $1) { + return $1 + ','; + }).replace(/\.$/, ''); +} + +/* 常用加解密 */ +tool.crypto = { + //MD5加密 + MD5(data){ + return CryptoJS.MD5(data).toString() + }, + //BASE64加解密 + BASE64: { + encrypt(data){ + return CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(data)) + }, + decrypt(cipher){ + return CryptoJS.enc.Base64.parse(cipher).toString(CryptoJS.enc.Utf8) + } + }, + //AES加解密 + AES: { + encrypt(data, secretKey, config={}){ + if(secretKey.length % 8 != 0){ + console.warn("[SCUI error]: 秘钥长度需为8的倍数,否则解密将会失败。") + } + const result = CryptoJS.AES.encrypt(data, CryptoJS.enc.Utf8.parse(secretKey), { + iv: CryptoJS.enc.Utf8.parse(config.iv || ""), + mode: CryptoJS.mode[config.mode || "ECB"], + padding: CryptoJS.pad[config.padding || "Pkcs7"] + }) + return result.toString() + }, + decrypt(cipher, secretKey, config={}){ + const result = CryptoJS.AES.decrypt(cipher, CryptoJS.enc.Utf8.parse(secretKey), { + iv: CryptoJS.enc.Utf8.parse(config.iv || ""), + mode: CryptoJS.mode[config.mode || "ECB"], + padding: CryptoJS.pad[config.padding || "Pkcs7"] + }) + return CryptoJS.enc.Utf8.stringify(result); + } + } +} + +tool.tree_to_list = function (tree, config={children: 'children'}) { + let res = [] + tree.forEach(item => { + const tmp = {...item} + if (tmp[config.children]) { + let tmpRoute = { ...item } + res.push(tmpRoute) + let childrenRoutes = tool.tree_to_list(tmp[config.children]) + childrenRoutes.map(sub => { + res.push(sub) + }) + }else{ + res.push(tmp) + } + }) + return res +} + +tool.get_parents = function (list, config={id: 0, pid: 'parent_id', field: []}) { + let res = null + list.forEach(item => { + if (item[config.pid] == id) { + if (config.field.length > 1) { + config.field.forEach(field => { + res[field] = item[field] + }) + }else if(config.field.length == 1){ + res = item[config.field[0]] + } + } + }) + return res +} + +tool.get_data_field = function (data, field) { + if (field.length == 1) { + return data[field[0]] + }else if(field.length > 1){ + let res = {} + field.forEach(field => { + res[field] = data[field] + }) + return res + }else{ + return data + } +} + + +export default tool diff --git a/resources/admin/src/utils/useTabs.js b/resources/admin/src/utils/useTabs.js new file mode 100644 index 0000000..8813e9f --- /dev/null +++ b/resources/admin/src/utils/useTabs.js @@ -0,0 +1,61 @@ +import { nextTick } from 'vue' +import NProgress from 'nprogress' +import 'nprogress/nprogress.css' +import router from '@/router' +import store from '@/store' + +export default { + //刷新标签 + refresh() { + NProgress.start() + const route = router.currentRoute.value + store.commit("removeKeepLive", route.name) + store.commit("setRouteShow", false) + nextTick(() => { + store.commit("pushKeepLive", route.name) + store.commit("setRouteShow", true) + NProgress.done() + }) + }, + //关闭标签 + close(tag) { + const route = tag || router.currentRoute.value + store.commit("removeViewTags", route) + store.commit("removeIframeList", route) + store.commit("removeKeepLive", route.name) + const tagList = store.state.viewTags.viewTags + const latestView = tagList.slice(-1)[0] + if (latestView) { + router.push(latestView) + } else { + router.push('/') + } + }, + //关闭标签后处理 + closeNext(next) { + const route = router.currentRoute.value + store.commit("removeViewTags", route) + store.commit("removeIframeList", route) + store.commit("removeKeepLive", route.name) + if(next){ + const tagList = store.state.viewTags.viewTags + next(tagList) + } + }, + //关闭其他 + closeOther() { + const route = router.currentRoute.value + const tagList = [...store.state.viewTags.viewTags] + tagList.forEach(tag => { + if(tag.meta&&tag.meta.affix || route.fullPath==tag.fullPath){ + return true + }else{ + this.close(tag) + } + }) + }, + //设置标题 + setTitle(title){ + store.commit("updateViewTagsTitle", title) + } +} diff --git a/resources/admin/src/utils/verificate.js b/resources/admin/src/utils/verificate.js new file mode 100644 index 0000000..9ed521c --- /dev/null +++ b/resources/admin/src/utils/verificate.js @@ -0,0 +1,27 @@ + +//验证手机号 +export function verifyPhone(rule, value, callback) { + let reg = /^[1][3, 4, 5, 6, 7, 8, 9][0-9]{9}$/ + if(!reg.test(value)){ + return callback(new Error('请输入正确的手机号码')) + } + callback() +} + +//车牌号码 +export function verifyCars(rule, value, callback) { + let reg = /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-HJ-NP-Z][A-HJ-NP-Z0-9]{4,5}[A-HJ-NP-Z0-9挂学警港澳]$/ + if(!reg.test(value)){ + return callback(new Error('请输入正确的车牌号码')) + } + callback() +} + +export function verifyCard(rule, value, callback) { + //15位和18位身份证号码的基本校验 + let reg = /^\d{6}(18|19|20)?\d{2}(0[1-9]|1[012])(0[1-9]|[12]\d|3[01])\d{3}(\d|[xX])$/ + if(!reg.test(value)){ + return callback(new Error('请输入正确的身份证号码')) + } + callback() +} diff --git a/resources/admin/vite.config.js b/resources/admin/vite.config.js new file mode 100644 index 0000000..eec36ed --- /dev/null +++ b/resources/admin/vite.config.js @@ -0,0 +1,88 @@ +import { defineConfig } from 'vite' +import path from 'path' +import vue from '@vitejs/plugin-vue' + +export default defineConfig({ + base:'./', + plugins: [vue()], + resolve: { + alias: { + // 设置路径 + '~': path.resolve(__dirname, './'), + // 设置别名 + '@': path.resolve(__dirname, './src') + }, + extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'] + }, + define: { + __VUE_I18N_FULL_INSTALL__: true, + __VUE_I18N_LEGACY_API__: true, + __INTLIFY_PROD_DEVTOOLS__: false + }, + server: { + port: 8080, + host: true, + open: false, + proxy: { + // https://cn.vitejs.dev/config/#server-proxy + // '/api': { + // target: 'https://www.fastmock.site/mock/5039c4361c39a7e3252c5b55971f1bd3/api', + // changeOrigin: true, + // rewrite: (p) => p.replace(/^\/api/, '') + // } + }, + }, + css: { + postcss: { + plugins: [ + { + postcssPlugin: 'internal:charset-removal', + AtRule: { + charset: (atRule) => { + if (atRule.name === 'charset') { + atRule.remove(); + } + } + } + } + ], + }, + }, + build: { + emptyOutDir: true, + outDir: 'dist', + assetsDir: './static/admin', + minify: 'terser', + terserOptions: { + compress: { + drop_console: true, + drop_debugger: true + } + }, + rollupOptions: { + input: { + index: path.resolve(__dirname, 'index.html'), + }, + // 拆包 + output: { + chunkFileNames: 'static/js/[name]-[hash].js', + entryFileNames: 'static/js/[name]-[hash].js', + assetFileNames(assetInfo){ + const imgExts = ['.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.bmp', '.ico', '.svg'] + if(assetInfo.name.endsWith('.css')){ + return 'static/style/[name]-[hash].css' + }else if(imgExts.some(ext => assetInfo.name.endsWith(ext))){ + return 'static/images/[name]-[hash].[ext]' + }else{ + return 'static/other/[name]-[hash].[ext]' + } + }, + manualChunks(id) { + if (id.includes('node_modules')) { + return id.split('/node_modules/').pop()?.split('/')[0] + } + } + } + } + } +}) diff --git a/resources/mobile/.gitignore b/resources/mobile/.gitignore new file mode 100644 index 0000000..148899a --- /dev/null +++ b/resources/mobile/.gitignore @@ -0,0 +1,15 @@ +node_modules +.DS_Store +dist +*.local + +# Editor directories and files +.idea +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? +package-lock.json +unpackage +.hbuilderx diff --git a/resources/mobile/App.vue b/resources/mobile/App.vue new file mode 100644 index 0000000..d225469 --- /dev/null +++ b/resources/mobile/App.vue @@ -0,0 +1,13 @@ + diff --git a/resources/mobile/index.html b/resources/mobile/index.html new file mode 100644 index 0000000..b5d330d --- /dev/null +++ b/resources/mobile/index.html @@ -0,0 +1,20 @@ + + + + + + + + + + +
+ + + diff --git a/resources/mobile/main.js b/resources/mobile/main.js new file mode 100644 index 0000000..a7874d9 --- /dev/null +++ b/resources/mobile/main.js @@ -0,0 +1,9 @@ +import App from "./App"; + +import { createSSRApp } from "vue"; +export function createApp() { + const app = createSSRApp(App); + return { + app, + }; +} diff --git a/resources/mobile/manifest.json b/resources/mobile/manifest.json new file mode 100644 index 0000000..ae2d977 --- /dev/null +++ b/resources/mobile/manifest.json @@ -0,0 +1,72 @@ +{ + "name" : "mobile", + "appid" : "__UNI__F0A4488", + "description" : "", + "versionName" : "1.0.0", + "versionCode" : "100", + "transformPx" : false, + /* 5+App特有相关 */ + "app-plus" : { + "usingComponents" : true, + "nvueStyleCompiler" : "uni-app", + "compilerVersion" : 3, + "splashscreen" : { + "alwaysShowBeforeRender" : true, + "waiting" : true, + "autoclose" : true, + "delay" : 0 + }, + /* 模块配置 */ + "modules" : {}, + /* 应用发布信息 */ + "distribute" : { + /* android打包配置 */ + "android" : { + "permissions" : [ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ] + }, + /* ios打包配置 */ + "ios" : {}, + /* SDK配置 */ + "sdkConfigs" : {} + } + }, + /* 快应用特有相关 */ + "quickapp" : {}, + /* 小程序特有相关 */ + "mp-weixin" : { + "appid" : "", + "setting" : { + "urlCheck" : false + }, + "usingComponents" : true + }, + "mp-alipay" : { + "usingComponents" : true + }, + "mp-baidu" : { + "usingComponents" : true + }, + "mp-toutiao" : { + "usingComponents" : true + }, + "uniStatistics" : { + "enable" : false + }, + "vueVersion" : "3" +} diff --git a/resources/mobile/package.json b/resources/mobile/package.json new file mode 100644 index 0000000..a3e89b5 --- /dev/null +++ b/resources/mobile/package.json @@ -0,0 +1,15 @@ +{ + "name": "mobile", + "version": "1.0.0", + "description": "记账APP", + "keywords": [ + "记账APP", + "家庭记账" + ], + "license": "MIT", + "author": { "name": "molong", "email": "ycgpp@126.com" }, + "dependencies": { + "@dcloudio/uni-ui": "^1.5.11", + "luch-request": "^3.1.1" + } +} diff --git a/resources/mobile/pages.json b/resources/mobile/pages.json new file mode 100644 index 0000000..3cf183a --- /dev/null +++ b/resources/mobile/pages.json @@ -0,0 +1,22 @@ +{ + "pages": [ + { "path": "pages/index/index" } + ], + "globalStyle": { + "navigationBarTextStyle": "black", + "navigationBarTitleText": "家庭记账", + "navigationBarBackgroundColor": "#F8F8F8", + "navigationStyle": "custom", + "backgroundColor": "#F8F8F8", + "app-plus": { + "bounce": "none" + } + }, + "easycom": { + "autoscan": true, + "custom": { + "^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue" + } + }, + "uniIdRouter": {} +} diff --git a/resources/mobile/pages/index/index.vue b/resources/mobile/pages/index/index.vue new file mode 100644 index 0000000..ec0ec26 --- /dev/null +++ b/resources/mobile/pages/index/index.vue @@ -0,0 +1,52 @@ + + + + + diff --git a/resources/mobile/static/logo.png b/resources/mobile/static/logo.png new file mode 100644 index 0000000..b5771e2 Binary files /dev/null and b/resources/mobile/static/logo.png differ diff --git a/resources/mobile/uni.promisify.adaptor.js b/resources/mobile/uni.promisify.adaptor.js new file mode 100644 index 0000000..5fec4f3 --- /dev/null +++ b/resources/mobile/uni.promisify.adaptor.js @@ -0,0 +1,13 @@ +uni.addInterceptor({ + returnValue (res) { + if (!(!!res && (typeof res === "object" || typeof res === "function") && typeof res.then === "function")) { + return res; + } + return new Promise((resolve, reject) => { + res.then((res) => { + if (!res) return resolve(res) + return res[0] ? reject(res[0]) : resolve(res[1]) + }); + }); + }, +}); \ No newline at end of file diff --git a/resources/mobile/uni.scss b/resources/mobile/uni.scss new file mode 100644 index 0000000..b9249e9 --- /dev/null +++ b/resources/mobile/uni.scss @@ -0,0 +1,76 @@ +/** + * 这里是uni-app内置的常用样式变量 + * + * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量 + * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App + * + */ + +/** + * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能 + * + * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件 + */ + +/* 颜色变量 */ + +/* 行为相关颜色 */ +$uni-color-primary: #007aff; +$uni-color-success: #4cd964; +$uni-color-warning: #f0ad4e; +$uni-color-error: #dd524d; + +/* 文字基本颜色 */ +$uni-text-color:#333;//基本色 +$uni-text-color-inverse:#fff;//反色 +$uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息 +$uni-text-color-placeholder: #808080; +$uni-text-color-disable:#c0c0c0; + +/* 背景颜色 */ +$uni-bg-color:#ffffff; +$uni-bg-color-grey:#f8f8f8; +$uni-bg-color-hover:#f1f1f1;//点击状态颜色 +$uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色 + +/* 边框颜色 */ +$uni-border-color:#c8c7cc; + +/* 尺寸变量 */ + +/* 文字尺寸 */ +$uni-font-size-sm:12px; +$uni-font-size-base:14px; +$uni-font-size-lg:16px; + +/* 图片尺寸 */ +$uni-img-size-sm:20px; +$uni-img-size-base:26px; +$uni-img-size-lg:40px; + +/* Border Radius */ +$uni-border-radius-sm: 2px; +$uni-border-radius-base: 3px; +$uni-border-radius-lg: 6px; +$uni-border-radius-circle: 50%; + +/* 水平间距 */ +$uni-spacing-row-sm: 5px; +$uni-spacing-row-base: 10px; +$uni-spacing-row-lg: 15px; + +/* 垂直间距 */ +$uni-spacing-col-sm: 4px; +$uni-spacing-col-base: 8px; +$uni-spacing-col-lg: 12px; + +/* 透明度 */ +$uni-opacity-disabled: 0.3; // 组件禁用态的透明度 + +/* 文章场景相关 */ +$uni-color-title: #2C405A; // 文章标题颜色 +$uni-font-size-title:20px; +$uni-color-subtitle: #555555; // 二级标题颜色 +$uni-font-size-subtitle:26px; +$uni-color-paragraph: #3F536E; // 文章段落颜色 +$uni-font-size-paragraph:15px; diff --git a/resources/mobile/utils/request.js b/resources/mobile/utils/request.js new file mode 100644 index 0000000..80d7394 --- /dev/null +++ b/resources/mobile/utils/request.js @@ -0,0 +1,208 @@ +import http from 'luch-request' + +// 创建实例 +const httpInstance = http.create({ + baseURL: import.meta.env.VITE_API_BASE_URL || '/api', + timeout: 10000, + header: { + 'Content-Type': 'application/json;charset=UTF-8' + } +}) + +// 请求拦截器 +httpInstance.interceptors.request.use( + (config) => { + // 从本地存储获取 token + const token = uni.getStorageSync('token') + if (token) { + config.header = { + ...config.header, + 'Authorization': `Bearer ${token}` + } + } + + // 添加时间戳,防止缓存 + if (config.method === 'GET') { + config.params = { + ...config.params, + _t: Date.now() + } + } + + // 显示加载提示 + if (config.loading !== false) { + uni.showLoading({ + title: config.loadingText || '加载中...', + mask: true + }) + } + + return config + }, + (config) => { + return Promise.reject(config) + } +) + +// 响应拦截器 +httpInstance.interceptors.response.use( + (response) => { + // 隐藏加载提示 + if (response.config.loading !== false) { + uni.hideLoading() + } + + const { statusCode, data } = response + + // HTTP 状态码判断 + if (statusCode !== 200) { + showError('网络请求失败') + return Promise.reject(response) + } + + // 业务状态码判断 + if (data.code !== undefined) { + // 成功响应 + if (data.code === 0 || data.code === 200 || data.code === 1) { + return data.data !== undefined ? data.data : data + } + + // 业务错误处理 + if (data.code === 401) { + // token 失效,跳转登录页 + uni.removeStorageSync('token') + uni.removeStorageSync('userInfo') + uni.showToast({ + title: '登录已过期,请重新登录', + icon: 'none', + duration: 2000 + }) + setTimeout(() => { + uni.navigateTo({ + url: '/pages/login/index' + }) + }, 2000) + return Promise.reject(data) + } + + // 其他业务错误 + showError(data.message || data.msg || '请求失败') + return Promise.reject(data) + } + + return data + }, + (error) => { + // 隐藏加载提示 + if (error.config && error.config.loading !== false) { + uni.hideLoading() + } + + let errorMessage = '网络请求失败' + + if (error.response) { + const { statusCode, data } = error.response + switch (statusCode) { + case 400: + errorMessage = data?.message || '请求参数错误' + break + case 401: + errorMessage = '登录已过期,请重新登录' + uni.removeStorageSync('token') + uni.removeStorageSync('userInfo') + setTimeout(() => { + uni.navigateTo({ + url: '/pages/login/index' + }) + }, 2000) + break + case 403: + errorMessage = '没有权限访问' + break + case 404: + errorMessage = '请求的资源不存在' + break + case 500: + errorMessage = '服务器内部错误' + break + case 502: + errorMessage = '网关错误' + break + case 503: + errorMessage = '服务不可用' + break + case 504: + errorMessage = '网关超时' + break + default: + errorMessage = data?.message || `请求失败 (${statusCode})` + } + } else if (error.errMsg) { + // 网络错误 + if (error.errMsg.includes('timeout')) { + errorMessage = '请求超时,请检查网络连接' + } else if (error.errMsg.includes('network')) { + errorMessage = '网络连接失败,请检查网络' + } + } + + showError(errorMessage) + return Promise.reject(error) + } +) + +// 显示错误提示 +function showError(message) { + uni.showToast({ + title: message, + icon: 'none', + duration: 3000 + }) +} + +// 封装常用请求方法 +export default { + // GET 请求 + get(url, params = {}, config = {}) { + return httpInstance.get(url, { + params, + ...config + }) + }, + + // POST 请求 + post(url, data = {}, config = {}) { + return httpInstance.post(url, data, config) + }, + + // PUT 请求 + put(url, data = {}, config = {}) { + return httpInstance.put(url, data, config) + }, + + // DELETE 请求 + delete(url, data = {}, config = {}) { + return httpInstance.delete(url, { + data, + ...config + }) + }, + + // 文件上传 + upload(url, filePath, formData = {}, config = {}) { + return httpInstance.upload(url, { + filePath, + formData, + name: 'file', + ...config + }) + }, + + // 文件下载 + download(url, config = {}) { + return httpInstance.download(url, config) + }, + + // 原始实例,用于特殊场景 + instance: httpInstance +} diff --git a/resources/views/error.blade.php b/resources/views/error.blade.php new file mode 100644 index 0000000..83bb44b --- /dev/null +++ b/resources/views/error.blade.php @@ -0,0 +1,35 @@ + + + + + + +SentCMS网站管理系统 + + + + + + +
+
+
+
+
+ {{ $message }} +
+
+
+ + diff --git a/resources/views/welcome.blade.php b/resources/views/welcome.blade.php new file mode 100644 index 0000000..e2553c5 --- /dev/null +++ b/resources/views/welcome.blade.php @@ -0,0 +1,42 @@ + + + + + + +SentCMS网站管理系统 + + + + + + +
+
+
+
+
+
+ SentOS管理系统 +
+ +
+
+
+ + diff --git a/resources/web/App.vue b/resources/web/App.vue new file mode 100644 index 0000000..f09c343 --- /dev/null +++ b/resources/web/App.vue @@ -0,0 +1,20 @@ + + + + + diff --git a/resources/web/app.js b/resources/web/app.js new file mode 100644 index 0000000..d40fc8c --- /dev/null +++ b/resources/web/app.js @@ -0,0 +1,6 @@ +import { createApp } from 'vue' + +import App from './App.vue' + +const app = createApp(App) +app.mount('#app') diff --git a/routes/admin.php b/routes/admin.php new file mode 100644 index 0000000..22b0465 --- /dev/null +++ b/routes/admin.php @@ -0,0 +1,125 @@ + +// +---------------------------------------------------------------------- +use Illuminate\Support\Facades\Route; + +// 权限路由 +Route::post('/auth/login', [App\Http\Controllers\Admin\Auth\Index::class, 'login'])->name('auth.login'); +Route::name('auth.')->prefix('auth')->middleware(['auth.check:admin'])->group(function () { + Route::get('/user', [App\Http\Controllers\Admin\Auth\Index::class, 'user'])->name('user'); + Route::post('/logout', [App\Http\Controllers\Admin\Auth\Index::class, 'logout'])->name('logout'); + Route::post('/refresh', [App\Http\Controllers\Admin\Auth\Index::class, 'refresh'])->name('refresh'); + + Route::controller(App\Http\Controllers\Admin\Auth\Users::class)->prefix('users')->name('users.')->group(function () { + Route::get('/index', 'index')->name('index'); + Route::post('/add', 'add')->name('add'); + Route::put('/edit', 'edit')->name('edit'); + Route::put('/uprole', 'uprole')->name('uprole'); + Route::put('/passwd', 'passwd')->name('passwd'); + Route::delete('/delete', 'delete')->name('delete'); + }); + Route::controller(App\Http\Controllers\Admin\Auth\Menu::class)->prefix('menu')->name('menu.')->group(function () { + Route::get('/index', 'index')->name('index'); + Route::get('/my', 'my')->name('my'); + Route::post('/add', 'add')->name('add'); + Route::put('/edit', 'edit')->name('edit'); + Route::delete('/delete', 'delete')->name('delete'); + }); + Route::controller(App\Http\Controllers\Admin\Auth\Role::class)->prefix('role')->name('role.')->group(function () { + Route::get('/index', 'index')->name('index'); + Route::post('/add', 'add')->name('add'); + Route::put('/edit', 'edit')->name('edit'); + Route::put('/auth', 'auth')->name('auth'); + Route::delete('/delete', 'delete')->name('delete'); + }); + Route::controller(App\Http\Controllers\Admin\Auth\Department::class)->prefix('department')->name('department.')->group(function () { + Route::get('/index', 'index')->name('index'); + Route::post('/add', 'add')->name('add'); + Route::put('/edit', 'edit')->name('edit'); + Route::delete('/delete', 'delete')->name('delete'); + }); +}); + + +// 系统路由配置 +Route::name('system.')->prefix('system')->middleware(['auth.check:admin'])->group(function () { + Route::controller(App\Http\Controllers\Admin\System\Index::class)->prefix('index')->name('index.')->group(function () { + Route::get('/version', 'version')->name('version'); + Route::get('/info', 'info')->name('info'); + Route::post('/clearcache', 'clearcache')->name('clearcache'); + }); + Route::controller(App\Http\Controllers\Admin\System\File::class)->prefix('file')->name('file.')->group(function () { + Route::post('/upload', 'upload')->name('upload'); + Route::get('/menu', 'menu')->name('menu'); + Route::get('/list', 'lists')->name('list'); + Route::post('/delete', 'delete')->name('delete'); + }); + Route::controller(App\Http\Controllers\Admin\System\Log::class)->prefix('log')->name('log.')->group(function () { + Route::get('/index', 'index')->name('index'); + Route::get('/my', 'my')->name('my'); + Route::post('/clear', 'clear')->name('clear'); + Route::delete('/delete', 'delete')->name('delete'); + }); + Route::controller(App\Http\Controllers\Admin\System\Setting::class)->prefix('setting')->name('setting.')->group(function () { + Route::get('/index', 'index')->name('index'); + Route::post('/add', 'add')->name('add'); + Route::put('/edit', 'edit')->name('edit'); + Route::put('/save', 'save')->name('save'); + Route::delete('/delete', 'delete')->name('delete'); + Route::get('/fields', 'fields')->name('fields'); + }); + Route::controller(App\Http\Controllers\Admin\System\Dict::class)->prefix('dict')->name('dict.')->group(function () { + Route::get('/all', 'all')->name('all'); + Route::get('/lists', 'lists')->name('lists'); + Route::get('/category', 'category')->name('category'); + Route::post('/addcate', 'addcate')->name('addcate'); + Route::put('/editcate', 'editcate')->name('editcate'); + Route::delete('/deletecate', 'deletecate')->name('deletecate'); + Route::post('/add', 'add')->name('add'); + Route::put('/edit', 'edit')->name('edit'); + Route::delete('/delete', 'delete')->name('delete'); + }); + Route::controller(App\Http\Controllers\Admin\System\Area::class)->prefix('area')->name('area.')->group(function () { + Route::get('/index', 'index')->name('index'); + Route::post('/add', 'add')->name('add'); + Route::put('/edit', 'edit')->name('edit'); + Route::delete('/delete', 'delete')->name('delete'); + }); + Route::controller(App\Http\Controllers\Admin\System\Client::class)->prefix('client')->name('client.')->group(function () { + Route::get('/index', 'index')->name('index'); + Route::post('/add', 'add')->name('add'); + Route::put('/edit', 'edit')->name('edit'); + Route::delete('/delete', 'delete')->name('delete'); + }); + Route::controller(App\Http\Controllers\Admin\System\Menu::class)->prefix('menu')->name('menu.')->group(function () { + Route::get('/index', 'index')->name('index'); + Route::post('/add', 'add')->name('add'); + Route::put('/edit', 'edit')->name('edit'); + Route::delete('/delete', 'delete')->name('delete'); + }); + Route::controller(App\Http\Controllers\Admin\System\Modules::class)->prefix('modules')->name('modules.')->group(function () { + Route::get('/index', 'index')->name('index'); + Route::post('/update', 'update')->name('update'); + }); + Route::controller(App\Http\Controllers\Admin\System\Tasks::class)->prefix('tasks')->name('tasks.')->group(function () { + Route::get('/index', 'index')->name('index'); + Route::post('/add', 'add')->name('add'); + Route::put('/edit', 'edit')->name('edit'); + Route::delete('/delete', 'delete')->name('delete'); + }); + + Route::controller(App\Http\Controllers\Admin\System\Crontab::class)->prefix('crontab')->name('crontab.')->group(function () { + Route::get('/index', 'index')->name('index'); + Route::post('/add', 'add')->name('add'); + Route::put('/edit', 'edit')->name('edit'); + Route::put('/reload', 'reload')->name('reload'); + Route::delete('/delete', 'delete')->name('delete'); + }); +}); + +// 记账管理路由已迁移到 modules/Account/routes/admin.php diff --git a/routes/api.php b/routes/api.php new file mode 100644 index 0000000..33660ad --- /dev/null +++ b/routes/api.php @@ -0,0 +1,24 @@ + +// +---------------------------------------------------------------------- +use Illuminate\Support\Facades\Route; + +Route::name('system.')->prefix('system')->middleware(['auth.check:api'])->group(function () { + Route::controller(Modules\System\Controllers\Api\Area::class)->prefix('area')->name('area.')->group(function () { + Route::get('/', 'index')->name('lists'); + }); + Route::controller(Modules\System\Controllers\Api\Index::class)->prefix('index')->name('index.')->group(function () { + Route::get('/qrcode', 'qrcode')->name('qrcode'); + }); + Route::controller(Modules\System\Controllers\Api\Wechat::class)->prefix('wechat')->name('wechat.')->group(function () { + Route::get('/serve', 'serve')->name('serve'); + Route::post('/jssdk', 'jssdk')->name('jssdk'); + }); +}); + +// 记账相关路由已迁移到 modules/Account/routes/api.php diff --git a/routes/console.php b/routes/console.php new file mode 100644 index 0000000..21ae3d9 --- /dev/null +++ b/routes/console.php @@ -0,0 +1,11 @@ +comment(Inspiring::quote()); +})->purpose('Display an inspiring quote')->hourly(); + +Schedule::exec('php --version')->everySecond(); diff --git a/routes/web.php b/routes/web.php new file mode 100644 index 0000000..1a51209 --- /dev/null +++ b/routes/web.php @@ -0,0 +1,44 @@ + +// +---------------------------------------------------------------------- +use Illuminate\Support\Facades\Route; +use Illuminate\Http\Request; + +Route::get('/', function (Request $request) { + $config = cache('config', []); + if (isset($config['site_close']) && $config['site_close'] == 1){ + return view('close'); + } + if (isset($config['default_site']) && $config['default_site'] && Route::has($config['default_site'])){ + return redirect()->route($config['default_site'], $request->all()); + } + if (Route::has('home')){ + return redirect()->route('home', $request->all()); + }else{ + if (file_exists(resource_path('web/dist/index.html'))){ + if (is_dir(resource_path('web/dist/assets')) && !is_dir(public_path('assets'))){ + symlink(resource_path('web/dist/assets'), public_path('assets')); + } + return file_get_contents(resource_path('web/dist/index.html')); + }else{ + return view('welcome'); + } + } +})->name('welcome'); + +Route::get('/admin', function (Request $request) { + if (file_exists(resource_path('admin/dist/index.html'))){ + if (is_dir(resource_path('admin/dist/static')) && !is_dir(public_path('static'))){ + symlink(resource_path('admin/dist/static'), public_path('static')); + symlink(resource_path('admin/dist/admin.js'), public_path('admin.js')); + } + return file_get_contents(resource_path('admin/dist/index.html')); + }else { + return view('welcome'); + } +})->name('admin'); diff --git a/storage/app/.gitignore b/storage/app/.gitignore new file mode 100644 index 0000000..8f4803c --- /dev/null +++ b/storage/app/.gitignore @@ -0,0 +1,3 @@ +* +!public/ +!.gitignore diff --git a/storage/app/public/.gitignore b/storage/app/public/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/storage/app/public/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/storage/debugbar/.gitignore b/storage/debugbar/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/storage/debugbar/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/storage/framework/.gitignore b/storage/framework/.gitignore new file mode 100644 index 0000000..05c4471 --- /dev/null +++ b/storage/framework/.gitignore @@ -0,0 +1,9 @@ +compiled.php +config.php +down +events.scanned.php +maintenance.php +routes.php +routes.scanned.php +schedule-* +services.json diff --git a/storage/framework/cache/.gitignore b/storage/framework/cache/.gitignore new file mode 100644 index 0000000..01e4a6c --- /dev/null +++ b/storage/framework/cache/.gitignore @@ -0,0 +1,3 @@ +* +!data/ +!.gitignore diff --git a/storage/framework/sessions/.gitignore b/storage/framework/sessions/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/storage/framework/sessions/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/storage/framework/testing/.gitignore b/storage/framework/testing/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/storage/framework/testing/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/storage/framework/views/.gitignore b/storage/framework/views/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/storage/framework/views/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/storage/logs/.gitignore b/storage/logs/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/storage/logs/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/storage/modules_statuses.json b/storage/modules_statuses.json new file mode 100644 index 0000000..a2419b5 --- /dev/null +++ b/storage/modules_statuses.json @@ -0,0 +1,6 @@ +{ + "Account": true, + "Ads": true, + "Member": true, + "Wechat": true +} \ No newline at end of file diff --git a/storage/stubs/action-invoke.stub b/storage/stubs/action-invoke.stub new file mode 100644 index 0000000..d0a85c7 --- /dev/null +++ b/storage/stubs/action-invoke.stub @@ -0,0 +1,11 @@ + $attributes + */ + public function get(Model $model, string $key, mixed $value, array $attributes): mixed + { + return $value; + } + + /** + * Prepare the given value for storage. + * + * @param array $attributes + */ + public function set(Model $model, string $key, mixed $value, array $attributes): mixed + { + return $value; + } +} diff --git a/storage/stubs/channel.stub b/storage/stubs/channel.stub new file mode 100644 index 0000000..f68b0b0 --- /dev/null +++ b/storage/stubs/channel.stub @@ -0,0 +1,24 @@ + + + diff --git a/storage/stubs/composer.stub b/storage/stubs/composer.stub new file mode 100644 index 0000000..d2ae161 --- /dev/null +++ b/storage/stubs/composer.stub @@ -0,0 +1,30 @@ +{ + "name": "$VENDOR$/$LOWER_NAME$", + "description": "", + "authors": [ + { + "name": "$AUTHOR_NAME$", + "email": "$AUTHOR_EMAIL$" + } + ], + "extra": { + "laravel": { + "providers": [], + "aliases": { + + } + } + }, + "autoload": { + "psr-4": { + "$MODULE_NAMESPACE$\\$STUDLY_NAME$\\": "$APP_FOLDER_NAME$", + "$MODULE_NAMESPACE$\\$STUDLY_NAME$\\Database\\Factories\\": "database/factories/", + "$MODULE_NAMESPACE$\\$STUDLY_NAME$\\Database\\Seeders\\": "database/seeders/" + } + }, + "autoload-dev": { + "psr-4": { + "$MODULE_NAMESPACE$\\$STUDLY_NAME$\\Tests\\": "tests/" + } + } +} diff --git a/storage/stubs/controller-api.stub b/storage/stubs/controller-api.stub new file mode 100644 index 0000000..a4a286d --- /dev/null +++ b/storage/stubs/controller-api.stub @@ -0,0 +1,65 @@ + +// +---------------------------------------------------------------------- +namespace $CLASS_NAMESPACE$; + +use App\Http\Controllers\Controller; +use Illuminate\Http\Request; + +class $CLASS$ extends Controller +{ + /** + * Display a listing of the resource. + */ + public function index() + { + // + + return response()->json([]); + } + + /** + * Store a newly created resource in storage. + */ + public function store(Request $request) + { + // + + return response()->json([]); + } + + /** + * Show the specified resource. + */ + public function show($id) + { + // + + return response()->json([]); + } + + /** + * Update the specified resource in storage. + */ + public function update(Request $request, $id) + { + // + + return response()->json([]); + } + + /** + * Remove the specified resource from storage. + */ + public function destroy($id) + { + // + + return response()->json([]); + } +} diff --git a/storage/stubs/controller-plain.stub b/storage/stubs/controller-plain.stub new file mode 100644 index 0000000..94d0a5b --- /dev/null +++ b/storage/stubs/controller-plain.stub @@ -0,0 +1,15 @@ + +// +---------------------------------------------------------------------- +namespace $CLASS_NAMESPACE$; + +use Illuminate\Routing\Controller; + +class $CLASS$ extends Controller +{ +} diff --git a/storage/stubs/controller.invokable.stub b/storage/stubs/controller.invokable.stub new file mode 100644 index 0000000..9512b78 --- /dev/null +++ b/storage/stubs/controller.invokable.stub @@ -0,0 +1,23 @@ + +// +---------------------------------------------------------------------- +namespace $CLASS_NAMESPACE$; + +use App\Http\Controllers\Controller; +use Illuminate\Http\Request; + +class $CLASS$ extends Controller +{ + /** + * Handle the incoming request. + */ + public function __invoke(Request $request) + { + return response()->json([]); + } +} diff --git a/storage/stubs/controller.stub b/storage/stubs/controller.stub new file mode 100644 index 0000000..b48468a --- /dev/null +++ b/storage/stubs/controller.stub @@ -0,0 +1,73 @@ + +// +---------------------------------------------------------------------- +namespace $CLASS_NAMESPACE$; + +use App\Http\Controllers\Controller; +use Illuminate\Http\RedirectResponse; +use Illuminate\Http\Request; +use Illuminate\Http\Response; + +class $CLASS$ extends Controller +{ + /** + * Display a listing of the resource. + */ + public function index() + { + return view('$LOWER_NAME$::index'); + } + + /** + * Show the form for creating a new resource. + */ + public function create() + { + return view('$LOWER_NAME$::create'); + } + + /** + * Store a newly created resource in storage. + */ + public function store(Request $request): RedirectResponse + { + // + } + + /** + * Show the specified resource. + */ + public function show($id) + { + return view('$LOWER_NAME$::show'); + } + + /** + * Show the form for editing the specified resource. + */ + public function edit($id) + { + return view('$LOWER_NAME$::edit'); + } + + /** + * Update the specified resource in storage. + */ + public function update(Request $request, $id): RedirectResponse + { + // + } + + /** + * Remove the specified resource from storage. + */ + public function destroy($id) + { + // + } +} diff --git a/storage/stubs/enum.stub b/storage/stubs/enum.stub new file mode 100644 index 0000000..50d822a --- /dev/null +++ b/storage/stubs/enum.stub @@ -0,0 +1,14 @@ + +// +---------------------------------------------------------------------- +namespace $CLASS_NAMESPACE$; + +enum $CLASS$ +{ + // +} diff --git a/storage/stubs/event-provider.stub b/storage/stubs/event-provider.stub new file mode 100644 index 0000000..6d79670 --- /dev/null +++ b/storage/stubs/event-provider.stub @@ -0,0 +1,38 @@ + +// +---------------------------------------------------------------------- +namespace $NAMESPACE$; + +use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; + +class $CLASS$ extends ServiceProvider +{ + /** + * The event handler mappings for the application. + * + * @var array> + */ + protected $listen = []; + + /** + * Indicates if events should be discovered. + * + * @var bool + */ + protected static $shouldDiscoverEvents = true; + + /** + * Configure the proper event listeners for email verification. + * + * @return void + */ + protected function configureEmailVerification(): void + { + + } +} diff --git a/storage/stubs/event.stub b/storage/stubs/event.stub new file mode 100644 index 0000000..45ef3a8 --- /dev/null +++ b/storage/stubs/event.stub @@ -0,0 +1,40 @@ + +// +---------------------------------------------------------------------- +namespace $NAMESPACE$; + +use Illuminate\Broadcasting\Channel; +use Illuminate\Broadcasting\InteractsWithSockets; +use Illuminate\Broadcasting\PresenceChannel; +use Illuminate\Broadcasting\PrivateChannel; +use Illuminate\Contracts\Broadcasting\ShouldBroadcast; +use Illuminate\Foundation\Events\Dispatchable; +use Illuminate\Queue\SerializesModels; + +class $CLASS$ +{ + use Dispatchable, InteractsWithSockets, SerializesModels; + + /** + * Create a new event instance. + */ + public function __construct() + { + // + } + + /** + * Get the channels the event should be broadcast on. + */ + public function broadcastOn(): array + { + return [ + new PrivateChannel('channel-name'), + ]; + } +} diff --git a/storage/stubs/exception-render-report.stub b/storage/stubs/exception-render-report.stub new file mode 100644 index 0000000..38e487c --- /dev/null +++ b/storage/stubs/exception-render-report.stub @@ -0,0 +1,32 @@ + +// +---------------------------------------------------------------------- +namespace $CLASS_NAMESPACE$; + +use Exception; +use Illuminate\Http\Request; +use Illuminate\Http\Response; + +class $CLASS$ extends Exception +{ + /** + * Report the exception. + */ + public function report(): void + { + // + } + + /** + * Render the exception as an HTTP response. + */ + public function render(Request $request): Response + { + // + } +} diff --git a/storage/stubs/exception-render.stub b/storage/stubs/exception-render.stub new file mode 100644 index 0000000..9ff047d --- /dev/null +++ b/storage/stubs/exception-render.stub @@ -0,0 +1,24 @@ + +// +---------------------------------------------------------------------- +namespace $CLASS_NAMESPACE$; + +use Exception; +use Illuminate\Http\Request; +use Illuminate\Http\Response; + +class $CLASS$ extends Exception +{ + /** + * Render the exception as an HTTP response. + */ + public function render(Request $request): Response + { + // + } +} diff --git a/storage/stubs/exception-report.stub b/storage/stubs/exception-report.stub new file mode 100644 index 0000000..743fd69 --- /dev/null +++ b/storage/stubs/exception-report.stub @@ -0,0 +1,22 @@ + +// +---------------------------------------------------------------------- +namespace $CLASS_NAMESPACE$; + +use Exception; + +class $CLASS$ extends Exception +{ + /** + * Report the exception. + */ + public function report(): void + { + // + } +} diff --git a/storage/stubs/exception.stub b/storage/stubs/exception.stub new file mode 100644 index 0000000..0b2f2a6 --- /dev/null +++ b/storage/stubs/exception.stub @@ -0,0 +1,16 @@ + +// +---------------------------------------------------------------------- +namespace $CLASS_NAMESPACE$; + +use Exception; + +class $CLASS$ extends Exception +{ + // +} diff --git a/storage/stubs/factory.stub b/storage/stubs/factory.stub new file mode 100644 index 0000000..5d7669f --- /dev/null +++ b/storage/stubs/factory.stub @@ -0,0 +1,28 @@ + +// +---------------------------------------------------------------------- +namespace $NAMESPACE$; + +use Illuminate\Database\Eloquent\Factories\Factory; + +class $NAME$Factory extends Factory +{ + /** + * The name of the factory's corresponding model. + */ + protected $model = \$MODEL_NAMESPACE$\$NAME$::class; + + /** + * Define the model's default state. + */ + public function definition(): array + { + return []; + } +} + diff --git a/storage/stubs/feature-test.stub b/storage/stubs/feature-test.stub new file mode 100644 index 0000000..83bcf89 --- /dev/null +++ b/storage/stubs/feature-test.stub @@ -0,0 +1,26 @@ + +// +---------------------------------------------------------------------- +namespace $NAMESPACE$; + +use Tests\TestCase; +use Illuminate\Foundation\Testing\WithFaker; +use Illuminate\Foundation\Testing\RefreshDatabase; + +class $CLASS$ extends TestCase +{ + /** + * A basic feature test example. + */ + public function testExample(): void + { + $response = $this->get('/'); + + $response->assertStatus(200); + } +} diff --git a/storage/stubs/helper-invoke.stub b/storage/stubs/helper-invoke.stub new file mode 100644 index 0000000..6d3806c --- /dev/null +++ b/storage/stubs/helper-invoke.stub @@ -0,0 +1,17 @@ + +// +---------------------------------------------------------------------- +namespace $CLASS_NAMESPACE$; + +class $CLASS$ +{ + public function __invoke() + { + // + } +} diff --git a/storage/stubs/helper.stub b/storage/stubs/helper.stub new file mode 100644 index 0000000..3197d97 --- /dev/null +++ b/storage/stubs/helper.stub @@ -0,0 +1,17 @@ + +// +---------------------------------------------------------------------- +namespace $CLASS_NAMESPACE$; + +class $CLASS$ +{ + public function handle() + { + // + } +} diff --git a/storage/stubs/interface.stub b/storage/stubs/interface.stub new file mode 100644 index 0000000..a9d0a94 --- /dev/null +++ b/storage/stubs/interface.stub @@ -0,0 +1,14 @@ + +// +---------------------------------------------------------------------- +namespace $CLASS_NAMESPACE$; + +class $CLASS$ +{ + +} diff --git a/storage/stubs/job-queued.stub b/storage/stubs/job-queued.stub new file mode 100644 index 0000000..e95d02c --- /dev/null +++ b/storage/stubs/job-queued.stub @@ -0,0 +1,36 @@ + +// +---------------------------------------------------------------------- +namespace $NAMESPACE$; + +use Illuminate\Bus\Queueable; +use Illuminate\Queue\SerializesModels; +use Illuminate\Queue\InteractsWithQueue; +use Illuminate\Contracts\Queue\ShouldQueue; +use Illuminate\Foundation\Bus\Dispatchable; + +class $CLASS$ implements ShouldQueue +{ + use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; + + /** + * Create a new job instance. + */ + public function __construct() + { + // + } + + /** + * Execute the job. + */ + public function handle(): void + { + // + } +} diff --git a/storage/stubs/job.stub b/storage/stubs/job.stub new file mode 100644 index 0000000..e3ec166 --- /dev/null +++ b/storage/stubs/job.stub @@ -0,0 +1,33 @@ + +// +---------------------------------------------------------------------- +namespace $NAMESPACE$; + +use Illuminate\Bus\Queueable; +use Illuminate\Foundation\Bus\Dispatchable; + +class $CLASS$ implements ShouldQueue +{ + use Dispatchable, Queueable; + + /** + * Create a new job instance. + */ + public function __construct() + { + // + } + + /** + * Execute the job. + */ + public function handle(): void + { + // + } +} diff --git a/storage/stubs/json.stub b/storage/stubs/json.stub new file mode 100644 index 0000000..43095e7 --- /dev/null +++ b/storage/stubs/json.stub @@ -0,0 +1,11 @@ +{ + "name": "$STUDLY_NAME$", + "alias": "$LOWER_NAME$", + "description": "", + "keywords": [], + "priority": 0, + "providers": [ + "$MODULE_NAMESPACE$\\$STUDLY_NAME$\\$PROVIDER_NAMESPACE$\\$STUDLY_NAME$ServiceProvider" + ], + "files": [] +} diff --git a/storage/stubs/listener-duck.stub b/storage/stubs/listener-duck.stub new file mode 100644 index 0000000..d780192 --- /dev/null +++ b/storage/stubs/listener-duck.stub @@ -0,0 +1,25 @@ +view('view.name'); + } +} diff --git a/storage/stubs/middleware.stub b/storage/stubs/middleware.stub new file mode 100644 index 0000000..bdd192d --- /dev/null +++ b/storage/stubs/middleware.stub @@ -0,0 +1,17 @@ +id(); + $FIELDS$ + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('$TABLE$'); + } +}; diff --git a/storage/stubs/migration/delete.stub b/storage/stubs/migration/delete.stub new file mode 100644 index 0000000..788cdee --- /dev/null +++ b/storage/stubs/migration/delete.stub @@ -0,0 +1,28 @@ +id(); + $FIELDS$ + $table->timestamps(); + }); + } +}; diff --git a/storage/stubs/migration/plain.stub b/storage/stubs/migration/plain.stub new file mode 100644 index 0000000..88fa2f3 --- /dev/null +++ b/storage/stubs/migration/plain.stub @@ -0,0 +1,24 @@ +line('The introduction to the notification.') + ->action('Notification Action', 'https://laravel.com') + ->line('Thank you for using our application!'); + } + + /** + * Get the array representation of the notification. + */ + public function toArray($notifiable): array + { + return []; + } +} diff --git a/storage/stubs/observer.stub b/storage/stubs/observer.stub new file mode 100644 index 0000000..ca49863 --- /dev/null +++ b/storage/stubs/observer.stub @@ -0,0 +1,48 @@ + +// +---------------------------------------------------------------------- +namespace $NAMESPACE$; + +use Illuminate\Support\Facades\Route; +use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider; + +class $CLASS$ extends ServiceProvider +{ + /** + * Called before routes are registered. + * + * Register any model bindings or pattern based filters. + */ + public function boot(): void + { + parent::boot(); + } + + /** + * Define the routes for the application. + */ + public function map(): void + { + $this->mapApiRoutes(); + + $this->mapWebRoutes(); + + $this->mapAdminRoutes(); + } + + /** + * Define the "web" routes for the application. + * + * These routes all receive session state, CSRF protection, etc. + */ + protected function mapWebRoutes(): void + { + Route::middleware('web')->group(module_path('$MODULE$', '$WEB_ROUTES_PATH$')); + } + + /** + * Define the "api" routes for the application. + * + * These routes are typically stateless. + */ + protected function mapApiRoutes(): void + { + Route::middleware('api')->prefix('api')->name('api.')->group(module_path('$MODULE$', '$API_ROUTES_PATH$')); + } + + /** + * Define the "api" routes for the application. + * + * These routes are typically stateless. + */ + protected function mapAdminRoutes(): void + { + Route::middleware('api')->prefix('admin')->name('admin.')->group(module_path('$MODULE$', '/routes/admin.php')); + } +} diff --git a/storage/stubs/routes/admin.stub b/storage/stubs/routes/admin.stub new file mode 100644 index 0000000..a9b7c71 --- /dev/null +++ b/storage/stubs/routes/admin.stub @@ -0,0 +1,14 @@ + +// +---------------------------------------------------------------------- +use Illuminate\Support\Facades\Route; +use $MODULE_NAMESPACE$\$STUDLY_NAME$\$CONTROLLER_NAMESPACE$\$STUDLY_NAME$Controller; + +Route::middleware(['auth.check:admin'])->group(function () { + Route::apiResource('$LOWER_NAME$', $STUDLY_NAME$Controller::class)->names('$LOWER_NAME$'); +}); diff --git a/storage/stubs/routes/api.stub b/storage/stubs/routes/api.stub new file mode 100644 index 0000000..a9fd654 --- /dev/null +++ b/storage/stubs/routes/api.stub @@ -0,0 +1,14 @@ + +// +---------------------------------------------------------------------- +use Illuminate\Support\Facades\Route; +use $MODULE_NAMESPACE$\$STUDLY_NAME$\$CONTROLLER_NAMESPACE$\$STUDLY_NAME$Controller; + +Route::middleware(['auth.check:api'])->group(function () { + Route::apiResource('$LOWER_NAME$', $STUDLY_NAME$Controller::class)->names('$LOWER_NAME$'); +}); diff --git a/storage/stubs/routes/web.stub b/storage/stubs/routes/web.stub new file mode 100644 index 0000000..95a447e --- /dev/null +++ b/storage/stubs/routes/web.stub @@ -0,0 +1,14 @@ + +// +---------------------------------------------------------------------- +use Illuminate\Support\Facades\Route; +use $MODULE_NAMESPACE$\$STUDLY_NAME$\$CONTROLLER_NAMESPACE$\$STUDLY_NAME$Controller; + +Route::group([], function () { + Route::resource('$LOWER_NAME$', $STUDLY_NAME$Controller::class)->names('$LOWER_NAME$'); +}); diff --git a/storage/stubs/rule.implicit.stub b/storage/stubs/rule.implicit.stub new file mode 100644 index 0000000..1b7e8ea --- /dev/null +++ b/storage/stubs/rule.implicit.stub @@ -0,0 +1,28 @@ + +// +---------------------------------------------------------------------- +namespace $NAMESPACE$; + +use Closure; +use Illuminate\Contracts\Validation\ValidationRule; + +class $CLASS$ implements ValidationRule +{ + /** + * Indicates whether the rule should be implicit. + */ + public bool $implicit = true; + + /** + * Run the validation rule. + */ + public function validate(string $attribute, mixed $value, Closure $fail): void + { + // + } +} diff --git a/storage/stubs/rule.stub b/storage/stubs/rule.stub new file mode 100644 index 0000000..a5c907c --- /dev/null +++ b/storage/stubs/rule.stub @@ -0,0 +1,23 @@ + +// +---------------------------------------------------------------------- +namespace $NAMESPACE$; + +use Closure; +use Illuminate\Contracts\Validation\ValidationRule; + +class $CLASS$ implements ValidationRule +{ + /** + * Run the validation rule. + */ + public function validate(string $attribute, mixed $value, Closure $fail): void + { + // + } +} diff --git a/storage/stubs/scaffold/config.stub b/storage/stubs/scaffold/config.stub new file mode 100644 index 0000000..5ceca0e --- /dev/null +++ b/storage/stubs/scaffold/config.stub @@ -0,0 +1,5 @@ + '$STUDLY_NAME$', +]; diff --git a/storage/stubs/scaffold/provider.stub b/storage/stubs/scaffold/provider.stub new file mode 100644 index 0000000..494f100 --- /dev/null +++ b/storage/stubs/scaffold/provider.stub @@ -0,0 +1,120 @@ +registerCommands(); + $this->registerCommandSchedules(); + $this->registerTranslations(); + $this->registerConfig(); + $this->registerViews(); + $this->loadMigrationsFrom(module_path($this->moduleName, '$MIGRATIONS_PATH$')); + } + + /** + * Register the service provider. + */ + public function register(): void + { + $this->app->register(EventServiceProvider::class); + $this->app->register(RouteServiceProvider::class); + } + + /** + * Register commands in the format of Command::class + */ + protected function registerCommands(): void + { + // $this->commands([]); + } + + /** + * Register command Schedules. + */ + protected function registerCommandSchedules(): void + { + // $this->app->booted(function () { + // $schedule = $this->app->make(Schedule::class); + // $schedule->command('inspire')->hourly(); + // }); + } + + /** + * Register translations. + */ + public function registerTranslations(): void + { + $langPath = resource_path('lang/modules/'.$this->moduleNameLower); + + if (is_dir($langPath)) { + $this->loadTranslationsFrom($langPath, $this->moduleNameLower); + $this->loadJsonTranslationsFrom($langPath); + } else { + $this->loadTranslationsFrom(module_path($this->moduleName, '$PATH_LANG$'), $this->moduleNameLower); + $this->loadJsonTranslationsFrom(module_path($this->moduleName, '$PATH_LANG$')); + } + } + + /** + * Register config. + */ + protected function registerConfig(): void + { + $this->publishes([module_path($this->moduleName, '$PATH_CONFIG$/config.php') => config_path($this->moduleNameLower.'.php')], 'config'); + $this->mergeConfigFrom(module_path($this->moduleName, '$PATH_CONFIG$/config.php'), $this->moduleNameLower); + } + + /** + * Register views. + */ + public function registerViews(): void + { + $viewPath = resource_path('views/modules/'.$this->moduleNameLower); + $sourcePath = module_path($this->moduleName, '$PATH_VIEWS$'); + + $this->publishes([$sourcePath => $viewPath], ['views', $this->moduleNameLower.'-module-views']); + + $this->loadViewsFrom(array_merge($this->getPublishableViewPaths(), [$sourcePath]), $this->moduleNameLower); + + $componentNamespace = str_replace('/', '\\', config('modules.namespace').'\\'.$this->moduleName.'\\'.ltrim(config('modules.paths.generator.component-class.path'), config('modules.paths.app_folder', ''))); + Blade::componentNamespace($componentNamespace, $this->moduleNameLower); + } + + /** + * Get the services provided by the provider. + * + * @return array + */ + public function provides(): array + { + return []; + } + + /** + * @return array + */ + private function getPublishableViewPaths(): array + { + $paths = []; + foreach (config('view.paths') as $path) { + if (is_dir($path.'/modules/'.$this->moduleNameLower)) { + $paths[] = $path.'/modules/'.$this->moduleNameLower; + } + } + + return $paths; + } +} diff --git a/storage/stubs/scope.stub b/storage/stubs/scope.stub new file mode 100644 index 0000000..e4aa5db --- /dev/null +++ b/storage/stubs/scope.stub @@ -0,0 +1,24 @@ + +// +---------------------------------------------------------------------- +namespace $CLASS_NAMESPACE$; + +use Illuminate\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Scope; + +class $CLASS$ implements Scope +{ + /** + * Apply the scope to a given Eloquent query builder. + */ + public function apply(Builder $builder, Model $model): void + { + // + } +} diff --git a/storage/stubs/seeder.stub b/storage/stubs/seeder.stub new file mode 100644 index 0000000..9ca9baa --- /dev/null +++ b/storage/stubs/seeder.stub @@ -0,0 +1,22 @@ + +// +---------------------------------------------------------------------- +namespace $NAMESPACE$; + +use Illuminate\Database\Seeder; + +class $NAME$ extends Seeder +{ + /** + * Run the database seeds. + */ + public function run(): void + { + // $this->call([]); + } +} diff --git a/storage/stubs/service-invoke.stub b/storage/stubs/service-invoke.stub new file mode 100644 index 0000000..6d3806c --- /dev/null +++ b/storage/stubs/service-invoke.stub @@ -0,0 +1,17 @@ + +// +---------------------------------------------------------------------- +namespace $CLASS_NAMESPACE$; + +class $CLASS$ +{ + public function __invoke() + { + // + } +} diff --git a/storage/stubs/service.stub b/storage/stubs/service.stub new file mode 100644 index 0000000..3197d97 --- /dev/null +++ b/storage/stubs/service.stub @@ -0,0 +1,17 @@ + +// +---------------------------------------------------------------------- +namespace $CLASS_NAMESPACE$; + +class $CLASS$ +{ + public function handle() + { + // + } +} diff --git a/storage/stubs/trait.stub b/storage/stubs/trait.stub new file mode 100644 index 0000000..0a03ae4 --- /dev/null +++ b/storage/stubs/trait.stub @@ -0,0 +1,8 @@ +assertTrue(true); + } +} diff --git a/storage/stubs/view.stub b/storage/stubs/view.stub new file mode 100644 index 0000000..c7ab22b --- /dev/null +++ b/storage/stubs/view.stub @@ -0,0 +1,3 @@ +
+ +
diff --git a/storage/stubs/views/index.stub b/storage/stubs/views/index.stub new file mode 100644 index 0000000..1a535d4 --- /dev/null +++ b/storage/stubs/views/index.stub @@ -0,0 +1,7 @@ +@extends('$LOWER_NAME$::layouts.master') + +@section('content') +

Hello World

+ +

Module: {!! config('$LOWER_NAME$.name') !!}

+@endsection diff --git a/storage/stubs/views/master.stub b/storage/stubs/views/master.stub new file mode 100644 index 0000000..8fa6ac4 --- /dev/null +++ b/storage/stubs/views/master.stub @@ -0,0 +1,29 @@ + + + + + + + + + + $STUDLY_NAME$ Module - {{ config('app.name', 'Laravel') }} + + + + + + + + + + {{-- Vite CSS --}} + {{-- {{ module_vite('build-$LOWER_NAME$', 'resources/assets/sass/app.scss') }} --}} + + + + @yield('content') + + {{-- Vite JS --}} + {{-- {{ module_vite('build-$LOWER_NAME$', 'resources/assets/js/app.js') }} --}} + diff --git a/storage/stubs/vite.stub b/storage/stubs/vite.stub new file mode 100644 index 0000000..314ef81 --- /dev/null +++ b/storage/stubs/vite.stub @@ -0,0 +1,26 @@ +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; + +export default defineConfig({ + build: { + outDir: '../../public/build-$LOWER_NAME$', + emptyOutDir: true, + manifest: true, + }, + plugins: [ + laravel({ + publicDirectory: '../../public', + buildDirectory: 'build-$LOWER_NAME$', + input: [ + __dirname + '/resources/assets/sass/app.scss', + __dirname + '/resources/assets/js/app.js' + ], + refresh: true, + }), + ], +}); + +//export const paths = [ +// 'Modules/$STUDLY_NAME$/resources/assets/sass/app.scss', +// 'Modules/$STUDLY_NAME$/resources/assets/js/app.js', +//]; \ No newline at end of file diff --git a/storage/workerman/202407/14.log b/storage/workerman/202407/14.log new file mode 100644 index 0000000..e69de29 diff --git a/tests/Browser/ExampleTest.php b/tests/Browser/ExampleTest.php new file mode 100644 index 0000000..d5ad6b6 --- /dev/null +++ b/tests/Browser/ExampleTest.php @@ -0,0 +1,21 @@ +browse(function (Browser $browser) { + $browser->visit('/') + ->assertSee('Laravel'); + }); + } +} diff --git a/tests/Browser/Pages/HomePage.php b/tests/Browser/Pages/HomePage.php new file mode 100644 index 0000000..45d9283 --- /dev/null +++ b/tests/Browser/Pages/HomePage.php @@ -0,0 +1,36 @@ + + */ + public function elements(): array + { + return [ + '@element' => '#selector', + ]; + } +} diff --git a/tests/Browser/Pages/Page.php b/tests/Browser/Pages/Page.php new file mode 100644 index 0000000..eb9a2de --- /dev/null +++ b/tests/Browser/Pages/Page.php @@ -0,0 +1,20 @@ + + */ + public static function siteElements(): array + { + return [ + '@element' => '#selector', + ]; + } +} diff --git a/tests/Browser/console/.gitignore b/tests/Browser/console/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/tests/Browser/console/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/tests/Browser/screenshots/.gitignore b/tests/Browser/screenshots/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/tests/Browser/screenshots/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/tests/Browser/source/.gitignore b/tests/Browser/source/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/tests/Browser/source/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/tests/DuskTestCase.php b/tests/DuskTestCase.php new file mode 100644 index 0000000..aa4124e --- /dev/null +++ b/tests/DuskTestCase.php @@ -0,0 +1,46 @@ +addArguments(collect([ + $this->shouldStartMaximized() ? '--start-maximized' : '--window-size=1920,1080', + ])->unless($this->hasHeadlessDisabled(), function (Collection $items) { + return $items->merge([ + '--disable-gpu', + '--headless=new', + ]); + })->all()); + + return RemoteWebDriver::create( + $_ENV['DUSK_DRIVER_URL'] ?? 'http://localhost:9515', + DesiredCapabilities::chrome()->setCapability( + ChromeOptions::CAPABILITY, $options + ) + ); + } +} diff --git a/tests/Feature/ExampleTest.php b/tests/Feature/ExampleTest.php new file mode 100644 index 0000000..8364a84 --- /dev/null +++ b/tests/Feature/ExampleTest.php @@ -0,0 +1,19 @@ +get('/'); + + $response->assertStatus(200); + } +} diff --git a/tests/TestCase.php b/tests/TestCase.php new file mode 100644 index 0000000..fe1ffc2 --- /dev/null +++ b/tests/TestCase.php @@ -0,0 +1,10 @@ +assertTrue(true); + } +} diff --git a/vite-module-loader.js b/vite-module-loader.js new file mode 100644 index 0000000..ffaa429 --- /dev/null +++ b/vite-module-loader.js @@ -0,0 +1,47 @@ +import fs from 'fs/promises'; +import path from 'path'; + +async function collectModuleAssetsPaths(paths, modulesPath) { + modulesPath = path.join(__dirname, modulesPath); + + const moduleStatusesPath = path.join(__dirname, 'modules_statuses.json'); + + try { + // Read module_statuses.json + const moduleStatusesContent = await fs.readFile(moduleStatusesPath, 'utf-8'); + const moduleStatuses = JSON.parse(moduleStatusesContent); + + // Read module directories + const moduleDirectories = await fs.readdir(modulesPath); + + for (const moduleDir of moduleDirectories) { + if (moduleDir === '.DS_Store') { + // Skip .DS_Store directory + continue; + } + + // Check if the module is enabled (status is true) + if (moduleStatuses[moduleDir] === true) { + const viteConfigPath = path.join(modulesPath, moduleDir, 'vite.config.js'); + + try { + await fs.access(viteConfigPath); + // Import the module-specific Vite configuration + const moduleConfig = await import(viteConfigPath); + + if (moduleConfig.paths && Array.isArray(moduleConfig.paths)) { + paths.push(...moduleConfig.paths); + } + } catch (error) { + // vite.config.js does not exist, skip this module + } + } + } + } catch (error) { + console.error(`Error reading module statuses or module configurations: ${error}`); + } + + return paths; +} + +export default collectModuleAssetsPaths; diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..ca3db77 --- /dev/null +++ b/vite.config.js @@ -0,0 +1,26 @@ +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; +import vue from '@vitejs/plugin-vue' + +export default defineConfig({ + plugins: [ + laravel({ + input: ['resources/web/app.js'], + refresh: true, + }), + require('./vite-module-loader.js'), + vue({ + template: { + transformAssetUrls: { + base: null, + includeAbsolute: false, + }, + }, + }) + ], + resolve: { + alias: { + '@': '/resources/web' + } + } +});