commit dd44cab777e8a984c515d963ecb6f3b52a1e0274
Author: php_Team <>
Date: Sun Apr 16 23:05:17 2023 +0800
Initial commit
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..daef398
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+/.idea
+/.vscode
+/vendor
+/public/uploads/**
+*.log
+.env
+*.http
+runtime/*
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..36f7b6f
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,42 @@
+sudo: false
+
+language: php
+
+branches:
+ only:
+ - stable
+
+cache:
+ directories:
+ - $HOME/.composer/cache
+
+before_install:
+ - composer self-update
+
+install:
+ - composer install --no-dev --no-interaction --ignore-platform-reqs
+ - zip -r --exclude='*.git*' --exclude='*.zip' --exclude='*.travis.yml' ThinkPHP_Core.zip .
+ - composer require --update-no-dev --no-interaction "topthink/think-image:^1.0"
+ - composer require --update-no-dev --no-interaction "topthink/think-migration:^1.0"
+ - composer require --update-no-dev --no-interaction "topthink/think-captcha:^1.0"
+ - composer require --update-no-dev --no-interaction "topthink/think-mongo:^1.0"
+ - composer require --update-no-dev --no-interaction "topthink/think-worker:^1.0"
+ - composer require --update-no-dev --no-interaction "topthink/think-helper:^1.0"
+ - composer require --update-no-dev --no-interaction "topthink/think-queue:^1.0"
+ - composer require --update-no-dev --no-interaction "topthink/think-angular:^1.0"
+ - composer require --dev --update-no-dev --no-interaction "topthink/think-testing:^1.0"
+ - zip -r --exclude='*.git*' --exclude='*.zip' --exclude='*.travis.yml' ThinkPHP_Full.zip .
+
+script:
+ - php think unit
+
+deploy:
+ provider: releases
+ api_key:
+ secure: TSF6bnl2JYN72UQOORAJYL+CqIryP2gHVKt6grfveQ7d9rleAEoxlq6PWxbvTI4jZ5nrPpUcBUpWIJHNgVcs+bzLFtyh5THaLqm39uCgBbrW7M8rI26L8sBh/6nsdtGgdeQrO/cLu31QoTzbwuz1WfAVoCdCkOSZeXyT/CclH99qV6RYyQYqaD2wpRjrhA5O4fSsEkiPVuk0GaOogFlrQHx+C+lHnf6pa1KxEoN1A0UxxVfGX6K4y5g4WQDO5zT4bLeubkWOXK0G51XSvACDOZVIyLdjApaOFTwamPcD3S1tfvuxRWWvsCD5ljFvb2kSmx5BIBNwN80MzuBmrGIC27XLGOxyMerwKxB6DskNUO9PflKHDPI61DRq0FTy1fv70SFMSiAtUv9aJRT41NQh9iJJ0vC8dl+xcxrWIjU1GG6+l/ZcRqVx9V1VuGQsLKndGhja7SQ+X1slHl76fRq223sMOql7MFCd0vvvxVQ2V39CcFKao/LB1aPH3VhODDEyxwx6aXoTznvC/QPepgWsHOWQzKj9ftsgDbsNiyFlXL4cu8DWUty6rQy8zT2b4O8b1xjcwSUCsy+auEjBamzQkMJFNlZAIUrukL/NbUhQU37TAbwsFyz7X0E/u/VMle/nBCNAzgkMwAUjiHM6FqrKKBRWFbPrSIixjfjkCnrMEPw=
+ file:
+ - ThinkPHP_Core.zip
+ - ThinkPHP_Full.zip
+ skip_cleanup: true
+ on:
+ tags: true
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..574a39c
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,32 @@
+
+ThinkPHP遵循Apache2开源协议发布,并提供免费使用。
+版权所有Copyright © 2006-2016 by ThinkPHP (http://thinkphp.cn)
+All rights reserved。
+ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。
+
+Apache Licence是著名的非盈利开源组织Apache采用的协议。
+该协议和BSD类似,鼓励代码共享和尊重原作者的著作权,
+允许代码修改,再作为开源或商业软件发布。需要满足
+的条件:
+1. 需要给代码的用户一份Apache Licence ;
+2. 如果你修改了代码,需要在被修改的文件中说明;
+3. 在延伸的代码中(修改和有源代码衍生的代码中)需要
+带有原来代码中的协议,商标,专利声明和其他原来作者规
+定需要包含的说明;
+4. 如果再发布的产品中包含一个Notice文件,则在Notice文
+件中需要带有本协议内容。你可以在Notice中增加自己的
+许可,但不可以表现为对Apache Licence构成更改。
+具体的协议参考:http://www.apache.org/licenses/LICENSE-2.0
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..2929dad
--- /dev/null
+++ b/README.md
@@ -0,0 +1,56 @@
+ThinkPHP 6.0
+===============
+
+> 运行环境要求PHP7.1+,兼容PHP8.0。
+
+[官方应用服务市场](https://market.topthink.com) | [`ThinkAPI`——官方统一API服务](https://docs.topthink.com/think-api)
+
+ThinkPHPV6.0版本由[亿速云](https://www.yisu.com/)独家赞助发布。
+
+## 主要新特性
+
+* 采用`PHP7`强类型(严格模式)
+* 支持更多的`PSR`规范
+* 原生多应用支持
+* 更强大和易用的查询
+* 全新的事件系统
+* 模型事件和数据库事件统一纳入事件系统
+* 模板引擎分离出核心
+* 内部功能中间件化
+* SESSION/Cookie机制改进
+* 对Swoole以及协程支持改进
+* 对IDE更加友好
+* 统一和精简大量用法
+
+## 安装
+
+~~~
+composer create-project topthink/think tp 6.0.*
+~~~
+
+如果需要更新框架使用
+~~~
+composer update topthink/framework
+~~~
+
+## 文档
+
+[完全开发手册](https://www.kancloud.cn/manual/thinkphp6_0/content)
+
+## 参与开发
+
+请参阅 [ThinkPHP 核心框架包](https://github.com/top-think/framework)。
+
+## 版权信息
+
+ThinkPHP遵循Apache2开源协议发布,并提供免费使用。
+
+本项目包含的第三方源码和二进制文件之版权信息另行标注。
+
+版权所有Copyright © 2006-2020 by ThinkPHP (http://thinkphp.cn)
+
+All rights reserved。
+
+ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。
+
+更多细节参阅 [LICENSE.txt](LICENSE.txt)
diff --git a/app/.htaccess b/app/.htaccess
new file mode 100644
index 0000000..3418e55
--- /dev/null
+++ b/app/.htaccess
@@ -0,0 +1 @@
+deny from all
\ No newline at end of file
diff --git a/app/AppService.php b/app/AppService.php
new file mode 100644
index 0000000..96556e8
--- /dev/null
+++ b/app/AppService.php
@@ -0,0 +1,22 @@
+app = $app;
+ $this->request = $this->app->request;
+
+ // 控制器初始化
+ $this->initialize();
+ }
+
+ // 初始化
+ protected function initialize()
+ {
+ }
+
+ /**
+ * 验证数据
+ * @access protected
+ * @param array $data 数据
+ * @param string|array $validate 验证器名或者验证规则数组
+ * @param array $message 提示信息
+ * @param bool $batch 是否批量验证
+ * @return array|string|true
+ * @throws ValidateException
+ */
+ protected function validate(array $data, $validate, array $message = [], bool $batch = true)
+ {
+ if (is_array($validate)) {
+ $v = new Validate();
+ $v->rule($validate);
+ } else {
+ if (strpos($validate, '.')) {
+ // 支持场景
+ [$validate, $scene] = explode('.', $validate);
+ }
+ $class = false !== strpos($validate, '\\') ? $validate : $this->app->parseClass('validate', $validate);
+ $v = new $class();
+ if (!empty($scene)) {
+ $v->scene($scene);
+ }
+ }
+
+ $v->message($message);
+
+ // 是否批量验证
+ if ($batch || $this->batchValidate) {
+ $v->batch(true);
+ }
+
+ return $v->failException(true)->check($data);
+ }
+
+ /**
+ * 开启事务
+ *
+ * @param callable $cb
+ * @param array $args
+ * @return mixed
+ * @date 2022-07-22
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ protected static function transaction(callable $cb, array $args = [])
+ {
+ Db::startTrans();
+ try {
+ $result = invoke($cb, $args);
+ Db::commit();
+ return $result;
+ } catch (\Throwable $th) {
+ Db::rollback();
+ throw $th;
+ }
+ }
+
+ /**
+ * 分页包装器
+ *
+ * @param $query
+ * @date 2022-03-03
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ protected static function pageWrapper($query)
+ {
+ return (clone $query)->page(
+ (int) Request::param('page', 1),
+ (int) Request::param('limit', 10)
+ );
+ }
+}
diff --git a/app/BaseModel.php b/app/BaseModel.php
new file mode 100644
index 0000000..ce65c75
--- /dev/null
+++ b/app/BaseModel.php
@@ -0,0 +1,16 @@
+respone([
+ 'code' => 40050,
+ 'msg' => $e->getMessage(),
+ ]);
+ }
+ // 登录超时
+ if ($e instanceof LoginTimeOut) {
+ return $this->respone([
+ 'code' => 40026,
+ 'msg' => $request->isDev() ? $e->getMessage() : '登录超时',
+ ]);
+ }
+ // 参数验证不通过
+ if ($e instanceof ValidateException) {
+ return $this->respone([
+ 'code' => 40030,
+ 'msg' => $e->getMessage()
+ ]);
+ }
+ // 业务异常
+ if ($e instanceof ErrorMsg || $e instanceof ExceptionErrorMsg) {
+ $code = $e->getCode();
+ return $this->respone([
+ 'code' => $code == 0 ? 40052 : $code,
+ 'msg' => $e->getMessage(),
+ ], $e);
+ }
+ // 服务器异常
+ // if ($e instanceof Exception) {
+ // return $this->respone([
+ // 'code' => 40051,
+ // 'msg' => '服务器异常',
+ // ], $e);
+ // }
+ // 其他错误交给系统处理
+ return parent::render($request, $e);
+ }
+
+ /**
+ * 返回
+ *
+ * @param array $data
+ * @param Throwable $e
+ * @return Response
+ * @date 2022-04-15
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ private function respone(array $data, $e = null): Response
+ {
+ $request = $this->app->request;
+ if ($e && Env::get('app_show_error')) {
+ $data['__error'] = [
+ 'File' => $e->getFile(),
+ 'Line' => $e->getLine(),
+ 'Message' => $e->getMessage(),
+ 'Trace' => $e->getTrace()
+ ];
+ $data['__param'] = $request->param();
+ }
+ if ($request->isAjax()) {
+ return Response::create($data, 'json');
+ } else {
+ return Response::create(json_encode($data), 'html');
+ }
+ }
+}
diff --git a/app/Request.php b/app/Request.php
new file mode 100644
index 0000000..dc5103c
--- /dev/null
+++ b/app/Request.php
@@ -0,0 +1,80 @@
+param('dirName');
+
+ $upload = new UploadFile('uploads', 'file');
+ $path = $upload->putFile($dirName . 'Img');
+ return [
+ 'code' => 0,
+ 'data' => [
+ "name" => $path,
+ "url" => "/uploads/" . $path,
+ ],
+ 'msg' => '上传成功!'
+ ];
+ }
+
+ /**
+ * 上传文件
+ */
+ public function uploadFile(Request $request)
+ {
+ $dirName = $request->param('dirName');
+
+ $upload = new UploadFile('uploads', 'file');
+ $path = $upload->putFile($dirName . 'File');
+ return [
+ 'code' => 0,
+ 'data' => [
+ "name" => $path,
+ "url" => "/uploads/" . $path,
+ ],
+ 'msg' => '上传成功!'
+ ];
+ }
+}
diff --git a/app/admin/controller/Dictionary/Dictionary.php b/app/admin/controller/Dictionary/Dictionary.php
new file mode 100644
index 0000000..3a6533c
--- /dev/null
+++ b/app/admin/controller/Dictionary/Dictionary.php
@@ -0,0 +1,234 @@
+order([
+ 'dictionary_index',
+ 'dictionary_order' => 'desc',
+ ])->select();
+ return [
+ 'code' => 0,
+ 'msg' => 'ok',
+ 'data' => $select
+ ];
+ }
+
+ /**
+ * 获取菜单树
+ *
+ * @param Request $request
+ * @return array
+ * @date 2022-03-05
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function getDictionaryTree(Request $request)
+ {
+ $dictionary = ModelDictionary::withoutGlobalScope(['status'])->order([
+ 'dictionary_index',
+ 'dictionary_order' => 'desc',
+ ])->field([
+ 'dictionary_parent_guid',
+ 'dictionary_name',
+ 'dictionary_index',
+ 'dictionary_value',
+ 'dictionary_order',
+ 'dictionary_status',
+ 'dictionary_allow_update',
+ 'dictionary_list_class',
+ 'dictionary_guid',
+ ])->select()->toArray();
+
+ array_unshift($dictionary, [
+ 'dictionary_guid' => '0',
+ 'dictionary_parent_guid' => '-1',
+ 'dictionary_name' => '系统字典',
+ 'dictionary_index' => 0,
+ 'dictionary_value' => '',
+ 'dictionary_order' => 0,
+ 'dictionary_status' => 0,
+ 'dictionary_allow_update' => 2,
+ 'dictionary_list_class' => '',
+ ]);
+
+ $Traverse = new Traverse('dictionary_guid', 'dictionary_parent_guid');
+ $dictionaryTree = $Traverse->tree($dictionary, '-1', function ($v) {
+ return [
+ 'label' => $v['dictionary_name'],
+ 'id' => $v['dictionary_guid'],
+ 'parent_id' => $v['dictionary_parent_guid'],
+ 'index' => $v['dictionary_index'],
+ 'order' => $v['dictionary_order'],
+ 'status' => $v['dictionary_status'],
+ 'allow' => $v['dictionary_allow_update'],
+ 'value' => $v['dictionary_value'],
+ 'list_class' => $v['dictionary_list_class']
+ ];
+ });
+
+ return [
+ 'code' => 0,
+ 'msg' => 'ok',
+ 'data' => $dictionaryTree
+ ];
+ }
+
+ /**
+ * 添加字典
+ *
+ * @param Request $request
+ * @return array
+ * @date 2022-03-05
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function addDictionary(Request $request)
+ {
+ $params = $request->param();
+ $this->validate($params, [
+ 'dictionary_name' => 'require',
+ 'dictionary_index' => 'require',
+ 'dictionary_order' => 'require',
+ 'dictionary_parent_guid' => 'require',
+ ]);
+ if (isset($params['dictionary_guid'])) {
+ unset($params['dictionary_guid']);
+ }
+ ModelDictionary::create($params);
+ return [
+ 'code' => 0,
+ 'msg' => '添加成功'
+ ];
+ }
+
+ /**
+ * 更新部门
+ *
+ * @param Request $request
+ * @return array
+ * @date 2022-03-05
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function updateDictionary(Request $request)
+ {
+ $params = $request->param();
+ $this->validate($params, [
+ 'dictionary_guid' => 'require'
+ ]);
+ ModelDictionary::withoutGlobalScope(['status'])->update($params, [
+ 'dictionary_guid' => $params['dictionary_guid']
+ ]);
+ return [
+ 'code' => 0,
+ 'msg' => '更新成功'
+ ];
+ }
+
+ /**
+ * 删除菜单
+ *
+ * @param Request $request
+ * @return array
+ * @date 2022-03-05
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function deleteDictionary(Request $request)
+ {
+ $params = $request->param();
+ $this->validate($params, [
+ 'dictionary_guid' => 'require'
+ ]);
+ ModelDictionary::find($params['dictionary_guid'])->delete();
+ return [
+ 'code' => 0,
+ 'msg' => '删除成功'
+ ];
+ }
+
+ // 获取字典
+ public function getDictionary(Request $request)
+ {
+ $dictionary_value = $request->param('dictionary_value');
+
+ $dictionary_guid = ModelDictionary::where('dictionary_value', $dictionary_value)->find()['dictionary_guid'];
+ $res = ModelDictionary::field([
+ 'dictionary_guid',
+ 'dictionary_parent_guid',
+ 'dictionary_name',
+ 'dictionary_value',
+ 'dictionary_list_class',
+ ])->where('dictionary_parent_guid', $dictionary_guid)
+ ->where('dictionary_status', 1)->select();
+ if (!$res) throwErrorMsg("字典不存在");
+ else return $res;
+ }
+
+
+ /**
+ * 下载导入模板
+ */
+ public function downloadTemplate(Request $request)
+ {
+ $params = $request->param();
+ $data = [
+ [
+ '上级字典',
+ '名称',
+ '值',
+ '层级',
+ '排序',
+ '回显',
+ ]
+ ];
+
+ $data[] = ['','状态','status','1','0','',];
+ $data[] = ['状态','启用','1','2','1','primary'];
+ $data[] = ['状态','禁用','2','2','2','danger',];
+
+ $excel = (new Excel())->exporTsheet($data);
+ $excel->save('字典导入模板.xlsx');
+ }
+
+ /**
+ * 导入excel
+ */
+ public function importExcel(Request $request)
+ {
+ $file = new UploadFile('uploads', 'fileExt:xlsx');
+ $file->putFile('Dictionary');
+
+ $msg = ModelDictionary::importExcel($file);
+
+ return [
+ 'code' => 0,
+ 'msg' => $msg
+ ];
+ }
+}
diff --git a/app/admin/controller/Flow/Flow.php b/app/admin/controller/Flow/Flow.php
new file mode 100644
index 0000000..7edceac
--- /dev/null
+++ b/app/admin/controller/Flow/Flow.php
@@ -0,0 +1,307 @@
+track();
+
+ /**
+ * 查询流量访问记录
+ * @throws \think\db\exception\DbException
+ */
+ public function getFlowRecord(Request $request): array
+ {
+ $query = ModelFlow::where([]);
+ $select = self::pageWrapper($query)
+ ->field([
+ 'flow_id',
+ 'flow_record_no',
+ 'flow_visitor_ip',
+ 'flow_location',
+ 'flow_create_time',
+ 'flow_source',
+ 'flow_target',
+ 'flow_os',
+ 'flow_browser'
+ ])
+ ->order('flow_id', 'desc')
+ ->select();
+
+ $count = $query->count();
+ return [
+ 'msg' => '查询成功',
+ 'code' => 0,
+ 'data' => $select,
+ 'count' => $count
+ ];
+ }
+
+ /**
+ * 查询流量来源统计
+ */
+ public function getFlowSourceCount(): array
+ {
+ $query = ModelFlow::where([]);
+ $count = $query->count();
+ $select = $query->field([
+ 'flow_id',
+ 'flow_source',
+ ])->select();
+ $GDP = $query->field([
+
+ 'flow_source',
+ ])->group('flow_source')->select();
+ foreach ($GDP as $k => &$i) {
+ $i['number'] = 0;
+ $i['percentage'] = 0;
+ }
+ foreach ($select as $key => &$item) {
+ foreach ($GDP as $k => &$i) {
+ if ($item['flow_source'] === $i['flow_source']) {
+ $i['number'] += 1;
+ }
+ }
+ }
+ foreach ($GDP as $k => $i) {
+ $i['percentage'] = round($i['number'] / $count * 100);
+ }
+ return [
+ 'msg' => '查询完成',
+ 'code' => 0,
+ 'count' => $count,
+ 'data' => [
+ 'GDP' => $GDP,
+ 'select' => $select
+ ]
+ ];
+ }
+
+ /**
+ * 查询流量浏览器统计
+ */
+ public function getFlowBrowserCount(): array
+ {
+ $query = ModelFlow::where([]);
+ $count = $query->count();
+ $select = $query->field([
+ 'flow_id',
+ 'flow_browser',
+ ])->select();
+ $GDP = $query->field([
+
+ 'flow_browser',
+ ])->group('flow_browser')->select();
+ foreach ($GDP as $k => &$i) {
+ $i['number'] = 0;
+ $i['percentage'] = 0;
+ }
+ foreach ($select as $key => &$item) {
+ foreach ($GDP as $k => &$i) {
+ if ($item['flow_browser'] === $i['flow_browser']) {
+ $i['number'] += 1;
+ }
+ }
+ }
+ foreach ($GDP as $k => $i) {
+ $i['percentage'] = round($i['number'] / $count * 100);
+ }
+ return [
+ 'msg' => '查询完成',
+ 'code' => 0,
+ 'count' => $count,
+ 'data' => [
+ 'select' => $select,
+ 'GDP' => $GDP
+ ]
+ ];
+ }
+
+ /**
+ * 查询流量月统计
+ */
+ public function getFlowMonthCount(Request $request): array
+ {
+ $current_year = input('post.current_year') ?? 0;
+ $query = ModelFlow::where([]);
+ $current_year = date('Y', strtotime("$current_year year"));
+ $count = $query->whereYear('flow_create_time', $current_year)
+ ->field([
+ 'flow_create_time',
+ ])
+ ->select()->count();
+ $select = $query->whereYear('flow_create_time', $current_year)
+ ->field([
+ 'flow_id',
+ 'flow_record_no',
+ 'flow_visitor_ip',
+ 'flow_location',
+ 'flow_create_time',
+ 'flow_source',
+ 'flow_target',
+ 'flow_os',
+ 'flow_browser'
+ ])
+ ->order('flow_id', 'desc')
+ ->select()->toArray();
+ $res_select = [];
+ for ($i = 1; $i <= 12; $i++) {
+ array_push($res_select, [
+ 'flow_Date' => $current_year .
+ '-' .
+ date('m', strtotime($current_year . '-' . $i)),
+ 'number' => 0,
+ 'percentage' => 0
+ ]);
+ }
+ foreach ($select as $key => &$item) {
+ $date_format = explode('-', explode(' ', $item['flow_create_time'])[0]);
+
+ $item['flow_Date'] = $date_format[0] . '-' . $date_format[1];
+ foreach ($res_select as $k => &$it) {
+ if ($item['flow_Date'] == $it['flow_Date']) {
+ $it['number'] += 1;
+ }
+ }
+ foreach ($res_select as $k => &$it) {
+ $it['percentage'] = round($it['number'] / $count * 100);
+ }
+ };
+ foreach ($res_select as $k => &$it) {
+ foreach ($select as $key => &$item) {
+ if ($it['flow_Date'] === $item['flow_Date']) {
+ $it = array_merge($it, $item);
+ }
+ }
+ }
+ return [
+ 'code' => 0,
+ 'msg' => '查询成功',
+ 'count' => $count,
+ 'data' => [
+ 'current_date' => ['y' => $current_year],
+ 'select' => $res_select
+ ]
+ ];
+ }
+
+ /**
+ * 查询流量日统计
+ */
+ public function getFlowDayCount(Request $request)
+ {
+ $current_month = input('post.current_month') ?? 0;
+ // return ['a'=>$current_month];
+ $query = ModelFlow::where([]);
+ $current_month_date = date('Y-m', strtotime(date('Y-m-01') . "$current_month month"));
+ // return ['a'=>$current_month_date];
+ $count = $query->whereMonth('flow_create_time', $current_month_date)
+ ->field([
+ 'flow_create_time',
+ ])
+ ->select()->count();
+ // $select = $query->whereMonth('flow_create_time', $current_month_date)
+ // ->field([
+ // 'flow_create_time',
+ // ])
+ // ->order('flow_id', 'desc')
+ // ->select()->toArray();
+ $select = $query->whereMonth('flow_create_time', $current_month_date)
+ ->field([
+ 'flow_create_time',
+ ])
+ ->buildSql();
+ return $select;
+ $GDP = [];
+ $res_select = [];
+ for ($i = 1; $i <= date('t', strtotime(date('Y-m-01') . "$current_month month")); $i++) {
+
+ array_push($res_select, [
+ 'week' => $this->getWeek(date('Y-m', strtotime(date('Y-m-01') . "$current_month month"))
+ . '-'
+ . date('d', strtotime(date('Y-m', strtotime(date('Y-m-01') . "$current_month month")) . "-$i"))),
+ 'flow_Date' => date('Y-m', strtotime(date('Y-m-01') . "$current_month month"))
+ . '-'
+ . date('d', strtotime(date('Y-m', strtotime(date('Y-m-01') . "$current_month month")) . "-$i")),
+ 'number' => 0, 'percentage' => 0
+ ]);
+ }
+ foreach ($select as $key => &$item) {
+
+ $item['flow_Date'] = explode(' ', $item['flow_create_time'])[0];
+ foreach ($res_select as $k => &$it) {
+ if ($item['flow_Date'] == $it['flow_Date']) {
+ $it['number'] += 1;
+ }
+ }
+ foreach ($res_select as $k => &$it) {
+ $it['percentage'] = round($it['number'] / $count * 100);
+ }
+ };
+ foreach ($res_select as $k => &$it) {
+ foreach ($select as $key => &$item) {
+ if ($it['flow_Date'] === $item['flow_Date']) {
+ $it = array_merge($it, $item);
+ }
+ }
+ }
+ return [
+ 'code' => 0,
+ 'msg' => '查询成功',
+ 'count' => $count,
+ 'data' => [
+ 'current_date' => ['y' => date('Y', strtotime(date('Y-m-01') . "$current_month month")), 'm' => date('m', strtotime(date('Y-m-01') . "$current_month month"))],
+ 'select' => $res_select
+ ]
+ ];
+ }
+
+ /**
+ * @param $date
+ * @return string
+ * @descript 获取指定日期星期
+ */
+ private function getWeek($date): string
+ {
+ //强制转换日期格式
+ $date_str = date('Y-m-d', strtotime($date));
+
+ //封装成数组
+ $arr = explode("-", $date_str);
+
+ //参数赋值
+ //年
+ $year = $arr[0];
+
+ //月,输出2位整型,不够2位右对齐
+ $month = sprintf('%02d', $arr[1]);
+
+ //日,输出2位整型,不够2位右对齐
+ $day = sprintf('%02d', $arr[2]);
+
+ //时分秒默认赋值为0;
+ $hour = $minute = $second = 0;
+
+ //转换成时间戳
+ $strap = mktime($hour, $minute, $second, $month, $day, $year);
+
+ //获取数字型星期几
+ $number_wk = date("w", $strap);
+
+ //自定义星期数组
+ $weekArr = array("星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六");
+
+ //获取数字对应的星期
+ return $weekArr[$number_wk];
+ }
+
+}
diff --git a/app/admin/controller/Gen/Gen.php b/app/admin/controller/Gen/Gen.php
new file mode 100644
index 0000000..8a33200
--- /dev/null
+++ b/app/admin/controller/Gen/Gen.php
@@ -0,0 +1,2039 @@
+ 0,
+ 'msg' => '一键删除表成功!'
+ ];
+ } catch (\Throwable $th) {
+ throw $th;
+ }
+ }
+
+ //一键生成表
+ public function createTable()
+ {
+ try {
+ Db::execute("
+ CREATE TABLE `gen_table` (
+ `tableId` int(0) NOT NULL AUTO_INCREMENT COMMENT '编号',
+ `tableName` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '表名称',
+ `tableComment` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '表描述',
+ `className` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '实体类名称',
+ `tplCategory` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT 'crud' COMMENT '使用的模板(crud单表操作 tree树表操作)',
+ `moduleName` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '生成模块名',
+ `businessName` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '生成业务名',
+ `functionName` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '生成功能名',
+ `functionAuthor` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '生成功能作者',
+ `genType` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '0' COMMENT '生成代码方式(0zip压缩包 1自定义路径)',
+ `genPath` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '/' COMMENT '生成路径(不填默认项目路径)',
+ `options` varchar(1000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '其它生成选项',
+ `remark` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '备注',
+ `create_time` datetime(0) NULL DEFAULT NULL,
+ `create_user_guid` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+ `update_time` datetime(0) NULL DEFAULT NULL,
+ `update_user_guid` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+ `delete_time` datetime(0) NULL DEFAULT NULL,
+ `delete_user_guid` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+ PRIMARY KEY (`tableId`) USING BTREE
+ ) ENGINE = InnoDB AUTO_INCREMENT = 79 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '生成表' ROW_FORMAT = DYNAMIC;
+ ");
+
+ Db::execute("
+ CREATE TABLE `gen_table_column` (
+ `columnId` int(0) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '编号',
+ `tableName` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '表名',
+ `tableId` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '归属表编号',
+ `columnName` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '列名称',
+ `columnComment` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '列描述',
+ `columnType` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '列类型',
+ `isPk` tinyint(1) NULL DEFAULT NULL COMMENT '是否主键(1是)',
+ `isIncrement` tinyint(1) NULL DEFAULT NULL COMMENT '是否自增(1是)',
+ `isRequired` tinyint(1) NULL DEFAULT NULL COMMENT '是否必填(1是)',
+ `isInsert` tinyint(1) NULL DEFAULT NULL COMMENT '是否为插入字段(1是)',
+ `isEdit` tinyint(1) NULL DEFAULT NULL COMMENT '是否编辑字段(1是)',
+ `isList` tinyint(1) NULL DEFAULT NULL COMMENT '是否列表字段(1是)',
+ `isQuery` tinyint(0) NULL DEFAULT NULL COMMENT '是否查询字段(1是)',
+ `isSort` tinyint(0) NULL DEFAULT NULL COMMENT '是否排序字段(1是)',
+ `queryType` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'EQ' COMMENT '查询方式(等于、不等于、大于、小于、范围)',
+ `htmlType` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '显示类型(文本框、文本域、下拉框、复选框、单选框、日期控件)',
+ `dictType` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '字典类型',
+ `sort` int(0) NULL DEFAULT NULL COMMENT '排序',
+ `isInit` tinyint(1) NULL DEFAULT 0 COMMENT '是否初始化字段(1是)',
+ `create_time` datetime(0) NULL DEFAULT NULL,
+ `create_user_guid` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+ `update_time` datetime(0) NULL DEFAULT NULL,
+ `update_user_guid` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+ `delete_time` datetime(0) NULL DEFAULT NULL,
+ `delete_user_guid` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
+ PRIMARY KEY (`columnId`) USING BTREE
+ ) ENGINE = InnoDB AUTO_INCREMENT = 534 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '生成表字段' ROW_FORMAT = DYNAMIC;
+ ");
+
+ return [
+ 'code' => 0,
+ 'msg' => '一键生成表成功!'
+ ];
+ } catch (\Throwable $th) {
+ throw $th;
+ }
+ }
+
+ // 获取所有表名称
+ public function getTatbleList(Request $request)
+ {
+ $data_base_name_arr = Db::query("select database()");
+ $tableName = $request->param('tableName');
+
+ // $select = Db::getTables();
+ $select = Db::query("show tables");
+ $selecJs = [];
+
+ $data_base_name = $data_base_name_arr[0]['database()'];
+
+ foreach ($select as $key => $value) {
+ array_push($selecJs, ['name' => $value["Tables_in_{$data_base_name}"]]);
+ }
+
+ $arr = [];
+ if ($tableName) {
+ foreach ($selecJs as $key => $value) {
+ if (strstr($value["name"], $tableName)) {
+ // return 1;
+ array_push($arr, $value);
+ }
+ }
+ }
+ if (count($arr) > 0) {
+ $selecJs = $arr;
+ }
+
+ $count = count($selecJs);
+ return [
+ 'code' => 200,
+ 'data' => [
+ 'pageIndex' => 1,
+ 'pageSize' => 10,
+ 'result' => $selecJs,
+ 'totalNum' => $count,
+ 'totalPage' => 1,
+ ],
+ 'msg' => 'SUCCESS'
+ ];
+ }
+
+ // 获取生成表列表
+ public function getGenTableList(Request $request)
+ {
+ $tableName = $request->param('tableName');
+ $search = [];
+ if ($tableName) {
+ $search['tableName'] = $tableName;
+ }
+
+ $select = Db::name('gen_table')
+ ->where($search)
+ ->order('tableId', 'desc');
+
+ $count = $select->count();
+ $select = self::pageWrapper($select)->select();
+
+ return [
+ 'code' => 0,
+ 'data' => $select,
+ 'count' => $count,
+ 'msg' => 'ok'
+ ];
+ }
+
+ // 获取生成表详情
+ public function getGenTableDetail(Request $request)
+ {
+ $tableId = $request->param('tableId');
+
+ $select = Db::name('gen_table')
+ ->where('tableId', $tableId)
+ ->find();
+
+ $columns = Db::name('gen_table_column')
+ ->where('tableId', $tableId)
+ ->select()->toArray();
+
+ $select['options'] = json_decode($select['options']);
+ foreach ($columns as $key => $value) {
+ $columns[$key]['isEdit'] = $this->intToBool($value['isEdit']);
+ $columns[$key]['isPk'] = $this->intToBool($value['isPk']);
+ $columns[$key]['isIncrement'] = $this->intToBool($value['isIncrement']);
+ $columns[$key]['isRequired'] = $this->intToBool($value['isRequired']);
+ $columns[$key]['isInsert'] = $this->intToBool($value['isInsert']);
+ $columns[$key]['isList'] = $this->intToBool($value['isList']);
+ $columns[$key]['isQuery'] = $this->intToBool($value['isQuery']);
+ $columns[$key]['isSort'] = $this->intToBool($value['isSort']);
+ $columns[$key]['isInit'] = $this->intToBool($value['isInit']);
+ }
+
+ return [
+ 'code' => 0,
+ 'data' => $select,
+ 'columns' => $columns,
+ 'msg' => 'ok'
+ ];
+ }
+
+ // 删除生成表
+ public function delTable(Request $request)
+ {
+ $params = $request->param();
+
+ $gen = Db::name('gen_table')->where([
+ 'tableId' => $params['tableId']
+ ]);
+ $id = $gen->find()['tableId'];
+ // return $gen->find()['tableId'];
+ $gen->delete();
+
+ $gen_column = Db::name('gen_table_column')->where('tableId', $id);
+ $gen_column->delete();
+
+ return [
+ 'code' => 200,
+ 'msg' => "删除成功"
+ ];
+ }
+
+ // 导入表
+ public function importTable(Request $request)
+ {
+ $tables = $request->param('tables');
+
+ // 初始化表数据
+ $tableInfo = Db::query("SHOW TABLE STATUS where Name = '$tables[0]'")[0];
+ $tabledata = $this->initTable($tableInfo);
+ // return $tabledata;
+ $id = Db::name('gen_table')->strict()->insertGetId($tabledata);
+
+ // 初始化字段数据
+ $fields = Db::getFields($tables[0]);
+
+ foreach ($fields as $value) {
+ // return $value;
+ $fieldsdata = $this->initFields($tables[0], $id, $value);
+ $fieldid = Db::name('gen_table_column')->strict()->insertGetId($fieldsdata);
+ }
+
+ return [
+ 'code' => 200,
+ 'msg' => '导入成功'
+ ];
+ }
+
+ /**
+ * 编辑生成
+ */
+ public function updateGenTable(Request $request)
+ {
+ $params = $request->param();
+ // return $params['options'];
+
+ $options = [
+ "ParentMenuId" => $params['ParentMenuId'],
+ "SortType" => $params['SortType'],
+ "SortField" => $params['SortField'],
+ "CheckedBtn" => $params['CheckedBtn'],
+ "ColNum" => $params['ColNum'],
+ ];
+
+ $model = Db::name('gen_table')->where([
+ 'tableId' => $params['tableId']
+ ])->update([
+ 'tableName' => $params['tableName'],
+ 'tableComment' => $params['tableComment'],
+ 'className' => $params['className'],
+ 'tplCategory' => $params['tplCategory'],
+ 'moduleName' => $params['moduleName'],
+ 'businessName' => $params['businessName'],
+ 'functionName' => $params['functionName'],
+ 'functionAuthor' => $params['functionAuthor'],
+ 'genType' => $params['genType'],
+ 'genPath' => $params['genPath'],
+ 'options' => json_encode($options),
+ 'remark' => $params['remark'],
+ ]);
+ // if (!$model) {
+ // throwErrorMsg("生成表不存在", 1);
+ // }
+ // $model->save($params);
+
+ $model2 = Db::name('gen_table_column')->where([
+ 'tableId' => $params['tableId']
+ ])->select();
+ if (!$model2) {
+ throwErrorMsg("生成表字段不存在", 1);
+ }
+ // return $model2;
+ foreach ($model2 as $key => $item1) {
+ // return $item1;
+ foreach ($params['columns'] as $key => $item2) {
+ if ($item1['columnId'] == $item2['columnId']) {
+ // return $item2;
+ // return var_dump($item2);
+ $item = Db::name('gen_table_column')->where([
+ 'columnId' => $item2['columnId']
+ ])->update($item2);
+ }
+ }
+ }
+
+ return [
+ 'code' => 200,
+ 'msg' => '编辑成功'
+ ];
+ }
+
+ // 生成代码入口
+ public function genCode(Request $request)
+ {
+ $tableId = $request->param('tableId');
+
+ $table = Db::name('gen_table')
+ ->where('tableId', $tableId)
+ ->find();
+
+ $columns = Db::name('gen_table_column')
+ ->where('tableId', $tableId)
+ ->select()->toArray();
+
+ $table['options'] = json_decode($table['options']);
+ foreach ($columns as $key => $value) {
+ $columns[$key]['isEdit'] = $this->intToBool($value['isEdit']);
+ $columns[$key]['isPk'] = $this->intToBool($value['isPk']);
+ $columns[$key]['isIncrement'] = $this->intToBool($value['isIncrement']);
+ $columns[$key]['isRequired'] = $this->intToBool($value['isRequired']);
+ $columns[$key]['isInsert'] = $this->intToBool($value['isInsert']);
+ $columns[$key]['isList'] = $this->intToBool($value['isList']);
+ $columns[$key]['isQuery'] = $this->intToBool($value['isQuery']);
+ $columns[$key]['isSort'] = $this->intToBool($value['isSort']);
+ $columns[$key]['isInit'] = $this->intToBool($value['isInit']);
+ }
+
+ // return $table;
+
+ try {
+ if ($table['tplCategory'] == "web_static") {
+ $this->createAdminController($columns, $table);
+ $this->createNewModel($columns, $table);
+ $this->createJsWebStaticIndex($columns, $table);
+ $this->createJsApi($columns, $table);
+ } else if ($table['tplCategory'] == "crud") {
+ $this->createAdminController($columns, $table);
+ $this->createNewModel($columns, $table);
+ $this->createJsVue($columns, $table);
+ $this->createJsAdd($columns, $table);
+ $this->createJsEdit($columns, $table);
+ $this->createJsDetail($columns, $table);
+ $this->createJsApi($columns, $table);
+ }
+ } catch (\Throwable $th) {
+ throw $th;
+ }
+
+ return [
+ 'code' => 200,
+ 'msg' => '生成成功'
+ ];
+ }
+
+ // 生成Api代码入口
+ public function codeGeneratorApi(Request $request)
+ {
+ $tableId = $request->param('tableId');
+
+ $table = Db::name('gen_table')
+ ->where('tableId', $tableId)
+ ->find();
+
+ $columns = Db::name('gen_table_column')
+ ->where('tableId', $tableId)
+ ->select()->toArray();
+
+ $table['options'] = json_decode($table['options']);
+ foreach ($columns as $key => $value) {
+ $columns[$key]['isEdit'] = $this->intToBool($value['isEdit']);
+ $columns[$key]['isPk'] = $this->intToBool($value['isPk']);
+ $columns[$key]['isIncrement'] = $this->intToBool($value['isIncrement']);
+ $columns[$key]['isRequired'] = $this->intToBool($value['isRequired']);
+ $columns[$key]['isInsert'] = $this->intToBool($value['isInsert']);
+ $columns[$key]['isList'] = $this->intToBool($value['isList']);
+ $columns[$key]['isQuery'] = $this->intToBool($value['isQuery']);
+ $columns[$key]['isSort'] = $this->intToBool($value['isSort']);
+ $columns[$key]['isInit'] = $this->intToBool($value['isInit']);
+ }
+
+ $this->createApiController($columns, $table, 2);
+
+ return [
+ 'code' => 200,
+ 'msg' => '生成成功'
+ ];
+ }
+
+ // 初始化表数据
+ private function initTable($tableInfo)
+ {
+ $options = [
+ "ParentMenuId" => 0,
+ "SortType" => "desc",
+ "SortField" => $tableInfo['Name'] . "_update_time",
+ "CheckedBtn" => [1, 2, 3, 5],
+ "ColNum" => 0,
+ ];
+
+ $tabledata = [
+ // 'tplCategory' => $tableInfo['tplCategory'],
+ 'tableName' => $tableInfo['Name'],
+ 'tableComment' => $tableInfo['Comment'],
+ 'className' => Tool::camelize($tableInfo['Name']),
+ 'businessName' => $tableInfo['Name'],
+ 'moduleName' => Tool::camelize($tableInfo['Name']),
+ 'functionName' => $tableInfo['Comment'],
+ 'functionAuthor' => 'admin',
+ 'genType' => 1,
+ 'genPath' => $this->address, // 自定义路径
+ 'options' => json_encode($options),
+ 'create_time' => date('Y-m-d H:i:s'),
+ ];
+ return $tabledata;
+ }
+
+ // 初始化字段数据
+ private function initFields($tableName, $id, $value)
+ {
+ // 初始字段
+ $inputDtoNoFieldArr = [
+ $tableName . "_" . "id",
+ $tableName . "_" . "guid",
+ $tableName . "_" . "create_time",
+ $tableName . "_" . "create_user_guid",
+ $tableName . "_" . "update_time",
+ $tableName . "_" . "update_user_guid",
+ $tableName . "_" . "delete_time",
+ $tableName . "_" . "delete_user_guid"
+ ];
+ // 查询字段
+ $ListFieldArr = [
+ $tableName . "_" . "create_time",
+ $tableName . "_" . "create_user_guid",
+ $tableName . "_" . "update_time",
+ $tableName . "_" . "update_user_guid",
+ $tableName . "_" . "delete_time",
+ $tableName . "_" . "delete_user_guid"
+ ];
+ // 图片字段
+ $imageFiledArr = [
+ $tableName . "_" . "icon",
+ $tableName . "_" . "img",
+ $tableName . "_" . "image",
+ $tableName . "_" . "url",
+ $tableName . "_" . "pic",
+ $tableName . "_" . "photo",
+ $tableName . "_" . "avatar"
+ ];
+ // 下拉框字段
+ $selectFiledArr = [
+ $tableName . "_" . "status",
+ $tableName . "_" . "type",
+ $tableName . "_" . "state",
+ $tableName . "_" . "sex",
+ $tableName . "_" . "gender"
+ ];
+ // 类型判断
+ $type = $this->FieldsType($value['type']);
+ $queryType = '';
+
+ if (in_array($value["name"], $inputDtoNoFieldArr)) $isInsert = false;
+ else $isInsert = true;
+
+ if (in_array($value["name"], $inputDtoNoFieldArr)) $isInit = true;
+ else $isInit = false;
+
+ if ($value["primary"] || $value["autoinc"] || in_array($value["name"], $inputDtoNoFieldArr)) $isEdit = false;
+ else $isEdit = true;
+
+ if (in_array($value["name"], $ListFieldArr)) $isList = false;
+ else $isList = true;
+
+ //时间类型初始化between范围查询
+ if ($value['type'] == "datetime") {
+ $queryType = "BETWEEN";
+ }
+
+ $fieldData = [
+ 'tableName' => $tableName,
+ 'tableId' => $id,
+ 'columnName' => $value["name"],
+ 'columnComment' => $value["comment"],
+ 'columnType' => $type,
+ 'isPk' => $value["primary"],
+ 'isIncrement' => $value["autoinc"],
+ 'isRequired' => $value["notnull"],
+ 'isInsert' => $isInsert,
+ 'isEdit' => $isEdit,
+ 'isList' => $isList,
+ 'isQuery' => false,
+ 'htmlType' => $this->FieldsHtmlType($value["name"], $tableName),
+ 'queryType' => $queryType,
+ 'isInit' => $isInit,
+ 'create_time' => date('Y-m-d H:i:s'),
+ ];
+
+ return $fieldData;
+ // return 6;
+ }
+
+ //生成模型
+ private function createNewModel($fields, $table)
+ {
+ $root = base_path(); //根地址
+ $model_path_name = $table['className'];
+
+ //模型路径并创建
+ $is_multiple = false;
+ $multiple_name = '';
+
+ $model_path = str_replace('/', DIRECTORY_SEPARATOR, $root . "common/model/" . $table['moduleName']);
+ if (true !== $res = $this->mkdir($model_path)) {
+ return $res;
+ }
+
+ $temp_path = str_replace('/', DIRECTORY_SEPARATOR, $root . 'resources/view/admin/model.tpl');
+ $gen_path = str_replace('/', DIRECTORY_SEPARATOR, $root . "common/model/" . $table['moduleName'] . '/' . $model_path_name . ".php"); //生成的模型地址\
+
+ $fieldArr = '';
+ foreach ($fields as $value) {
+ $fieldArr .= '
+ ' . '"' . $value['columnName'] . '"' . " => " . '"' . $value['columnType'] . '",' . "
+ ";
+ }
+
+ $checked_btn_arr = $table['options']->CheckedBtn; //其他选项 4:导出 6:导入
+ $init_fields = []; //初始化(业务字段) 例: user_name => 用户名称
+ foreach ($fields as $key => $val) {
+ if (!$val['isInit']) $init_fields[$val['columnName']] = $val['columnComment'];
+ }
+
+ //模型的示例代码
+ $tem_f = fopen($temp_path, "r");
+ $temp_str = fread($tem_f, filesize($temp_path));
+ self::getImportExcelTempStr($fields, $table, $init_fields, 'imp_mod');
+ $temp_str = str_replace(
+ [
+ '{$className}',
+ '{$moduleName}',
+ '{$businessName}',
+ '{$fields}',
+ '{$multiple_name}',
+ '{$exportExcelContent}',
+ '{$importExcelContent}',
+ '{$importExcelInitContent}',
+ '{$importExcelField}'
+ ],
+ [
+ $table['className'],
+ $table['moduleName'],
+ $table['businessName'],
+ $fieldArr,
+ $multiple_name,
+ in_array('4', $checked_btn_arr) ? self::getExportExcelTempStr($fields, $table, $init_fields, 'export_mod') : null,
+ in_array('6', $checked_btn_arr) ? self::getImportExcelTempStr($fields, $table, $init_fields, 'imp_mod') : null,
+ in_array('6', $checked_btn_arr) ? self::getImportExcelTempStr($fields, $table, $init_fields, 'imp_init') : null,
+ in_array('6', $checked_btn_arr) ? self::getImportExcelTempStr($fields, $table, $init_fields, 'imp_fie') : null,
+ ],
+ $temp_str
+ );
+ // return $temp_str;
+ $gen_model = fopen($gen_path, 'w');
+ fwrite($gen_model, $temp_str);
+ return true;
+ }
+
+ //生成后台控制器
+ private function createAdminController($fields, $table)
+ {
+ $root = base_path(); //根地址
+ $module_name = $table['moduleName']; //模块名
+ $class_name = $table['className']; //实体类名
+
+ //控制器模块构建
+ $con_path = str_replace('/', DIRECTORY_SEPARATOR, "{$root}admin/controller/{$module_name}");
+ if (true !== $res = $this->mkdir($con_path)) return $res;
+ //模板路径构建
+
+ if ($table['tplCategory'] == "crud") {
+ $temp_path = str_replace('/', DIRECTORY_SEPARATOR, "{$root}resources/view/admin/controller.tpl");
+ } else if ($table['tplCategory'] == "web_static") {
+ $temp_path = str_replace('/', DIRECTORY_SEPARATOR, "{$root}resources/view/business/webController.tpl");
+ }
+
+ //控制器生成后的路径构建
+ $gen_path = str_replace('/', DIRECTORY_SEPARATOR, "{$root}admin/controller/{$module_name}/{$class_name}.php");
+
+
+ $function_name = $table['functionName']; //功能名
+ $business_name = $table['businessName']; //业务名
+ $checked_btn_arr = $table['options']->CheckedBtn; //其他选项 4:导出 6:导入
+ $query_fields = []; //查询列表可显字段
+ $order_field = "'{$table['options']->SortField}'"; //排序字段
+ $order_mode = "'{$table['options']->SortType}'"; //排序方式
+ $edit_allow_fields = ["{$business_name}_update_user_guid"]; //编辑允许字段
+ $add_allow_fields = ["{$business_name}_guid", "{$business_name}_create_user_guid", "{$business_name}_update_user_guid"]; //新增允许字段
+ $where_content_arr = []; //列表查询条件
+ $init_fields = []; //初始化(业务字段)
+ $is_img_upload = false;
+ $add_require_fields = []; //新增必填字段
+ $edit_require_fields = []; //编辑必填字段
+ foreach ($fields as $key => $val) {
+ $column_name = $val['columnName'];
+ if (!$val['isInit']) $init_fields[$column_name] = $val['columnComment'];
+ if ($val['isList']) $query_fields[] = $column_name;
+
+ if ($val['isEdit'] && !in_array($column_name, $edit_allow_fields)) {
+ $edit_allow_fields[] = $column_name;
+ if ($val['isRequired']) $edit_require_fields[] = $column_name;
+ }
+ if ($val['isInsert'] && !in_array($column_name, $add_allow_fields)) {
+ $add_allow_fields[] = $column_name;
+ if ($val['isRequired']) $add_require_fields[] = $column_name;
+ }
+ if ($val['isQuery']) $where_content_arr[] = [$column_name, $val['queryType']];
+ if ($val['htmlType'] == 'imageUpload') $is_img_upload = true;
+ if ($val['htmlType'] == 'fileUpload') $is_file_upload = true;
+ }
+
+ //打开模板文件资源(只读)
+ $temp_res_r = fopen($temp_path, "r");
+ //获取模板内容
+ $temp_res_str = fread($temp_res_r, filesize($temp_path));
+ //模板内容替换
+ $temp_str = str_replace(
+ [
+ '{$moduleName}',
+ '{$className}',
+ '{$functionName}',
+ '{$businessName}',
+ '{$queryFields}',
+ '{$orderField}',
+ '{$orderMode}',
+ '{$editAllowFields}',
+ '{$addAllowFields}',
+ '{$whereContent}',
+ '{$exportExcelContent}',
+ '{$importExcelContent}',
+ '{$downloadTempContent}',
+ '{$editRequireFields}',
+ '{$addRequireFields}',
+ ],
+ [
+ $module_name,
+ $class_name,
+ $function_name,
+ $business_name,
+ self::toFormTempStr($query_fields, $init_fields),
+ $order_field ?? "'{$business_name}_update_time'",
+ $order_mode ?? "'desc'",
+ self::toFormTempStr($edit_allow_fields, $init_fields),
+ self::toFormTempStr($add_allow_fields, $init_fields),
+ self::toFormTempStr($where_content_arr, $init_fields, 3),
+ in_array('4', $checked_btn_arr) ? self::getExportExcelTempStr($fields, $table, $init_fields, 'export_con') : null,
+ in_array('6', $checked_btn_arr) ? self::getImportExcelTempStr($fields, $table, $init_fields, 'imp_con') : null,
+ in_array('6', $checked_btn_arr) ? self::getExportExcelTempStr($fields, $table, $init_fields, 'temp') : null,
+ self::toFormTempStr($edit_require_fields, $init_fields, 2),
+ self::toFormTempStr($add_require_fields, $init_fields, 2),
+
+ ],
+ $temp_res_str
+ );
+ //渲染(写入)文件
+ $gen_con = fopen($gen_path, 'w');
+ fwrite($gen_con, $temp_str);
+ return true;
+ }
+
+ //生成前台控制器
+ private function createApiController($fields, $table)
+ {
+ $root = base_path(); //根地址
+ $module_name = $table['moduleName']; //模块名
+ $class_name = $table['className']; //实体类名
+ $function_name = $table['functionName']; //功能名
+ $business_name = $table['businessName']; //业务名
+
+ //控制器模块构建
+ $con_path = str_replace('/', DIRECTORY_SEPARATOR, "{$root}api/controller/{$module_name}");
+ if (true !== $res = $this->mkdir($con_path)) return $res;
+ //模板路径构建
+ if ($table['tplCategory'] == "crud") {
+ $temp_path = str_replace('/', DIRECTORY_SEPARATOR, "{$root}resources/view/api/controller.tpl");
+ } else if ($table['tplCategory'] == "web_static") {
+ $temp_path = str_replace('/', DIRECTORY_SEPARATOR, "{$root}resources/view/business/webApiController.tpl");
+ }
+
+ //控制器生成后的路径构建
+ $gen_path = str_replace('/', DIRECTORY_SEPARATOR, "{$root}api/controller/{$module_name}/{$class_name}.php");
+
+ $query_fields = []; //查询列表可显字段
+ $order_field = "'{$table['options']->SortField}'"; //排序字段
+ $order_mode = "'{$table['options']->SortType}'"; //排序方式
+ $edit_allow_fields = ["{$business_name}_update_user_guid"]; //编辑允许字段
+ $add_allow_fields = ["{$business_name}_guid", "{$business_name}_create_user_guid", "{$business_name}_update_user_guid"]; //新增允许字段
+ $where_content_arr = []; //列表查询条件
+ $init_fields = []; //初始化(业务字段)
+ $add_require_fields = []; //新增必填字段
+ $edit_require_fields = []; //编辑必填字段
+ foreach ($fields as $key => $val) {
+ $column_name = $val['columnName'];
+ if (!$val['isInit']) $init_fields[$column_name] = $val['columnComment'];
+ if ($val['isList']) $query_fields[] = $column_name;
+ if ($val['isEdit'] && !in_array($column_name, $edit_allow_fields)) {
+ $edit_allow_fields[] = $column_name;
+ if ($val['isRequired']) $edit_require_fields[] = $column_name;
+ }
+ if ($val['isInsert'] && !in_array($column_name, $add_allow_fields)) {
+ $add_allow_fields[] = $column_name;
+ if ($val['isRequired']) $add_require_fields[] = $column_name;
+ }
+ if ($val['isQuery']) $where_content_arr[] = [$column_name, $val['queryType']];
+ }
+
+ //打开模板文件资源(只读)
+ $temp_res_r = fopen($temp_path, "r");
+ //获取模板内容
+ $temp_res_str = fread($temp_res_r, filesize($temp_path));
+ //模板内容替换
+ $temp_str = str_replace(
+ [
+ '{$moduleName}',
+ '{$className}',
+ '{$functionName}',
+ '{$businessName}',
+ '{$queryFields}',
+ '{$orderField}',
+ '{$orderMode}',
+ '{$editAllowFields}',
+ '{$addAllowFields}',
+ '{$whereContent}',
+ '{$imgUploadContent}',
+ '{$fileUpload}',
+ '{$imgUrlPrefixPadding}',
+ '{$editRequireFields}',
+ '{$addRequireFields}',
+ ],
+ [
+ $module_name,
+ $class_name,
+ $function_name,
+ $business_name,
+ self::toFormTempStr($query_fields, $init_fields),
+ $order_field ?? "'{$business_name}_update_time'",
+ $order_mode ?? "'desc'",
+ self::toFormTempStr($edit_allow_fields, $init_fields),
+ self::toFormTempStr($add_allow_fields, $init_fields),
+ self::toFormTempStr($where_content_arr, $init_fields, 3),
+ self::toFormTempStr($edit_require_fields, $init_fields, 2),
+ self::toFormTempStr($add_require_fields, $init_fields, 2),
+ ],
+ $temp_res_str
+ );
+ //渲染(写入)文件
+ $gen_con = fopen($gen_path, 'w');
+ fwrite($gen_con, $temp_str);
+ return true;
+ }
+
+ // Js版本 1.20
+
+ //生成JsVue页面
+ private function createJsVue($fields, $table)
+ {
+ $root = base_path(); //根地址
+
+ // return $table;
+ $vue_path = $table['genPath'] . DIRECTORY_SEPARATOR . "src" . DIRECTORY_SEPARATOR . "pages\index" . DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR . $table['businessName'];
+ $vue_compoment_path = $vue_path . DIRECTORY_SEPARATOR . "components";
+ if (true !== $res = $this->mkdir($vue_path)) {
+ return $res;
+ }
+ if (true !== $res = $this->mkdir($vue_compoment_path)) {
+ return $res;
+ }
+
+ // return $fields;
+ $columnsArr = "
+ {
+ fixed: true,
+ type: 'selection'
+ },";
+
+ $btn = '';
+ $btnFunName = '';
+ $btnFun = '';
+ $imgTemplate = '';
+ $search = '';
+ $dictFun = '';
+ $dict = '';
+ $dictScope = '';
+ $query = '';
+
+ $btn_arr = $table['options']->CheckedBtn; // 勾选按钮
+ // return $btn_arr;
+ foreach ($btn_arr as $key => $item) {
+ switch ($item) {
+ case 6:
+ $btn .= '
+
+
+ 导入
+
+
+
+ 下载导入模板
+ ';
+
+ $btnFunName .= ', downloadTemplate , importExcel ';
+
+ $btnFun .= '
+ // 导入方法
+ let loadingImoprt = null;
+ const uploadLoading = () => {
+ loadingImoprt = ElLoading.service({
+ lock: true,
+ text: "正在导入中...",
+ background: "rgba(255, 255, 255, 0.7)",
+ });
+ };
+ const closeUploadLoading = () => loadingImoprt.close();
+ const handleExcelSuccess = (value) => {
+ if (value.code == 0) {
+ ElMessageBox.alert(value.msg, "导入信息", {
+ dangerouslyUseHTMLString: true,
+ confirmButtonText: "确定",
+ });
+ } else {
+ ElMessage.error(value.msg);
+ }
+ closeUploadLoading();
+ tableRef.value.reload();
+ };
+
+ ';
+ break;
+ case 4:
+ $btn .= '
+
+ 导出
+ ';
+
+ $btnFunName .= ' , exportExcel';
+ break;
+ }
+ }
+
+ foreach ($fields as $value) {
+ if ($value['isList'] == true) {
+ if ($value['isInit'] == false) {
+ $columnsArr .= '
+ ' . '{
+ prop: "' . $value['columnName'] . '",
+ ' .
+ "label: '" . $value['columnComment'] . "',
+ " .
+ "width: '150'
+ },";
+ }
+ }
+
+ if ($value['isQuery'] == true) {
+ $query .= $value['columnName'] . ': "",
+ ';
+ }
+
+ if ($value['isQuery'] == true) {
+ if ($value['htmlType'] == "input") {
+ $search .= '
+ ' . '
+ ' . "
+ ";
+ }
+ if ($value['htmlType'] == "select") {
+ $search .= '
+ ' . '
+ ' . '
+
+
+ ';
+ }
+ }
+
+ if ($value['dictType'] != null) {
+ $dictFun = ", getDictionary";
+
+ $dictScope .= "
+
+
+
+ ";
+
+ $dict .= "
+ // 字典获取
+ const " . $value['dictType'] . " = ref([]);" . '
+ ' . "async function get_" . $value['dictType'] . "() {
+ await getDictionary({ dictionary_value: '" . $value['dictType'] . "'}).then((res) => {" . '
+ ' . $value['dictType'] . ".value = res
+ })
+ }" . '
+ ' .
+ "get_" . $value['dictType'] . "()";
+ }
+
+ if ($value['htmlType'] == 'imageUpload') {
+ $imgTemplate .= "
+ ' . '
+ ' . '
+ 暂无图片
+ ';
+ }
+ }
+ $columnsArr .= "
+ {
+ label: '操作',
+ prop: 'chaoz',
+ width: '250'
+ }";
+
+ // return $imgTemplate;
+
+ // //模型的示例代码
+ $temp_path = str_replace('/', DIRECTORY_SEPARATOR, $root . 'resources/view/jsVue/index.tpl');
+ $gen_path = $vue_path . DIRECTORY_SEPARATOR . "index.vue"; //生成Js的index.vue的地址
+ $tem_f = fopen($temp_path, "r");
+ $temp_str = fread($tem_f, filesize($temp_path));
+ $temp_str = str_replace(
+ [
+ '${functionName}',
+ '${moduleName}',
+ '${search}',
+ '${className}',
+ '${btn}',
+ '${imgTemplate}',
+ '${dictFun}',
+ '${cloumns}',
+ '${businessName}',
+ '${dict}',
+ '${btnFunName}',
+ '${btnFun}',
+ '${query}',
+ '${dictScope}'
+ ],
+ [
+ $table['functionName'],
+ $table['moduleName'],
+ $search,
+ $table['className'],
+ $btn,
+ $imgTemplate,
+ $dictFun,
+ $columnsArr,
+ $table['businessName'],
+ $dict,
+ $btnFunName,
+ $btnFun,
+ $query,
+ $dictScope
+ ],
+ $temp_str
+ );
+ // return $temp_str;
+ $gen_model = fopen($gen_path, 'w');
+ fwrite($gen_model, $temp_str);
+ return true;
+ }
+
+ //生成JsWebStaticIndex页面
+ private function createJsWebStaticIndex($fields, $table)
+ {
+ $root = base_path(); //根地址
+ $vue_path = $table['genPath'] . DIRECTORY_SEPARATOR . "src" . DIRECTORY_SEPARATOR . "pages\index" . DIRECTORY_SEPARATOR . $table['businessName'];
+ if (true !== $res = $this->mkdir($vue_path)) {
+ return $res;
+ }
+
+ $rulesArr = "";
+ $col = "";
+ $mapParm = '';
+ $mapFun = '';
+ $mapOpen = '';
+ $mapOpenFun = 'getContent()';
+
+ $col = $this->getCol($fields);
+
+ foreach ($fields as $value) {
+ if ($value['isInit'] == false) {
+ if ($value['isRequired'] == true) {
+ if ($value['isInsert'] == true) {
+ $rulesArr .=
+ $value['columnName'] . ": [
+ {
+ required: true,
+ message: '" . $value['columnComment'] . "不能为空'
+ }
+ ],
+ ";
+ }
+ }
+ }
+
+ if ($value['htmlType'] == "map") {
+ $mapParm = "const locationList = ref({})";
+
+ $mapFun = "
+ // 地址处理
+ if (!locationList.value.address) {
+ ElMessage.error('请选择{$value['columnName']}');
+ return
+ }
+ let locationData = locationList.value
+ formData.{$value['columnName']} = locationData.address
+ formData.longitude = locationData.longitude
+ formData.latitude = locationData.latitude
+ ";
+
+ $mapOpen = "
+ if (formData.value.longitude) {
+ locationList.value.address = formData.value.{$value['columnName']};
+ locationList.value.longitude = formData.value.longitude;
+ locationList.value.latitude = formData.value.latitude;
+ }
+ ";
+
+ $mapOpenFun = "
+ let mapCb = function(){
+ getContent()
+ }";
+ }
+ }
+
+ $temp_path = str_replace('/', DIRECTORY_SEPARATOR, $root . 'resources/view/business/webIndex.tpl');
+ $gen_path = $vue_path . DIRECTORY_SEPARATOR . "index.vue"; //生成Js的index.vue的地址
+ $tem_f = fopen($temp_path, "r");
+ $temp_str = fread($tem_f, filesize($temp_path));
+ $temp_str = str_replace(
+ [
+ '${functionName}',
+ '${className}',
+ '${rules}',
+ '${businessName}',
+ '${col}',
+ '${mapParm}',
+ '${mapFun}',
+ '${mapOpen}',
+ '${mapOpenFun}',
+ ],
+ [
+ $table['functionName'],
+ $table['className'],
+ $rulesArr,
+ $table['businessName'],
+ $col,
+ $mapParm,
+ $mapFun,
+ $mapOpen,
+ $mapOpenFun
+ ],
+ $temp_str
+ );
+ $gen_model = fopen($gen_path, 'w');
+ fwrite($gen_model, $temp_str);
+ return true;
+ }
+
+ //生成JsAdd添加页面
+ private function createJsAdd($fields, $table)
+ {
+ $root = base_path(); //根地址
+ $vue_compoment_path = $table['genPath'] . DIRECTORY_SEPARATOR . "src" . DIRECTORY_SEPARATOR . "pages\index" . DIRECTORY_SEPARATOR . $table['businessName'] . DIRECTORY_SEPARATOR . "components";
+
+ $rulesArr = "";
+ $col = "";
+ $dictFunName = '';
+ $dictFunName2 = '';
+ $dictFun = '';
+ $mapParm = '';
+ $mapFun = '';
+
+ $col = $this->getCol($fields);
+
+ foreach ($fields as $value) {
+ if ($value['isInit'] == false) {
+ if ($value['isRequired'] == true) {
+ if ($value['isInsert'] == true) {
+ $rulesArr .=
+ $value['columnName'] . ": [
+ {
+ required: true,
+ message: '" . $value['columnComment'] . "不能为空'
+ }
+ ],
+ ";
+ }
+ }
+ }
+
+ if ($value['dictType'] != null) {
+ $dictFunName = ", getDictionary";
+ $dictFunName2 .= "get_" . $value['dictType'] . "()" . '
+ ';
+
+ $dictFun .= "
+ // 字典获取
+ const " . $value['dictType'] . " = ref([]);" . '
+ ' . "async function get_" . $value['dictType'] . "() {
+ await getDictionary({ dictionary_value: '" . $value['dictType'] . "'}).then((res) => {" . '
+ ' . $value['dictType'] . ".value = res
+ })
+ }";
+ }
+
+ if ($value['htmlType'] == "map") {
+ $mapParm = "const locationList = ref({})";
+
+ $mapFun = "
+ // 地址处理
+ if (!locationList.value.address) {
+ ElMessage.error('请选择{$value['columnName']}');
+ return
+ }
+ let locationData = locationList.value
+ formData.{$value['columnName']} = locationData.address
+ formData.longitude = locationData.longitude
+ formData.latitude = locationData.latitude
+ ";
+ }
+ }
+
+ $temp_path = str_replace('/', DIRECTORY_SEPARATOR, $root . 'resources/view/jsVue/add.tpl');
+ $gen_path = $vue_compoment_path . DIRECTORY_SEPARATOR . "Add" . $table['className'] . 'Dialog' . ".vue"; //生成Js的index.vue的地址
+ $tem_f = fopen($temp_path, "r");
+ $temp_str = fread($tem_f, filesize($temp_path));
+ $temp_str = str_replace(
+ [
+ '${functionName}',
+ '${className}',
+ '${rules}',
+ '${businessName}',
+ '${dictFunName}',
+ '${dictFunName2}',
+ '${dictFun}',
+ '${col}',
+ '${mapParm}',
+ '${mapFun}',
+ ],
+ [
+ $table['functionName'],
+ $table['className'],
+ $rulesArr,
+ $table['businessName'],
+ $dictFunName,
+ $dictFunName2,
+ $dictFun,
+ $col,
+ $mapParm,
+ $mapFun,
+ ],
+ $temp_str
+ );
+ $gen_model = fopen($gen_path, 'w');
+ fwrite($gen_model, $temp_str);
+ return true;
+ }
+
+ //生成JsEdit编辑页面
+ private function createJsEdit($fields, $table)
+ {
+ $root = base_path(); //根地址
+
+ $vue_compoment_path = $table['genPath'] . DIRECTORY_SEPARATOR . "src" . DIRECTORY_SEPARATOR . "pages\index" . DIRECTORY_SEPARATOR . $table['businessName'] . DIRECTORY_SEPARATOR . "components";
+
+ // return $fields;
+ $rulesArr = "";
+ $dictFunName = '';
+ $dictFunName2 = '';
+ $dictFun = '';
+ $col = '';
+ $mapParm = '';
+ $mapFun = '';
+ $mapOpen = '';
+
+ $col = $this->getCol($fields);
+
+ foreach ($fields as $value) {
+ if ($value['isInit'] == false) {
+ if ($value['isRequired'] == true) {
+ if ($value['isEdit'] == true) {
+ $rulesArr .=
+ $value['columnName'] . ": [
+ {
+ required: true,
+ message: '" . $value['columnComment'] . "不能为空'
+ }
+ ],
+ ";
+ }
+ }
+ }
+
+ if ($value['dictType'] != null) {
+ $dictFunName = ", getDictionary";
+ $dictFunName2 .= "get_" . $value['dictType'] . "()" . '
+ ';
+
+ $dictFun .= "
+ // 字典获取
+ const " . $value['dictType'] . " = ref([]);" . '
+ ' . "async function get_" . $value['dictType'] . "() {
+ await getDictionary({ dictionary_value: '" . $value['dictType'] . "'}).then((res) => {" . '
+ ' . $value['dictType'] . ".value = res
+ })
+ }";
+ }
+
+ if ($value['htmlType'] == "map") {
+ $mapParm = "const locationList = ref({})";
+
+ $mapFun = "
+ // 地址处理
+ let locationData = locationList.value
+ formData.value.{$value['columnName']} = locationData.address
+ formData.value.longitude = locationData.longitude
+ formData.value.latitude = locationData.latitude
+ ";
+
+ $mapOpen = "
+ if (formData.value.longitude) {
+ locationList.value.address = formData.value.{$value['columnName']};
+ locationList.value.longitude = formData.value.longitude;
+ locationList.value.latitude = formData.value.latitude;
+ }
+ ";
+ }
+ }
+ // return $rulesArr;
+
+ $temp_path = str_replace('/', DIRECTORY_SEPARATOR, $root . 'resources/view/jsVue/edit.tpl');
+ $gen_path = $vue_compoment_path . DIRECTORY_SEPARATOR . "Edit" . $table['className'] . 'Dialog' . ".vue"; //生成Js的index.vue的地址
+ $tem_f = fopen($temp_path, "r");
+ $temp_str = fread($tem_f, filesize($temp_path));
+ $temp_str = str_replace(
+ [
+ '${functionName}',
+ '${className}',
+ '${rules}',
+ '${businessName}',
+ '${dictFunName}',
+ '${dictFunName2}',
+ '${dictFun}',
+ '${col}',
+ '${mapParm}',
+ '${mapFun}',
+ '${mapOpen}',
+ ],
+ [
+ $table['functionName'],
+ $table['className'],
+ $rulesArr,
+ $table['businessName'],
+ $dictFunName,
+ $dictFunName2,
+ $dictFun,
+ $col,
+ $mapParm,
+ $mapFun,
+ $mapOpen,
+ ],
+ $temp_str
+ );
+ // return $temp_str;
+ $gen_model = fopen($gen_path, 'w');
+ fwrite($gen_model, $temp_str);
+ return true;
+ }
+
+ //生成JsDetail详情页面
+ private function createJsDetail($fields, $table)
+ {
+ $root = base_path(); //根地址
+
+ $vue_compoment_path = $table['genPath'] . DIRECTORY_SEPARATOR . "src" . DIRECTORY_SEPARATOR . "pages\index" . DIRECTORY_SEPARATOR . $table['businessName'] . DIRECTORY_SEPARATOR . "components";
+
+ $dictFunName = '';
+ $dictFunName2 = '';
+ $dictFun = '';
+ $col = '';
+ $mapParm = '';
+ $mapOpen = '';
+
+ $col = $this->getCol($fields);
+
+ foreach ($fields as $key => $value) {
+ if ($value['dictType'] != null) {
+ $dictFunName = "import { getDictionary } from '~/service/{$table['businessName']}';";
+ $dictFunName2 .= "get_" . $value['dictType'] . "()" . '
+ ';
+
+ $dictFun .= "
+ // 字典获取
+ const " . $value['dictType'] . " = ref([]);" . '
+ ' . "async function get_" . $value['dictType'] . "() {
+ await getDictionary({ dictionary_value: '" . $value['dictType'] . "'}).then((res) => {" . '
+ ' . $value['dictType'] . ".value = res
+ })
+ }";
+ }
+ if ($value['htmlType'] == "map") {
+ $mapParm = "const locationList = ref({})";
+
+ $mapOpen = "
+ if (formData.value.longitude) {
+ locationList.value.address = formData.value.{$value['columnName']};
+ locationList.value.longitude = formData.value.longitude;
+ locationList.value.latitude = formData.value.latitude;
+ }
+ ";
+ }
+ }
+
+ $temp_path = str_replace('/', DIRECTORY_SEPARATOR, $root . 'resources/view/jsVue/detail.tpl');
+ $gen_path = $vue_compoment_path . DIRECTORY_SEPARATOR . "Detail" . $table['className'] . 'Dialog' . ".vue"; //生成的index.vue的地址
+ $tem_f = fopen($temp_path, "r");
+ $temp_str = fread($tem_f, filesize($temp_path));
+ $temp_str = str_replace(
+ [
+ '${functionName}',
+ '${className}',
+ '${businessName}',
+ '${dictFunName}',
+ '${dictFunName2}',
+ '${dictFun}',
+ '${col}',
+ '${mapParm}',
+ '${mapOpen}',
+ ],
+ [
+ $table['functionName'],
+ $table['className'],
+ $table['businessName'],
+ $dictFunName,
+ $dictFunName2,
+ $dictFun,
+ $col,
+ $mapParm,
+ $mapOpen,
+ ],
+ $temp_str
+ );
+ // return $temp_str;
+ $gen_model = fopen($gen_path, 'w');
+ fwrite($gen_model, $temp_str);
+ return true;
+ }
+
+ //生成Api页面
+ private function createJsApi($fields, $table)
+ {
+ $root = base_path(); //根地址
+
+ $vue_api_path = $table['genPath'] . DIRECTORY_SEPARATOR . "src" . DIRECTORY_SEPARATOR . "service";
+
+ // return $fields;
+
+ $urlStr = "";
+ $listArr = "";
+ $imageFun = "";
+ $fileFun = "";
+ $dictFun = "";
+
+ $excelFun = "";
+
+ $btn_arr = $table['options']->CheckedBtn; // 勾选按钮
+ // return $btn_arr;
+
+ foreach ($btn_arr as $key => $item) {
+ switch ($item) {
+ case 6:
+ $excelFun .= '
+ /**
+ * 下载' . $table['tableComment'] . '模板
+ * @param {Object} data
+ * @return {Promise} api
+ */
+ export function downloadTemplate(data) {
+ downloadFile(createApiUrl(' . "'" . $table['moduleName'] . "." . $table['className'] . "/downloadTemplate'), data);
+ }" . '
+
+ /**
+ * 导入' . $table['tableComment'] . '
+ * @param {Object} data
+ * @return {Promise} api
+ */
+ export const importExcel = createApiUrl(' . "'" . $table['moduleName'] . "." . $table['className'] . "/importExcel');
+ ";
+ break;
+ case 4:
+ $excelFun .= '
+ /**
+ * 导出' . $table['tableComment'] . '
+ * @param {Object} data
+ * @return {Promise} api
+ */
+ export function exportExcel(data) {
+ downloadFile(createApiUrl(' . "'" . $table['moduleName'] . "." . $table['className'] . "/exportExcel'), data);
+ }
+ ";
+ break;
+ }
+ }
+
+ foreach ($fields as $value) {
+ if ($value['isList'] == true) {
+ if ($value['isInit'] == false) {
+ $listArr .=
+ $value['columnName'] . ":" . self::FieldsJsType($value['columnType']) . ",
+ ";
+
+ if ($value['htmlType'] == 'imageUpload' || $value['htmlType'] == 'fileUpload') {
+ $urlStr = 'url: "",';
+ }
+ }
+
+ if ($value['dictType'] != null) {
+ $dictFun = "
+ /**
+ * 获取字典值
+ * @param {Object} data
+ * @return {Promise} api
+ */
+ export function getDictionary(data) {
+ return api.post('Dictionary.Dictionary/getDictionary', data, {
+ });
+ }
+ ";
+ }
+ // if ($value['htmlType'] == 'imageUpload') {
+ // $imageFun = "
+ // /**
+ // * 上传图片
+ // */
+ // export const upload" . $table['className'] . "Img = createApiUrl('" . $table['className'] . "." . $table['className'] . "/upload" . $table['className'] . "Img');
+ // ";
+ // }
+ // if ($value['htmlType'] == 'fileUpload') {
+ // $fileFun = "
+ // /**
+ // * 上传文件
+ // */
+ // export const upload" . $table['className'] . "File = createApiUrl('" . $table['className'] . "." . $table['className'] . "/upload" . $table['className'] . "File');
+ // ";
+ // }
+ }
+ }
+
+ if ($table['tplCategory'] == "web_static") $temp_path = str_replace('/', DIRECTORY_SEPARATOR, $root . 'resources/view/business/webApi.tpl');
+ else if ($table['tplCategory'] == "crud") $temp_path = str_replace('/', DIRECTORY_SEPARATOR, $root . 'resources/view/jsVue/api.tpl');
+ $gen_path = $vue_api_path . DIRECTORY_SEPARATOR . $table['businessName'] . ".js"; //生成的api的地址
+ $tem_f = fopen($temp_path, "r");
+ $temp_str = fread($tem_f, filesize($temp_path));
+ $temp_str = str_replace(
+ [
+ '${moduleName}',
+ '${functionName}',
+ '${className}',
+ '${businessName}',
+ '${list}',
+ '${imageFun}',
+ '${fileFun}',
+ '${dictFun}',
+ '${excelFun}',
+ '${urlStr}',
+ ],
+ [
+ $table['moduleName'],
+ $table['functionName'],
+ $table['className'],
+ $table['businessName'],
+ $listArr,
+ $imageFun,
+ $fileFun,
+ $dictFun,
+ $excelFun,
+ $urlStr,
+ ],
+ $temp_str
+ );
+ // return $temp_str;
+ $gen_model = fopen($gen_path, 'w');
+ fwrite($gen_model, $temp_str);
+ return true;
+ }
+
+
+
+ //创建文件夹
+ private function mkdir($path)
+ {
+ if (!is_dir($path)) {
+ if (false === @mkdir($path, 0777, true) && !is_dir($path)) {
+ throwErrorMsg('创建文件夹失败:' . $path);
+ }
+ }
+ return true;
+ }
+
+ //判断字段类型
+ private function FieldsType($type)
+ {
+ // return $type;
+ switch ($type) {
+ case strstr($type, 'int'):
+ return "int";
+ break;
+ case strstr($type, 'double'):
+ return "double";
+ break;
+ case strstr($type, 'varchar'):
+ return "string";
+ break;
+ case strstr($type, 'text'):
+ return "string";
+ break;
+ case strstr($type, 'datetime'):
+ return "datetime";
+ break;
+
+ default:
+ $type;
+ }
+ }
+
+ //判断Js字段类型
+ private function FieldsJsType($type)
+ {
+ switch ($type) {
+ case strstr($type, 'int'):
+ return "number";
+ break;
+ case strstr($type, 'decimal'):
+ return "number";
+ break;
+ case strstr($type, 'datetime'):
+ return "Date";
+ break;
+
+ default:
+ $type;
+ }
+ return $type;
+ }
+
+ //判断Js默认值字段类型
+ private function FieldsJsDefaultValue($type)
+ {
+ switch ($type) {
+ case strstr($type, 'string'):
+ return '""';
+ break;
+ case strstr($type, 'int'):
+ return "0";
+ break;
+ case strstr($type, 'decimal'):
+ return "0";
+ break;
+ case strstr($type, 'datetime'):
+ return 'null';
+ break;
+
+ default:
+ $type;
+ }
+ return "";
+ }
+
+ //判断HTML类型
+ private function FieldsHtmlType($name, $tableName)
+ {
+ // 图片字段
+ $imageFiledArr = [
+ $tableName . "_" . "icon",
+ $tableName . "_" . "img",
+ $tableName . "_" . "image",
+ $tableName . "_" . "url",
+ $tableName . "_" . "pic",
+ $tableName . "_" . "photo",
+ $tableName . "_" . "avatar"
+ ];
+ // 下拉框字段
+ $selectFiledArr = [
+ $tableName . "_" . "status",
+ $tableName . "_" . "type",
+ $tableName . "_" . "state",
+ $tableName . "_" . "sex",
+ $tableName . "_" . "gender"
+ ];
+ // 时间字段
+ $timeFiledArr = [
+ $tableName . "_" . "datetime",
+ $tableName . "_" . "time",
+ $tableName . "_" . "date",
+ $tableName . "_" . "timestamp"
+ ];
+
+
+ $htmlType = 'input';
+
+ if (in_array($name, $imageFiledArr)) {
+ $htmlType = "imageUpload";
+ } else if (in_array($name, $timeFiledArr)) {
+ $htmlType = "datetime";
+ } else if (strstr($name, 'content')) {
+ $htmlType = "editor";
+ } else if (in_array($name, $selectFiledArr)) {
+ $htmlType = "select";
+ }
+
+ return $htmlType;
+ }
+
+ public function intToBool($value)
+ {
+ return (bool)$value;
+ }
+
+
+ public function getCol($fields)
+ {
+ $col = '';
+
+ foreach ($fields as $value) {
+ if ($value['isList'] == true) {
+ if ($value['isInit'] == false) {
+ if ($value['htmlType'] == "input") {
+ $col .= '
+
+ ' . '
+ ' . "
+
+ ";
+ }
+ if ($value['htmlType'] == "textarea") {
+ $col .= '
+
+ ' . '
+ ' . "
+
+ ";
+ }
+ if ($value['htmlType'] == "select") {
+ $col .= '
+
+ ' . '
+ ' . '
+
+
+
+ ';
+ }
+ if ($value['htmlType'] == "datetime") {
+ $col .= '
+
+
+
+
+ ';
+ }
+ if ($value['htmlType'] == "imageUpload") {
+
+ $col .= "
+
+
+
+
+
+ ";
+ }
+ if ($value['htmlType'] == "fileUpload") {
+
+ $col .= "
+
+
+
+
+
+ ";
+ }
+ if ($value['htmlType'] == "editor") {
+ $col .= '
+
+ ' . '
+ ' . "
+
+ ";
+ }
+ if ($value['htmlType'] == "inputNumber") {
+ $col .= '
+
+ ' . '
+ ' . "
+
+ ";
+ }
+ if ($value['htmlType'] == "map") {
+ $col .= "
+
+
+
+
+
+ ";
+ }
+ }
+ }
+ }
+
+ return $col;
+ }
+
+
+ /**
+ * 数组转换为模板字符串
+ * @param array $arr 数组
+ * @param array $service_fields 业务字段
+ * @param int $type 模板字符类型 1:一维数组格式 2:验证器格式 3:列表条件查询 4:关联数组格式
+ */
+ private static function toFormTempStr(array $arr, array $service_fields, int $type = 1)
+ {
+ $str = "";
+ switch ($type) {
+ case 1:
+ $str = str_replace('##', "'", "[\n##" . implode("##,\n##", $arr) . "##\n]");
+ break;
+ case 2:
+ foreach ($service_fields as $key => &$val) {
+ foreach ($arr as $key1 => &$val1) {
+ if ($key == $val1) {
+ $val1 .= "|{$val}";
+ }
+ }
+ }
+ $str = str_replace('##', "'", "[\n##" . implode("##=>'require',\n##", $arr) . "##=>##require##\n]");
+ break;
+ case 3:
+ $str .= "\$con = Tool::getOptionalQuery(";
+ foreach ($arr as $key => $val) {
+ if ($val[1]) {
+ $str .= self::getListConditionalQuery($val[0], $val[1]);
+ }
+ };
+ $str .= ");";
+ break;
+ case 4:
+ foreach ($arr as $key => $val) {
+ $str .= "'{$key}' => '{$val}',\n";
+ }
+ $str = "[\n$str],";
+ break;
+ default:
+ break;
+ }
+ return $str;
+ }
+
+ /**
+ * 获取列表条件表达式查询对应模板字符串
+ * @param string $column_name 字段名
+ * @param string $ope 运算符
+ */
+ private static function getListConditionalQuery(string $column_name, string $ope): string
+ {
+ return [
+ 'LIKE' => self::geJsimpleExpTempStr($column_name, 'LIKE'),
+ 'BETWEEN' => self::geJsimpleExpTempStr($column_name, 'BETWEEN'),
+ 'EQ' => self::geJsimpleExpTempStr($column_name, '='),
+ 'LT' => self::geJsimpleExpTempStr($column_name, '<'),
+ 'LTE' => self::geJsimpleExpTempStr($column_name, '<='),
+ 'GTE' => self::geJsimpleExpTempStr($column_name, '>='),
+ 'GT' => self::geJsimpleExpTempStr($column_name, '>'),
+ 'NE' => self::geJsimpleExpTempStr($column_name, '!='),
+ ][$ope];
+ }
+
+ /**
+ * 获取简易表达式对应模板字符串
+ * @param string $column_name 字段名
+ * @param string $ope 运算符
+ */
+ private static function geJsimpleExpTempStr(string $column_name, string $ope): string
+ {
+ return "['{$column_name}','{$ope}'],";
+ }
+
+ /**
+ * 获取导出模板字符串
+ * @param array $fields 字段信息
+ * @param array $table 表信息
+ * @param array $init_fields 业务字段
+ * @param string $type 导出类型: export_con:导出-控制器 | export_mod:导出-模型层 | temp : 下载模板
+ */
+ private static function getExportExcelTempStr(array $fields, array $table, array $init_fields, string $type): string
+ {
+ $module_name = $table['moduleName']; //模块名 例:News
+ $function_name = $table['functionName']; //功能名 例:新闻
+ $business_name = $table['businessName']; //业务名 例:news
+ $class_name = $table['className']; //类名 例:News
+ $init_fields_arr = [];
+ $init_fields_text = [];
+ $data_str = '';
+
+ $img_field = false;
+ foreach ($fields as $key => $val) {
+ if ($val['htmlType'] == 'imageUpload') $img_field = $val['columnName'];
+ }
+
+ foreach ($init_fields as $key => $val) {
+ $init_fields_arr[] = $key;
+ $init_fields_text[] = $val;
+
+ if ($key == $img_field) {
+ $data_str .= "Excel::ExportImgFiled(\$val['$img_field']),\n";
+ } else {
+ $data_str .= "\$val['{$key}'],\n";
+ }
+ }
+ $data_str = "[\n{$data_str}]"; //导出数据模板字段
+ $init_fields_arr_str = self::toFormTempStr($init_fields_arr, $init_fields); //字段名(模板字符串)
+ $init_fields_text_str = self::toFormTempStr($init_fields_text, $init_fields); //字段注释(模板字符串)
+ switch ($type) {
+ case 'export_con':
+ $order_field = "'{$table['options']->SortField}'"; //排序字段
+ $order_mode = "'{$table['options']->SortType}'"; //排序方式
+ $order_field ?? $order_field = "'{$business_name}_update_time'";
+ $order_mode ?? $order_mode = "'desc'";
+ return "/**\n* 导出Excel\n*/
+ public function exportExcel(Request \$request)
+ {
+ \$params = \$request->param();
+ \$select = Model{$class_name}::field({$init_fields_arr_str})
+ ->order({$order_field}, $order_mode )
+ ->select();
+ return Model{$class_name}::exportExcel(\$select);
+ }";
+ case 'export_mod':
+ return "/**\n* 导出Excel\n*/
+ public static function exportExcel(\$select)
+ {
+ \$data = [{$init_fields_text_str}];
+ foreach (\$select as \$key => \$val) {
+ \$data[] = {$data_str};
+ }
+ \$excel = (new Excel())->exporTsheet(\$data);
+ \$excel->save('{$function_name}.xlsx');
+ }";
+ case 'temp':
+ return "/**\n* 下载导入模板\n*/
+ public function downloadTemplate(Request \$request)
+ {
+ \$params = \$request->param();
+ \$data = array_values(Model{$class_name}::EXCELFIELD);
+ \$excel = (new Excel())->exporTsheet(\$data);
+ \$excel->save('{$function_name}导入模板.xlsx');
+ }";
+ default:
+ break;
+ }
+ }
+
+ /**
+ * 获取有关文件上传的模板字符串
+ * @param array $table 表信息
+ * @param string $type 类型 img图片上传 file文件上传
+ * @param string $is_multiple 是否为多应用类型 false => 单应用 true =>多应用
+ */
+ private static function getFileUploadTempStr(array $table, string $type): string
+ {
+ $class_name = $table['className']; //类名 例:News
+
+ return [
+ 'img' =>
+ "/**\n* 上传图片\n*/
+ public function upload{$class_name}Img(Request \$request)
+ {
+ \$upload = new UploadFile('uploads', 'file');
+ \$path = \$upload->putFile('{$table['className']}Img');
+
+ return [
+ 'code' => 0,
+ 'data' => '/uploads/' . \$path,
+ 'msg' => '上传成功!'
+ ];
+ }",
+ 'file' =>
+ "/**\n* 上传文件\n*/
+ public function uploadFile(Request \$request): array
+ {
+ \$upload = new UploadFile('uploads', 'file');
+ \$path = \$upload->putFile('{$table['className']}File');
+ \$FileUrl = Env::get('自行替换.FileUrl');
+ return [
+ 'code' => 0,
+ 'data' => \$path,
+ 'url' => \$FileUrl,
+ 'msg' => '上传成功!'
+ ];
+ }"
+ ][$type];
+ }
+
+ /**
+ * 获取导入模板字符串
+ * @param array $fields 字段信息
+ * @param array $table 表信息
+ * @param array $init_fields 业务字段
+ * @param string $type 导出类型: imp_con:导入-控制器 | imp_mod:导入-模型层 | imp_init : 导入初始化 | imp_fie : 导入/下载模板表头
+ */
+ private static function getImportExcelTempStr(array $fields, array $table, array $init_fields, string $type): string
+ {
+ $module_name = $table['moduleName']; //模块名 例:News
+ $function_name = $table['functionName']; //功能名 例:新闻
+ $business_name = $table['businessName']; //业务名 例:news
+ $class_name = $table['className']; //类名 例:News
+
+ $fields_val_allocation_str = ""; //字段值分配
+ $add_fields_str = ""; //新增允许字段模板字符串
+ foreach ($init_fields as $field => $name) {
+ $fields_val_allocation_str .= "\${$field} = \$value['{$field}'];";
+ $add_fields_str .= "'{$field}' => \${$field},\n";
+ };
+ $add_fields_str = "[\n$add_fields_str]";
+
+ $init_fields_str = self::toFormTempStr($init_fields, $init_fields, 4);
+ $init_fields_str = substr($init_fields_str, 0, strlen($init_fields_str) - 1);
+ switch ($type) {
+ case "imp_con":
+ return "/**\n* 导入excel\n*/
+ public function importExcel(Request \$request)
+ {
+ \$file = new UploadFile('uploads', 'fileExt:xlsx');
+ \$file->putFile('{$business_name}');
+
+ \$msg = Model{$class_name}::importExcel(\$file);
+ return [
+ 'code' => 0,
+ 'msg' => \$msg
+ ];
+ }";
+ case "imp_mod":
+ return "/**\n* 导入excel\n*/
+ public static function importExcel(\$file)
+ {
+ \$msg = [];
+
+ Db::startTrans();
+ try {
+ \$excel = new Excel(\$file);
+ \$data = \$excel->parseExcel(
+ Tool::getExcelRule(self::EXCELFIELD),
+ [
+ 'titleLine' => [1]
+ ]);
+ if (!\$data) throwErrorMsg('excel无数据', 1);
+ \$msg = [];
+ foreach (\$data as \$line => \$value) {
+ try {
+ \$model = self::importExcelInit(\$value);
+ \$msg[] = \"{\$line} 新增成功!
\";
+ } catch (\Throwable \$th) {
+ \$msg[] = \"{\$line} {\$th->getMessage()}
\";
+ }
+ }
+ Db::commit();
+ return implode(', ', \$msg);
+ } catch (\Throwable \$th) {
+ Db::rollback();
+ throw \$th;
+ }
+ }";
+ case "imp_init":
+ return "/**\n* 导入excel初始化\n*/
+ public static function importExcelInit(\$value)
+ {
+ {$fields_val_allocation_str}
+ return self::create({$add_fields_str});
+ }";
+ case "imp_fie":
+ return "
+ // excel导入/下载模板表头
+ public const EXCELFIELD = {$init_fields_str};
+ ";
+ };
+ }
+}
diff --git a/app/admin/controller/Login.php b/app/admin/controller/Login.php
new file mode 100644
index 0000000..63d99bd
--- /dev/null
+++ b/app/admin/controller/Login.php
@@ -0,0 +1,145 @@
+middleware[SessionInit::class] = [
+ 'only' => ['accountLogin', 'getCaptcha']
+ ];
+ }
+
+ /**
+ * 验证token
+ *
+ * @param Request $request
+ * @return void
+ */
+ public function validateToken(Request $request)
+ {
+ $token = $request->getCurrentToken();
+ return [
+ 'code' => 0,
+ 'data' => [
+ 'exp_time' => $token->token_exp_time,
+ ],
+ 'msg' => 'ok'
+ ];
+ }
+
+ /**
+ * 西北政法大学单点登陆
+ *
+ * @param Request $request
+ * @return Response
+ * @date 2023-01-04
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function casOauthLogin(Request $request): Response
+ {
+ $url = $request->param('url');
+ return LogicLogin::casOauthLogin(
+ LogicLogin::casOauthLoginHandle($url)
+ );
+ }
+
+ /**
+ * 西北政法大学单点登出
+ *
+ * @param Request $request
+ * @return Response
+ * @date 2023-01-04
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function casOauthLogout(Request $request): Response
+ {
+ $url = $request->param('url');
+ return LogicLogin::casOauthLogout($url);
+ }
+
+ /**
+ * 用户账号登录
+ *
+ * @param Request $request
+ * @date 2022-03-05
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function accountLogin(Request $request): array
+ {
+ $param = Validate::param([
+ 'account|账号' => 'require',
+ 'password|密码' => 'require',
+ // 'captcha|验证码' => $request->isProd() ? 'require|captcha' : false
+ ]);
+ $token = LogicLogin::accountLogin(
+ $param['account'],
+ $param['password']
+ );
+
+ return [
+ 'code' => 0,
+ 'data' => [
+ 'token' => $token->token_content,
+ 'exp_time' => $token->token_exp_time,
+ ],
+ 'msg' => 'ok'
+ ];
+ }
+
+ /**
+ * 生成验证码
+ *
+ * @date 2022-03-05
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function getCaptcha()
+ {
+ return Captcha::create();
+ }
+
+ /**
+ * 用户登出
+ *
+ * @param Request $request
+ * @date 2022-03-09
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function userLogout(Request $request): array
+ {
+ $token = $request->getCurrentToken();
+ $token->logout();
+ return [
+ 'code' => 0,
+ 'msg' => '登出成功'
+ ];
+ }
+}
diff --git a/app/admin/controller/Menu/Menu.php b/app/admin/controller/Menu/Menu.php
new file mode 100644
index 0000000..00ead1e
--- /dev/null
+++ b/app/admin/controller/Menu/Menu.php
@@ -0,0 +1,368 @@
+ 'desc',
+ ])->field([
+ 'menu_guid',
+ 'menu_parent_guid',
+ 'menu_name',
+ 'menu_url',
+ ])->select()->toArray();
+
+ $menuApi = [];
+ foreach (MenuApi::select() as $value) {
+ if (!isset($menuApi[$value->menu_guid])) {
+ $menuApi[$value->menu_guid] = [];
+ }
+ $menuApi[$value->menu_guid][] = $value->menu_api_url;
+ }
+
+ $Traverse = new Traverse('menu_guid', 'menu_parent_guid');
+ $menuTree = $Traverse->tree($menu, '0', function ($v) use ($menuApi) {
+ return [
+ 'menu_api_url' => isset($menuApi[$v['menu_guid']]) ? $menuApi[$v['menu_guid']] : [],
+ 'menu_name' => $v['menu_name'],
+ 'menu_guid' => $v['menu_guid'],
+ 'menu_parent_guid' => $v['menu_parent_guid'],
+ 'menu_url' => $v['menu_url'],
+ ];
+ }, function ($v) {
+ $v['hasChildren'] = isset($value['children']) && count($value['children']);
+ return $v;
+ });
+
+ return [
+ 'code' => 0,
+ 'msg' => 'ok',
+ 'data' => $menuTree,
+ ];
+ }
+
+ /**
+ * 添加菜单接口
+ *
+ * @param Request $request
+ * @return array
+ * @date 2022-03-08
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function addMenuApi(Request $request)
+ {
+ Db::startTrans();
+ try {
+ $params = $request->param();
+ $this->validate($params, [
+ 'menu_guid' => 'require',
+ 'menu_api_url' => 'require'
+ ]);
+ foreach (explode(',', $params['menu_api_url']) as $menu_api_url) {
+ MenuApi::create([
+ 'menu_guid' => $params['menu_guid'],
+ 'menu_api_url' => $menu_api_url,
+ ]);
+ }
+ Db::commit();
+ return [
+ 'code' => 0,
+ 'msg' => '添加成功',
+ ];
+ } catch (\Throwable $th) {
+ Db::rollback();
+ throw $th;
+ }
+ }
+
+ /**
+ * 删除菜单接口
+ *
+ * @param Request $request
+ * @return array
+ * @date 2022-03-08
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function deleteMenuApi(Request $request)
+ {
+ $params = $request->param();
+ $this->validate($params, [
+ 'menu_api_guid' => 'require'
+ ]);
+ MenuApi::where([
+ 'menu_api_guid' => explode(',', $params['menu_api_guid'])
+ ])->select()->delete();
+ return [
+ 'code' => 0,
+ 'msg' => "删除成功"
+ ];
+ }
+
+ /**
+ * 获取菜单接口列表
+ *
+ * @param Request $request
+ * @return array
+ * @date 2022-03-07
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function getMenuApiList(Request $request)
+ {
+ $params = $request->param();
+ $this->validate($params, [
+ 'menu_guid' => 'require'
+ ]);
+ $query = ModelMenu::find($params['menu_guid'])->apis()->append([
+ 'menu_api_status_text'
+ ]);
+ $data = self::pageWrapper($query)->select();
+ $count = $query->count();
+ return [
+ 'code' => 0,
+ 'msg' => 'ok',
+ 'data' => $data,
+ 'count' => $count
+ ];
+ }
+
+ /**
+ * 获取菜单树
+ *
+ * @param Request $request
+ * @return array
+ * @date 2022-03-05
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function getMenuTree(Request $request)
+ {
+ $menu = ModelMenu::order([
+ 'menu_index',
+ 'menu_order' => 'desc',
+ ])->field([
+ 'menu_parent_guid',
+ 'menu_name',
+ 'menu_url',
+ 'menu_index',
+ 'menu_order',
+ 'menu_status',
+ 'menu_show',
+ 'menu_guid',
+ ])->select()->toArray();
+
+ array_unshift($menu, [
+ 'menu_guid' => '0',
+ 'menu_parent_guid' => '-1',
+ 'menu_name' => '系统菜单',
+ 'menu_index' => 0,
+ 'menu_order' => 0,
+ 'menu_status' => 0,
+ 'menu_show' => 0,
+ 'menu_url' => '',
+ ]);
+
+ $Traverse = new Traverse('menu_guid', 'menu_parent_guid');
+ $menuTree = $Traverse->tree($menu, '-1', function ($v) {
+ return [
+ 'label' => $v['menu_name'],
+ 'id' => $v['menu_guid'],
+ 'parent_id' => $v['menu_parent_guid'],
+ 'index' => $v['menu_index'],
+ 'order' => $v['menu_order'],
+ 'status' => $v['menu_status'],
+ 'show' => $v['menu_show'],
+ 'url' => $v['menu_url'],
+ ];
+ });
+
+ return [
+ 'code' => 0,
+ 'msg' => 'ok',
+ 'data' => $menuTree
+ ];
+ }
+
+ /**
+ * 获取角色菜单
+ *
+ * @param Request $request
+ * @return array
+ * @date 2022-03-09
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function getRoleMenu(Request $request)
+ {
+ $params = $request->param();
+ $this->validate($params, [
+ 'role_guid' => 'require'
+ ]);
+ $menus = Role::getByRoleGuid($params['role_guid'])->menus;
+ $menu_guids = [];
+ $menu_parent_guids = [];
+ foreach ($menus as $menu) {
+ if (!in_array($menu->menu_guid, $menu_guids)) {
+ $menu_guids[] = $menu->menu_guid;
+ }
+ if (!in_array($menu->menu_parent_guid, $menu_parent_guids)) {
+ $menu_parent_guids[] = $menu->menu_parent_guid;
+ }
+ }
+ // 去除父id 存在父id会全选子节点
+ $temp = [];
+ foreach ($menu_guids as $menu_guid) {
+ if (!in_array($menu_guid, $menu_parent_guids)) {
+ $temp[] = $menu_guid;
+ }
+ }
+ return [
+ 'code' => 0,
+ 'data' => $temp,
+ 'msg' => 'ok'
+ ];
+ }
+
+ /**
+ * 绑定角色菜单
+ *
+ * @param Request $request
+ * @return array
+ * @date 2022-03-09
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function bindRoleMenu(Request $request)
+ {
+ $params = $request->param();
+ $this->validate($params, [
+ 'role_guid' => 'require',
+ 'menu_guid' => 'require'
+ ]);
+ $menus = array_map(function ($v) {
+ return ModelMenu::find($v);
+ }, explode(',', $params['menu_guid']));
+ RoleMenu::rebindRoleMenu(
+ [
+ Role::getByRoleGuid($params['role_guid']),
+ ],
+ array_filter($menus)
+ );
+ return [
+ 'code' => 0,
+ 'msg' => '绑定成功'
+ ];
+ }
+
+ /**
+ * 添加菜单
+ *
+ * @param Request $request
+ * @return array
+ * @date 2022-03-05
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function addMenu(Request $request)
+ {
+ $params = $request->param();
+ $this->validate($params, [
+ 'menu_name' => 'require',
+ 'menu_index' => 'require',
+ 'menu_order' => 'require',
+ 'menu_show' => 'require',
+ 'menu_parent_guid' => 'require',
+ ]);
+ if (isset($params['menu_guid'])) {
+ unset($params['menu_guid']);
+ }
+ ModelMenu::create($params);
+ return [
+ 'code' => 0,
+ 'msg' => '添加成功'
+ ];
+ }
+
+ /**
+ * 更新菜单
+ *
+ * @param Request $request
+ * @return array
+ * @date 2022-03-05
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function updateMenu(Request $request)
+ {
+ $params = $request->param();
+ $this->validate($params, [
+ 'menu_guid' => 'require'
+ ]);
+ ModelMenu::withoutGlobalScope(['status'])->update($params, [
+ 'menu_guid' => $params['menu_guid']
+ ]);
+ return [
+ 'code' => 0,
+ 'msg' => '更新成功'
+ ];
+ }
+
+ /**
+ * 删除菜单
+ *
+ * @param Request $request
+ * @return array
+ * @date 2022-03-05
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function deleteMenu(Request $request)
+ {
+ $params = $request->param();
+ $this->validate($params, [
+ 'menu_guid' => 'require'
+ ]);
+ ModelMenu::where([
+ 'menu_guid' => explode(',', $params['menu_guid'])
+ ])->select()->delete();
+ return [
+ 'code' => 0,
+ 'msg' => '删除成功'
+ ];
+ }
+}
diff --git a/app/admin/controller/News/News.php b/app/admin/controller/News/News.php
new file mode 100644
index 0000000..ee7c4b2
--- /dev/null
+++ b/app/admin/controller/News/News.php
@@ -0,0 +1,291 @@
+param();
+ $con = [];
+
+ if (isset($params['news_title']) && $params['news_title']) {
+ $con[] = ['news_title', 'LIKE', '%' . $params['news_title'] . '%'];
+ };
+ if (isset($params['news_type']) && $params['news_type']) {
+ $con[] = ['news_type', '=', $params['news_type']];
+ };
+
+ $query = ModelNews::where($con);
+ $select = self::pageWrapper($query)
+ ->field([
+ 'news_id',
+ 'news_guid',
+ 'news_title',
+ 'news_author',
+ 'news_intro',
+ 'news_type',
+ 'news_img',
+ 'news_content',
+ 'news_num'
+ ])
+ ->order('news_update_time', 'desc')
+ ->select();
+
+ $count = $query->count();
+
+ return [
+ 'code' => 0,
+ 'data' => $select,
+ 'count' => $count,
+ 'msg' => 'ok'
+ ];
+ }
+
+ /**
+ * 编辑新闻
+ */
+ public function editNews(Request $request): array
+ {
+ $params = $request->param();
+ $this->validate($params, [
+ 'news_title|新闻标题' => 'require',
+ 'news_author|新闻作者' => 'require',
+ 'news_intro|新闻简介' => 'require',
+ 'news_type|新闻类型' => 'require',
+ 'news_img|新闻封面' => 'require',
+ ]);
+ $model = ModelNews::where('news_guid', $params['news_guid'])->find();
+ if (!$model) throwErrorMsg("该新闻不存在", 1);
+ $model->allowField([
+ 'news_update_user_guid',
+ 'news_title',
+ 'news_author',
+ 'news_intro',
+ 'news_type',
+ 'news_img',
+ 'news_content',
+
+ 'news_num'
+ ])->save($params);
+ return [
+ 'code' => 0,
+ 'msg' => '编辑成功'
+ ];
+ }
+
+ /**
+ * 添加新闻
+ */
+ public function addNews(Request $request): array
+ {
+ $params = $request->param();
+ $this->validate($params, [
+ 'news_title|新闻标题' => 'require',
+ 'news_author|新闻作者' => 'require',
+ 'news_intro|新闻简介' => 'require',
+ 'news_type|新闻类型' => 'require',
+ 'news_img|新闻封面' => 'require',
+
+ ]);
+ $model = ModelNews::create($params, [
+ 'news_guid',
+ 'news_create_user_guid',
+ 'news_update_user_guid',
+ 'news_title',
+ 'news_author',
+ 'news_intro',
+ 'news_type',
+ 'news_img',
+ 'news_content',
+
+
+ 'news_num'
+ ]);
+ return [
+ 'code' => 0,
+ 'msg' => '添加成功'
+ ];
+ }
+
+ /**
+ * 删除新闻
+ */
+ public function deleteNews(Request $request): array
+ {
+ $params = $request->param();
+ $this->validate($params, [
+ 'news_guid' => 'require',
+ ]);
+ $news = ModelNews::where([
+ 'news_guid' => explode(',', $params['news_guid'])
+ ])->select();
+ $news->delete();
+ return [
+ 'code' => 0,
+ 'msg' => "删除成功"
+ ];
+ }
+
+ /**
+ * 导出Excel
+ */
+ public function exportExcel(Request $request)
+ {
+ $params = $request->param();
+ $select = ModelNews::field([
+ 'news_title',
+ 'news_author',
+ 'news_intro',
+ 'news_type',
+ 'news_img',
+ 'news_content',
+
+ 'news_num'
+ ])
+ ->order('news_update_time', 'desc')
+ ->select();
+ $data = [[
+ '新闻标题',
+ '新闻作者',
+ '新闻简介',
+ '新闻类型',
+ '新闻封面',
+ '新闻内容',
+
+ '新闻数量'
+ ]];
+ foreach ($select as $key => $val) {
+ $data[] = [
+ $val['news_title'],
+ $val['news_author'],
+ $val['news_intro'],
+ $val['news_type'],
+ $val['news_img'],
+ $val['news_content'],
+
+ $val['news_num'],
+ ];
+ }
+ $excel = (new Excel())->exporTsheet($data);
+ $excel->save('新闻.xlsx');
+ }
+
+ /**
+ * 下载导入模板
+ */
+ public function downloadTemplate(Request $request)
+ {
+ $params = $request->param();
+ $data = [
+ '新闻标题',
+ '新闻作者',
+ '新闻简介',
+ '新闻类型',
+ '新闻封面',
+ '新闻内容',
+
+ '新闻数量'
+ ];
+ $excel = (new Excel())->exporTsheet($data);
+ $excel->save('新闻导入模板.xlsx');
+ }
+
+ /**
+ * 导入excel
+ */
+ public function importExcel(Request $request)
+ {
+ $file = new UploadFile('uploads', 'fileExt:xlsx');
+ $file->putFile('news');
+ Db::startTrans();
+ try {
+ $excel = new Excel($file);
+ $data = $excel->parseExcel(
+ [[
+ 'title' => '新闻标题',
+ 'validate' => 'require',
+ 'field' => 'news_title',
+ ], [
+ 'title' => '新闻作者',
+ 'validate' => 'require',
+ 'field' => 'news_author',
+ ], [
+ 'title' => '新闻简介',
+ 'validate' => 'require',
+ 'field' => 'news_intro',
+ ], [
+ 'title' => '新闻类型',
+ 'validate' => 'require',
+ 'field' => 'news_type',
+ ], [
+ 'title' => '新闻封面',
+ 'validate' => 'require',
+ 'field' => 'news_img',
+ ], [
+ 'title' => '新闻内容',
+ 'validate' => 'require',
+ 'field' => 'news_content',
+ ], [
+ 'title' => '新闻数量',
+ 'validate' => 'require',
+ 'field' => 'news_num',
+ ],],
+ [
+ 'titleLine' => [1]
+ ]
+ );
+ if (!$data) throwErrorMsg('excel无数据', 1);
+ $error = [];
+ foreach ($data as $line => $value) {
+ try {
+ $news_title = $value['news_title'];
+ $news_author = $value['news_author'];
+ $news_intro = $value['news_intro'];
+ $news_type = $value['news_type'];
+ $news_img = $value['news_img'];
+ $news_content = $value['news_content'];
+ $news_data = $value['news_data'];
+ $news_time = $value['news_time'];
+ $news_num = $value['news_num'];
+ ModelNews::create([
+ 'news_title' => $news_title,
+ 'news_author' => $news_author,
+ 'news_intro' => $news_intro,
+ 'news_type' => $news_type,
+ 'news_img' => $news_img,
+ 'news_content' => $news_content,
+
+ 'news_num' => $news_num,
+ ]);
+ } catch (\Throwable $th) {
+ $error[] = $line . ':' . $th->getMessage();
+ }
+ }
+ if ($error) throwErrorMsg(implode(', ', $error), 1);
+ Db::commit();
+ } catch (\Throwable $th) {
+ Db::rollback();
+ throw $th;
+ }
+ return [
+ 'code' => 0,
+ 'msg' => '导入成功!'
+ ];
+ }
+}
diff --git a/app/admin/controller/Product/Product.php b/app/admin/controller/Product/Product.php
new file mode 100644
index 0000000..9b6923c
--- /dev/null
+++ b/app/admin/controller/Product/Product.php
@@ -0,0 +1,220 @@
+param();
+ $con = [];
+
+ $con = Tool::getOptionalQuery(
+ ['product.product_name', 'LIKE'],
+ ['product.product_type_guid'],
+ );
+
+ $query = ModelProduct::where($con)
+ ->leftJoin('product_type', 'product_type.product_type_guid = product.product_type_guid')
+ ->field([
+ 'product.product_id',
+ 'product.product_guid',
+ 'product.product_type_guid',
+ 'product.product_name',
+ 'product.product_img',
+ 'product.product_params',
+ 'product.product_details',
+ 'product.product_price',
+ 'product_type.product_type_name'
+ ])
+ ->order('product_id', 'desc');
+
+ return msg("获取产品列表成功!", $query);
+ Db::commit();
+ } catch (\Throwable $th) {
+ Db::rollback();
+ throw $th;
+ }
+ }
+
+ /**
+ * 编辑产品接口
+ *
+ * @param Request request
+ * @date 2023-03-25
+ * @example
+ * @author xjh
+ * @since 1.0.0
+ */
+ public function editProduct(Request $request): array
+ {
+ $params = $request->param();
+ $this->validate($params, [
+ 'product_type_guid|产品系列guid' => 'require',
+ 'product_name|名称' => 'require',
+ 'product_img|图片' => 'require',
+ 'product_details|详情' => 'require',
+ 'product_price|价格' => 'require'
+ ]);
+ $model = ModelProduct::where('product_guid', $params['product_guid'])->find();
+ if (!$model) throwErrorMsg("该产品不存在", 1);
+ // Tool::sortEditProc(
+ // new ModelProduct,
+ // ["product_guid" => $params['product_guid']],
+ // ["product_type_order" => $params['product_type_order']],
+ // );
+ $model->allowField([
+ 'product_update_user_guid',
+ 'product_type_guid',
+ 'product_name',
+ 'product_img',
+ 'product_params',
+ 'product_details',
+ 'product_price'
+ ])->save($params);
+ return msg('编辑成功!');
+ }
+
+ /**
+ * 添加产品接口
+ *
+ * @param Request request
+ * @date 2023-03-25
+ * @example
+ * @author xjh
+ * @since 1.0.0
+ */
+ public function addProduct(Request $request): array
+ {
+ $params = $request->param();
+ $this->validate($params, [
+ 'product_type_guid|产品系列guid' => 'require',
+ 'product_name|名称' => 'require',
+ 'product_img|图片' => 'require',
+ 'product_details|详情' => 'require',
+ 'product_price|价格' => 'require'
+ ]);
+
+ ModelProduct::create($params, [
+ 'product_guid',
+ 'product_create_user_guid',
+ 'product_update_user_guid',
+ 'product_type_guid',
+ 'product_name',
+ 'product_img',
+ 'product_params',
+ 'product_details',
+ 'product_price'
+ ]);
+ return msg('添加成功!');
+ }
+
+ /**
+ * 删除产品接口
+ *
+ * @param Request request
+ * @date 2023-03-25
+ * @example
+ * @author xjh
+ * @since 1.0.0
+ */
+ public function deleteProduct(Request $request): array
+ {
+ $params = $request->param();
+ $this->validate($params, [
+ 'product_guid|产品系列' => 'require',
+ ]);
+ $product = ModelProduct::where([
+ 'product_guid' => explode(',', $params['product_guid'])
+ ])->select();
+ $product->delete();
+ return msg('删除成功!');
+ }
+
+ /**
+ * 导出Excel接口
+ *
+ * @param Request request
+ * @date 2023-03-25
+ * @example
+ * @author xjh
+ * @since 1.0.0
+ */
+ public function exportExcel(Request $request)
+ {
+ $params = $request->param();
+ $select = ModelProduct::field([
+ 'product_type_guid',
+ 'product_name',
+ 'product_img',
+ 'product_params',
+ 'product_details',
+ 'product_price'
+ ])
+ ->order('product_update_time', 'desc')
+ ->select();
+ return ModelProduct::exportExcel($select);
+ }
+
+ /**
+ * 下载导入模板接口
+ *
+ * @param Request request
+ * @date 2023-03-25
+ * @example
+ * @author xjh
+ * @since 1.0.0
+ */
+ public function downloadTemplate(Request $request)
+ {
+ $params = $request->param();
+ $data = array_values(ModelProduct::EXCELFIELD);
+ $excel = (new Excel())->exporTsheet($data);
+ $excel->save('产品导入模板.xlsx');
+ }
+
+ /**
+ * 导入excel接口
+ *
+ * @param Request request
+ * @date 2023-03-25
+ * @example
+ * @author xjh
+ * @since 1.0.0
+ */
+ public function importExcel(Request $request)
+ {
+ $file = new UploadFile('uploads', 'fileExt:xlsx');
+ $file->putFile('product');
+
+ $msg = ModelProduct::importExcel($file);
+ return [
+ 'code' => 0,
+ 'msg' => $msg
+ ];
+ }
+}
diff --git a/app/admin/controller/Product/ProductParam.php b/app/admin/controller/Product/ProductParam.php
new file mode 100644
index 0000000..9c7be06
--- /dev/null
+++ b/app/admin/controller/Product/ProductParam.php
@@ -0,0 +1,148 @@
+field([
+ 'product_param.product_param_id',
+ 'product_param.product_param_guid',
+ 'product_param.product_param_name',
+ 'product_param.product_type_guid',
+ 'product_param.product_param_order',
+ 'product_type.product_type_name'
+ ])
+ ->leftJoin('product_type', 'product_type.product_type_guid = product_param.product_type_guid')
+ ->order('product_param_order', 'asc');
+
+ return msg("获取产品参数列表成功!", $query);
+ }
+
+ /**
+ * 获取指定系列参数模板接口
+ *
+ * @param Request request
+ * @date 2023-04-02
+ * @example
+ * @author xjh
+ * @since 1.0.0
+ */
+ public function getProductParamTemplate(Request $request): array
+ {
+ $params = $request->param();
+ $this->validate($params, [
+ 'product_type_guid|产品系列guid' => 'require'
+ ]);
+
+ $data = ModelProductParam::field(['product_param_name', 'product_param_order'])
+ ->where('product_type_guid', $params['product_type_guid'])
+ ->order('product_param_order', 'desc')
+ ->append(['product_param_value'])
+ ->select();
+
+ return msg(0, '获取指定系列参数模板成功!', ['data' => $data]);
+ }
+
+ /**
+ * 编辑产品参数接口
+ *
+ * @param Request request
+ * @date 2023-04-02
+ * @example
+ * @author xjh
+ * @since 1.0.0
+ */
+ public function editProductParam(Request $request): array
+ {
+ $params = $request->param();
+ $this->validate($params, [
+ 'product_param_name|产品参数名称' => 'require',
+ 'product_type_guid|产品系列guid' => 'require'
+ ]);
+ $model = ModelProductParam::where('product_param_guid', $params['product_param_guid'])->find();
+ if (!$model) throwErrorMsg("该产品参数不存在", 1);
+ $model->allowField([
+ 'product_param_update_user_guid',
+ 'product_param_name',
+ 'product_type_guid',
+ 'product_param_order'
+ ])->save($params);
+ return msg('编辑成功!');
+ }
+
+ /**
+ * 添加产品参数接口
+ *
+ * @param Request request
+ * @date 2023-04-02
+ * @example
+ * @author xjh
+ * @since 1.0.0
+ */
+ public function addProductParam(Request $request): array
+ {
+ $params = $request->param();
+ $this->validate($params, [
+ 'product_param_name|产品参数名称' => 'require',
+ 'product_type_guid|产品系列guid' => 'require'
+ ]);
+ $model = ModelProductParam::create($params, [
+ 'product_param_guid',
+ 'product_param_create_user_guid',
+ 'product_param_update_user_guid',
+ 'product_param_name',
+ 'product_param_order',
+ 'product_type_guid'
+ ]);
+ return msg('添加成功!');
+ }
+
+ /**
+ * 删除产品参数接口
+ *
+ * @param Request request
+ * @date 2023-04-02
+ * @example
+ * @author xjh
+ * @since 1.0.0
+ */
+ public function deleteProductParam(Request $request): array
+ {
+ $params = $request->param();
+ $this->validate($params, [
+ 'product_param_guid' => 'require',
+ ]);
+ $product_param = ModelProductParam::where([
+ 'product_param_guid' => explode(',', $params['product_param_guid'])
+ ])->select();
+ $product_param->delete();
+ return msg('删除成功!');
+ }
+}
diff --git a/app/admin/controller/Product/ProductType.php b/app/admin/controller/Product/ProductType.php
new file mode 100644
index 0000000..4086cfa
--- /dev/null
+++ b/app/admin/controller/Product/ProductType.php
@@ -0,0 +1,271 @@
+ "product_type_parent_name",
+ 'a.product_type_order',
+ 'a.product_type_guid',
+ 'a.product_type_icon',
+ 'a.product_type_cover',
+ ])
+ ->alias('a')
+ ->leftjoin('product_type b', 'a.product_type_parent_guid = b.product_type_guid')
+ ->where($con)
+ ->order(['product_type_order' => 'asc'])
+ ->select()->toArray();
+
+ $Traverse = new Traverse('product_type_guid', 'product_type_parent_guid');
+ $product_type_tree = $Traverse->tree($product_type, '0', function ($v) {
+ return [
+ 'product_type_name' => $v['product_type_name'],
+ 'product_type_parent_name' => $v['product_type_parent_name'],
+ 'product_type_guid' => $v['product_type_guid'],
+ 'product_type_parent_guid' => $v['product_type_parent_guid'],
+ 'product_type_order' => $v['product_type_order'],
+ 'product_type_icon' => $v['product_type_icon'],
+ 'product_type_cover' => $v['product_type_cover'],
+ ];
+ });
+
+ return msg("获取产品系列列表成功!", $product_type_tree);
+ }
+
+ /**
+ * 编辑产品系列接口
+ *
+ * @param Request request
+ * @date 2023-03-25
+ * @example
+ * @author xjh
+ * @since 1.0.0
+ */
+ public function editProductType(Request $request): array
+ {
+ Db::startTrans();
+ try {
+ $params = $request->param();
+ $this->validate($params, [
+ 'product_type_name|产品系列名称' => 'require',
+ 'product_type_icon|产品系列图标' => 'require',
+ 'product_type_cover|产品系列封面' => 'require',
+ 'product_type_guid|产品系列guid' => 'require',
+ 'product_type_order|产品系列排序' => 'require',
+ ]);
+
+ $model = ModelProductType::where('product_type_guid', $params['product_type_guid'])->find();
+ if (!$model) throwErrorMsg("该产品系列不存在", 1);
+
+ ModelProductType::isDuplicateName($params['product_type_name'], $params['product_type_guid']);
+
+ $params['product_type_ancestors_guid'] = ModelProductType::buildAncestorsGuid($params['product_type_parent_guid']);
+
+ Tool::sortEditProc(
+ new ModelProductType,
+ ["product_type_order" => $params['product_type_order']],
+ ["product_type_guid" => $params['product_type_guid']],
+ ["product_type_parent_guid" => $params['product_type_parent_guid']],
+ );
+
+
+ $model->allowField([
+ 'product_type_update_user_guid',
+ 'product_type_title',
+ 'product_type_icon',
+ 'product_type_cover',
+ 'product_type_ancestors_guid',
+ 'product_type_parent_guid',
+ 'product_type_order',
+ ])->save($params);
+ Db::commit();
+ return msg('编辑成功!');
+ } catch (\Throwable $th) {
+ Db::rollback();
+ throw $th;
+ }
+ }
+
+
+ /**
+ * 添加产品系列接口
+ *
+ * @param Request request
+ * @date 2023-03-25
+ * @example
+ * @author xjh
+ * @since 1.0.0
+ */
+ public function addProductType(Request $request): array
+ {
+ Db::startTrans();
+ try {
+ $params = $request->param();
+ $this->validate($params, [
+ 'product_type_name|产品类型名称' => 'require',
+ 'product_type_icon|产品系列图标' => 'require',
+ 'product_type_cover|产品系列封面' => 'require',
+ 'product_type_order|产品系列排序' => 'require'
+ ]);
+
+ ModelProductType::isDuplicateName($params['product_type_name']);
+
+
+ $product_type_parent_where = [];
+ if (!empty($params['product_type_parent_guid'])) {
+ $params['product_type_ancestors_guid'] = ModelProductType::buildAncestorsGuid($params['product_type_parent_guid']);
+ $product_type_parent_where['product_type_parent_guid'] = $params['product_type_parent_guid'];
+ }
+
+ Tool::sortInsertProc(
+ new ModelProductType,
+ ["product_type_order" => $params['product_type_order']],
+ $product_type_parent_where,
+ );
+
+ ModelProductType::create($params, [
+ 'product_type_guid',
+ 'product_type_create_user_guid',
+ 'product_type_update_user_guid',
+ 'product_type_parent_guid',
+ 'product_type_ancestors_guid',
+ 'product_type_name',
+ 'product_type_icon',
+ 'product_type_cover',
+ 'product_type_order'
+ ]);
+ Db::commit();
+ return msg("添加成功!");
+ } catch (\Throwable $th) {
+ Db::rollback();
+ throw $th;
+ }
+ }
+
+ /**
+ * 删除产品系列接口接口
+ *
+ * @param Request request
+ * @date 2023-03-25
+ * @example
+ * @author xjh
+ * @since 1.0.0
+ */
+ public function deleteProductType(Request $request): array
+ {
+ Db::startTrans();
+ try {
+ $params = $request->param();
+ $this->validate($params, [
+ 'product_type_guid|产品系列guid' => 'require',
+ ]);
+ $guids = explode(',', $params['product_type_guid']);
+
+ Tool::sortDeleteProc(
+ new ModelProductType,
+ 'product_type_order',
+ ["product_type_guid" => $guids],
+ ["product_type_parent_guid"],
+ );
+ ModelProductType::where(['product_type_guid' => $guids])->select()->delete();
+ Db::commit();
+ return msg('删除成功!');
+ } catch (\Throwable $th) {
+ Db::rollback();
+ throw $th;
+ }
+ }
+
+ /**
+ * 导出Excel接口
+ *
+ * @param Request request
+ * @date 2023-03-25
+ * @example
+ * @author xjh
+ * @since 1.0.0
+ */
+ public function exportExcel(Request $request)
+ {
+ $params = $request->param();
+ $select = ModelProductType::field([
+ 'product_type_title',
+ 'product_type_icon',
+ 'product_type_cover'
+ ])
+ ->order('product_type_update_time', 'desc')
+ ->select();
+ return ModelProductType::exportExcel($select);
+ }
+
+ /**
+ * 下载导入模板接口
+ *
+ * @param Request request
+ * @date 2023-03-25
+ * @example
+ * @author xjh
+ * @since 1.0.0
+ */
+ public function downloadTemplate(Request $request)
+ {
+ $params = $request->param();
+ $data = array_values(ModelProductType::EXCELFIELD);
+ $excel = (new Excel())->exporTsheet($data);
+ $excel->save('产品系列导入模板.xlsx');
+ }
+
+ /**
+ * 导入excel接口
+ *
+ * @param Request request
+ * @date 2023-03-25
+ * @example
+ * @author xjh
+ * @since 1.0.0
+ */
+ public function importExcel(Request $request)
+ {
+ $file = new UploadFile('uploads', 'fileExt:xlsx');
+ $file->putFile('product_type');
+
+ $msg = ModelProductType::importExcel($file);
+ return [
+ 'code' => 0,
+ 'msg' => $msg
+ ];
+ }
+}
diff --git a/app/admin/controller/Role/Role.php b/app/admin/controller/Role/Role.php
new file mode 100644
index 0000000..9299621
--- /dev/null
+++ b/app/admin/controller/Role/Role.php
@@ -0,0 +1,198 @@
+param('role_status');
+ $role = $request->param('role');
+ $scope = explode(',', $request->param('scope', ''));
+ $con = [
+ // 'role_status' => 1
+ ];
+ if ($role) {
+ $con['role_name'] = $role;
+ }
+ if ($role_status) {
+ $con['role_status'] = $role_status;
+ }
+ $query = ModelRole::scope($scope)->withSearch(['role_name'], $con)->where($con);
+ $select = self::pageWrapper($query)->field([
+ 'role_name',
+ 'role_status',
+ 'role_guid',
+ ])->append([
+ 'role_status_text'
+ ])->order([
+ 'role_id' => 'desc'
+ ])->select();
+ $count = $query->count();
+ return [
+ 'code' => 0,
+ 'data' => $select,
+ 'count' => $count,
+ 'msg' => 'ok'
+ ];
+ }
+
+ /**
+ * 获取角色菜单
+ *
+ * @param Request $request
+ * @date 2022-03-08
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function getRoleMenu(Request $request): array
+ {
+ $params = $request->param();
+ $this->validate($params, [
+ 'role_guid' => 'require'
+ ]);
+ dump(ModelRole::getByRoleGuid($params['role_guid'])->menus);
+ return [
+ 'code' => 0,
+ 'msg' => 'ok',
+ // 'data' => $data
+ ];
+ }
+
+ /**
+ * 编辑角色
+ *
+ * @param Request $request
+ * @date 2022-02-28
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function editRole(Request $request): array
+ {
+ $params = $request->param();
+ $this->validate($params, [
+ 'role_guid' => 'require',
+ 'role_name' => 'require',
+ ]);
+ $role = ModelRole::where([
+ 'role_guid' => $params['role_guid']
+ ])->find();
+ if (!$role) {
+ throwErrorMsg("角色不存在", 1);
+ }
+ $role->save([
+ 'role_name' => $params['role_name'],
+ ]);
+ return [
+ 'code' => 0,
+ 'msg' => '编辑成功'
+ ];
+ }
+
+ /**
+ * 添加角色
+ *
+ * @param Request $request
+ * @date 2022-02-28
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function addRole(Request $request): array
+ {
+ $params = $request->param();
+ $this->validate($params, [
+ 'role_name' => 'require',
+ ]);
+ ModelRole::create([
+ 'role_name' => $params['role_name'],
+ ]);
+ return [
+ 'code' => 0,
+ 'msg' => '添加成功'
+ ];
+ }
+
+ /**
+ * 删除角色
+ *
+ * @param Request $request
+ * @date 2022-02-28
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function deleteRole(Request $request): array
+ {
+ $params = $request->param();
+ $this->validate($params, [
+ 'role_guid' => 'require',
+ ]);
+ $roles = ModelRole::where([
+ 'role_guid' => ['in', $params['role_guid']]
+ ])->select();
+ $roles->delete();
+ return [
+ 'code' => 0,
+ 'msg' => "删除成功"
+ ];
+ }
+
+ /**
+ * 更新角色状态
+ *
+ * @param Request $request
+ * @date 2022-02-28
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function updateRoleStatus(Request $request): array
+ {
+ $params = $request->param();
+ $this->validate($params, [
+ 'role_guid' => 'require',
+ 'role_status' => 'require|in:1,2',
+ ]);
+ $role_status = $params['role_status'];
+ $roles = ModelRole::where([
+ 'role_guid' => explode(',', $params['role_guid'])
+ ])->select();
+ $roles->update([
+ 'role_status' => $role_status
+ ]);
+ return [
+ 'code' => 0,
+ 'msg' => "更新成功"
+ ];
+ }
+}
diff --git a/app/admin/controller/School/Classes.php b/app/admin/controller/School/Classes.php
new file mode 100644
index 0000000..796b581
--- /dev/null
+++ b/app/admin/controller/School/Classes.php
@@ -0,0 +1,111 @@
+param();
+ $con = [];
+
+ if (isset($params['classes_name']) && $params['classes_name']) {
+ $con[] = ['classes_name', 'LIKE', '%' . $params['classes_name'] . '%'];
+ };
+
+ $query = ModelClasses::where($con);
+ $select = self::pageWrapper($query)
+ ->field([
+ 'classes_id',
+ 'classes_guid',
+ 'classes_name'
+ ])
+ ->order('classes_update_time', 'desc')
+ ->select();
+
+ $count = $query->count();
+
+ return [
+ 'code' => 0,
+ 'data' => $select,
+ 'count' => $count,
+ 'msg' => 'ok'
+ ];
+ }
+
+ /**
+ * 编辑班级
+ */
+ public function editClasses(Request $request): array
+ {
+ $params = $request->param();
+ $this->validate($params, [
+ 'classes_name|班级名称' => 'require'
+ ]);
+ $model = ModelClasses::where('classes_guid', $params['classes_guid'])->find();
+ if (!$model) throwErrorMsg("该班级不存在", 1);
+ $model->allowField([
+ 'classes_update_user_guid',
+ 'classes_name'
+ ])->save($params);
+ return [
+ 'code' => 0,
+ 'msg' => '编辑成功'
+ ];
+ }
+
+ /**
+ * 添加班级
+ */
+ public function addClasses(Request $request): array
+ {
+ $params = $request->param();
+ $this->validate($params, [
+ 'classes_name|班级名称' => 'require'
+ ]);
+ $model = ModelClasses::create($params, [
+ 'classes_guid',
+ 'classes_create_user_guid',
+ 'classes_update_user_guid',
+ 'classes_name'
+ ]);
+ return [
+ 'code' => 0,
+ 'msg' => '添加成功'
+ ];
+ }
+
+ /**
+ * 删除班级
+ */
+ public function deleteClasses(Request $request): array
+ {
+ $params = $request->param();
+ $this->validate($params, [
+ 'classes_guid' => 'require',
+ ]);
+ $classes = ModelClasses::where([
+ 'classes_guid' => explode(',', $params['classes_guid'])
+ ])->select();
+ $classes->delete();
+ return [
+ 'code' => 0,
+ 'msg' => "删除成功"
+ ];
+ }
+}
diff --git a/app/admin/controller/School/Student.php b/app/admin/controller/School/Student.php
new file mode 100644
index 0000000..9bac8c4
--- /dev/null
+++ b/app/admin/controller/School/Student.php
@@ -0,0 +1,544 @@
+user_guid;
+
+ $jwd = Map::getLongitudeAndLatitude("广东省佛山市顺德区容桂街道保利·德胜湾保利外滩一号");
+ return $jwd;
+ // return date('Y-m-d H:i:s', strtotime('+3 month'));
+ }
+
+ /**
+ * 获取学生列表
+ */
+ public function getStudentList(Request $request)
+ {
+ $params = $request->param();
+ $con = [];
+ $conOr = [];
+
+ if (isset($params['student_name']) && $params['student_name']) {
+ $con[] = ['student_name', 'LIKE', '%' . $params['student_name'] . '%'];
+ };
+ if (isset($params['studnet_phone']) && $params['studnet_phone']) {
+ $con[] = ['studnet_phone', 'LIKE', '%' . $params['studnet_phone'] . '%'];
+ };
+ if (isset($params['student_id_card']) && $params['student_id_card']) {
+ $con[] = ['student_id_card', 'LIKE', '%' . $params['student_id_card'] . '%'];
+ };
+ if (isset($params['student_email']) && $params['student_email']) {
+ $con[] = ['student_email', 'LIKE', '%' . $params['student_email'] . '%'];
+ };
+ if (isset($params['student_sex']) && $params['student_sex']) {
+ $con[] = ['student_sex', '=', $params['student_sex']];
+ };
+ if (isset($params['student_membe_type']) && $params['student_membe_type']) {
+ $con[] = ['student_membe_type', '=', $params['student_membe_type']];
+ };
+ if (isset($params['student_audit_status']) && $params['student_audit_status']) {
+ $con[] = ['student_audit_status', '=', $params['student_audit_status']];
+ };
+ if (isset($params['product_type']) && $params['product_type']) {
+ $con[] = ['a.product_type', '=', $params['product_type']];
+ $conOr[] = ['c.product_type_parent_guid', '=', $params['product_type']];
+ };
+ if (isset($params['product_guid']) && $params['product_guid']) {
+ $con[] = ['a.product_guid', '=', $params['product_guid']];
+ };
+ if (isset($params['product_parts_guid']) && $params['product_parts_guid']) {
+ $con[] = ['a.product_parts_guid', '=', $params['product_parts_guid']];
+ };
+ if (isset($params['student_brithday']) && $params['student_brithday']) {
+ $con[] = ['student_brithday', 'BETWEEN', implode(',', $params['student_brithday'])];
+ };
+
+ // return $con;
+
+ $query = ModelStudent::alias('a')
+ ->leftjoin('user b', 'a.user_guid = b.user_guid')
+ ->leftjoin('product_type c', 'a.product_type = c.product_type_guid')
+ ->leftjoin('product d', 'a.product_guid = d.product_guid')
+ ->leftjoin('product_parts e', 'a.product_parts_guid = e.product_parts_guid')
+ ->leftjoin('classes f', 'a.classes_guid = f.classes_guid')
+ ->where($con)
+ ->whereOr(function ($query) use ($conOr) {
+ $query->whereOr($conOr);
+ });
+
+ if (isset($params['student_member_time']) && $params['student_member_time']) {
+ $query->where("
+ (
+ student_member_begin_time >= '{$params['student_member_time'][0]}'
+ AND
+ student_member_begin_time <= '{$params['student_member_time'][1]}'
+ )
+ OR
+ (
+ student_member_end_time >= '{$params['student_member_time'][0]}'
+ AND
+ student_member_end_time <= '{$params['student_member_time'][1]}'
+ )
+ OR
+ (
+ student_member_begin_time >= '{$params['student_member_time'][0]}'
+ AND
+ student_member_end_time <= '{$params['student_member_time'][1]}'
+ )
+ ");
+ }
+
+
+
+ $select = self::pageWrapper($query)
+ ->field([
+ 'a.student_id',
+ 'a.student_guid',
+ 'a.student_name',
+ 'a.user_guid',
+ 'b.user_name',
+ 'a.classes_guid',
+ 'f.classes_name',
+ 'a.studnet_phone',
+ 'a.student_id_card',
+ 'a.student_email',
+ 'a.student_sex',
+ 'a.student_price',
+ 'a.student_brithday',
+ 'a.product_type',
+ 'c.product_type_name',
+ 'a.product_guid',
+ 'd.product_name',
+ 'a.product_parts_guid',
+ 'e.product_parts_name',
+ 'a.student_img',
+ 'a.student_banner_img',
+ 'a.student_attachment',
+ 'a.student_intro',
+ 'a.student_location',
+ 'a.longitude',
+ 'a.latitude',
+ 'a.student_membe_type',
+ 'a.student_member_begin_time',
+ 'a.student_member_end_time',
+ 'a.student_audit_status',
+ 'a.student_audit_user_guid',
+ "(SELECT `user`.`user_name` FROM `user` WHERE `user`.user_guid = `a`.`student_audit_user_guid`) as student_audit_user_name",
+ ])
+ ->append([
+ 'student_member_time',
+ 'student_service',
+ ])
+ ->order('student_update_time', 'desc')
+ ->select();
+
+ $count = $query->count();
+
+ return [
+ 'code' => 0,
+ 'data' => $select,
+ 'count' => $count,
+ 'msg' => 'ok'
+ ];
+ }
+
+ /**
+ * 编辑学生
+ */
+ public function editStudent(Request $request)
+ {
+ $params = $request->param();
+ $this->validate($params, [
+ 'student_name|学生名称' => 'require',
+ 'user_guid|用户' => 'require',
+ 'classes_guid|班级' => 'require',
+ 'studnet_phone|手机号' => 'require',
+ 'student_id_card|身份证号' => 'require',
+ 'student_sex|性别' => 'require',
+ 'student_price|学生价格' => 'require',
+ 'product_type|产品类型' => 'require',
+ 'product_guid|产品' => 'require',
+ 'product_parts_guid|产品零件' => 'require',
+ 'student_img|头像' => 'require',
+ 'student_intro|简介' => 'require',
+ 'student_location|家庭住址' => 'require',
+ 'longitude|经度' => 'require',
+ 'latitude|纬度' => 'require'
+ ]);
+ $model = ModelStudent::where('student_guid', $params['student_guid'])->find();
+ if (!$model) throwErrorMsg("该学生不存在", 1);
+
+
+ $student_member_time = $params['student_member_time'];
+ $params['student_member_begin_time'] = $student_member_time[0];
+ $params['student_member_end_time'] = $student_member_time[1];
+
+ // return $params;
+
+
+ $model->allowField([
+ 'student_update_user_guid',
+ 'student_name',
+ 'user_guid',
+ 'classes_guid',
+ 'studnet_phone',
+ 'student_id_card',
+ 'student_email',
+ 'student_sex',
+ 'student_price',
+ 'student_brithday',
+ 'product_type',
+ 'product_guid',
+ 'product_parts_guid',
+ 'student_img',
+ 'student_banner_img',
+ 'student_attachment',
+ 'student_intro',
+ 'student_location',
+ 'longitude',
+ 'latitude',
+ 'student_membe_type',
+ 'student_member_begin_time',
+ 'student_member_end_time',
+ 'student_audit_status',
+ 'student_audit_user_guid'
+ ])->save($params);
+
+ // 学生服务编辑
+ ModelStudent::editStudentService($params);
+
+ return [
+ 'code' => 0,
+ 'msg' => '编辑成功'
+ ];
+ }
+
+ /**
+ * 添加学生
+ */
+ public function addStudent(Request $request)
+ {
+ $params = $request->param();
+ $this->validate($params, [
+ 'student_name|学生名称' => 'require',
+ 'user_name|用户' => 'require',
+ 'classes_guid|班级' => 'require',
+ 'studnet_phone|手机号' => 'require',
+ 'student_id_card|身份证号' => 'require',
+ 'student_sex|性别' => 'require',
+ 'student_price|学生价格' => 'require',
+ 'product_type|产品类型' => 'require',
+ 'product_guid|产品' => 'require',
+ 'product_parts_guid|产品零件' => 'require',
+ 'student_img|头像' => 'require',
+ 'student_intro|简介' => 'require',
+ 'student_location|家庭住址' => 'require',
+ 'longitude|经度' => 'require',
+ 'latitude|纬度' => 'require',
+ 'student_membe_type|会员类型' => 'require',
+ ]);
+
+ $params = ModelStudent::initAdd($params);
+
+ $model = ModelStudent::create($params, [
+ 'student_guid',
+ 'student_create_user_guid',
+ 'student_update_user_guid',
+ 'student_name',
+ 'user_guid',
+ 'classes_guid',
+ 'studnet_phone',
+ 'student_id_card',
+ 'student_email',
+ 'student_sex',
+ 'student_price',
+ 'student_brithday',
+ 'product_type',
+ 'product_guid',
+ 'product_parts_guid',
+ 'student_img',
+ 'student_banner_img',
+ 'student_attachment',
+ 'student_intro',
+ 'student_location',
+ 'longitude',
+ 'latitude',
+ 'student_membe_type',
+ 'student_member_begin_time',
+ 'student_member_end_time',
+ 'student_audit_status',
+ 'student_audit_user_guid'
+ ]);
+
+ if (!$model) throwErrorMsg("添加失败");
+ $student_guid = $model->student_guid;
+
+ // 添加学生服务
+ ModelStudent::addStudentService($student_guid, $params);
+
+ return [
+ 'code' => 0,
+ 'msg' => '添加成功'
+ ];
+ }
+
+ /**
+ * 删除学生
+ */
+ public function deleteStudent(Request $request): array
+ {
+ $params = $request->param();
+ $this->validate($params, [
+ 'student_guid' => 'require',
+ ]);
+ $student = ModelStudent::where([
+ 'student_guid' => explode(',', $params['student_guid'])
+ ])->select();
+ $student->delete();
+
+ // 从学生服务(副表)查询出所有当前学生的服务,进行删除
+ ModelStudentService::where('student_guid', $params['student_guid'])->select()->delete();
+
+ return [
+ 'code' => 0,
+ 'msg' => "删除成功"
+ ];
+ }
+
+ /**
+ * 查找未绑定用户列表
+ */
+ public function notBindUserList(Request $request)
+ {
+ $user = $request->param('user');
+ $con = [
+ 'user_status' => 1,
+ ];
+ $search = [];
+ if ($user) {
+ $search['user_name'] = $user;
+ }
+
+ $query = ModelUser::alias('a')
+ ->join('student b', 'a.user_guid != b.user_guid')
+ ->withSearch(array_keys($search), $search)->where($con)
+ ->whereNotExists(function ($query) {
+ $query->table('student')->alias('b')->where("a.user_guid = b.user_guid");
+ });
+ $select = self::pageWrapper($query)->field([
+ 'a.user_name',
+ ])
+ ->group('user.user_guid')->order([
+ 'user.user_update_time' => 'desc'
+ ])->select();
+ $count = $query->count();
+ return [
+ 'code' => 0,
+ 'data' => $select,
+ 'count' => $count,
+ 'msg' => 'ok'
+ ];
+ }
+
+ /**
+ * 审核学生
+ */
+ public function auditStudent(Request $request)
+ {
+ $params = $request->param();
+ $this->validate($params, [
+ 'student_guid|学生guid' => 'require',
+ 'student_audit_status|审核状态' => 'require|in:2,3',
+ ]);
+
+ ModelStudent::auditStudent($params);
+
+ return [
+ 'code' => 0,
+ 'msg' => '审核成功'
+ ];
+ }
+
+ /**
+ * 导出Excel
+ */
+ public function exportExcel(Request $request)
+ {
+ $params = $request->param();
+ return ModelStudent::exportStudent();
+ }
+
+ /**
+ * 下载导入模板
+ */
+ public function downloadTemplate(Request $request)
+ {
+ $params = $request->param();
+ $data = [
+ ['学生名称', '用户', '班级', '手机号', '身份证号', '邮箱', '性别', '学生价格', '生日', '产品类型', '产品', '产品零件', '头像', '轮播图片', '简介', '家庭住址', '会员类型',]
+ ];
+
+ $data[] = ['张思', '负责人', '网络3+2', '19882556281', '110223790813697', '2679599887@163.com', '男', '99.99', '2023-02-19', '小米10', '小米10代手机 全面屏 3亿像素', '小米专属钢化膜', 'https://hjczx.net/intelligent_clique/public/uploads/hjczx//student_img/2022/459/210744-compression.jpg', 'http://aerwen.net/prod-api/student/20230218/C9B76FC1E616EE6A.jpg,http://aerwen.net/prod-api/key/20230217/628A9C1A391ABD61.jpg', '我是三好学生', '广东省佛山市顺德区容桂街道胡锦超职业技术学校', '季卡'];
+ $excel = (new Excel())->exporTsheet($data);
+ $excel->save('学生导入模板.xlsx');
+ }
+
+ /**
+ * 导入excel
+ */
+ public function importExcel(Request $request)
+ {
+ $file = new UploadFile('uploads', 'fileExt:xlsx');
+ $file->putFile('student');
+
+ $msg = ModelStudent::ImportStudent($file);
+ return [
+ 'code' => 0,
+ 'msg' => $msg
+ ];
+ }
+
+
+ /**
+ * 获取点击后的产品列表
+ */
+ public function getClickProduct(Request $request)
+ {
+ $params = $request->param();
+ $this->validate($params, [
+ 'product_type_guid|产品类型' => 'require',
+ ]);
+
+ $product_type_guid = $params['product_type_guid'];
+
+ $products = ModelProduct::alias('a')
+ ->leftjoin('product_type b', "a.product_type_guid = b.product_type_guid")
+ ->where("a.product_type_guid = '$product_type_guid' or b.product_type_parent_guid = '$product_type_guid'")->select();
+ return [
+ 'code' => 0,
+ 'data' => $products,
+ 'msg' => "获取点击后的产品列表成功"
+ ];
+ }
+
+ /**
+ * 获取点击后的产品零件列表
+ */
+ public function getClickProductParts(Request $request)
+ {
+ $params = $request->param();
+ $this->validate($params, [
+ 'product_guid|产品' => 'require',
+ ]);
+
+ $products_parts = ModelProductParts::where('product_guid', $params['product_guid'])->select();
+ return [
+ 'code' => 0,
+ 'data' => $products_parts,
+ 'msg' => "获取点击后的产品零件列表成功"
+ ];
+ }
+
+
+ /**
+ * 获取侧边产品数据
+ */
+ public function getProductTree(): array
+ {
+ $data = [];
+ $product_types = ModelProductType::field([
+ 'product_type_name',
+ 'product_type_guid',
+ 'product_type_parent_guid',
+ ])->select();
+ foreach ($product_types as $key => $value) {
+ $product_type_guid = $value['product_type_guid'];
+ $product_type_parent_guid = $value['product_type_parent_guid'];
+ $data[] = [
+ 'id' => $product_type_guid,
+ 'parent_id' => $product_type_parent_guid,
+ 'name' => $value['product_type_name'],
+ 'type' => 'product_type',
+ 'data' => [
+ 'parent_id' => $product_type_parent_guid,
+ 'product_type_guid' => $value['product_type_guid']
+ ],
+ ];
+ }
+ $products = ModelProduct::field([
+ 'product_name',
+ 'product_guid',
+ 'product_type_guid',
+ ])->select();
+ foreach ($products as $value) {
+ $product_guid = $value['product_guid'];
+ $product_type_guid = $value['product_type_guid'];
+ $id = $product_guid;
+ $data[] = [
+ 'id' => $id,
+ 'parent_id' => $product_type_guid,
+ 'name' => $value['product_name'],
+ 'type' => 'product',
+ 'data' => [
+ 'product_type_guid' => $product_type_guid,
+ 'product_guid' => $product_guid
+ ],
+ ];
+
+ $product_parts = ModelProductParts::field([
+ 'product_parts_name',
+ 'product_parts_guid',
+ 'product_guid',
+ ])->where('product_guid', $product_guid)->select();
+ foreach ($product_parts as $value) {
+ $product_parts_guid = $value['product_parts_guid'];
+ $product_guid = $value['product_guid'];
+ $id = $product_parts_guid;
+ $data[] = [
+ 'id' => $id,
+ 'parent_id' => $product_guid,
+ 'name' => $value['product_parts_name'],
+ 'type' => 'product_parts',
+ 'data' => [
+ 'product_type_guid' => $product_type_guid,
+ 'product_guid' => $product_guid,
+ 'product_parts_guid' => $product_parts_guid
+ ],
+ ];
+ }
+ }
+ // return $data;
+ $Traverse = new Traverse('id', 'parent_id');
+ return $Traverse->tree($data, '0', function (array $value) {
+ return $value;
+ });
+ return [
+ 'code' => 0,
+ 'data' => $data,
+ 'msg' => 'ok'
+ ];
+ }
+}
diff --git a/app/admin/controller/Tdk/Tdk.php b/app/admin/controller/Tdk/Tdk.php
new file mode 100644
index 0000000..8b044db
--- /dev/null
+++ b/app/admin/controller/Tdk/Tdk.php
@@ -0,0 +1,133 @@
+param();
+ $con = [];
+
+ if (isset($params['tdk_type']) && $params['tdk_type']) {
+ $con[] = ['tdk_type', '=', $params['tdk_type']];
+ };
+ if (isset($params['tdk_title']) && $params['tdk_title']) {
+ $con[] = ['tdk_title', 'LIKE', '%' . $params['tdk_title'] . '%'];
+ };
+
+ $query = ModelTdk::where($con);
+ $select = self::pageWrapper($query)
+ ->field([
+ 'tdk_id',
+ 'tdk_guid',
+ 'tdk_type',
+ 'tdk_title',
+ 'tdk_description',
+ 'tdk_keyword'
+ ])
+ ->order('tdk_update_time', 'desc')
+ ->select();
+
+ $count = $query->count();
+
+ return [
+ 'code' => 0,
+ 'data' => $select,
+ 'count' => $count,
+ 'msg' => 'ok'
+ ];
+ }
+
+ /**
+ * 编辑网站tdk
+ */
+ public function editTdk(Request $request): array
+ {
+ $params = $request->param();
+ $this->validate($params, [
+ 'tdk_type|tdk所属模块' => 'require',
+ 'tdk_title|网页标题' => 'require',
+ 'tdk_description|网页简介' => 'require',
+ 'tdk_keyword|网页关键词' => 'require'
+ ]);
+ $model = ModelTdk::where('tdk_guid', $params['tdk_guid'])->find();
+ if (!$model) throwErrorMsg("该网站tdk不存在", 1);
+ $model->allowField([
+ 'tdk_update_user_guid',
+ 'tdk_type',
+ 'tdk_title',
+ 'tdk_description',
+ 'tdk_keyword'
+ ])->save($params);
+ return [
+ 'code' => 0,
+ 'msg' => '编辑成功'
+ ];
+ }
+
+ /**
+ * 添加网站tdk
+ */
+ public function addTdk(Request $request): array
+ {
+ $params = $request->param();
+ $this->validate($params, [
+ 'tdk_type|tdk所属模块' => 'require',
+ 'tdk_title|网页标题' => 'require',
+ 'tdk_description|网页简介' => 'require',
+ 'tdk_keyword|网页关键词' => 'require'
+ ]);
+
+ $type = ModelTdk::where('tdk_type',$params['tdk_type'])->find();
+ if($type) throwErrorMsg("{$type->title} 已设置,请勿重复设置!");
+
+ $model = ModelTdk::create($params, [
+ 'tdk_guid',
+ 'tdk_create_user_guid',
+ 'tdk_update_user_guid',
+ 'tdk_type',
+ 'tdk_title',
+ 'tdk_description',
+ 'tdk_keyword'
+ ]);
+ return [
+ 'code' => 0,
+ 'msg' => '添加成功'
+ ];
+ }
+
+ /**
+ * 删除网站tdk
+ */
+ public function deleteTdk(Request $request): array
+ {
+ $params = $request->param();
+ $this->validate($params, [
+ 'tdk_guid' => 'require',
+ ]);
+ $tdk = ModelTdk::where([
+ 'tdk_guid' => explode(',', $params['tdk_guid'])
+ ])->select();
+ $tdk->delete();
+ return [
+ 'code' => 0,
+ 'msg' => "删除成功"
+ ];
+ }
+}
diff --git a/app/admin/controller/User/User.php b/app/admin/controller/User/User.php
new file mode 100644
index 0000000..0f80762
--- /dev/null
+++ b/app/admin/controller/User/User.php
@@ -0,0 +1,371 @@
+getCurrentUser();
+ return [
+ 'code' => 0,
+ 'data' => [
+ 'user_name' => $user['user_name']
+ ],
+ 'msg' => 'ok'
+ ];
+ }
+
+ /**
+ * 获取用户菜单
+ *
+ * @param Request $request
+ * @return array
+ * @date 2022-03-05
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function getUserMenu(Request $request)
+ {
+ // $user = $request->getCurrentUser();
+ // $menus = [];
+ // foreach ($user->roles as $role) {
+ // $menus = array_merge($menus, $role->menus->toArray());
+ // }
+
+ // array_multisort(array_column($menus, 'menu_order'), SORT_DESC, $menus);
+
+ // var_dump($menus);
+
+ $token = Token::getCurrent();
+ $menus = $token->token_menu;
+
+ $Traverse = new Traverse('menu_guid', 'menu_parent_guid');
+
+ $tree = $Traverse->tree($menus, '0', function ($v) {
+ return [
+ 'key' => $v['menu_guid'],
+ 'name' => $v['menu_name'],
+ 'url' => $v['menu_url'],
+ 'show' => $v['menu_show'],
+ 'icon' => $v['menu_icon'],
+ ];
+ });
+
+ return [
+ 'code' => 0,
+ 'msg' => 'ok',
+ 'data' => $tree
+ ];
+ }
+
+ /**
+ * 获取用户列表
+ *
+ * @param Request $request
+ * @date 2022-02-25
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function getUserList(Request $request): array
+ {
+ $user = $request->param('user');
+ $con = [
+ // 'user_status' => 1
+ ];
+ $search = [];
+ if ($user) {
+ $search['user_name'] = $user;
+ }
+
+ $query = ModelUser::scope(['admin'])
+ ->withSearch(array_keys($search), $search)->where($con)
+ ->leftjoin('user_role', 'user_role.user_guid = user.user_guid')
+ ->join('role', join(' AND ', [
+ 'role.role_guid = user_role.role_guid',
+ 'role.role_status = 1',
+ 'role.role_delete_time IS NULL',
+ ]), 'left')->where($con);
+ $select = self::pageWrapper($query)->field([
+ 'user.user_guid',
+ 'user.user_name',
+ 'user.user_phone',
+ 'user.user_position',
+ 'user.user_department',
+ 'user.user_img',
+ 'user.user_status',
+ 'role.role_name',
+ ])
+ ->append([
+ 'roles',
+ ])
+ ->group('user.user_guid')->order([
+ 'user.user_update_time' => 'desc'
+ ])->select();
+ $count = $query->count();
+ return [
+ 'code' => 0,
+ 'data' => $select,
+ 'count' => $count,
+ 'msg' => 'ok'
+ ];
+ }
+
+ /**
+ * 编辑用户
+ *
+ * @param Request $request
+ * @date 2022-02-28
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function editUser(Request $request): array
+ {
+ $params = Validate::param([
+ 'user_guid' => 'require',
+ 'user_name' => 'require',
+ 'roles|角色' => 'require',
+ ]);
+ $model = ModelUser::where([
+ 'user_guid' => $params['user_guid']
+ ])->find();
+ if (!$model) {
+ throwErrorMsg("用户不存在", 1);
+ }
+ $model->save($params);
+ ModelUser::editUserRole($params);
+ return [
+ 'code' => 0,
+ 'msg' => '编辑成功'
+ ];
+ }
+
+ /**
+ * 添加用户
+ *
+ * @param Request $request
+ * @date 2022-02-28
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function addUser(Request $request): array
+ {
+ $params = Validate::param([
+ 'user_name' => 'require',
+ 'user_password' => 'require',
+ 'roles|角色' => 'require',
+ ]);
+ $params['user_status'] = 1;
+
+ $model = ModelUser::create($params);
+
+ $user_guid = $model->user_guid;
+
+ ModelUser::addUserRole($user_guid, $params);
+
+ return [
+ 'code' => 0,
+ 'msg' => '添加成功'
+ ];
+ }
+
+ /**
+ * 删除用户
+ *
+ * @param Request $request
+ * @date 2022-02-28
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function deleteUser(Request $request): array
+ {
+ $params = $request->param();
+ $this->validate($params, [
+ 'user_guid' => 'require',
+ ]);
+ $users = ModelUser::where([
+ 'user_guid' => explode(',', $params['user_guid'])
+ ])->select();
+ $users->delete();
+ return [
+ 'code' => 0,
+ 'msg' => "删除成功"
+ ];
+ }
+
+ /**
+ * 更新用户状态
+ *
+ * @param Request $request
+ * @date 2022-02-28
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function updateUserStatus(Request $request): array
+ {
+ $params = $request->param();
+ $this->validate($params, [
+ 'user_guid' => 'require',
+ 'user_status' => 'require|in:1,2',
+ ]);
+ $user_status = $params['user_status'];
+ $users = ModelUser::where([
+ 'user_guid' => explode(',', $params['user_guid'])
+ ])->select();
+ $users->update([
+ 'user_status' => $user_status
+ ]);
+ return [
+ 'code' => 0,
+ 'msg' => "更新成功"
+ ];
+ }
+
+ /**
+ * 重置用户密码
+ *
+ * @param Request $request
+ * @date 2022-02-25
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function resetUserPassword(Request $request): array
+ {
+ $params = Validate::param([
+ 'user_guid' => 'require',
+ 'password' => 'require',
+ ]);
+ $password = $params['password'];
+ $users = ModelUser::select(explode(',', $params['user_guid']));
+ // $users = ModelUser::where([
+ // 'user_guid' => ['in', $params['user_guid']]
+ // ])->select();
+ $users->update([
+ 'user_password' => $password
+ ]);
+ return [
+ 'code' => 0,
+ 'msg' => "密码已重置为:$password ,请及时修改密码"
+ ];
+ }
+
+
+ /**
+ * 导出Excel
+ */
+ public function exportExcel(Request $request)
+ {
+ $user = $request->param('user');
+ $con = [
+ // 'user_status' => 1
+ ];
+ $search = [];
+ if ($user) {
+ $search['user_name'] = $user;
+ }
+
+ $select = ModelUser::scope(['admin'])
+ ->where($con)->field([
+ 'user.user_guid',
+ 'user.user_name',
+ 'user.user_phone',
+ 'user.user_position',
+ 'user.user_department',
+ 'user.user_img',
+ 'role.role_name',
+ ])
+ ->append([
+ 'roles',
+ ])
+ ->withSearch(array_keys($search), $search)->where($con)
+ ->leftjoin('user_role', 'user_role.user_guid = user.user_guid')
+ ->join('role', join(' AND ', [
+ 'role.role_guid = user_role.role_guid',
+ 'role.role_status = 1',
+ 'role.role_delete_time IS NULL',
+ ]), 'left')
+ ->group('user.user_guid')->order([
+ 'user.user_update_time' => 'desc'
+ ])->select();
+
+ return ModelUser::exportExcel($select);
+ }
+
+ /**
+ * 下载导入模板
+ */
+ public function downloadTemplate(Request $request)
+ {
+ $params = $request->param();
+ $data = [
+ [
+ '用户名',
+ '头像',
+ '角色',
+ '手机号',
+ '密码',
+ ]
+ ];
+
+ $data[] = [
+ '负责人',
+ 'https://img13.360buyimg.com/n5/jfs/t1/195344/2/24691/95759/6295e145E6fae20b9/1172b44c351eeaab.jpg.avif',
+ '管理员,部门负责人',
+ '10086',
+ '123456@aerwen',
+ ];
+
+ $excel = (new Excel())->exporTsheet($data);
+ $excel->save('用户导入模板.xlsx');
+ }
+
+ /**
+ * 导入excel
+ */
+ public function importExcel(Request $request)
+ {
+ $file = new UploadFile('uploads', 'fileExt:xlsx');
+ $file->putFile('User');
+
+ $msg = ModelUser::importExcel($file);
+
+ return [
+ 'code' => 0,
+ 'msg' => $msg
+ ];
+ }
+
+}
diff --git a/app/admin/controller/User/UserRole.php b/app/admin/controller/User/UserRole.php
new file mode 100644
index 0000000..971e593
--- /dev/null
+++ b/app/admin/controller/User/UserRole.php
@@ -0,0 +1,153 @@
+param('role_guid');
+ $user = $request->param('user');
+ $this->validate($request->param(), [
+ 'role_guid' => 'require'
+ ]);
+ $user_guids = [];
+ $con = [];
+ $users = ModelRole::getByRoleGuid($role_guid)->users;
+ if ($users) {
+ $user_guids = $users->column('user_guid');
+ }
+ if ($user) {
+ $con['user_name'] = $user;
+ }
+ $query = ModelUser::withSearch(['user_name'], $con)->where($con)->where('user_guid', 'not in', $user_guids);
+ $select = self::pageWrapper($query)->field([
+ 'user_name',
+ 'user_guid',
+ ])->order([
+ 'user_id' => 'desc'
+ ])->select();
+ $count = $query->count();
+ return [
+ 'code' => 0,
+ 'data' => $select,
+ 'count' => $count,
+ 'msg' => 'ok'
+ ];
+ }
+ /**
+ * 获取绑定角色用户列表
+ *
+ * @param Request $request
+ * @return void
+ * @date 2022-03-02
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function getBindUserList(Request $request): array
+ {
+ $role_guid = $request->param('role_guid');
+ $user = $request->param('user');
+ $this->validate($request->param(), [
+ 'role_guid' => 'require'
+ ]);
+ $user_ids = [];
+ $con = [];
+ $users = ModelRole::getByRoleGuid($role_guid)->users;
+ if ($users) {
+ $user_ids = $users->column('user_id');
+ }
+ if ($user) {
+ $con['user_name'] = $user;
+ }
+ $query = ModelUser::withSearch(['user_name'], $con)->where($con)->where('user_id', 'in', $user_ids);
+ $select = self::pageWrapper($query)->field([
+ 'user_name',
+ 'user_guid',
+ ])->order([
+ 'user_id' => 'desc'
+ ])->select();
+ $count = $query->count();
+ return [
+ 'code' => 0,
+ 'data' => $select,
+ 'count' => $count,
+ 'msg' => 'ok'
+ ];
+ }
+
+ /**
+ * 绑定用户角色
+ *
+ * @param Request $request
+ * @date 2022-03-02
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function bindUserRole(Request $request): array
+ {
+ $params = $request->param();
+ $this->validate($params, [
+ 'user_guid' => 'require',
+ 'role_guid' => 'require',
+ ]);
+ $users = array_map(function ($v) {
+ return ModelUser::getByUserGuid($v);
+ }, explode(',', $params['user_guid']));
+ $roles = array_map(function ($v) {
+ return ModelRole::getByRoleGuid($v);
+ }, explode(',', $params['role_guid']));
+ ModelUserRole::bindUserRole($users, $roles);
+ return [
+ 'code' => 0,
+ 'msg' => '绑定成功'
+ ];
+ }
+
+ /**
+ * 绑定用户角色
+ *
+ * @param Request $request
+ * @date 2022-03-02
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function unBindUserRole(Request $request): array
+ {
+ $params = $request->param();
+ $this->validate($params, [
+ 'user_guid' => 'require',
+ 'role_guid' => 'require',
+ ]);
+ $users = array_map(function ($v) {
+ return ModelUser::getByUserGuid($v);
+ }, explode(',', $params['user_guid']));
+ $roles = array_map(function ($v) {
+ return ModelRole::getByRoleGuid($v);
+ }, explode(',', $params['role_guid']));
+ ModelUserRole::unbindUserRole($users, $roles);
+ return [
+ 'code' => 0,
+ 'msg' => '解绑成功'
+ ];
+ }
+}
diff --git a/app/admin/middleware/Auth.php b/app/admin/middleware/Auth.php
new file mode 100644
index 0000000..c67b42f
--- /dev/null
+++ b/app/admin/middleware/Auth.php
@@ -0,0 +1,84 @@
+ ['*'],
+ 'admin' => ['*'],
+ ];
+
+ /**
+ * 处理请求
+ *
+ * @param \app\Request $request
+ * @param \Closure $next
+ * @return Response
+ */
+ public function handle(Request $request, \Closure $next): Response
+ {
+ $this->request = $request;
+
+ if ($this->isIgnoreLogin()) {
+ return $next($request);
+ }
+
+ if ($request->isLogin()) {
+ $user = $request->getCurrentUser();
+ if ($user->user_admin) {
+ return $next($request);
+ }
+ }
+
+ $api = join('/', [
+ $request->controller(),
+ $request->action()
+ ]);
+
+ $token = Token::getCurrent();
+
+ /**
+ * 缓存接口权限验证
+ */
+ $validate = $this->validateApi($token->token_api);
+ /**
+ * 缓存权限验证不通过
+ */
+ if ($validate === false) {
+ /**
+ * 自定义验证权限
+ * 如果验证通过刷新缓存接口
+ */
+ $validate = $this->validateUser();
+ if ($validate) {
+ Token::getCurrentUser()->login();
+ }
+ }
+ /**
+ * 权限验证通过
+ */
+ if ($validate) {
+ return $next($request);
+ }
+ throw new NotAuthApi("未授权接口 [ $api ]", 1);
+ }
+}
diff --git a/app/admin/route.php b/app/admin/route.php
new file mode 100644
index 0000000..90d0835
--- /dev/null
+++ b/app/admin/route.php
@@ -0,0 +1,12 @@
+ [
+ // 接口鉴权
+ \app\admin\middleware\Auth::class
+ ],
+];
diff --git a/app/api/controller/Crawler/Crawler.php b/app/api/controller/Crawler/Crawler.php
new file mode 100644
index 0000000..aef3bea
--- /dev/null
+++ b/app/api/controller/Crawler/Crawler.php
@@ -0,0 +1,112 @@
+request('GET', $url);
+
+ // 选择要解析的HTML元素
+ $titles = $crawler->filter('.productsBody .productItem .products');
+ $imgs = $crawler->filter('.productsBody .productItem .products .productImg');
+ // return $titles;
+ // dump($titles);
+ // die;
+
+ $title111 = $titles->filter('.productType')->text();
+ $img111 = $titles->filter('.productImg')->eq(2)->attr('lazyload');
+ // return $img111;
+ // return $title111;
+
+
+ // 在全部元素中筛选
+ // $products = $crawler->filter('.productsBody .productItem .products');
+
+ // foreach ($products as $key => $value) {
+ // // dump($value->filter('.productType')->eq($key)->text());
+ // // die;
+ // foreach ($titles->filter('.productType')->eq($key) as $key => $value) {
+ // $title = $value->textContent;
+ // }
+
+ // // return $title;
+ // }
+
+
+ $titleArr = [];
+ // 遍历元素并输出结果
+ foreach ($titles as $title) {
+ $titleArr[] = $title->textContent;
+ }
+
+ return $titleArr;
+
+ // $imgArr = [];
+ foreach ($imgs as $img) {
+ // var_dump($img->getAttribute('lazyload'));
+ // die;
+ // return $img->getAttribute('lazyload');
+ $src = $img->getAttribute('lazyload');
+
+ if (strpos($src, 'http') === 0) {
+ $img_url = $src;
+ } else {
+ $img_url = $base_url . $src;
+ }
+ // return $img_url;
+
+ // 文件夹名称
+ $dirName = "product" . "Img";
+ // 文件保存位置
+ $fileSaveLocation = public_path('uploads') . $dirName . "\\";
+ // return $fileSaveLocation;
+ if (true !== $res = Tool::mkdir($fileSaveLocation)) {
+ return $res;
+ }
+
+ // 获取图片二进制数据
+ $imageContent = file_get_contents($img_url);
+ // var_dump($imageContent);
+ // die;
+ // return $imageContent;
+
+ // 保存图片到本地文件系统
+ file_put_contents($fileSaveLocation . basename($img_url), $imageContent);
+
+ $res_img_url = "/uoloads" . "/" . $dirName . "/" . basename($img_url);
+ return $res_img_url;
+ }
+
+
+ // return $imgArr;
+ return $titleArr;
+ }
+}
diff --git a/app/api/controller/Flow/Flow.php b/app/api/controller/Flow/Flow.php
new file mode 100644
index 0000000..2a7bbfc
--- /dev/null
+++ b/app/api/controller/Flow/Flow.php
@@ -0,0 +1,33 @@
+param();
+ $this->validate($params, ['flow_target' => 'require']);
+ $flow_target = $params['flow_target'];
+
+ try {
+ return (new ModelFlow)->track($flow_target);
+ return json(msg("添加流量访问记录成功"));
+ } catch (\Throwable $th) {
+ throwErrorMsg("错误信息:" . $th);
+ }
+ }
+
+}
diff --git a/app/api/controller/Login.php b/app/api/controller/Login.php
new file mode 100644
index 0000000..f6704b5
--- /dev/null
+++ b/app/api/controller/Login.php
@@ -0,0 +1,144 @@
+middleware[SessionInit::class] = [
+ 'only' => ['accountLogin', 'getCaptcha']
+ ];
+ }
+
+ /**
+ * 验证token
+ *
+ * @param Request $request
+ * @return void
+ */
+ public function validateToken(Request $request)
+ {
+ $token = $request->getCurrentToken();
+ return [
+ 'code' => 0,
+ 'data' => [
+ 'exp_time' => $token->token_exp_time,
+ ],
+ 'msg' => 'ok'
+ ];
+ }
+
+ /**
+ * 西北政法大学单点登陆
+ *
+ * @param Request $request
+ * @return Response
+ * @date 2023-01-04
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function casOauthLogin(Request $request): Response
+ {
+ $url = $request->param('url');
+ return LogicLogin::casOauthLogin(
+ LogicLogin::casOauthLoginHandle($url)
+ );
+ }
+
+ /**
+ * 西北政法大学单点登出
+ *
+ * @param Request $request
+ * @return Response
+ * @date 2023-01-04
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function casOauthLogout(Request $request): Response
+ {
+ $url = $request->param('url');
+ return LogicLogin::casOauthLogout($url);
+ }
+
+ /**
+ * 用户账号登录
+ *
+ * @param Request $request
+ * @date 2022-03-05
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function accountLogin(Request $request): array
+ {
+ $param = Validate::param([
+ 'account|账号' => 'require',
+ 'password|密码' => 'require',
+ 'captcha|验证码' => $request->isProd() ? 'require|captcha' : false
+ ]);
+ $token = LogicLogin::accountLogin(
+ $param['account'],
+ $param['password']
+ );
+
+ return [
+ 'code' => 0,
+ 'data' => [
+ 'token' => $token->token_content,
+ 'exp_time' => $token->token_exp_time,
+ ],
+ 'msg' => 'ok'
+ ];
+ }
+
+ /**
+ * 生成验证码
+ *
+ * @date 2022-03-05
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function getCaptcha()
+ {
+ return Captcha::create();
+ }
+
+ /**
+ * 用户登出
+ *
+ * @param Request $request
+ * @date 2022-03-09
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function userLogout(Request $request): array
+ {
+ $token = $request->getCurrentToken();
+ $token->logout();
+ return [
+ 'code' => 0,
+ 'msg' => '登出成功'
+ ];
+ }
+}
diff --git a/app/api/controller/News/News.php b/app/api/controller/News/News.php
new file mode 100644
index 0000000..f2185c9
--- /dev/null
+++ b/app/api/controller/News/News.php
@@ -0,0 +1,88 @@
+param();
+ $con = [];
+
+ $con = Tool::getOptionalQuery(['news_type', '='],);
+
+ $query = ModelNews::where($con)
+ ->field([
+ 'news_id',
+ 'news_guid',
+ 'news_title',
+ 'news_intro',
+ 'news_type',
+ 'news_img',
+ 'news_create_time',
+ ])
+ ->append(['news_created_time'])
+ ->hidden(['news_create_time'])
+ // ->append('news_create_time')
+ ->order('news_create_time', 'desc');
+
+
+
+ return msg("获取新闻列表成功!", $query);
+ }
+
+ /**
+ * 获取新闻详情
+ */
+ public function getNewsInfo(Request $request): array
+ {
+ $params = $request->param();
+
+ $this->validate($params, ['news_id' => 'require']);
+
+ $find = ModelNews::field([
+ 'news_id',
+ 'news_guid',
+ 'news_title',
+ 'news_author',
+ 'news_intro',
+ 'news_type',
+ 'news_img',
+ 'news_content',
+ 'news_num',
+ 'news_create_time'
+ ])
+ ->where('news_id', $params['news_id'])
+ ->find();
+ if (!$find) throwErrorMsg('该新闻不存在!');
+
+ $last_next_id = Tool::getLastNextId(
+ new ModelNews,
+ ['news_id', $find->news_id],
+ 'all',
+ [['news_type', '=', $find->news_type]]
+ );
+
+ return msg(0, '获取新闻详情成功!', [
+ 'data' => $find,
+ 'last_id' => $last_next_id[0],
+ 'next_id' => $last_next_id[1],
+ ]);
+ }
+}
diff --git a/app/api/controller/Product/Product.php b/app/api/controller/Product/Product.php
new file mode 100644
index 0000000..2f2b572
--- /dev/null
+++ b/app/api/controller/Product/Product.php
@@ -0,0 +1,107 @@
+param();
+ $this->validate($params, [
+ 'product_type_id|产品系列' => 'require',
+ ]);
+
+ $product_type = ModelProductType::where('product_type_id', $params['product_type_id'])->find();
+ if (!$product_type) throwErrorMsg('产品系列不存在!');
+
+ $con = [
+ ['product_type_guid', '=', $product_type->product_type_guid]
+ ];
+
+ $query = ModelProduct::where($con)
+ ->field([
+ 'product_id',
+ 'product_guid',
+ 'product_type_guid',
+ 'product_name',
+ 'product_img',
+ 'product_price',
+ ])
+ ->withAttr('product_img', function ($value, $data) {
+ return explode(',', $data['product_img'])[0];
+ })
+ ->order('product_id', 'desc');
+
+ return msg("获取产品列表成功!", $query);
+ }
+
+ /**
+ * 获取产品详情接口
+ *
+ * @param Request request
+ * @date 2023-04-03
+ * @example
+ * @author xjh
+ * @since 1.0.0
+ */
+ public function getProductInfo(Request $request)
+ {
+ $params = $request->param();
+ $this->validate($params, [
+ 'product_id|产品id' => 'require',
+ ]);
+
+ $find = ModelProduct::where('product_id', $params['product_id'])
+ ->field([
+ 'product_id',
+ 'product_guid',
+ 'product_type_guid',
+ 'product_name',
+ 'product_img',
+ 'product_price',
+ 'product_params',
+ 'product_details',
+ ])
+ ->find();
+ if (!$find) throwErrorMsg('该产品不存在!');
+
+ $last_next_id = Tool::getLastNextId(
+ new ModelProduct,
+ ['product_id', $find->product_id],
+ 'all',
+ [['product_type_guid', '=', $find->product_type_guid]]
+ );
+
+ return msg(
+ "获取产品详情成功!",
+ [
+ 'data' => $find,
+ 'last_id' => $last_next_id[0],
+ 'next_id' => $last_next_id[1],
+ ]
+ );
+ }
+}
diff --git a/app/api/controller/Product/ProductType.php b/app/api/controller/Product/ProductType.php
new file mode 100644
index 0000000..2bfa84f
--- /dev/null
+++ b/app/api/controller/Product/ProductType.php
@@ -0,0 +1,68 @@
+ "product_type_parent_name",
+ 'a.product_type_order',
+ 'a.product_type_guid',
+ 'a.product_type_id',
+ 'a.product_type_icon',
+ 'a.product_type_cover',
+ ])
+ ->alias('a')
+ ->leftjoin('product_type b', 'a.product_type_parent_guid = b.product_type_guid')
+ ->where($con)
+ ->order(['product_type_order' => 'asc'])
+ ->select()->toArray();
+
+ $Traverse = new Traverse('product_type_guid', 'product_type_parent_guid');
+ $product_type_tree = $Traverse->tree($product_type, '0', function ($v) {
+ return [
+ 'product_type_name' => $v['product_type_name'],
+ 'product_type_parent_name' => $v['product_type_parent_name'],
+ 'product_type_guid' => $v['product_type_guid'],
+ 'product_type_id' => $v['product_type_id'],
+ 'product_type_parent_guid' => $v['product_type_parent_guid'],
+ 'product_type_order' => $v['product_type_order'],
+ 'product_type_icon' => $v['product_type_icon'],
+ 'product_type_cover' => $v['product_type_cover'],
+ ];
+ });
+
+ return msg("获取产品系列列表成功!", $product_type_tree);
+ }
+}
diff --git a/app/api/controller/User/User.php b/app/api/controller/User/User.php
new file mode 100644
index 0000000..8cff71e
--- /dev/null
+++ b/app/api/controller/User/User.php
@@ -0,0 +1,130 @@
+ 'require',
+ 'user_phone' => 'require|mobile',
+ 'user_id_card' => 'require'
+ ]);
+ $user = $request->getCurrentUser();
+ $user->allowField([
+ 'user_id_card',
+ 'user_phone',
+ 'user_name'
+ ]);
+ $user->save($param);
+ return [
+ 'code' => 0,
+ 'msg' => '更新成功'
+ ];
+ }
+ /**
+ * 获取用户列表
+ */
+ public function getUserList(Request $request): array
+ {
+ $user = $request->param('user');
+ $con = [
+ 'user_status' => UserUser::STATUS_ENABLE
+ ];
+ $search = [];
+ if ($user) {
+ $search['user_name'] = $user;
+ }
+ $query = UserUser::withSearch(
+ array_keys($search),
+ $search
+ )->where($con);
+ $select = self::pageWrapper($query)->field([
+ 'user.user_guid',
+ 'user.user_name',
+ 'user.user_phone',
+ 'user.user_position',
+ 'user.user_department',
+ ])->group('user.user_guid')->order([
+ 'user_id' => 'desc'
+ ])->select();
+ $count = $query->count();
+ return [
+ 'code' => 0,
+ 'data' => $select,
+ 'count' => $count,
+ 'msg' => 'ok'
+ ];
+ }
+ /**
+ * 获取用户信息
+ */
+ public function getUserInfo(Request $request): array
+ {
+ $user = $request->getCurrentUser();
+ $token = $request->getCurrentToken();
+ return [
+ 'code' => 0,
+ 'data' => [
+ 'user_img' => $user->user_img,
+ 'user_name' => $user->user_name,
+ 'user_phone' => $user->user_phone,
+ 'user_position' => $user->user_position,
+ 'user_department' => $user->user_department,
+ 'user_create_name' => $user->user_create_name,
+ 'user_department' => $user->user_department,
+ 'user_sex' => $user->user_sex,
+ 'token_update_time' => $token->token_update_time,
+ ],
+ 'msg' => 'ok'
+ ];
+ }
+
+ /**
+ * 更新用户头像
+ */
+ public function updateUserImg(Request $request): array
+ {
+ $user = $request->getCurrentUser();
+ $upload = new UploadFile('uploads', 'image');
+ $path = $upload->putFile('UserImg');
+ $user->user_img = $path;
+ $user->save();
+ return [
+ 'code' => 0,
+ 'msg' => '更新成功'
+ ];
+ }
+
+ /**
+ * 更新用户密码
+ */
+ public function updateUserPassword(Request $request): array
+ {
+ $param = Validate::param([
+ 'origin' => 'require',
+ 'new' => 'require',
+ ]);
+ $user = $request->getCurrentUser();
+ $origin = UserUser::encryptPassword($param['origin']);
+ if ($origin != $user->user_password) {
+ throwErrorMsg('原密码错误');
+ }
+ $user->user_password = $param['new'];
+ $user->save();
+ return [
+ 'code' => 0,
+ 'msg' => '修改成功'
+ ];
+ }
+}
diff --git a/app/api/middleware/Auth.php b/app/api/middleware/Auth.php
new file mode 100644
index 0000000..4c16d83
--- /dev/null
+++ b/app/api/middleware/Auth.php
@@ -0,0 +1,85 @@
+ ['*'],
+ 'admin' => ['*'],
+ 'Consult' => ['*'],
+ ];
+
+ /**
+ * 处理请求
+ *
+ * @param \app\Request $request
+ * @param \Closure $next
+ * @return Response
+ */
+ public function handle(Request $request, \Closure $next): Response
+ {
+ $this->request = $request;
+
+ if ($this->isIgnoreLogin()) {
+ return $next($request);
+ }
+
+ if ($request->isLogin()) {
+ $user = $request->getCurrentUser();
+ if ($user->user_admin) {
+ return $next($request);
+ }
+ }
+
+ $api = join('/', [
+ $request->controller(),
+ $request->action()
+ ]);
+
+ $token = Token::getCurrent();
+
+ /**
+ * 缓存接口权限验证
+ */
+ $validate = $this->validateApi($token->token_api);
+ /**
+ * 缓存权限验证不通过
+ */
+ if ($validate === false) {
+ /**
+ * 自定义验证权限
+ * 如果验证通过刷新缓存接口
+ */
+ $validate = $this->validateUser();
+ if ($validate) {
+ Token::getCurrentUser()->login();
+ }
+ }
+ /**
+ * 权限验证通过
+ */
+ if ($validate) {
+ return $next($request);
+ }
+ throw new NotAuthApi("未授权接口 [ $api ]", 1);
+ }
+}
diff --git a/app/api/route.php b/app/api/route.php
new file mode 100644
index 0000000..4198024
--- /dev/null
+++ b/app/api/route.php
@@ -0,0 +1,12 @@
+ [
+ // 接口鉴权
+ // \app\api\middleware\Auth::class
+ ],
+];
diff --git a/app/common.php b/app/common.php
new file mode 100644
index 0000000..14fbb3b
--- /dev/null
+++ b/app/common.php
@@ -0,0 +1,178 @@
+\n(\s+)/m', '] => ', ob_get_clean());
+
+ if (!extension_loaded('xdebug')) {
+ $output = htmlspecialchars($output, $flags);
+ }
+
+ $output = '
' . $label . $output . '
';
+
+ if ($echo) {
+ echo ($output);
+ return;
+ }
+
+ return $output;
+}
+
+/**
+ * 抛出业务异常
+ *
+ * @param string $msg
+ * @param integer $code
+ * @return void
+ * @throws ErrorMsg
+ * @date 2022-12-27
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+function throwErrorMsg(string $msg, int $code = 1): void
+{
+ throw new ErrorMsg($msg, $code);
+}
+
+/**
+ * 计算年龄
+ *
+ * @param string $birthday Y-m-d
+ * @return int
+ * @date 2022-03-11
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+function sumAge($birthday): int
+{
+ $age = 0;
+ try {
+ if (!is_string($birthday)) {
+ throw new ErrorMsg("非法日期", 1);
+ }
+ $birthday = explode("-", date('Y-m-d', strtotime($birthday)));
+ $date = explode("-", date('Y-m-d'));
+ if (count($birthday) === 3) {
+ list($y, $m, $d) = $birthday;
+ } else {
+ list($y, $m, $d) = $date;
+ }
+ // list($y, $m, $d) = $birthday;
+ list($dy, $dm, $dd) = $date;
+
+ $age = $dy - $y;
+ if ($dm >= $m) {
+ if ($dm == $m) {
+ if ($dd >= $d) {
+ } else {
+ $age--;
+ }
+ }
+ } else {
+ $age--;
+ }
+ return $age;
+ } catch (\Throwable $th) {
+ }
+ return $age;
+}
+
+Validate::maker(function (ThinkValidate $validate) {
+ $validate->extend(
+ 'string',
+ function ($value) {
+ return is_string($value);
+ },
+ ':attribute 数据类型非法 不是字符串'
+ );
+});
+
+/**
+ * 接口返回封装
+ */
+function msg(...$arr)
+{
+ $msg_data = ['code' => 0];
+ $default_code_msg = [0 => '操作成功!', 1 => '操作失败!'];
+
+ //data数据构建
+ function constructData(&$msg_data, $code, $msg_str, &$arr2)
+ {
+ $msg_data['code'] = $code;
+ $msg_data['msg'] = $msg_str;
+ if (is_array($arr2)) { //数组
+ $msg_data['data'] = $arr2;
+ }
+ if (is_object($arr2)) { //查询对象
+ $obj = $arr2;
+ $soft_delete_field = '_delete_time'; //软删除默认后缀
+ //模型层 || Db层
+ if ($obj instanceof think\db\Query || $obj instanceof think\Collection) {
+ //join软删除字段过滤补全(暂时只适应软删除为 【前缀名_delete_time】存储为datetime 的设计)
+ if (isset($obj->getOptions()['join']) && $join_data = $obj->getOptions('join')) {
+ $join_soft_delete = [];
+ foreach ($join_data as $key => $join) {
+ $join_table_name = is_array($join[0]) ? array_keys($join[0])[0] : $join[0]; //联表表名
+ $join_soft_delete[] = [$join_table_name . $soft_delete_field, 'NULL', null];
+ }
+ $obj = $obj->where($join_soft_delete);
+ }
+ //select()、count()补全
+ $msg_data['data'] = $obj->page((int) Request::param('page', 1), (int) Request::param('limit', 10))->select();
+ $msg_data['count'] = $obj->count();
+ } else {
+ return ['code' => 444, 'msg' => '对象只允许传递来自think\db\Query类和instanceof think\Collection类的实例!'];
+ }
+ }
+ };
+
+ switch (count($arr)) {
+ case 1: //单参数
+ constructData($msg_data, 0, '查询成功!', $arr[0],);
+ if (is_string($arr[0])) $msg_data['msg'] = $arr[0];
+ if (is_int($arr[0])) {
+ $msg_data['code'] = $arr[0];
+ $msg_data['msg'] = $default_code_msg[$arr[0]];
+ }
+ break;
+ case 2: //双参数
+ if (is_int($arr[0]) && is_string($arr[1])) {
+ $msg_data['code'] = $arr[0];
+ $msg_data['msg'] = $arr[1];
+ } else {
+ constructData($msg_data, 0, $arr[0], $arr[1]);
+ }
+ break;
+ case 3: //三参数
+ $msg_data['code'] = $arr[0];
+ $msg_data['msg'] = $arr[1];
+ if (is_object($arr[2])) {
+ $msg_data['data'] = $arr[2];
+ } else {
+ foreach ($arr[2] as $key => $val)
+ $msg_data[$key] = $val;
+ }
+ break;
+ default:
+ return ['code' => 444, 'msg' => 'msg()最多参数只允许3个!'];
+ }
+
+ return $msg_data;
+}
diff --git a/app/common/arw/adjfut/composer.json b/app/common/arw/adjfut/composer.json
new file mode 100644
index 0000000..7991c90
--- /dev/null
+++ b/app/common/arw/adjfut/composer.json
@@ -0,0 +1,39 @@
+{
+ "name": "arw/adjfut",
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "arw\\adjfut\\": "src/"
+ }
+ },
+ "authors": [{
+ "name": "arw",
+ "email": "2679599887@qq.com"
+ }],
+ "require": {
+ "php": ">=7.1.0",
+ "phpoffice/phpspreadsheet": "^1.12",
+ "topthink/framework": "^6.0.0",
+ "topthink/think-filesystem": "^2.0"
+ },
+ "config": {
+ "sort-packages": true
+ },
+ "repositories": {
+ "packagist": {
+ "type": "composer",
+ "url": "https://mirrors.aliyun.com/composer/"
+ }
+ },
+ "extra": {
+ "think": {
+ "services": [
+ "arw\\adjfut\\Service\\Validate"
+ ],
+ "config": {
+ "wechat": "src/Config/wechat.php",
+ "chunkUpload": "src/Config/chunkUpload.php"
+ }
+ }
+ }
+}
diff --git a/app/common/arw/adjfut/src/ArrayFilter.php b/app/common/arw/adjfut/src/ArrayFilter.php
new file mode 100644
index 0000000..dcd0ede
--- /dev/null
+++ b/app/common/arw/adjfut/src/ArrayFilter.php
@@ -0,0 +1,170 @@
+data = $data;
+ }
+
+ /**
+ * 二维数组排除字段
+ *
+ * @param array $array
+ * @param array $except
+ * @return array
+ * @date 2023-01-13
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public static function exceptFields(array $array, array $except): array
+ {
+ foreach ($array as &$value) {
+ $self = new self($value);
+ $value = $self->except($except)->toArray();
+ unset($value, $self);
+ }
+ return $array;
+ }
+
+ /**
+ * 二维数组指定字段
+ *
+ * @param array $array
+ * @param array $only
+ * @return array
+ * @date 2023-01-13
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public static function onlyFields(array $array, array $only): array
+ {
+ foreach ($array as &$value) {
+ $self = new self($value);
+ $value = $self->only($only)->toArray();
+ unset($value, $self);
+ }
+ return $array;
+ }
+
+ /**
+ * 排除某些变量
+ *
+ * @param array $fields
+ * @return self
+ * @date 2023-01-13
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function except(array $fields): self
+ {
+ $data = $this->data;
+ // $fields = [id,name]
+ foreach ($fields as $field) {
+ if (isset($data[$field])) {
+ unset($data[$field]);
+ }
+ }
+ return new self($data);
+ }
+
+ /**
+ * 获取部分变量
+ *
+ * @param array $fields
+ * @return self
+ * @date 2023-01-13
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function only(array $fields): self
+ {
+ $data = $this->data;
+ $result = [];
+ // $fields = [id,name]
+ // $fields = [id=>0,name=>asdasd]
+ foreach ($fields as $key => $value) {
+ if (is_int($key)) {
+ if (isset($data[$value])) {
+ $result[$value] = $data[$value];
+ }
+ } else {
+ $result[$key] = isset($data[$key]) ? $data[$key] : $value;
+ }
+ }
+ return new self($result);
+ }
+
+ /**
+ * 获取数据
+ *
+ * @return array
+ * @date 2023-01-13
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function toArray(): array
+ {
+ return $this->data;
+ }
+
+
+ public function __get($name)
+ {
+ return $this->offsetGet($name);
+ }
+
+ public function __set($name, $value)
+ {
+ return $this->offsetSet($name, $value);
+ }
+
+ public function offsetExists($offset): bool
+ {
+ return isset($this->data[$offset]);
+ }
+
+ public function offsetGet($offset)
+ {
+ return $this->data[$offset];
+ }
+
+ public function offsetSet($offset, $value): void
+ {
+ $this->data[$offset] = $value;
+ }
+
+ public function offsetUnset($offset): void
+ {
+ unset($this->data[$offset]);
+ }
+}
diff --git a/app/common/arw/adjfut/src/Base64.php b/app/common/arw/adjfut/src/Base64.php
new file mode 100644
index 0000000..4bb6653
--- /dev/null
+++ b/app/common/arw/adjfut/src/Base64.php
@@ -0,0 +1,174 @@
+ 'html',
+ 'text/css' => 'css',
+ 'text/javascript' => 'js',
+ 'image/gif' => 'gif',
+ 'image/png' => 'png',
+ 'image/jpeg' => 'jpg',
+ 'image/x-icon' => 'ico',
+ ];
+ /**
+ * 实例化
+ *
+ * @param string $base64
+ * @date 2023-01-09
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function __construct(string $base64)
+ {
+ $parse = self::parse($base64);
+ $this->base64 = $base64;
+ $this->type = $parse['type'];
+ $this->body = $parse['body'];
+ }
+
+ /**
+ * 获取文件后缀
+ *
+ * @return string
+ * @date 2023-01-09
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function getFileExt(): string
+ {
+ return Arr::get(self::FILE_EXT_MAP, $this->type, '');
+ }
+
+ /**
+ * 判断是否是base64图片字符串
+ *
+ * @return boolean
+ * @date 2023-01-09
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function isImage(): bool
+ {
+ return in_array($this->type, [
+ 'image/gif',
+ 'image/png',
+ 'image/jpeg',
+ 'image/x-icon',
+ ]);
+ }
+
+ /**
+ * 保存base64图片
+ *
+ * @param string $path
+ * @return void
+ * @date 2023-01-09
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function saveImage(string $path): void
+ {
+ file_put_contents($path, base64_decode($this->body));
+ }
+
+ /**
+ * 判断是否是base64图片字符串
+ *
+ * @param string $base64
+ * @return boolean
+ * @date 2023-01-09
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public static function isBase64Image(string $base64): bool
+ {
+ $ins = new self($base64);
+ return $ins->isImage();
+ }
+
+ /**
+ * 保存base64图片
+ *
+ * @param string $base64
+ * @param string $path
+ * @return void
+ */
+ public static function saveBase64Image(string $base64, string $path): void
+ {
+ $ins = new self($base64);
+ $ins->saveImage($path);
+ }
+
+ /**
+ * 解析base64
+ *
+ * @param string $base64
+ * @return array
+ * @date 2023-01-09
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ private static function parse(string $base64): array
+ {
+ $prefix = 'data:';
+ $validate = substr($base64, 0, strlen($prefix)) === $prefix;
+ if (!$validate) {
+ throw new ErrorMsg("非法base64 开头data:", 1);
+ }
+ $explode = explode(';base64,', $base64);
+ if (count($explode) != 2) {
+ throw new ErrorMsg("非法base64 不存在;base64,", 1);
+ }
+ list($type, $body) = $explode;
+ $type = str_replace('data:', '', $type);
+ return [
+ 'type' => $type,
+ 'body' => $body
+ ];
+ }
+}
diff --git a/app/common/arw/adjfut/src/ChunkUpload.php b/app/common/arw/adjfut/src/ChunkUpload.php
new file mode 100644
index 0000000..1990fc2
--- /dev/null
+++ b/app/common/arw/adjfut/src/ChunkUpload.php
@@ -0,0 +1,258 @@
+id = $id;
+ if (is_string($file)) {
+ $file = Request::file($file);
+ }
+ if (is_array($file)) {
+ $file = new UploadedFile(...$file);
+ }
+ if (!($file instanceof UploadedFile)) {
+ throw new ErrorMsg("非法文件", 1);
+ }
+ $this->file = $file;
+ }
+
+ /**
+ * 获取存储配置
+ *
+ * @return Driver
+ * @date 2022-08-21
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function getDisk(): Driver
+ {
+ return Filesystem::disk(self::$disk);
+ }
+
+ /**
+ * 分片是否上传完成
+ *
+ * @return boolean
+ * @date 2022-08-22
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function isDone(): bool
+ {
+ return $this->isDone;
+ }
+
+ /**
+ * 合并文件
+ *
+ * @return self
+ * @date 2022-08-21
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function merge(string $name): self
+ {
+ if (!$this->isDone()) {
+ throw new ErrorMsg("文件未上传完成 禁止合并", 1);
+ }
+
+ $id = $this->id;
+
+ $root = self::getDiskConfig('root');
+
+ $path = join('', [
+ $root,
+ $id,
+ '.' . pathinfo($name, PATHINFO_EXTENSION)
+ ]);
+
+ for ($i = 0; $i <= $this->total; $i++) {
+ $_path = $root . $id . DIRECTORY_SEPARATOR . $i;
+ file_put_contents(
+ $path,
+ file_get_contents($_path),
+ FILE_APPEND
+ );
+ unlink($_path);
+ }
+ rmdir($root . $id);
+ $this->mergeFile = new UploadedFile($path, $name);
+ return $this;
+ }
+
+ /**
+ * 获取合并文件
+ *
+ * @return UploadedFile
+ * @date 2022-08-22
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function getMergeFile(): UploadedFile
+ {
+ if (!$this->mergeFile) {
+ throw new ErrorMsg("未合并文件", 1);
+ }
+ return $this->mergeFile;
+ }
+
+ /**
+ * 获取上传文件
+ *
+ * @param string $diskName
+ * @param string $validate
+ * @return UploadFile
+ * @date 2022-08-22
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function getUploadFile(string $diskName, string $validate = ''): UploadFile
+ {
+ return new UploadFile(
+ $diskName,
+ $validate,
+ $this->getMergeFile()
+ );
+ }
+
+ /**
+ * 保存文件
+ *
+ * @param integer $index
+ * @param integer $total
+ * @return self
+ * @date 2022-08-21
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function put(int $index, int $total): self
+ {
+ $this->total = $total;
+ $this->getDisk()->putFileAs(
+ $this->id,
+ $this->file,
+ $index
+ );
+ $this->isDone = $index === $total;
+ return $this;
+ }
+
+ /**
+ * 删除文件
+ *
+ * @return void
+ * @date 2022-08-22
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function delete(): void
+ {
+ $this->getDisk()->delete($this->getMergeFile()->getFilename());
+ }
+
+ /**
+ * 初始化配置
+ *
+ * @param array $config
+ * @return void
+ * @date 2022-08-21
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public static function init(array $config): void
+ {
+ self::$disk = Arr::get($config, 'disk', '');
+ $type = self::getDiskConfig('type');
+ if ($type != 'local') {
+ throw new ErrorMsg("分片上传仅支持 local类型磁盘", 1);
+ }
+ }
+
+ /**
+ * 获取磁盘配置
+ *
+ * @param string $name
+ * @param mixed $default
+ * @return mixed
+ * @date 2022-08-22
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public static function getDiskConfig($name = null, $default = null)
+ {
+ return Filesystem::getDiskConfig(self::$disk, $name, $default);
+ }
+
+ /**
+ * 生成上传唯一id
+ *
+ * @return string
+ * @date 2022-08-21
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public static function generateUploadId(): string
+ {
+ return md5(join('-', [
+ Request::ip(),
+ time()
+ ]));
+ }
+}
diff --git a/app/common/arw/adjfut/src/Config/chunkUpload.php b/app/common/arw/adjfut/src/Config/chunkUpload.php
new file mode 100644
index 0000000..6a888bb
--- /dev/null
+++ b/app/common/arw/adjfut/src/Config/chunkUpload.php
@@ -0,0 +1,9 @@
+ ''
+];
diff --git a/app/common/arw/adjfut/src/Config/wechat.php b/app/common/arw/adjfut/src/Config/wechat.php
new file mode 100644
index 0000000..7c798be
--- /dev/null
+++ b/app/common/arw/adjfut/src/Config/wechat.php
@@ -0,0 +1,39 @@
+ '',
+ // 中控层地址
+ 'server_base' => 'http://clique.dszjjt.com:7001/',
+ // 公众号配置
+ 'gzh' => [
+ // 缓存key
+ 'cache_key' => 'wechat.gzh',
+ // 日志通道
+ 'log_channel' => 'wechat/gzh',
+ // 公众号模板推送
+ 'template' => [
+ // {{first.DATA}}
+ // 申请人:{{keyword1.DATA}}
+ // 申请时间:{{keyword2.DATA}}
+ // 调课时间:{{keyword3.DATA}}
+ // {{remark.DATA}}
+ '申请审核通知' => 'iLuqzUrQM8-v_7ilxageUZoi9v-SQWipkTYVZXB1GeI'
+ ],
+ 'appid' => 'wx99caf571c9fb1cc5',
+ 'secret' => '61100d9c4fff5acf97517f0c8799e2e2',
+ ],
+ // 小程序配置
+ 'xcx' => [
+ // 缓存key
+ 'cache_key' => 'wechat.xcx',
+ // 日志通道
+ 'log_channel' => 'wechat/xcx',
+
+ 'appid' => 'wx2495492e15854b02',
+ 'secret' => '5b4fcb27324398083fba4a3c39e8a557',
+ ],
+];
diff --git a/app/common/arw/adjfut/src/Curl.php b/app/common/arw/adjfut/src/Curl.php
new file mode 100644
index 0000000..22f65f6
--- /dev/null
+++ b/app/common/arw/adjfut/src/Curl.php
@@ -0,0 +1,790 @@
+ [],
+ /**
+ * http 超时
+ */
+ CURLOPT_TIMEOUT => 0,
+ /**
+ * http 代理
+ */
+ CURLOPT_PROXY => '',
+ /**
+ * http 代理端口
+ */
+ CURLOPT_PROXYPORT => '',
+ /**
+ * 来源页面
+ */
+ CURLOPT_REFERER => '',
+ /**
+ * 用户代理
+ */
+ CURLOPT_USERAGENT => '',
+ /**
+ * 响应中是否显示header
+ */
+ CURLOPT_HEADER => 0
+ ];
+ /**
+ * Get请求参数
+ *
+ * @var null
+ */
+ private $httpParams = null;
+ /**
+ * Post请求参数
+ *
+ * @var null
+ */
+ private $httpData = null;
+ /**
+ * 异常信息
+ *
+ * @var string
+ */
+ private $error = '';
+ /**
+ * url是否编码
+ *
+ * @var boolean
+ */
+ private $urlEncode = false;
+ /**
+ * 验证http请求状态码
+ *
+ * @var boolean
+ */
+ private $httpCode = true;
+ /**
+ * 请求路径
+ *
+ * @var string
+ */
+ private $path = '';
+ /**
+ * 请求url
+ *
+ * @var string
+ */
+ private $url = '';
+ /**
+ * 请求类型
+ *
+ * @var string
+ */
+ private $method = 'get';
+ /**
+ * 返回内容
+ *
+ * @var string
+ */
+ private $content = '';
+ /**
+ * 请求信息
+ *
+ * @var array
+ */
+ private $status = [];
+ /**
+ * 调试模式
+ *
+ * @var boolean
+ */
+ private $debug = false;
+ /**
+ * 是否上传文件
+ *
+ * @var boolean
+ */
+ private $isUploadFile = false;
+
+ /**
+ * 初始化
+ *
+ * @date 2022-03-24
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function __construct()
+ {
+ }
+
+ /**
+ * 初始化请求
+ *
+ * @return $this
+ */
+ public static function instance()
+ {
+ return new self;
+ }
+
+ /**
+ * 获取请求url
+ */
+ public function getUrl(): string
+ {
+ return $this->url;
+ }
+
+ /**
+ * 获取请求路径
+ */
+ public function getPath(): string
+ {
+ return $this->path;
+ }
+
+ /**
+ * 获取返回内容
+ *
+ * @return string
+ */
+ public function getContent(): string
+ {
+ return $this->content;
+ }
+
+ /**
+ * 格式化返回
+ *
+ * @param string $type 返回类型(默认使用 $this->dataType)
+ * @return mixed
+ * @date 2022-03-24
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function formatContent(string $type = '')
+ {
+ $content = $this->content;
+ if (!$type) {
+ $type = $this->dataType;
+ }
+ switch ($type) {
+ case 'json':
+ case 'object':
+ return json_decode($content, $type === 'json');
+ break;
+ default:
+ return $content;
+ break;
+ }
+ }
+
+ /**
+ * 获取请求信息
+ */
+ public function getStatus(): array
+ {
+ return $this->status;
+ }
+
+ /**
+ * 设置是否上传文件
+ */
+ public function setIsUploadFile(bool $isUploadFile): self
+ {
+ $this->isUploadFile = $isUploadFile;
+ return $this;
+ }
+
+ /**
+ * 设置请求路径
+ */
+ public function setPath(string $path): self
+ {
+ if (count(explode('?', $path)) != 1) {
+ throw new Exception("请求路径不能携带参数 请使用setParams设置请求参数", 1);
+ }
+ $this->path = $path;
+ return $this;
+ }
+
+ /**
+ * 设置调试模式
+ */
+ public function setDeBug(bool $debug): self
+ {
+ $this->debug = $debug;
+ return $this;
+ }
+
+ /**
+ * 验证http状态码
+ *
+ * @param boolean|int $code
+ */
+ public function setHttpCode($code): self
+ {
+ $this->httpCode = $code;
+ return $this;
+ }
+
+ /**
+ * 设置url是否编码
+ */
+ public function setUrlEncode(bool $urlEncode): self
+ {
+ $this->urlEncode = $urlEncode;
+ return $this;
+ }
+
+ /**
+ * 设置请求前缀
+ */
+ public function setBaseUrl(string $baseUrl): self
+ {
+ if (count(explode('?', $baseUrl)) != 1) {
+ throw new Exception("请求前缀不能携带参数 请使用setParams设置请求参数", 1);
+ }
+ $this->baseUrl = $baseUrl;
+ return $this;
+ }
+
+ /**
+ * 设置返回数据类型
+ */
+ public function setDataType(string $dataType): self
+ {
+ $this->dataType = $dataType;
+ return $this;
+ }
+
+ /**
+ * 设置http header
+ */
+ public function setHeader(array $header): self
+ {
+ $_header = [];
+ foreach ($header as $key => $value) {
+ if (is_numeric($key)) {
+ $_header[] = $value;
+ } else {
+ $_header[] = "$key: $value";
+ }
+ }
+ $this->config[CURLOPT_HTTPHEADER] = array_merge($this->config[CURLOPT_HTTPHEADER], $_header);
+ return $this;
+ }
+
+ /**
+ * 设置http 超时
+ */
+ public function setTimeout(int $time): self
+ {
+ $this->config[CURLOPT_TIMEOUT] = $time <= 0 ? 5 : $time;
+ return $this;
+ }
+
+ /**
+ * 设置http 代理
+ */
+ public function setProxy(string $proxy): self
+ {
+ $this->config[CURLOPT_PROXY] = $proxy;
+ return $this;
+ }
+
+ /**
+ * 设置http 代理端口
+ */
+ public function setProxyPort(int $port): self
+ {
+ $this->config[CURLOPT_PROXYPORT] = $port;
+ return $this;
+ }
+
+ /**
+ * 设置来源页面
+ */
+ public function setReferer(string $referer = ""): self
+ {
+ $this->config[CURLOPT_REFERER] = $referer;
+ return $this;
+ }
+
+ /**
+ * 设置用户代理
+ */
+ public function setUserAgent(string $agent = ""): self
+ {
+ $this->config[CURLOPT_USERAGENT] = $agent;
+ return $this;
+ }
+
+ /**
+ * http响应中是否显示header
+ */
+ public function showResponseHeader(bool $show): self
+ {
+ $this->config[CURLOPT_HEADER] = $show ? 1 : 0;
+ return $this;
+ }
+
+ /**
+ * 获取get请求参数
+ */
+ public function getParams()
+ {
+ return $this->httpParams;
+ }
+
+ /**
+ * 设置get请求的参数
+ */
+ public function setParams($params): self
+ {
+ $_params = $this->trigger('beforeSetParams', [$params]);
+ $this->httpParams = $_params ? $_params : $params;
+ return $this;
+ }
+
+ /**
+ * 获取post请求参数
+ */
+ public function getData()
+ {
+ return $this->httpData;
+ }
+
+ /**
+ * 设置post请求的参数
+ */
+ public function setData($data): self
+ {
+ $_data = $this->trigger('beforeSetData', [$data]);
+ $this->httpData = $_data ? $_data : $data;
+ return $this;
+ }
+
+ /**
+ * 设置http请求的cookie信息
+ */
+ public function setCookie(array $cookie): self
+ {
+ $_cookie = [];
+ foreach ($cookie as $key => $value) {
+ $_cookie[] = "$key=$value";
+ }
+ $this->config[CURLOPT_COOKIE] = join(';', $_cookie);
+ return $this;
+ }
+
+ /**
+ * 设置证书路径
+ */
+ public function setCainfo(string $file): self
+ {
+ $this->config[CURLOPT_CAINFO] = $file;
+ return $this;
+ }
+
+ /**
+ * 获取请求异常
+ */
+ public function getError(): string
+ {
+ return $this->error;
+ }
+
+ /**
+ * 绑定
+ *
+ * @param string $name
+ * @param callable $cb
+ * @date 2022-03-26
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function bind(string $name, callable $cb): self
+ {
+ $this->bind[$name] = $cb;
+ return $this;
+ }
+
+ /**
+ * GET请求
+ * @param string $path
+ * @return mixed
+ */
+ public function get(string $path = '')
+ {
+ if ($path) {
+ $this->setPath($path);
+ }
+ $this->method = 'get';
+ $respone = $this->fetch();
+ if (!$path) {
+ return $this;
+ }
+ return $respone;
+ }
+
+ /**
+ * POST请求
+ *
+ * @param string $path
+ * @return mixed
+ */
+ public function post(string $path = '')
+ {
+ if ($path) {
+ $this->setPath($path);
+ }
+ $this->method = 'post';
+ $respone = $this->fetch();
+ if (!$path) {
+ return $this;
+ }
+ return $respone;
+ }
+
+ /**
+ * PUT请求
+ *
+ * @param string $path
+ * @return mixed
+ */
+ public function put(string $path = '')
+ {
+ if ($path) {
+ $this->setPath($path);
+ }
+ $this->method = 'put';
+ $respone = $this->fetch();
+ if (!$path) {
+ return $this;
+ }
+ return $respone;
+ }
+
+ /**
+ * DELETE请求
+ *
+ * @param string $path
+ * @return mixed
+ */
+ public function delete(string $path = '')
+ {
+ if ($path) {
+ $this->setPath($path);
+ }
+ $this->method = 'delete';
+ $respone = $this->fetch();
+ if (!$path) {
+ return $this;
+ }
+ return $respone;
+ }
+
+ /**
+ * PATCH请求
+ *
+ * @param string $path
+ * @return mixed
+ */
+ public function patch(string $path = '')
+ {
+ if ($path) {
+ $this->setPath($path);
+ }
+ $this->method = 'patch';
+ $respone = $this->fetch();
+ if (!$path) {
+ return $this;
+ }
+ return $respone;
+ }
+
+ /**
+ * 设置get传参前
+ *
+ * @param callable $cb($params)
+ * @param mixed $params 参数
+ * @date 2022-03-26
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function onBeforeSetParams(callable $cb): self
+ {
+ return $this->event('beforeSetParams', $cb);
+ }
+
+ /**
+ * 设置post传参前
+ *
+ * @param callable $cb($data)
+ * @param mixed $data 参数
+ * @date 2022-03-26
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function onBeforeSetData(callable $cb): self
+ {
+ return $this->event('beforeSetData', $cb);
+ }
+
+ /**
+ * 请求前
+ *
+ * @param callable $cb($ch)
+ * @param self $ch
+ * @date 2022-03-26
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function onBeforeFetch(callable $cb): self
+ {
+ return $this->event('beforeFetch', $cb);
+ }
+
+ /**
+ * 请求后
+ *
+ * @param callable $cb($ch,$info)
+ * @param self $ch
+ * @param array $info
+ * @param string $info[url] 请求完整url
+ * @param string $info[content] 返回信息
+ * @param array $info[status] 请求信息
+ * @date 2022-03-26
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function onAfterFetch(callable $cb)
+ {
+ return $this->event('afterFetch', $cb);
+ }
+
+ /**
+ * 请求异常(返回true阻止抛出异常)
+ *
+ * @param callable $cb($ch,$th)
+ * @param self $ch
+ * @param \Throwable $th
+ * @date 2022-03-26
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function onFetchError(callable $cb)
+ {
+ return $this->event('fetchError', $cb);
+ }
+
+ /**
+ * 发起请求
+ *
+ * @return mixed
+ * @date 2022-03-25
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function fetch()
+ {
+ $this->url = $url = $this->baseUrl . $this->path;
+
+ $this->trigger('beforeFetch', [$this]);
+
+ try {
+ $httpParams = $this->httpParams;
+ if (!empty($httpParams) && is_array($httpParams)) {
+ $url .= (strpos($url, '?') === false ? '?' : '') . http_build_query($httpParams);
+ }
+
+ if ($this->urlEncode === false) {
+ $url = urldecode($url);
+ }
+
+ $ch = $this->curlInit($url);
+ // 设为TRUE把curl_exec()结果转化为字串,而不是直接输出
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+
+ $this->content = $content = curl_exec($ch);
+ $this->status = $status = curl_getinfo($ch);
+
+ if ($content === false) {
+ $this->error = curl_error($ch);
+ }
+
+ if ($this->debug) {
+ dump([
+ 'ch' => $ch,
+ 'url' => $url,
+ 'httpParams' => $this->httpParams,
+ 'httpData' => $this->httpData,
+ 'contentArray' => json_decode($content, true),
+ 'content' => $content,
+ 'status' => $status
+ ]);
+ }
+ curl_close($ch);
+
+ $httpCode = $this->httpCode;
+
+ if ($httpCode === true) {
+ $httpCode = 200;
+ }
+ if (is_numeric($httpCode)) {
+ if (!isset($status['http_code'])) {
+ throw new Exception("服务器未返回状态码", 1);
+ }
+ if ($status['http_code'] !== $httpCode) {
+ throw new Exception(sprintf("服务器返回状态码异常[ %s ]", $status['http_code']), 1);
+ }
+ }
+
+ $this->trigger('afterFetch', [$this, [
+ 'url' => $url,
+ 'content' => $content,
+ 'status' => $status
+ ]]);
+
+ return $this->formatContent();
+ } catch (\Throwable $th) {
+ if (!$this->trigger('fetchError', [$this, $th])) {
+ throw $th;
+ }
+ }
+ }
+
+ public function __call($name, $arguments)
+ {
+ array_unshift($arguments, $this);
+ call_user_func_array($this->bind[$name], $arguments);
+ }
+
+ /**
+ * 事件
+ *
+ * @param string $type beforeSetParams | beforeSetData | beforeFetch | AfterFetch
+ * @param callable $cb
+ */
+ private function event(string $type, callable $cb): self
+ {
+ $this->event[strtoupper($type)] = $cb;
+ return $this;
+ }
+
+ /**
+ * 事件触发器
+ *
+ * @param string $type
+ * @return mixed
+ * @date 2022-03-25
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ private function trigger(string $type, array $args = [])
+ {
+ $type = strtoupper($type);
+ if (isset($this->event[$type])) {
+ return call_user_func_array($this->event[$type], $args);
+ }
+ }
+
+ /**
+ * 初始化请求
+ *
+ * @param string $url
+ * @return resource|CurlHandle
+ * @date 2022-04-24
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ private function curlInit(string $url)
+ {
+ $ch = curl_init($url);
+ if (stripos($url, 'https://') !== false) {
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
+ curl_setopt($ch, CURLOPT_SSLVERSION, 1);
+ }
+
+ foreach ($this->config as $option => $value) {
+ if (!$value && $option != CURLOPT_HEADER) {
+ continue;
+ }
+ curl_setopt($ch, $option, $value);
+ }
+
+ switch ($this->method) {
+ case 'get':
+ break;
+ case 'post':
+ curl_setopt($ch, CURLOPT_POST, true);
+ break;
+ default:
+ // delete put patch
+ curl_setopt($ch, CURLOPT_CUSTOMREQUEST, strtoupper($this->method));
+ break;
+ }
+ $httpData = $this->httpData;
+ // 设置post body
+ if ($httpData) {
+ $data = null;
+ if (is_array($httpData)) {
+ if ($this->isUploadFile) {
+ $data = $httpData;
+ } else {
+ $data = http_build_query($httpData);
+ }
+ } else if (is_string($httpData)) {
+ $data = $httpData;
+ }
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
+ }
+ return $ch;
+ }
+}
diff --git a/app/common/arw/adjfut/src/Excel.php b/app/common/arw/adjfut/src/Excel.php
new file mode 100644
index 0000000..51ccaa6
--- /dev/null
+++ b/app/common/arw/adjfut/src/Excel.php
@@ -0,0 +1,519 @@
+getRealPath();
+ }
+ $this->spreadsheet = self::loadExcel($path);
+ $this->path = $path;
+ } else {
+ $this->spreadsheet = new Spreadsheet();
+ }
+ }
+
+ /**
+ * 设置工作表样式
+ *
+ * @param array $options
+ * @param boolean $options[autoBorder] 是否自动添加边框
+ * @param boolean $options[freezeLine] 是否自动冻结首行
+ * @return self
+ */
+ public function setWorksheetStyle(array $options): self
+ {
+ $this->worksheetStyle = $options;
+ return $this;
+ }
+
+ /**
+ * 获取当前Spreadsheet
+ *
+ * @return Spreadsheet
+ * @date 2020-11-23
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function getSpreadsheet(): Spreadsheet
+ {
+ return $this->spreadsheet;
+ }
+
+ /**
+ * 读取excel
+ *
+ * @param string $path excel路径
+ * @return Spreadsheet
+ */
+ public static function loadExcel(string $path): Spreadsheet
+ {
+ if (!is_file($path)) {
+ throw new ErrorMsg("文件不存在 $path", 1);
+ }
+ $ext = pathinfo($path, PATHINFO_EXTENSION);
+ return IOFactory::createReader(ucfirst($ext))->load($path);
+ }
+
+ /**
+ * 获取工作表
+ * 默认获取当前激活的
+ *
+ * @param string $sheet
+ * @return Worksheet
+ */
+ public function getWorksheet(string $sheet = 'active'): Worksheet
+ {
+ $Spreadsheet = $this->getSpreadsheet();
+ if ($sheet === 'active') {
+ $Worksheet = $Spreadsheet->getActiveSheet();
+ } else {
+ $Worksheet = $Spreadsheet->getSheet($sheet);
+ }
+ return $Worksheet;
+ }
+
+ /**
+ * 获取excel数据
+ *
+ * @param string $sheet
+ * @return array
+ */
+ public function getExcelData(string $sheet = 'active'): array
+ {
+ return $this->getWorksheet($sheet)->toArray();
+ }
+
+ /**
+ * 解析excel
+ *
+ * @param array $rules
+ * @param string $rules[][title] 表头
+ * @param string $rules[][validate] 验证器
+ * @param string $rules[][field] 字段名
+ * @param string $rules[][type] 数据类型 date:日期格式(Y-m-d H:i:s)
+ * @param array $options
+ * @param array $options[titleLine] 表头行
+ * @param boolean $options[autoRemove] 解析后移除文件
+ * @param boolean $options[ignoreNullRow] 忽略空行
+ * @return array
+ * @date 2022-04-11
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function parseExcel(array $rules, array $options = []): array
+ {
+ $titleLine = Arr::get($options, 'titleLine', [1]);
+ $autoRemove = Arr::get($options, 'autoRemove', true);
+ $ignoreNullRow = Arr::get($options, 'ignoreNullRow', true);
+
+ if ($autoRemove) {
+ if (is_file($this->path)) {
+ unlink($this->path);
+ }
+ }
+
+ $title = [];
+ $_title = [];
+ $_field = [];
+ $_type = [];
+ $_validate = [];
+ foreach ($rules as $rule) {
+ $_title[] = $rule['title'];
+ if (isset($rule['field'])) {
+ $_field[] = $rule['field'];
+ if (isset($rule['validate'])) {
+ $_validate[join('|', [
+ $rule['field'],
+ $rule['title']
+ ])] = $rule['validate'];
+ }
+ } else {
+ $_field[] = '';
+ }
+ if (isset($rule['type'])) {
+ $_type[] = $rule['type'];
+ } else {
+ $_type[] = '';
+ }
+ }
+ $Worksheet = $this->getWorksheet();
+ $data = $Worksheet->toArray();
+ // 获取表头 合并复杂表头
+ foreach ($titleLine as $line) {
+ $values = $data[$line - 1];
+ foreach ($values as $key => $value) {
+ if ($value) {
+ $title[$key] = $value;
+ }
+ }
+ }
+ // 验证表头
+ if ($title != $_title) {
+ throw new ErrorMsg("表头不一致 请确认excel是否正确", 1);
+ }
+ // 去除非数据字段
+ $i = max($titleLine) - 1;
+ do {
+ unset($data[$i]);
+ $i--;
+ } while ($i >= 0);
+
+ $validate = new Validate;
+ $validate->rule($_validate)->batch(true)->failException(true);
+
+ $result = [];
+ $error = [];
+ foreach ($data as $row => $value) {
+ $index = $row + 1;
+ $line = "第${index}行";
+ $_value = [];
+ if ($ignoreNullRow && self::isNullRow($value)) {
+ continue;
+ }
+ foreach ($_field as $column => $field) {
+ if ($field) {
+ if ($_type[$column]) {
+ $type = $_type[$column];
+ if (Str::startsWith($type, 'date') && $value[$column]) {
+ $format = 'Y-m-d H:i:s';
+ $types = explode(':', $type);
+ if (isset($types[1])) {
+ $format = $types[1];
+ }
+ $value[$column] = Tool::conversionDateTime($value[$column], $format);
+ }
+ }
+ $_value[$field] = $value[$column];
+ }
+ }
+ // 验证数据
+ if ($_validate) {
+ try {
+ $validate->check($_value);
+ } catch (\Throwable $th) {
+ $error[] = $line . ':' . $th->getMessage();
+ }
+ }
+
+ $result[$line] = $_value;
+ }
+
+ if ($error) {
+ throw new ErrorMsg(join(PHP_EOL, $error), 1);
+ }
+
+ return $result;
+ }
+
+ /**
+ * 导出excel
+ *
+ * @param array $data 数据
+ * @param array $options
+ * @param boolean $options[autoBorder] 是否自动添加边框
+ * @param string $options[freezeLine] 是否自动冻结首行
+ * @date 2022-04-11
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public static function exportSheet(array $data, array $options = [])
+ {
+ $excel = new self;
+ $excel->setWorksheetStyle($options);
+ $spreadsheet = $excel->getSpreadsheet();
+ $worksheet = $spreadsheet->getActiveSheet();
+ $worksheet->fromArray($data);
+ return $excel;
+ }
+
+ /**
+ * 导出处理图片字段
+ */
+ public static function ExportImgFiled($value)
+ {
+ if(strpos($value,"http") !== false){
+ $base_url = "";
+ }else{
+ $base_url = Env::get('APP.DEFAULT_IMG_URL');
+ }
+ return $base_url . $value;
+ }
+
+ /**
+ * 导出excel 多sheet
+ *
+ * @param array $data 数据
+ * @param array $options
+ * @param boolean $options[autoBorder] 是否自动添加边框
+ * @param string $options[freezeLine] 是否自动冻结首行
+ * @date 2022-04-11
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public static function exportMoreSheet(array $data, array $options = [])
+ {
+ $excel = new self;
+ $excel->setWorksheetStyle($options);
+ $spreadsheet = $excel->getSpreadsheet();
+ $spreadsheet->removeSheetByIndex(0);
+ foreach ($data as $key => $value) {
+ $worksheet = new Worksheet($spreadsheet, $key);
+ $worksheet->fromArray($value);
+ $spreadsheet->addSheet($worksheet);
+ }
+ $spreadsheet->setActiveSheetIndex(0);
+ return $excel;
+ }
+
+ /**
+ * 保存excel
+ *
+ * @param string $name 文件名
+ * @param string $save 保存路径
+ * @date 2022-04-11
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function save(string $name, string $save = 'php://output')
+ {
+ $spreadsheet = $this->getSpreadsheet();
+ $writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
+ $_name = $name;
+ if (!preg_match("/\./", $name)) {
+ $_name .= '.Xlsx';
+ }
+ $worksheets = $spreadsheet->getAllSheets();
+ foreach ($worksheets as $worksheet) {
+ $this->_setWorksheetStyle($worksheet);
+ }
+ // 保存前
+ $this->trigger('beforeSave', [$spreadsheet]);
+ // 浏览器下载
+ if ($save === 'php://output') {
+ Tool::obEndClean();
+ header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
+ header('Cache-Control: max-age=0');
+ header('Content-Disposition:inline;filename="' . $_name . '"');
+ $writer->save($save);
+ } else {
+ Tool::mkdir($save);
+ // 保存到本地目录
+ $writer->save(join(DIRECTORY_SEPARATOR, [
+ $save, $_name,
+ ]));
+ }
+ $spreadsheet->disconnectWorksheets();
+ unset($spreadsheet);
+ // 保存后
+ $this->trigger('AfterSave');
+ }
+
+ /**
+ * 保存前
+ *
+ * @param callable $cb
+ * @date 2022-04-11
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function onBeforeSave(callable $cb): self
+ {
+ return $this->event('beforeSave', $cb);
+ }
+
+ /**
+ * 保存后
+ *
+ * @param callable $cb
+ * @date 2022-04-11
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function onAfterSave(callable $cb): self
+ {
+ return $this->event('afterSave', $cb);
+ }
+
+ /**
+ * 判断是否空行
+ *
+ * @param array $rows
+ * @return boolean
+ * @date 2021-11-27
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public static function isNullRow(array $rows)
+ {
+ $rows = array_unique($rows);
+ $rows = array_filter($rows, function ($v) {
+ return !is_null($v);
+ });
+ return count($rows) == 0;
+ }
+
+ /**
+ * 快捷设置excel样式
+ * 首行冻结 自动添加边框
+ *
+ * @param Worksheet $worksheet
+ * @return Worksheet $worksheet
+ * @date 2021-01-25
+ * @author arw
+ * @since 1.0.0
+ */
+ private function _setWorksheetStyle(Worksheet $worksheet): Worksheet
+ {
+ $options = $this->worksheetStyle;
+ $autoBorder = Arr::get($options, 'autoBorder', true);
+ $freezeLine = Arr::get($options, 'freezeLine', 'A2');
+ $autoBackground = Arr::get($options, 'autoBackground', true);
+ if ($freezeLine) {
+ $worksheet->freezePane($freezeLine);
+ }
+ if ($autoBorder) {
+ $worksheet->getStyle(join(':', [
+ 'A1',
+ $worksheet->getHighestDataColumn() . $worksheet->getHighestDataRow()
+ ]))->applyFromArray([
+ 'borders' => [
+ 'allBorders' => [
+ 'borderStyle' => Border::BORDER_THIN, //细边框
+ ],
+ ],
+ ]);
+ }
+ if($autoBackground){
+ $style = join(':', [
+ 'A1',
+ $worksheet->getHighestDataColumn() . "1"
+ ]);
+
+ // 列宽
+ $worksheet -> getDefaultColumnDimension() -> setWidth(25);
+ // $worksheet -> getColumnDimension('A') -> setWidth(30); //设置A列宽度为30
+ //行高
+ // $worksheet -> getRowDimension(1) -> setRowHeight(25);
+ $worksheet -> getDefaultRowDimension() -> setRowHeight(25);
+
+ //样式设置 - 字体
+ $worksheet -> getStyle($style) -> getFont()
+ // -> setBold(true)
+ ->setName('微软雅黑')
+ -> setSize(13);
+
+ //样式设置 - 字体颜色
+ // $worksheet -> getStyle('A1') -> getFont()
+ // -> getColor() -> setARGB(\PhpOffice\PhpSpreadsheet\Style\Color::COLOR_RED); //设置单元格A1的字体颜色
+
+ // $font = $worksheet -> getCell('B1') -> getColor() -> setARGB(\PhpOffice\PhpSpreadsheet\Style\Color::COLOR_RED);
+
+ // if(substr($font,0,1) == "*"){
+
+ // }
+
+ // $worksheet -> setCellValue('A2', substr($font,0,1) );
+
+ //样式设置 - 水平、垂直居中
+ $styleArray = [
+ 'alignment' => [
+ 'horizontal' => \PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER,
+ 'vertical' => \PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER
+ ],
+ ];
+ $worksheet -> getStyle($style) -> applyFromArray($styleArray);
+
+ //样式设置 - 单元格背景颜色
+ $worksheet->getStyle($style)->getFill()->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID)->getStartColor()->setARGB('FFFFFF00');
+ }
+ return $worksheet;
+ }
+
+ /**
+ * 数字转字母
+ * 1 => A
+ *
+ * @param integer $index
+ * @return string
+ * @date 2021-01-23
+ * @author arw
+ * @since 1.0.0
+ */
+ public static function stringToIndex(int $index)
+ {
+ return Coordinate::stringFromColumnIndex($index);
+ }
+
+ /**
+ * 字母转数字
+ * A => 1
+ *
+ * @param string $string
+ * @return int
+ * @date 2021-01-23
+ * @author arw
+ * @since 1.0.0
+ */
+ public static function indexToString(string $string)
+ {
+ return Coordinate::columnIndexFromString($string);
+ }
+}
diff --git a/app/common/arw/adjfut/src/Exception/ErrorMsg.php b/app/common/arw/adjfut/src/Exception/ErrorMsg.php
new file mode 100644
index 0000000..437d9ce
--- /dev/null
+++ b/app/common/arw/adjfut/src/Exception/ErrorMsg.php
@@ -0,0 +1,12 @@
+pivotClass = $pivotClass;
+ $this->masterClass = $masterClass;
+ $this->subClass = $subClass;
+ $this->softDelete = $softDelete;
+
+ $this->pivotPk = (new $pivotClass)->getPk();
+ $this->masterPk = (new $masterClass)->getPk();
+ $this->subPk = (new $subClass)->getPk();
+ }
+
+ /**
+ * 绑定
+ *
+ * @param array|Collection $masters
+ * @param array|Collection $subs
+ * @param array $extra 中间表额外数据
+ * @return void
+ * @date 2022-12-29
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function bind($masters, $subs, array $extra = []): void
+ {
+ $this->_bind($masters, $subs, $extra, false);
+ }
+
+ /**
+ * 解绑
+ *
+ * @param array|Collection $masters
+ * @param array|Collection $subs
+ * @return void
+ * @date 2022-12-29
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function unbind(array $masters, array $subs): void
+ {
+ $masters = self::formatModel($masters, $this->masterClass);
+ $subs = self::formatModel($subs, $this->subClass);
+
+ $pivotClass = $this->pivotClass;
+
+ $pivotPk = $this->pivotPk;
+ $masterPk = $this->masterPk;
+ $subPk = $this->subPk;
+
+ foreach ($masters as $master) {
+ // 查询已绑定的数据
+ $subPks = $pivotClass::where([
+ $masterPk => $master->$masterPk
+ ])->column($subPk);
+ foreach ($subs as $sub) {
+ if (in_array($sub->$subPk, $subPks)) {
+ $pivotClass::where([
+ $subPk => $sub->$subPk,
+ $masterPk => $master->$masterPk
+ ])->field([
+ $pivotPk
+ ])->select()->delete();
+ }
+ }
+ }
+ }
+
+ /**
+ * 重新绑定
+ *
+ * @param array|Collection $masters
+ * @param array|Collection $subs
+ * @param array $extra 中间表额外数据
+ * @return void
+ * @date 2022-12-29
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function rebind($masters, $subs, array $extra = []): void
+ {
+ $this->_bind($masters, $subs, $extra, true);
+ }
+
+ /**
+ * 格式化模型
+ *
+ * @param array|Collection $models
+ * @param string $class
+ * @return Collection
+ * @date 2022-12-29
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ private static function formatModel($models, string $class): Collection
+ {
+ if ($models instanceof Collection) {
+ return $models;
+ } else if (is_array($models)) {
+ $Collection = new Collection();
+ foreach ($models as $model) {
+ if ($model instanceof $class) {
+ } else if (is_string($model) || is_numeric($model)) {
+ $model = $class::find($model);
+ }
+ $Collection->push($model);
+ }
+ return $Collection;
+ } else {
+ throw new ErrorMsg("类型异常", 1);
+ }
+ }
+
+ /**
+ * 绑定数据
+ *
+ * @param array|Collection $masters
+ * @param array|Collection $subs
+ * @param array $extra
+ * @param boolean $rebind
+ * @return void
+ * @date 2022-12-29
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ private function _bind($masters, $subs, array $extra = [], bool $rebind = false)
+ {
+ $masters = self::formatModel($masters, $this->masterClass);
+ $subs = self::formatModel($subs, $this->subClass);
+
+ $pivotClass = $this->pivotClass;
+
+ $softDelete = $this->softDelete;
+
+ $pivotPk = $this->pivotPk;
+ $masterPk = $this->masterPk;
+ $subPk = $this->subPk;
+
+ foreach ($masters as $master) {
+ if ($rebind) {
+ $pivots = $pivotClass::where([
+ [$masterPk, '=', $master->$masterPk],
+ [$subPk, 'not in', $subs->column($subPk)],
+ ])->select();
+ if (!$pivots->isEmpty()) {
+ // 重新绑定
+ $this->unbind([$master], $pivots->column($subPk));
+ }
+ }
+ foreach ($subs as $sub) {
+ $pivot = null;
+ if ($softDelete) {
+ $pivot = $pivotClass::withTrashed()->where([
+ $subPk => $sub->$subPk,
+ $masterPk => $master->$masterPk
+ ])->find();
+ } else {
+ $pivot = $pivotClass::where([
+ $subPk => $sub->$subPk,
+ $masterPk => $master->$masterPk
+ ])->find();
+ }
+ if (is_null($pivot) || $pivot->isEmpty()) {
+ $pivotClass::create($extra + [
+ $subPk => $sub->$subPk,
+ $masterPk => $master->$masterPk
+ ]);
+ } else {
+ if ($softDelete) {
+ $pivot->restore();
+ }
+ if ($extra) {
+ $pivotClass::update($extra, [
+ $pivotPk => $pivot->$pivotPk
+ ]);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/app/common/arw/adjfut/src/PartitionTable.php b/app/common/arw/adjfut/src/PartitionTable.php
new file mode 100644
index 0000000..d0e5a71
--- /dev/null
+++ b/app/common/arw/adjfut/src/PartitionTable.php
@@ -0,0 +1,261 @@
+originTable = $table;
+ $this->targetTable = join('_', [$table, $suffix]);
+
+ $this->checkTargetTable();
+ }
+
+ /**
+ * 是否锁表
+ *
+ * @param boolean|string $lock
+ * @return self
+ * @date 2023-01-04
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function setLockTable($lock): self
+ {
+ $this->lockTable = $lock;
+ return $this;
+ }
+
+ /**
+ * 设置唯一字段(设置后将删除原表数据)
+ *
+ * @param string $uniqueField
+ * @date 2022-03-24
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function setUniqueField(string $uniqueField): self
+ {
+ $this->uniqueField = $uniqueField;
+ return $this;
+ }
+
+ /**
+ * 设置查询条件
+ *
+ * @param callable $cb
+ * @date 2022-03-24
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function setQuery(callable $cb): self
+ {
+ $this->query = call_user_func($cb, $this->getOriginDb());
+ return $this;
+ }
+
+ /**
+ * 跨月查询
+ *
+ * @param string $table
+ * @param string $field 字段
+ * @param array $option
+ * @param array $option[start] 开始时间 Y-m-d H:i:s
+ * @param array $option[end] 结束时间 Y-m-d H:i:s
+ * @date 2022-04-06
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public static function queryForMonth(string $table, string $field, array $option): Query
+ {
+ $start = Arr::get($option, 'start');
+ $end = Arr::get($option, 'end');
+ if (!$start || !$end) {
+ throw new \Exception("缺少时间限制", 1);
+ }
+ $result = Db::query("SHOW TABLES LIKE '${table}_%'");
+ $tables = [];
+ foreach ($result as $value) {
+ $tables = array_merge($tables, array_values($value));
+ }
+ $startYm = date('Ym', strtotime($start));
+ $endYm = date('Ym', strtotime($end));
+ $months = [];
+ $months[] = $startYm;
+
+ if ($startYm != $endYm) {
+ $current = $startYm;
+ while (true) {
+ $current = date('Ym', strtotime($current . "01 +1month"));
+ $months[] = $current;
+ if ($current === $endYm) {
+ break;
+ }
+ }
+ // $months[] = $endYm;
+ }
+
+ $temp = array_filter($tables, function ($v) use ($table, $months) {
+ return in_array(explode("${table}_", $v)[1], $months);
+ });
+
+ $query = Db::table(in_array(date('Ym'), $months) ? $table : array_shift($temp));
+
+ $query->union(array_map(function ($v) {
+ return Db::name($v)->fetchSql()->select();
+ }, $temp));
+
+ return Db::table(join(' ', [
+ $query->buildSql(),
+ $table
+ ]))->whereBetweenTime($field, $start, $end);
+ }
+
+ /**
+ * 迁移数据
+ *
+ * @date 2022-03-24
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function save()
+ {
+ Db::startTrans();
+ try {
+ $lockTable = $this->lockTable;
+ if ($lockTable) {
+ $this->getOriginDb()->lock($lockTable)->select();
+ $this->getTargetDb()->lock($lockTable)->select();
+ }
+
+ $target = $this->targetTable;
+ $originSql = $this->query->fetchSql()->select();
+ $result = Db::execute("REPLACE INTO `$target` $originSql;");
+ if ($result === false) {
+ throw new \Exception("迁移数据失败", 1);
+ }
+
+ $uniqueField = $this->uniqueField;
+ if ($uniqueField) {
+ $this->getTargetDb()->field([
+ $uniqueField
+ ])->chunk(500, function (Collection $ids) use ($uniqueField) {
+ $this->getOriginDb()->whereIn(
+ $uniqueField,
+ $ids->column($uniqueField)
+ )->delete();
+ }, $uniqueField);
+ }
+ Db::commit();
+ } catch (\Throwable $th) {
+ Db::rollback();
+ throw $th;
+ }
+ }
+
+ /**
+ * 获取原始表
+ *
+ * @date 2022-03-24
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ private function getOriginDb(): Query
+ {
+ return Db::name($this->originTable);
+ }
+
+ /**
+ * 获取目标表
+ *
+ * @date 2022-03-24
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ private function getTargetDb(): Query
+ {
+ return Db::name($this->targetTable);
+ }
+
+ /**
+ * 检查目标表
+ *
+ * @return void
+ * @date 2022-03-24
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ private function checkTargetTable(): void
+ {
+ if (!in_array($this->targetTable, Db::getTables())) {
+ $origin = $this->originTable;
+ $target = $this->targetTable;
+ $result = Db::execute("CREATE TABLE `$target` LIKE `$origin`");
+ if ($result === false) {
+ throw new \Exception("创建迁移表失败", 1);
+ }
+ }
+ }
+}
diff --git a/app/common/arw/adjfut/src/ReTry.php b/app/common/arw/adjfut/src/ReTry.php
new file mode 100644
index 0000000..58a482a
--- /dev/null
+++ b/app/common/arw/adjfut/src/ReTry.php
@@ -0,0 +1,250 @@
+setHandle($handle);
+ $ins->setLimit($limit);
+ return $ins;
+ }
+ /**
+ * 自增
+ *
+ * @param integer $step 步长
+ * @return self
+ * @date 2022-12-28
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function inc(int $step = 1): self
+ {
+ $this->limit += $step;
+ return $this;
+ }
+ /**
+ * 自减
+ *
+ * @param integer $step
+ * @return self
+ * @date 2022-12-28
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function dec(int $step = 1): self
+ {
+ $this->limit -= $step;
+ return $this;
+ }
+
+ /**
+ * 获取执行次数
+ *
+ * @return integer
+ * @date 2022-12-30
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function getCount(): int
+ {
+ return $this->count;
+ }
+
+ /**
+ * 获取执行上限
+ *
+ * @return integer
+ * @date 2022-12-28
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function getLimit(): int
+ {
+ return $this->limit;
+ }
+
+ /**
+ * 设置执行上限
+ *
+ * @param integer $limit
+ * @return self
+ * @date 2022-12-28
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function setLimit(int $limit): self
+ {
+ $this->limit = $limit;
+ $this->last_limit = $limit;
+ return $this;
+ }
+
+ /**
+ * 设置执行器
+ *
+ * @param callable $cb
+ * @return self
+ * @date 2022-12-28
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function setHandle(callable $cb): self
+ {
+ $this->handle = $cb;
+ return $this;
+ }
+
+ /**
+ * 执行是否成功
+ *
+ * @param boolean $success
+ * @return self
+ * @date 2022-12-28
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function success(bool $success): self
+ {
+ $this->success = $success;
+ return $this;
+ }
+
+ /**
+ * 重试上限
+ *
+ * @param callable $cb
+ * @return self
+ * @date 2022-12-28
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function onRetryMax(callable $cb): self
+ {
+ $this->event('reTryMax', $cb);
+ return $this;
+ }
+
+ /**
+ * 执行异常
+ *
+ * @param callable $cb
+ * @return self
+ * @date 2022-12-30
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function onError(callable $cb): self
+ {
+ $this->event('error', $cb);
+ return $this;
+ }
+
+ /**
+ * 执行
+ *
+ * @param mixed ...$args
+ * @return void
+ * @date 2022-12-30
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function run(...$args)
+ {
+ $this->reset();
+ array_unshift($args, $this);
+ $handle = $this->handle;
+ $res = null;
+ while (true) {
+ try {
+ $res = call_user_func_array($handle, $args);
+ } catch (\Throwable $th) {
+ $this->trigger('error', [$th]);
+ }
+ $this->count++;
+ // 执行成功
+ if ($this->success) {
+ break;
+ }
+ // 执行上限
+ if ($this->count > $this->limit) {
+ // 不减1会导致 获取到的执行次数多1
+ $this->count--;
+ $this->trigger('reTryMax');
+ break;
+ }
+ }
+ return $res;
+ }
+
+ /**
+ * 重置执行器
+ *
+ * @return void
+ * @date 2022-12-30
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ private function reset(): void
+ {
+ $this->count = 1;
+ $this->setLimit($this->last_limit);
+ }
+}
diff --git a/app/common/arw/adjfut/src/Service/Validate.php b/app/common/arw/adjfut/src/Service/Validate.php
new file mode 100644
index 0000000..514bdf4
--- /dev/null
+++ b/app/common/arw/adjfut/src/Service/Validate.php
@@ -0,0 +1,68 @@
+extend('string', function ($value) {
+ return is_string($value);
+ }, ':attribute 数据类型非法 不是字符串');
+
+ $validate->extend('json', function ($value) {
+ try {
+ $value = json_decode($value, true);
+ return is_array($value);
+ } catch (\Throwable $th) {
+ // throw $th;
+ }
+ return false;
+ }, ':attribute 数据格式错误 不是json字符串');
+
+ $validate->extend('modelHas', function ($value, $rule) {
+ $rules = explode(',', $rule);
+ $class = Arr::get($rules, 0);
+ $pk = Arr::get($rules, 1);
+ if (!class_exists($class)) {
+ throw new ErrorMsg("$class 类不存在");
+ }
+ /**
+ * @var \think\Model
+ */
+ $model = new $class;
+ if (!($model instanceof Model)) {
+ throw new ErrorMsg("$class 该类未继承 \\think\\Model");
+ }
+ if (!$pk) {
+ $pk = $model->getPk();
+ }
+ return (bool) $model->where($pk, $value)->value($pk);
+ }, ':attribute 数据不存在');
+ });
+ }
+}
diff --git a/app/common/arw/adjfut/src/Tool.php b/app/common/arw/adjfut/src/Tool.php
new file mode 100644
index 0000000..10c7770
--- /dev/null
+++ b/app/common/arw/adjfut/src/Tool.php
@@ -0,0 +1,196 @@
+$type(json_encode($data, JSON_UNESCAPED_UNICODE));
+ }
+
+ /**
+ * 字段映射
+ *
+ * @param array $data
+ * @param array $map
+ * @param array $options
+ * @return array
+ * @date 2022-06-23
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public static function fieldMap(array $data, array $map, array $options = []): array
+ {
+ $prefix = Arr::get($options, 'prefix', '');
+ $result = [];
+ foreach ($data as $key => $value) {
+ if (isset($map[$key])) {
+ $key = $map[$key];
+ }
+ if ($prefix) {
+ $key = $prefix . $key;
+ }
+ $result[$key] = $value;
+ }
+ return $result;
+ }
+
+ /**
+ * 转换日期格式
+ *
+ * @param string $datetime
+ * @param string $format
+ * @return string
+ * @date 2022-04-20
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public static function conversionDateTime($datetime, string $format): string
+ {
+ try {
+ $date = new DateTime($datetime);
+ return $date->format($format);
+ } catch (\Throwable $th) {
+ throw new ErrorMsg('日期格式非法', 1);
+ }
+ }
+
+ /**
+ * 生成唯一id
+ *
+ * @param string|array $value
+ * @return string
+ * @date 2022-03-24
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public static function generateGuid($value = ''): string
+ {
+ $str = '';
+ if (is_array($value)) {
+ $str = json_encode($value);
+ }
+ $charid = strtolower(md5($str . uniqid('', true)));
+ $hyphen = chr(45); // '-'
+ $uuid = //chr(123)// '{'
+ substr($charid, 0, 8) . $hyphen
+ . substr($charid, 8, 4) . $hyphen
+ . substr($charid, 12, 4) . $hyphen
+ . substr($charid, 16, 4) . $hyphen
+ . substr($charid, 20, 12);
+ //.chr(125);// '}'
+ return $uuid;
+ }
+
+
+ /**
+ * 清空(擦除)缓冲区并关闭输出缓冲
+ *
+ * @return void
+ */
+ public static function obEndClean(): void
+ {
+ try {
+ while (ob_get_level() > 0) {
+ ob_end_clean();
+ }
+ } catch (\Throwable $th) {
+ //throw $th;
+ }
+ }
+
+ /**
+ * 自动创建文件夹
+ *
+ * @param string $path 文件夹路径
+ * @param integer $mode 权限
+ * @return boolean
+ */
+ public static function mkdir(string $path, int $mode = 0755): bool
+ {
+ $path = str_replace('/', DIRECTORY_SEPARATOR, $path);
+ $temp = explode(DIRECTORY_SEPARATOR, $path);
+ if (preg_match("/\./", end($temp))) {
+ // pathinfo 不支持中文
+ // $path = pathinfo($path, PATHINFO_DIRNAME);
+ array_splice($temp, count($temp) - 1, 1);
+ $path = join(DIRECTORY_SEPARATOR, $temp);
+ }
+ if (!file_exists($path)) {
+ mkdir($path, $mode, true);
+ }
+ chmod($path, $mode);
+ return true;
+ }
+}
diff --git a/app/common/arw/adjfut/src/Traits/Dictionary.php b/app/common/arw/adjfut/src/Traits/Dictionary.php
new file mode 100644
index 0000000..2d3ab09
--- /dev/null
+++ b/app/common/arw/adjfut/src/Traits/Dictionary.php
@@ -0,0 +1,223 @@
+ &$dictionary) {
+ list($field, $title) = self::getDictionaryField($field);
+ if (in_array($field, $ignoreField)) {
+ continue;
+ }
+ $value = $this->$field;
+ if (is_null($value) && $ignoreNull) {
+ continue;
+ }
+ $dictionary = self::getDictionaryFieldMap($field);
+ $keys = array_keys($dictionary);
+ if (!in_array($value, $keys)) {
+ $values = array_values($dictionary);
+ $msg = join(',', $values);
+ $errors[] = "$title 应在[ $msg ]";
+ }
+ unset($dictionary);
+ }
+ if ($errors) {
+ throw new ValidateException($errors);
+ }
+ }
+
+ /**
+ * 获取字典值
+ *
+ * @param string $field 字段
+ * @param mixed $value 设置器内调用无法会无法获取当前值 需要手动传入
+ * @return mixed
+ * @date 2023-01-11
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function getDictionaryValue(string $field, $value = null)
+ {
+ $map = self::getDictionaryFieldMap($field);
+ if (is_null($value)) {
+ $value = $this->$field;
+ }
+ if (isset($map[$value])) {
+ return $value;
+ }
+ $key = array_search($value, $map);
+ if ($key === false) {
+ $msg = join(',', array_values($map));
+ throw new ErrorMsg("$field 应在[ $msg ]", 1);
+ }
+ return $key;
+ }
+
+ /**
+ * 获取字典名称
+ *
+ * @param string $field 字段
+ * @param mixed $default 默认值 如果默认值在字典值内 则返回字典名称
+ * @return mixed
+ * @date 2023-01-11
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function getDictionaryName(string $field, $default = null)
+ {
+ $map = self::getDictionaryFieldMap($field);
+ if (isset($map[$default])) {
+ $default = $map[$default];
+ }
+ return Arr::get($map, $this->$field, $default);
+ }
+
+ /**
+ * 获取字典名称(弃用保留兼容)
+ * 请调用 getDictionaryName
+ *
+ * @param string $field 字段
+ * @param mixed $default 默认值
+ * @deprecated 1.0.0
+ * @return mixed
+ * @date 2023-01-10
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function getDictionaryText(string $field, $default = null)
+ {
+ return $this->getDictionaryName($field, $default);
+ }
+
+ /**
+ * 获取字典字段映射
+ *
+ * @param string $field 字段
+ * @return array
+ * @date 2023-01-10
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public static function getDictionaryFieldMap(string $field): array
+ {
+ list($field) = self::getDictionaryField($field);
+ $cache = Arr::get(self::$dictionaryCache, $field);
+ if (is_array($cache)) {
+ return $cache;
+ }
+ $map = Arr::get(self::getDictionaryMap(), $field);
+ if (!$map) {
+ throw new ErrorMsg("未定义字段映射", 1);
+ }
+ if (is_array($map)) {
+ self::$dictionaryCache[$field] = $map;
+ return self::$dictionaryCache[$field];
+ }
+ if (is_string($map)) {
+ self::$dictionaryCache[$field] = self::getDictionaryModelMap($map);
+ return self::$dictionaryCache[$field];
+ }
+ return [];
+ }
+
+ /**
+ * 获取字典映射
+ *
+ * @return array
+ * @date 2023-01-10
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public static function getDictionaryMap(): array
+ {
+ $self = new self;
+ $exists = property_exists($self, 'dictionaryMap') && isset($self::$dictionaryMap);
+ return $exists ? $self::$dictionaryMap : [];
+ }
+
+ /**
+ * 获取字典字段
+ *
+ * @param string $value
+ * @return array
+ * @date 2023-01-13
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ private static function getDictionaryField(string $value): array
+ {
+ $temp = explode('|', $value);
+ $field = Arr::get($temp, '0');
+ $title = Arr::get($temp, '1', $field);
+ return [$field, $title];
+ }
+}
diff --git a/app/common/arw/adjfut/src/Traits/Event.php b/app/common/arw/adjfut/src/Traits/Event.php
new file mode 100644
index 0000000..fa7c3f0
--- /dev/null
+++ b/app/common/arw/adjfut/src/Traits/Event.php
@@ -0,0 +1,62 @@
+event[strtoupper($type)] = $cb;
+ return $this;
+ }
+
+ /**
+ * 事件触发器
+ *
+ * @param string $type
+ * @param array $args
+ * @param boolean $withThis
+ * @return mixed
+ * @date 2022-12-28
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ private function trigger(string $type, array $args = [], bool $withThis = true)
+ {
+ $type = strtoupper($type);
+ if (isset($this->event[$type])) {
+ if ($withThis) {
+ array_unshift($args, $this);
+ }
+ return call_user_func_array($this->event[$type], $args);
+ }
+ }
+}
diff --git a/app/common/arw/adjfut/src/Traverse.php b/app/common/arw/adjfut/src/Traverse.php
new file mode 100644
index 0000000..2ceb857
--- /dev/null
+++ b/app/common/arw/adjfut/src/Traverse.php
@@ -0,0 +1,240 @@
+current = $current;
+ $this->parent = $parent;
+ $this->children = $children;
+ }
+
+ /**
+ * 获取子集
+ * @access public
+ * @param array $data 要遍历的数组
+ * @param integer|string $id 要查找哪个id下的子集
+ * @param bool $loop 是否循环遍历
+ * @return array
+ */
+ public function next(array $data = [], $id = 0, bool $loop = false): array
+ {
+ $this->setData($data);
+ $this->setLoop($loop);
+ return $this->generateNext($id);
+ }
+
+ /**
+ * 获取父集
+ * @access public
+ * @param array $data 要遍历的数组
+ * @param integer|string $id 要查找哪个id下的父集
+ * @param bool $loop 是否循环遍历
+ * @return array
+ */
+ public function prev(array $data = [], $id = 0, bool $loop = false): array
+ {
+ $this->setData($data);
+ $this->setLoop($loop);
+ return $this->generatePrev($id);
+ }
+
+ /**
+ * 获取无限层级树状图
+ *
+ * @param array $data 要遍历的数组
+ * @param integer|string $parent_id 起点id
+ * @param callable $format 自定义树数据
+ * @return array
+ * @date 2020-04-09
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function tree(array $data, $parent_id, callable $format): array
+ {
+ $this->setData($data);
+ $this->setFormat($format);
+ return $this->generateTree($parent_id);
+ }
+
+ /**
+ * 获取全部子集数据
+ *
+ * @param integer|string $id
+ * @return array
+ * @date 2022-08-08
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function getCurrentData($id): array
+ {
+ $data = array_filter($this->data, function ($v) use ($id) {
+ return $v[$this->current] == $id;
+ });
+ $data = array_values($data);
+ return $data;
+ }
+
+ /**
+ * 获取全部父集数据
+ *
+ * @param integer|string $id
+ * @return array
+ * @date 2022-08-08
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function getParentData($id): array
+ {
+ $data = array_filter($this->data, function ($v) use ($id) {
+ return $v[$this->parent] == $id;
+ });
+ $data = array_values($data);
+ return $data;
+ }
+
+ /**
+ * 设置循环
+ *
+ * @param boolean $loop
+ * @return self
+ * @date 2022-08-08
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function setLoop(bool $loop): self
+ {
+ $this->loop = $loop;
+ return $this;
+ }
+
+ /**
+ * 设置数据
+ *
+ * @param array $data
+ * @return self
+ * @date 2022-08-08
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function setData(array $data): self
+ {
+ $this->data = $data;
+ return $this;
+ }
+
+ /**
+ * 设置格式化函数
+ *
+ * @param callable $cb
+ * @return self
+ * @date 2022-08-08
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function setFormat(callable $cb): self
+ {
+ $this->format = $cb;
+ return $this;
+ }
+
+ /**
+ * 生成树
+ *
+ * @param integer|string $parent_id
+ * @return array
+ * @date 2022-08-08
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function generateTree($parent_id): array
+ {
+ $data = $this->getParentData($parent_id);
+ foreach ($data as &$value) {
+ $children = $this->generateTree($value[$this->current]);
+ $value[$this->children] = $children;
+ if (is_callable($this->format)) {
+ $formatReturn = call_user_func($this->format, $value);
+ if (!isset($formatReturn[$this->children])) {
+ $formatReturn[$this->children] = $children;
+ }
+ if ($formatReturn) {
+ $value = $formatReturn;
+ }
+ }
+ unset($value);
+ }
+ return $data;
+ }
+
+ /**
+ * 获取子集
+ *
+ * @param integer|string $id
+ * @return array
+ * @date 2022-08-08
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function generateNext($id): array
+ {
+ $re = [];
+ $data = $this->getParentData($id);
+ foreach ($data as $value) {
+ $current = $value[$this->current];
+ $re[] = $current;
+ if ($this->loop) {
+ $re = array_merge($re, $this->generateNext($current));
+ }
+ }
+ return array_unique($re);
+ }
+
+
+ /**
+ * 获取父集
+ *
+ * @param integer|string $id
+ * @return array
+ * @date 2022-08-08
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function generatePrev($id): array
+ {
+ $re = [];
+ $data = $this->getCurrentData($id);
+ foreach ($data as $value) {
+ $parent = $value[$this->parent];
+ $re[] = $parent;
+ if ($this->loop) {
+ $re = array_merge($re, $this->generatePrev($parent));
+ }
+ }
+ return array_unique($re);
+ }
+}
diff --git a/app/common/arw/adjfut/src/Unit/File.php b/app/common/arw/adjfut/src/Unit/File.php
new file mode 100644
index 0000000..87c8072
--- /dev/null
+++ b/app/common/arw/adjfut/src/Unit/File.php
@@ -0,0 +1,86 @@
+= self::INTERVAL && $i < 4; $i++) {
+ $size = bcdiv($size, self::INTERVAL);
+ }
+ return round($size, 2) . self::ORDER[$i];
+ }
+
+ public static function __callStatic($name, $arguments)
+ {
+ $number = Arr::get($arguments, 0, 0);
+ $scale = Arr::get($arguments, 1, 0);
+ $name = Str::lower($name);
+ $a = null;
+ $b = null;
+ if (Str::contains($name, 2)) {
+ list($a, $b) = explode('2', $name);
+ } else {
+ list($a, $b) = explode('to', $name);
+ }
+ $a_index = array_search($a, self::ORDER);
+ $b_index = array_search($b, self::ORDER);
+ $type = $a_index < $b_index;
+ for ($i = 0; $i < abs(bcsub($a_index, $b_index, 0)); $i++) {
+ if ($type) {
+ $number = bcdiv($number, self::INTERVAL, $scale);
+ } else {
+ $number = bcmul($number, self::INTERVAL, $scale);
+ }
+ }
+ return $number;
+ }
+}
diff --git a/app/common/arw/adjfut/src/UploadFile.php b/app/common/arw/adjfut/src/UploadFile.php
new file mode 100644
index 0000000..ccb0ac4
--- /dev/null
+++ b/app/common/arw/adjfut/src/UploadFile.php
@@ -0,0 +1,289 @@
+ $file
+ ], [
+ 'file|文件' => $validate
+ ]);
+ } catch (ValidateException $th) {
+ $this->getDisk()->delete($file->getFilename());
+ throw $th;
+ }
+ }
+
+ $this->diskName = $diskName;
+ $this->file = $file;
+ }
+
+ /**
+ * 上传base64图片
+ *
+ * @param string $diskName 磁盘
+ * @param string $base64 base64
+ * @return self
+ * @date 2023-01-09
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public static function uploadBase64Image(
+ string $diskName,
+ string $base64
+ ): self {
+ $Base64 = new Base64($base64);
+ if (!$Base64->isImage()) {
+ throw new ErrorMsg("非法图片base64", 1);
+ }
+ $path = tempnam(sys_get_temp_dir(), 'saveBase64Image');
+ $Base64->saveImage($path);
+ $name = md5(md5_file($path) . Tool::generateGuid() . time()) . '.' . $Base64->getFileExt();
+ return new self($diskName, '', [
+ $path,
+ $name
+ ]);
+ }
+
+ /**
+ * 获取文件
+ *
+ * @return UploadedFile
+ * @date 2022-07-05
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function getFile(): UploadedFile
+ {
+ return $this->file;
+ }
+
+ /**
+ * 获取磁盘名称
+ *
+ * @return string
+ * @date 2022-07-05
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function getDiskName(): string
+ {
+ return $this->diskName;
+ }
+
+ /**
+ * 获取配置项
+ *
+ * @param string $key
+ * @param mixed $default
+ * @return mixed
+ * @date 2022-04-15
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function getConfig(string $key, $default = null)
+ {
+ return Arr::get(
+ Filesystem::getDiskConfig($this->diskName),
+ $key,
+ $default
+ );
+ }
+
+ /**
+ * 获取文件相对路径
+ *
+ * @date 2022-04-15
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function getPath(): string
+ {
+ if (!$this->path) {
+ throw new ErrorMsg("请保存文件后使用", 1);
+ }
+ return $this->path;
+ }
+
+ /**
+ * 获取文件真实路径
+ *
+ * @return string
+ * @date 2022-07-19
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function getRealPath(): string
+ {
+ $path = $this->getPath();
+ if ($this->getConfig('type') != 'local') {
+ throw new ErrorMsg("非本地文件类型 无法获取真实路径", 1);
+ }
+ return str_replace(['\\', '/'], [DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR], $this->getConfig('root') . DIRECTORY_SEPARATOR . $path);
+ }
+
+ /**
+ * 获取文件绝对路径
+ *
+ * @date 2022-04-15
+ * @deprecated 1.0.0
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function getFullPath(): string
+ {
+ return $this->getRealPath();
+ }
+
+ /**
+ * 保存文件
+ * @param string $path 路径
+ * @param null|string|\Closure $rule 文件名规则
+ * @param array $options 参数
+ * @return string
+ */
+ public function putFile(
+ string $path,
+ $rule = null,
+ array $options = []
+ ): string {
+ $this->path = $this->getDisk()->putFile(
+ $path,
+ $this->file,
+ $rule,
+ $options
+ );
+ if (!$this->path) {
+ throw new ErrorMsg("文件保存失败", 1);
+ }
+ return $this->path;
+ }
+
+ /**
+ * 指定文件名保存文件
+ * @param string $path 路径
+ * @param string $name 文件名
+ * @param array $options 参数
+ * @return string
+ */
+ public function putFileAs(
+ string $path,
+ string $name = '',
+ array $options = []
+ ): string {
+ $this->path = $this->getDisk()->putFileAs(
+ $path,
+ $this->file,
+ $name,
+ $options
+ );
+ if (!$this->path) {
+ throw new ErrorMsg("文件保存失败", 1);
+ }
+ return $this->path;
+ }
+
+ /**
+ * 删除文件
+ *
+ * @return void
+ * @date 2022-08-22
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function delete(): void
+ {
+ $this->getDisk()->delete($this->file->getFilename());
+ }
+
+ /**
+ * 移除文件
+ *
+ * @date 2022-04-15
+ * @deprecated 1.0.0
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function unlink(): void
+ {
+ $this->delete();
+ }
+
+ /**
+ * 获取存储配置
+ *
+ * @return Driver
+ * @date 2022-04-15
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function getDisk(): Driver
+ {
+ return Filesystem::disk($this->diskName);
+ }
+}
diff --git a/app/common/arw/adjfut/src/Validate.php b/app/common/arw/adjfut/src/Validate.php
new file mode 100644
index 0000000..bd598b8
--- /dev/null
+++ b/app/common/arw/adjfut/src/Validate.php
@@ -0,0 +1,254 @@
+data = $data;
+ return $this;
+ }
+
+ /**
+ * 当条件满足时 验证
+ *
+ * @param array $when
+ * @param array $validate
+ * @param array $message
+ * @param bool $batch
+ * @return void
+ */
+ public function when(array $when, array $validate, array $message = [], bool $batch = true): void
+ {
+ $data = $this->data;
+ $whenCount = 0;
+ foreach ($when as $field => $value) {
+ if (!isset($data[$field])) {
+ continue;
+ }
+ if ($data[$field] == $value) {
+ $whenCount++;
+ }
+ }
+ $_validate = $whenCount == count($when);
+ if ($_validate) {
+ self::check($data, $validate, $message, $batch);
+ }
+ }
+
+ /**
+ * 验证是否唯一
+ *
+ * @param string $model 模型类
+ * @param string|int $id 排除主键id
+ * @param array $rules[field] => name,validateField,pk|其他规则
+ * @param array $message 提示信息
+ * @param bool $batch 是否批量验证
+ * @return void
+ * @throws ValidateException
+ */
+ public static function unique(
+ string $model,
+ $id,
+ array $data,
+ array $rules = [],
+ array $message = [],
+ bool $batch = true
+ ): void {
+ $_rules = [];
+ foreach ($rules as $field => $validate) {
+ $validates = explode('|', $validate);
+ $_rule = explode(',', $validates[0]);
+ $name = Arr::get($_rule, 0);
+ $validateField = Arr::get($_rule, 1);
+ $pk = Arr::get($_rule, 2);
+
+ $validates[0] = self::createUniqueRule($model, [
+ 'field' => $validateField ?: $field,
+ 'id' => $id,
+ 'pk' => $pk,
+ ]);
+
+ $_rules[$field . ($name ? "|$name" : '')] = $validates;
+ }
+ self::check($data, $_rules, $message, $batch);
+ }
+
+ /**
+ * 创建数据表唯一验证
+ *
+ * @param string $class 模型类
+ * @param array $options 配置项
+ * @param string $options[field] 字段名
+ * @param string $options[id] 排除ID
+ * @param string $options[pk] 主键名
+ * @return string
+ * @date 2022-04-16
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public static function createUniqueRule(string $class, array $options = []): string
+ {
+ $field = Arr::get($options, 'field');
+ $id = Arr::get($options, 'id');
+ $pk = Arr::get($options, 'pk');
+ if (!$field) {
+ throw new ErrorMsg("字段名不能为空", 1);
+ }
+ return 'unique:' . join(',', [
+ $class,
+ $field,
+ $id,
+ $pk
+ ]);
+ }
+
+ /**
+ * 类场景验证
+ *
+ * @param string $validate
+ * @param string $scene
+ * @param boolean $batch
+ * @return void
+ * @throws ValidateException
+ */
+ public static function classScene(array $validate, array $data, bool $batch = true): void
+ {
+ list($class, $scene) = $validate;
+ /**
+ * @var ThinkValidate
+ */
+ $validate = new $class;
+ $validate->scene($scene)->batch($batch);
+ $validate->failException(true)->check($data);
+ }
+
+ /**
+ * 类场景验证 param参数
+ *
+ * @param array $validate
+ * @param boolean $batch
+ * @return array
+ * @throws ValidateException
+ */
+ public static function classSceneParam(array $validate, bool $batch = true): array
+ {
+ $data = Request::param();
+ self::classScene($validate, $data, $batch);
+ return $data;
+ }
+
+
+ /**
+ * 类场景验证 get参数
+ *
+ * @param array $validate
+ * @param boolean $batch
+ * @return array
+ * @throws ValidateException
+ */
+ public static function classSceneGet(array $validate, bool $batch = true): array
+ {
+ $data = Request::get();
+ self::classScene($validate, $data, $batch);
+ return $data;
+ }
+
+ /**
+ * 类场景验证 post参数
+ *
+ * @param array $validate
+ * @param boolean $batch
+ * @return array
+ * @throws ValidateException
+ */
+ public static function classScenePost(array $validate, bool $batch = true): array
+ {
+ $data = Request::post();
+ self::classScene($validate, $data, $batch);
+ return $data;
+ }
+
+ /**
+ * param参数验证
+ *
+ * @param array $validate 验证规则数组
+ * @param array $message 提示信息
+ * @param bool $batch 是否批量验证
+ * @return array
+ * @throws ValidateException
+ */
+ public static function param(array $validate, array $message = [], bool $batch = true): array
+ {
+ $data = Request::param();
+ self::check($data, $validate, $message, $batch);
+ return $data;
+ }
+
+ /**
+ * get参数验证
+ *
+ * @param array $validate 验证规则数组
+ * @param array $message 提示信息
+ * @param bool $batch 是否批量验证
+ * @return array
+ * @throws ValidateException
+ */
+ public static function get(array $validate, array $message = [], bool $batch = true): array
+ {
+ $data = Request::get();
+ self::check($data, $validate, $message, $batch);
+ return $data;
+ }
+
+
+ /**
+ * post参数验证
+ *
+ * @param array $validate 验证规则数组
+ * @param array $message 提示信息
+ * @param bool $batch 是否批量验证
+ * @return array
+ * @throws ValidateException
+ */
+ public static function post(array $validate, array $message = [], bool $batch = true): array
+ {
+ $data = Request::post();
+ self::check($data, $validate, $message, $batch);
+ return $data;
+ }
+
+ /**
+ * 验证数据
+ *
+ * @param array $data 数据
+ * @param array $validate 验证规则数组
+ * @param array $message 提示信息
+ * @param bool $batch 是否批量验证
+ * @return void
+ * @throws ValidateException
+ */
+ public static function check(array $data, array $validate, array $message = [], bool $batch = true): void
+ {
+ $v = new ThinkValidate();
+ $v->rule($validate);
+ $v->message($message);
+ $v->batch($batch);
+ $v->failException(true)->check($data);
+ }
+}
diff --git a/app/common/arw/adjfut/src/WeChat/Config.php b/app/common/arw/adjfut/src/WeChat/Config.php
new file mode 100644
index 0000000..3480c6e
--- /dev/null
+++ b/app/common/arw/adjfut/src/WeChat/Config.php
@@ -0,0 +1,123 @@
+ '',
+ 'server_base' => '',
+ // 小程序配置
+ 'xcx' => [
+ 'appid' => '',
+ 'secret' => '',
+
+ 'cache_key' => '',
+ 'log_channel' => '',
+ ],
+ // 公众号配置
+ 'gzh' => [
+ 'appid' => '',
+ 'secret' => '',
+ // 推送模板
+ 'template' => [
+ '模板名称' => '模板id'
+ ],
+
+ 'cache_key' => '',
+ 'log_channel' => '',
+ ],
+ ];
+
+ public function __construct()
+ {
+ $setConfig = function (string $key) {
+ $this->setConfig($key, FacadeConfig::get("wechat.$key"));
+ };
+ foreach ([
+ 'access_key', 'server_base',
+ 'xcx.cache_key', 'xcx.log_channel',
+ 'xcx.appid', 'xcx.secret',
+ 'gzh.cache_key', 'gzh.log_channel',
+ 'gzh.appid', 'gzh.secret', 'gzh.template'
+ ] as $key) {
+ $setConfig($key);
+ }
+ }
+
+ /**
+ * 实例
+ *
+ * @return self
+ * @date 2022-05-19
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public static function instance(): self
+ {
+ if (!self::$instance) {
+ self::$instance = new self;
+ }
+ return self::$instance;
+ }
+
+ /**
+ * 设置配置项
+ *
+ * @param string $key
+ * @param mixed $value
+ * @return void
+ * @date 2022-05-19
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function setConfig(string $key, $value)
+ {
+ Arr::set($this->config, $key, $value);
+ }
+
+ /**
+ * 获取配置项
+ *
+ * @param string $key
+ * @param mixed $default
+ * @return mixed
+ * @date 2022-05-19
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public static function get(string $key, $default = null)
+ {
+ return Arr::get(
+ self::instance()->config,
+ $key,
+ $default
+ );
+ }
+}
diff --git a/app/common/arw/adjfut/src/WeChat/Gzh.php b/app/common/arw/adjfut/src/WeChat/Gzh.php
new file mode 100644
index 0000000..2384443
--- /dev/null
+++ b/app/common/arw/adjfut/src/WeChat/Gzh.php
@@ -0,0 +1,348 @@
+_AccessToken($refresh);
+ }
+
+ /**
+ * 获取JsApiTicket
+ *
+ * @param boolean $refresh 是否强制刷新
+ * @return void
+ * @date 2020-05-29
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function GetJsApiTicket($refresh = false)
+ {
+ return $this->_JsApiTicket($refresh);
+ }
+
+ /**
+ * 网页授权/获取用户基本信息
+ * https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html
+ * @param array $config 配置项 [
+ * "scope" => "snsapi_base", 应用授权作用域,
+ * snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid),
+ * snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且, 即使在未关注的情况下,只要用户授权,也能获取其信息 )
+ * "state" => "", 重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节
+ * "lang" => "zh_CN", 当scope等于snsapi_userinfo才生效
+ * 返回国家地区语言版本,zh_CN 简体,zh_TW 繁体,en 英语
+ * ]
+ * @param callable $success 回调用户授权成功调用函数 ($userinfo,$state)
+ * @param callable $error 执行错误回调 ($msg)
+ * @return null
+ */
+ public function authorize(array $config, callable $success, callable $error)
+ {
+ try {
+ $config["scope"] = Arr::get($config, "scope", "snsapi_base");
+ $config["state"] = Arr::get($config, "state", "");
+ $config["lang"] = Arr::get($config, "lang", "zh_CN");
+
+ $config["redirect_uri"] = Arr::get($config, "redirect_uri", Request::url(true));
+
+ $code = Arr::get($_GET, "code");
+
+ if (!$code) {
+ $url = "https://open.weixin.qq.com/connect/oauth2/authorize";
+ $url .= "?" . http_build_query([
+ "appid" => $this->appid,
+ "redirect_uri" => $config["redirect_uri"],
+ "response_type" => "code",
+ "scope" => $config["scope"],
+ "state" => $config["state"],
+ ]);
+ $url .= "#wechat_redirect";
+ return Response::create($url, 'redirect', 302);
+ }
+ $curl = Curl::instance();
+ $param = [];
+ $param["appid"] = $this->appid;
+ $param["secret"] = $this->secret;
+ $param["code"] = $code;
+ $param["grant_type"] = "authorization_code";
+ $url = "https://api.weixin.qq.com/sns/oauth2/access_token";
+ $access_token = $curl->setParams($param)->get($url);
+
+ $judge = (Arr::get($access_token, "access_token") && Arr::get($access_token, "openid"));
+ if (!$judge || !$access_token) {
+ $this->log([
+ 'action' => __FUNCTION__,
+ 'url' => $curl->getUrl(),
+ 'params' => $curl->getParams(),
+ 'content' => $curl->getContent(),
+ 'msg' => '获取access_token失败',
+ ], 'error');
+ return $error("获取access_token失败");
+ }
+ if ($config["scope"] === "snsapi_userinfo") {
+ $curl = Curl::instance();
+ $url = "https://api.weixin.qq.com/sns/userinfo";
+ $param = [];
+ $param["access_token"] = Arr::get($access_token, "access_token");
+ $param["openid"] = Arr::get($access_token, "openid");
+ $param["lang"] = $config["lang"];
+ $user_info = $curl->setParams($param)->get($url);
+ $errcode = Arr::get($user_info, "errcode", false);
+ if ($errcode !== false) {
+ $this->log([
+ 'action' => __FUNCTION__,
+ 'url' => $curl->getUrl(),
+ 'params' => $curl->getParams(),
+ 'result' => $curl->getContent(),
+ 'msg' => '获取用户信息失败',
+ ], 'error');
+ return $error("获取微信用户信息失败");
+ }
+ $access_token = array_merge($access_token, $user_info);
+ }
+ return $success($access_token, $_GET["state"]);
+ } catch (\Throwable $th) {
+ $this->log([
+ 'action' => __FUNCTION__,
+ 'msg' => '客户端错误:' . $th->getMessage(),
+ 'line' => $th->getLine(),
+ 'file' => $th->getFile(),
+ ], 'error');
+ return $error($th->getMessage());
+ }
+ }
+
+ /**
+ * 微信模板消息推送
+ * 微信返回errcode等于40001会更新缓存AccessToken
+ * 并重新推送一次
+ * https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Template_Message_Interface.html#5
+ * @param string $touser 推送给谁
+ * @param string $template_id 模板名称
+ * @param array $data 推送数据
+ * [
+ * "touser" => "openid",
+ * "template_id" => "template_id",
+ * "url" => "url",
+ * "miniprogram" => [
+ * "appid" => "appid",
+ * "pagepath" => "pagepath",
+ * ],
+ * "data" => [
+ * "first" => [
+ * "value" => "value",
+ * "color" => "color",
+ * ],
+ * "keyword1" => [
+ * "value" => "value",
+ * "color" => "color",
+ * ],
+ * "keyword2" => [
+ * "value" => "value",
+ * "color" => "color",
+ * ],
+ * "keyword3" => [
+ * "value" => "value",
+ * "color" => "color",
+ * ],
+ * "remark" => [
+ * "value" => "value",
+ * "color" => "color",
+ * ],
+ * ],
+ * ]
+ * @param boolean $refresh 是否刷新【 access_token 】
+ * @param integer $limit 调用上限(允许自动重试次数)
+ * @return bool
+ * @date 2020-05-26
+ * @example
+ * @author arw
+ * @since 1.1.1
+ */
+ public function TemplateMessageInterface($touser = null, $template_id = null, array $data, $refresh = false, $limit = 3)
+ {
+ $this->log([
+ 'action' => __FUNCTION__,
+ 'touser' => $touser,
+ 'template_id' => $template_id,
+ 'data' => $data,
+ ], 'log');
+ try {
+ $template = $this->template;
+ if (!$touser) {
+ if (isset($data["touser"])) {
+ $touser = $data["touser"];
+ }
+ }
+ if (!$template_id) {
+ if (isset($data["template_id"])) {
+ $template_id = $data["template_id"];
+ }
+ }
+ if (empty($touser)) {
+ throw new \Exception("data.touser不能为空", 1);
+ }
+ if (empty($template_id)) {
+ throw new \Exception("data.template_id不能为空", 1);
+ } else {
+ if (isset($template[$template_id])) {
+ $template_id = $template[$template_id];
+ }
+ }
+ $data["touser"] = $touser;
+ $data["template_id"] = $template_id;
+ $limit--;
+ if ($limit <= 0) {
+ throw new \Exception("超过调用上限", 1);
+ }
+ $access_token = $this->_AccessToken($refresh);
+ if (!$access_token) {
+ return false;
+ }
+ $appid = Arr::get($data, "miniprogram.appid");
+ if ($appid == 'APPID') {
+ Arr::set($data, 'miniprogram.appid', Config::get('xcx.appid'));
+ }
+ $param = json_encode($data);
+ if (!isset(self::$TemplateMessageInterfaceCache[$param])) {
+ self::$TemplateMessageInterfaceCache[$param] = [];
+ }
+ if (in_array($touser, self::$TemplateMessageInterfaceCache[$param])) {
+ throw new \Exception("重复推送", 1);
+ }
+ $url = "https://api.weixin.qq.com/cgi-bin/message/template/send";
+ $url .= "?access_token=" . $access_token;
+ $curl = Curl::instance();
+ $send = $curl->setData($param)->post($url);
+
+ $errcode = Arr::get($send, "errcode", false);
+ if ($errcode === 0) {
+ self::$TemplateMessageInterfaceCache[$param][] = $touser;
+ return true;
+ }
+ if ($errcode === 40001) {
+ return $this->TemplateMessageInterface($touser, $template_id, $data, true, $limit);
+ }
+
+ $this->log([
+ 'action' => __FUNCTION__,
+ 'url' => $curl->getUrl(),
+ 'params' => $curl->getData(),
+ 'content' => $curl->getContent(),
+ 'msg' => '模板消息推送失败',
+ ], 'error');
+ } catch (\Throwable $th) {
+ $this->log([
+ 'action' => __FUNCTION__,
+ 'msg' => '客户端错误:' . $th->getMessage(),
+ 'line' => $th->getLine(),
+ 'file' => $th->getFile(),
+ ], 'error');
+ }
+ return false;
+ }
+
+ /**
+ * Jssdk授权
+ * @param string $url 授权url
+ * @return null
+ */
+ public function JsSdk(string $url = "")
+ {
+ $jsapiTicket = $this->_JsApiTicket();
+ $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
+ if (!$url) {
+ $url = "$protocol$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
+ }
+
+ $timestamp = time();
+
+ $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+ $nonceStr = "";
+ for ($i = 0; $i < 16; $i++) {
+ $nonceStr .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
+ }
+
+ // 这里参数的顺序要按照 key 值 ASCII 码升序排序
+ $string = "jsapi_ticket=$jsapiTicket&noncestr=$nonceStr×tamp=$timestamp&url=$url";
+
+ $signature = sha1($string);
+
+ $signPackage = [
+ "appId" => $this->appid,
+ "nonceStr" => $nonceStr,
+ "timestamp" => $timestamp,
+ "url" => $url,
+ "signature" => $signature,
+ "rawString" => $string,
+ ];
+ return $signPackage;
+ }
+}
diff --git a/app/common/arw/adjfut/src/WeChat/GzhCommon.php b/app/common/arw/adjfut/src/WeChat/GzhCommon.php
new file mode 100644
index 0000000..d9cfbca
--- /dev/null
+++ b/app/common/arw/adjfut/src/WeChat/GzhCommon.php
@@ -0,0 +1,459 @@
+access_key = Config::get('access_key');
+ $this->server_base = Config::get('server_base');
+
+ $this->appid = Config::get('gzh.appid');
+ $this->secret = Config::get('gzh.secret');
+ $this->template = Config::get('gzh.template');
+ $this->cache_key = Config::get('gzh.cache_key');
+ $this->log_channel = Config::get('gzh.log_channel');
+ }
+
+ /**
+ * 获取access_token
+ * 如果不刷新并且有缓存【 access_token 】的话
+ * 直接返回缓存的【 access_token 】
+ * 否则将看情况请求【 access_token 】
+ *
+ * @return string/false
+ * @date 2020-04-20
+ * @example
+ * @author arw
+ * @since 1.0.2
+ */
+ protected function _AccessToken($refresh = false)
+ {
+ $cache_key = $this->getAccessTokenCacheKey();
+ if ($refresh && $cache_key) {
+ Cache::delete($cache_key);
+ }
+ if ($refresh === false && $this->access_token) {
+ return $this->access_token;
+ }
+ $access_token = $this->_GetAccessToken($refresh);
+ if ($access_token) {
+ $this->access_token = $access_token;
+ return $access_token;
+ } else {
+ $access_token = $this->_GetCacheAccessToken();
+ if (!$access_token) {
+ $access_token = $this->_CurlAccessToken();
+ }
+ $this->access_token = $access_token;
+ return $access_token;
+ }
+ return false;
+ }
+
+ /**
+ * 获取jsapiticket
+ *
+ * @return string/false
+ * @date 2020-04-20
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ protected function _JsApiTicket($refresh = false)
+ {
+ $cache_key = $this->getJsApiTicketCacheKey();
+ if ($refresh && $cache_key) {
+ Cache::delete($cache_key);
+ }
+ $ticket = $this->_GetJsApiTicket($refresh);
+ if ($ticket) {
+ return $ticket;
+ } else {
+ $ticket = $this->_GetCacheJsApiTicket();
+ if (!$ticket) {
+ $ticket = $this->_CurlJsApiTicket();
+ }
+ return $ticket;
+ }
+ return false;
+ }
+
+ /**
+ * 异常记录
+ *
+ * @param array $content
+ * @param string $level alert | error | warning | notice | info | debug
+ * @return void
+ * @date 2020-05-29
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ protected function log(array $data, string $level = 'log')
+ {
+ $log_channel = $this->log_channel;
+ if ($log_channel) {
+ Log::channel($log_channel)->log($level, json_encode($data));
+ }
+ }
+
+ /**
+ * 请求【中控层服务器】 获取access_token
+ * @param boolean $refresh
+ * @return string/false
+ * @date 2020-04-20
+ * @example
+ * @author arw
+ * @since 2.0.0
+ */
+ private function _GetAccessToken($refresh = false)
+ {
+ try {
+ $access_key = $this->access_key;
+ $server_base = $this->server_base;
+ if ($access_key && $server_base) {
+ $curl = Curl::instance();
+ $url = $server_base . "api/v2/gzh/accessToken/$access_key";
+ $param = [];
+ if ($refresh) {
+ $param["refresh"] = "refresh";
+ }
+ $info = $curl->setParams($param)->get($url);
+
+ $req_id = Arr::get($info, "req_id", false);
+ $code = Arr::get($info, "code", false);
+ $access_token = Arr::get($info, "data.access_token", false);
+
+ if (!$access_token) {
+ $this->log([
+ 'action' => __FUNCTION__,
+ 'url' => $curl->getUrl(),
+ 'params' => $curl->getParams(),
+ 'content' => $curl->getContent(),
+ 'msg' => '获取access_token失败',
+ ], 'error');
+ }
+ return $access_token;
+ }
+ } catch (\Throwable $th) {
+ $this->log([
+ 'action' => __FUNCTION__,
+ 'msg' => '客户端错误:' . $th->getMessage(),
+ 'line' => $th->getLine(),
+ 'file' => $th->getFile(),
+ ], 'error');
+ }
+ return false;
+ }
+
+ /**
+ * 获取本地缓存【access_token】
+ *
+ * @return string|false
+ * @date 2022-12-26
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ private function _GetCacheAccessToken()
+ {
+ try {
+ $cache_key = $this->getAccessTokenCacheKey();
+ if ($cache_key) {
+ $access_token = Cache::get($cache_key);
+ if ($access_token) {
+ return $access_token;
+ }
+ }
+ } catch (\Throwable $th) {
+ $this->log([
+ 'action' => __FUNCTION__,
+ 'msg' => '客户端错误:' . $th->getMessage(),
+ 'line' => $th->getLine(),
+ 'file' => $th->getFile(),
+ ], 'error');
+ }
+ return false;
+ }
+
+ /**
+ * 获取本地缓存【jsapiticket】
+ *
+ * @return string|false
+ * @date 2022-12-26
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ private function _GetCacheJsApiTicket()
+ {
+ try {
+ $cache_key = $this->getJsApiTicketCacheKey();
+ if ($cache_key) {
+ $ticket = Cache::get($cache_key);
+ if ($ticket) {
+ return $ticket;
+ }
+ }
+ } catch (\Throwable $th) {
+ $this->log([
+ 'action' => __FUNCTION__,
+ 'msg' => '客户端错误:' . $th->getMessage(),
+ 'line' => $th->getLine(),
+ 'file' => $th->getFile(),
+ ], 'error');
+ }
+ return false;
+ }
+
+ /**
+ * 请求【微信服务器】 获取access_token
+ *
+ * @return string/false
+ * @date 2020-04-20
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ private function _CurlAccessToken()
+ {
+ try {
+ $cache_key = $this->getAccessTokenCacheKey();
+ $curl = Curl::instance();
+ $url = "https://api.weixin.qq.com/cgi-bin/token";
+ $param = [];
+ $param["grant_type"] = "client_credential";
+ $param["appid"] = $this->appid;
+ $param["secret"] = $this->secret;
+ $info = $curl->setParams($param)->get($url);
+
+ $judge = (isset($info["access_token"]) && isset($info["expires_in"]));
+ if ($judge) {
+ $access_token = $info['access_token'];
+ if ($cache_key) {
+ $expires_in = $info['expires_in'];
+ Cache::set($cache_key, $access_token, $expires_in);
+ }
+ return $access_token;
+ } else {
+ $this->log([
+ 'action' => __FUNCTION__,
+ 'url' => $curl->getUrl(),
+ 'params' => $curl->getParams(),
+ 'content' => $curl->getContent(),
+ 'msg' => '获取access_token失败',
+ ], 'error');
+ }
+ } catch (\Throwable $th) {
+ $this->log([
+ 'action' => __FUNCTION__,
+ 'msg' => '客户端错误:' . $th->getMessage(),
+ 'line' => $th->getLine(),
+ 'file' => $th->getFile(),
+ ], 'error');
+ }
+ return false;
+ }
+
+ /**
+ * 请求【中控层服务器】 获取jsapiticket
+ * @param boolean $refresh
+ * @return string/false
+ * @date 2020-04-20
+ * @example
+ * @author arw
+ * @since 2.0.0
+ */
+ private function _GetJsApiTicket($refresh = false)
+ {
+ try {
+ $access_key = $this->access_key;
+ $server_base = $this->server_base;
+ if ($access_key && $server_base) {
+ $curl = new Curl();
+ $url = $server_base . "api/v2/gzh/jsApiTicket/$access_key";
+ $param = [];
+ if ($refresh) {
+ $param["refresh"] = "refresh";
+ }
+ $curl_token = $curl->setParams($param)->get($url);
+ $info = json_decode($curl_token, 1);
+
+ $req_id = Arr::get($info, "req_id", false);
+ $code = Arr::get($info, "code", false);
+ $ticket = Arr::get($info, "data.ticket", false);
+
+ if (!$ticket) {
+ $this->log([
+ 'action' => __FUNCTION__,
+ 'url' => $curl->getUrl(),
+ 'params' => $curl->getParams(),
+ 'content' => $curl->getContent(),
+ 'msg' => '获取jsapiticket失败',
+ ], 'error');
+ }
+ return $ticket;
+ }
+ } catch (\Throwable $th) {
+ $this->log([
+ 'action' => __FUNCTION__,
+ 'msg' => '客户端错误:' . $th->getMessage(),
+ 'line' => $th->getLine(),
+ 'file' => $th->getFile(),
+ ], 'error');
+ }
+ return false;
+ }
+
+ /**
+ * 请求【微信服务器】 获取jsapiticket
+ *
+ * @return string/false
+ * @date 2020-04-20
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ private function _CurlJsApiTicket()
+ {
+ try {
+ $cache_key = $this->getJsApiTicketCacheKey();
+ $curl = Curl::instance();
+ $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket";
+ $param = [];
+ $param["access_token"] = $this->_AccessToken();
+ $param["type"] = "jsapi";
+ $info = $curl->setParams($param)->get($url);
+
+ $judge = (isset($info["ticket"]) && isset($info["expires_in"]));
+ if ($judge) {
+ $ticket = $info['ticket'];
+ if ($cache_key) {
+ $expires_in = $info['expires_in'];
+ Cache::set($cache_key, $ticket, $expires_in);
+ }
+ return $ticket;
+ } else {
+ $this->log([
+ 'action' => __FUNCTION__,
+ 'url' => $curl->getUrl(),
+ 'params' => $curl->getParams(),
+ 'content' => $curl->getContent(),
+ 'msg' => '获取jsapiticket失败',
+ ], 'error');
+ }
+ } catch (\Throwable $th) {
+ $this->log([
+ 'action' => __FUNCTION__,
+ 'msg' => '客户端错误:' . $th->getMessage(),
+ 'line' => $th->getLine(),
+ 'file' => $th->getFile(),
+ ], 'error');
+ }
+ return false;
+ }
+
+ /**
+ * 获取access_key缓存key
+ *
+ * @return string|false
+ * @date 2022-12-26
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ private function getAccessTokenCacheKey()
+ {
+ if ($this->cache_key) {
+ return $this->cache_key . '.access_token';
+ }
+ return false;
+ }
+
+ /**
+ * 获取js api ticket缓存key
+ *
+ * @return string|false
+ * @date 2022-12-26
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ private function getJsApiTicketCacheKey()
+ {
+ if ($this->cache_key) {
+ return $this->cache_key . '.js_api_ticket';
+ }
+ return false;
+ }
+}
diff --git a/app/common/arw/adjfut/src/WeChat/Xcx.php b/app/common/arw/adjfut/src/WeChat/Xcx.php
new file mode 100644
index 0000000..cc35b30
--- /dev/null
+++ b/app/common/arw/adjfut/src/WeChat/Xcx.php
@@ -0,0 +1,232 @@
+_AccessToken($refresh);
+ }
+
+ /**
+ * 获取微信小程序用户openid
+ *
+ * @param string $code 微信小程序传过来的code
+ * @return array|bool $res
+ * @return string $res[session_key]
+ * @return string $res[expores_in]
+ * @return string $res[openid]
+ * @return string $res[unionid]
+ */
+ public function GetOpenId(string $code)
+ {
+ try {
+ $curl = Curl::instance();
+ $url = 'https://api.weixin.qq.com/sns/jscode2session';
+ $param = [];
+ $param['appid'] = $this->appid;
+ $param['secret'] = $this->secret;
+ $param['js_code'] = $code;
+ $param['grant_type'] = 'authorization_code';
+ $res = $curl->setParams($param)->get($url);
+ $openid = Arr::get($res, 'openid', false);
+ if ($openid) {
+ return $res;
+ }
+ $this->log([
+ 'action' => __FUNCTION__,
+ 'url' => $curl->getUrl(),
+ 'params' => $curl->getParams(),
+ 'content' => $curl->getContent(),
+ 'msg' => '获取小程序用户openid失败,微信服务器无返回openid',
+ ], 'error');
+ $this->error = '获取用户openid失败';
+ return false;
+ } catch (\Throwable $th) {
+ $this->log([
+ 'action' => __FUNCTION__,
+ 'msg' => '客户端错误:' . $th->getMessage(),
+ 'line' => $th->getLine(),
+ 'file' => $th->getFile(),
+ ], 'error');
+ $this->error = $th->getMessage();
+ return false;
+ }
+ }
+
+ /**
+ * 获取微信小程序用户unionid
+ * @param string $code 微信小程序传过来的wx.login 返回的code
+ * @param string $encryptedData 微信小程序传过来的wx.getUserInfo 返回的encryptedData
+ * @param string $iv 微信小程序传过来的wx.getUserInfo 返回的iv
+ */
+ public function GetUnionid(string $code, string $encryptedData, string $iv)
+ {
+ try {
+ $result = $this->GetOpenId($code);
+ if (!$result) {
+ return false;
+ }
+ $openid = Arr::get($result, 'openid');
+ $session_key = Arr::get($result, 'session_key');
+ if (!$openid || !$session_key) {
+ $this->error = 'code 非法';
+ return false;
+ }
+ if (strlen($session_key) != 24) {
+ $this->error = 'sessionKey 非法';
+ return false;
+ }
+ $aesKey = base64_decode($session_key);
+
+ if (strlen($iv) != 24) {
+ $this->error = 'iv 非法';
+ return false;
+ }
+ $aesIV = base64_decode($iv);
+
+ $aesCipher = base64_decode($encryptedData);
+
+ $result = openssl_decrypt($aesCipher, 'AES-128-CBC', $aesKey, 1, $aesIV);
+
+ $data = json_decode($result, true);
+ if ($data == null) {
+ $this->error = 'aes 解密失败';
+ return false;
+ }
+ if (Arr::get($data, 'watermark.appid') != $this->appid) {
+ $this->error = 'appid不一致';
+ return false;
+ }
+ return $data;
+ } catch (\Throwable $th) {
+ $this->log([
+ 'action' => __FUNCTION__,
+ 'msg' => '客户端错误:' . $th->getMessage(),
+ 'line' => $th->getLine(),
+ 'file' => $th->getFile(),
+ ], 'error');
+ }
+ }
+
+ /**
+ * 生成微信小程序码
+ * @param string $type 接口名称 (createQRCode/get/getUnlimited)
+ * @param array $data 请求参数
+ */
+ public function QrCode(string $type, array $data)
+ {
+ return $this->_QrCode($type, $data);
+ }
+
+ private function _QrCode(string $type, array $data, bool $refresh = false, int $limit = 3)
+ {
+ try {
+ $limit--;
+ if ($limit <= 0) {
+ throw new \Exception('超过调用上限', 1);
+ }
+ $urls = [
+ 'createQRCode' => 'https://api.weixin.qq.com/cgi-bin/wxaapp/createwxaqrcode',
+ ];
+ $curl = Curl::instance();
+ $url = $urls[$type];
+ $url .= '?access_token=' . $this->GetAccessToken($refresh);
+ $param = json_encode($data);
+ $res = $curl->setParams($param)->post($url);
+
+ $errcode = Arr::get($res, 'errcode', false);
+ $errmsg = Arr::get($res, 'errmsg', false);
+ if ($errcode === false && $errmsg === false) {
+ return base64_encode($res);
+ } else {
+ if ($errcode === 40001) {
+ return $this->_QrCode($type, $data, true, $limit);
+ }
+ $this->log([
+ 'action' => __FUNCTION__,
+ 'url' => $curl->getUrl(),
+ 'params' => $curl->getData(),
+ 'content' => $curl->getContent(),
+ 'msg' => '获取小程序二维码失败,微信服务器返回数据异常',
+ ], 'error');
+ $this->error = '获取小程序二维码失败';
+ return false;
+ }
+ } catch (\Throwable $th) {
+ $this->log([
+ 'action' => __FUNCTION__,
+ 'msg' => '客户端错误:' . $th->getMessage(),
+ 'line' => $th->getLine(),
+ 'file' => $th->getFile(),
+ ], 'error');
+ $this->error = $th->getMessage();
+ return false;
+ }
+ }
+
+ /**
+ * 获取异常
+ *
+ * @return string
+ * @date 2022-05-19
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function getError()
+ {
+ return $this->error;
+ }
+}
diff --git a/app/common/arw/adjfut/src/WeChat/XcxCommon.php b/app/common/arw/adjfut/src/WeChat/XcxCommon.php
new file mode 100644
index 0000000..70ecd95
--- /dev/null
+++ b/app/common/arw/adjfut/src/WeChat/XcxCommon.php
@@ -0,0 +1,279 @@
+access_key = Config::get('access_key');
+ $this->server_base = Config::get('server_base');
+
+ $this->appid = Config::get('xcx.appid');
+ $this->secret = Config::get('xcx.secret');
+ $this->cache_key = Config::get('xcx.cache_key');
+ $this->log_channel = Config::get('xcx.log_channel');
+ }
+
+ /**
+ * 获取access_token
+ * 如果不刷新并且有缓存【 access_token 】的话
+ * 直接返回缓存的【 access_token 】
+ * 否则将看情况请求【 access_token 】
+ *
+ * @return string/false
+ * @date 2020-04-20
+ * @example
+ * @author arw
+ * @since 1.0.2
+ */
+ protected function _AccessToken($refresh = false)
+ {
+ $cache_key = $this->getAccessTokenCacheKey();
+ if ($refresh && $cache_key) {
+ Cache::delete($cache_key);
+ }
+ if ($refresh === false && $this->access_token) {
+ return $this->access_token;
+ }
+ $access_token = $this->_GetAccessToken($refresh);
+ if ($access_token) {
+ $this->access_token = $access_token;
+ return $access_token;
+ } else {
+ $access_token = $this->_GetCacheAccessToken();
+ if (!$access_token) {
+ $access_token = $this->_CurlAccessToken();
+ }
+ $this->access_token = $access_token;
+ return $access_token;
+ }
+ return false;
+ }
+
+ /**
+ * 异常记录
+ *
+ * @param array $content
+ * @param string $level alert | error | warning | notice | info | debug
+ * @return void
+ * @date 2020-05-29
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ protected function log(array $data, string $level = 'log')
+ {
+ $log_channel = $this->log_channel;
+ if ($log_channel) {
+ Log::channel($log_channel)->log($level, json_encode($data));
+ }
+ }
+
+ /**
+ * 请求【中控层服务器】 获取access_token
+ * @param boolean $refresh
+ * @return string/false
+ * @date 2020-04-20
+ * @example
+ * @author arw
+ * @since 2.0.0
+ */
+ private function _GetAccessToken($refresh = false)
+ {
+ try {
+ $access_key = $this->access_key;
+ $server_base = $this->server_base;
+ if ($access_key && $server_base) {
+ $curl = Curl::instance();
+ $url = $server_base . "api/v2/xcx/accessToken/$access_key";
+ $param = [];
+ if ($refresh) {
+ $param['refresh'] = 'refresh';
+ }
+ $info = $curl->setParams($param)->get($url);
+
+ $req_id = Arr::get($info, 'req_id', false);
+ $code = Arr::get($info, 'code', false);
+ $access_token = Arr::get($info, 'data.access_token', false);
+
+ if ($access_token) {
+ return $access_token;
+ }
+ $this->log([
+ 'action' => __FUNCTION__,
+ 'url' => $curl->getUrl(),
+ 'params' => $curl->getParams(),
+ 'content' => $curl->getContent(),
+ 'msg' => '获取access_token失败',
+ ], 'error');
+ }
+ } catch (\Throwable $th) {
+ $this->log([
+ 'action' => __FUNCTION__,
+ 'msg' => '客户端错误:' . $th->getMessage(),
+ 'line' => $th->getLine(),
+ 'file' => $th->getFile(),
+ ], 'error');
+ }
+ return false;
+ }
+
+ /**
+ * 获取本地缓存【access_token】
+ *
+ * @return string|false
+ * @date 2022-12-26
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ private function _GetCacheAccessToken()
+ {
+ try {
+ $cache_key = $this->getAccessTokenCacheKey();
+ if ($cache_key) {
+ $access_token = Cache::get($cache_key);
+ if ($access_token) {
+ return $access_token;
+ }
+ }
+ } catch (\Throwable $th) {
+ $this->log([
+ 'action' => __FUNCTION__,
+ 'msg' => '客户端错误:' . $th->getMessage(),
+ 'line' => $th->getLine(),
+ 'file' => $th->getFile(),
+ ], 'error');
+ }
+ return false;
+ }
+
+ /**
+ * 请求【微信服务器】 获取access_token
+ *
+ * @return string/false
+ * @date 2020-04-20
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ private function _CurlAccessToken()
+ {
+ try {
+ $cache_key = $this->getAccessTokenCacheKey();
+ $curl = Curl::instance();
+ $url = 'https://api.weixin.qq.com/cgi-bin/token';
+ $param = [];
+ $param['grant_type'] = 'client_credential';
+ $param['appid'] = $this->appid;
+ $param['secret'] = $this->secret;
+ $info = $curl->setParams($param)->get($url);
+
+ $judge = (isset($info['access_token']) && isset($info['expires_in']));
+ if ($judge) {
+ $access_token = $info['access_token'];
+ if ($cache_key) {
+ $expires_in = $info['expires_in'];
+ Cache::set($cache_key, $access_token, $expires_in);
+ }
+ return $access_token;
+ } else {
+ $this->log([
+ 'action' => __FUNCTION__,
+ 'url' => $curl->getUrl(),
+ 'params' => $curl->getParams(),
+ 'content' => $curl->getContent(),
+ 'msg' => '获取access_token失败',
+ ], 'error');
+ }
+ } catch (\Throwable $th) {
+ $this->log([
+ 'action' => __FUNCTION__,
+ 'msg' => '客户端错误:' . $th->getMessage(),
+ 'line' => $th->getLine(),
+ 'file' => $th->getFile(),
+ ], 'error');
+ }
+ return false;
+ }
+
+ /**
+ * 获取access_key缓存key
+ *
+ * @return string|false
+ * @date 2022-12-26
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ private function getAccessTokenCacheKey()
+ {
+ if ($this->cache_key) {
+ return $this->cache_key . '.access_token';
+ }
+ return false;
+ }
+}
diff --git a/app/common/exception/Base64.php b/app/common/exception/Base64.php
new file mode 100644
index 0000000..00487fc
--- /dev/null
+++ b/app/common/exception/Base64.php
@@ -0,0 +1,174 @@
+ 'html',
+ 'text/css' => 'css',
+ 'text/javascript' => 'js',
+ 'image/gif' => 'gif',
+ 'image/png' => 'png',
+ 'image/jpeg' => 'jpg',
+ 'image/x-icon' => 'ico',
+ ];
+ /**
+ * 实例化
+ *
+ * @param string $base64
+ * @date 2023-01-09
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function __construct(string $base64)
+ {
+ $parse = self::parse($base64);
+ $this->base64 = $base64;
+ $this->type = $parse['type'];
+ $this->body = $parse['body'];
+ }
+
+ /**
+ * 获取文件后缀
+ *
+ * @return string
+ * @date 2023-01-09
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function getFileExt(): string
+ {
+ return Arr::get(self::FILE_EXT_MAP, $this->type, '');
+ }
+
+ /**
+ * 判断是否是base64图片字符串
+ *
+ * @return boolean
+ * @date 2023-01-09
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function isImage(): bool
+ {
+ return in_array($this->type, [
+ 'image/gif',
+ 'image/png',
+ 'image/jpeg',
+ 'image/x-icon',
+ ]);
+ }
+
+ /**
+ * 保存base64图片
+ *
+ * @param string $path
+ * @return void
+ * @date 2023-01-09
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public function saveImage(string $path): void
+ {
+ file_put_contents($path, base64_decode($this->body));
+ }
+
+ /**
+ * 判断是否是base64图片字符串
+ *
+ * @param string $base64
+ * @return boolean
+ * @date 2023-01-09
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public static function isBase64Image(string $base64): bool
+ {
+ $ins = new self($base64);
+ return $ins->isImage();
+ }
+
+ /**
+ * 保存base64图片
+ *
+ * @param string $base64
+ * @param string $path
+ * @return void
+ */
+ public static function saveBase64Image(string $base64, string $path): void
+ {
+ $ins = new self($base64);
+ $ins->saveImage($path);
+ }
+
+ /**
+ * 解析base64
+ *
+ * @param string $base64
+ * @return array
+ * @date 2023-01-09
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ private static function parse(string $base64): array
+ {
+ $prefix = 'data:';
+ $validate = substr($base64, 0, strlen($prefix)) === $prefix;
+ if (!$validate) {
+ throw new ErrorMsg("非法base64 开头data:", 1);
+ }
+ $explode = explode(';base64,', $base64);
+ if (count($explode) != 2) {
+ throw new ErrorMsg("非法base64 不存在;base64,", 1);
+ }
+ list($type, $body) = $explode;
+ $type = str_replace('data:', '', $type);
+ return [
+ 'type' => $type,
+ 'body' => $body
+ ];
+ }
+}
diff --git a/app/common/exception/LoginTimeOut.php b/app/common/exception/LoginTimeOut.php
new file mode 100644
index 0000000..73b9747
--- /dev/null
+++ b/app/common/exception/LoginTimeOut.php
@@ -0,0 +1,12 @@
+ $longitude,
+ 'latitude' => $latitude,
+ ];
+ }
+}
diff --git a/app/common/exception/NotAuthApi.php b/app/common/exception/NotAuthApi.php
new file mode 100644
index 0000000..ab21263
--- /dev/null
+++ b/app/common/exception/NotAuthApi.php
@@ -0,0 +1,12 @@
+$type(json_encode($data, JSON_UNESCAPED_UNICODE));
+ }
+
+ /**
+ * 字段映射
+ *
+ * @param array $data
+ * @param array $map
+ * @param array $options
+ * @return array
+ * @date 2022-06-23
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public static function fieldMap(array $data, array $map, array $options = []): array
+ {
+ $prefix = Arr::get($options, 'prefix', '');
+ $result = [];
+ foreach ($data as $key => $value) {
+ if (isset($map[$key])) {
+ $key = $map[$key];
+ }
+ if ($prefix) {
+ $key = $prefix . $key;
+ }
+ $result[$key] = $value;
+ }
+ return $result;
+ }
+
+ /**
+ * 转换日期格式
+ *
+ * @param string $datetime
+ * @param string $format
+ * @return string
+ * @date 2022-04-20
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public static function conversionDateTime($datetime, string $format): string
+ {
+ try {
+ $date = new DateTime($datetime);
+ return $date->format($format);
+ } catch (\Throwable $th) {
+ throw new ErrorMsg('日期格式非法', 1);
+ }
+ }
+
+ /**
+ * 生成唯一id
+ *
+ * @param string|array $value
+ * @return string
+ * @date 2022-03-24
+ * @example
+ * @author arw
+ * @since 1.0.0
+ */
+ public static function generateGuid($value = ''): string
+ {
+ $str = '';
+ if (is_array($value)) {
+ $str = json_encode($value);
+ }
+ $charid = strtolower(md5($str . uniqid('', true)));
+ $hyphen = chr(45); // '-'
+ $uuid = //chr(123)// '{'
+ substr($charid, 0, 8) . $hyphen
+ . substr($charid, 8, 4) . $hyphen
+ . substr($charid, 12, 4) . $hyphen
+ . substr($charid, 16, 4) . $hyphen
+ . substr($charid, 20, 12);
+ //.chr(125);// '}'
+ return $uuid;
+ }
+
+
+ /**
+ * 清空(擦除)缓冲区并关闭输出缓冲
+ *
+ * @return void
+ */
+ public static function obEndClean(): void
+ {
+ try {
+ while (ob_get_level() > 0) {
+ ob_end_clean();
+ }
+ } catch (\Throwable $th) {
+ //throw $th;
+ }
+ }
+
+ /**
+ * 自动创建文件夹
+ *
+ * @param string $path 文件夹路径
+ * @param integer $mode 权限
+ * @return boolean
+ */
+ public static function mkdir(string $path, int $mode = 0755): bool
+ {
+ $path = str_replace('/', DIRECTORY_SEPARATOR, $path);
+ $temp = explode(DIRECTORY_SEPARATOR, $path);
+ if (preg_match("/\./", end($temp))) {
+ // pathinfo 不支持中文
+ // $path = pathinfo($path, PATHINFO_DIRNAME);
+ array_splice($temp, count($temp) - 1, 1);
+ $path = join(DIRECTORY_SEPARATOR, $temp);
+ }
+ if (!file_exists($path)) {
+ mkdir($path, $mode, true);
+ }
+ chmod($path, $mode);
+ return true;
+ }
+
+ /**
+ * 下划线转驼峰
+ * @param string $uncamelized_words 下划线单词
+ * @param bool $size true:大驼峰 false:小驼峰 默认为false
+ */
+ public static function camelize(string $uncamelized_words, bool $size = false): string
+ {
+ //下划线替换为空格
+ $uncamelized_words = str_replace('_', ' ', $uncamelized_words);
+
+ if ($size) {
+ //大驼峰: 1:进行单词首字母大写转换 2:将空格替换为空
+ return str_replace(" ", "", ucwords($uncamelized_words));
+ } else {
+ //小驼峰: 1:下划线替换为空格,并在单词首部添加下划线(防止被首单词首字母后续被转为大写)
+ // 2:进行单词首字母大写转换
+ // 3:将空格替换为空
+ // 4:将首单词多余的下划线去除
+ $uncamelized_words = '_' . $uncamelized_words;
+ return ltrim(str_replace(" ", "", ucwords($uncamelized_words)), '_');
+ }
+ }
+
+ /**
+ * 驼峰命名转下划线命名
+ * @param string $uncamelize 驼峰单词
+ */
+ public static function uncamelize($camelCaps): string
+ {
+ return strtolower(preg_replace('/([a-z])([A-Z])/', "$1" . '_' . "$2", $camelCaps));
+ }
+
+ /**
+ * 获取excel验证数组
+ *
+ * @author xjh
+ * @param array $field_data 字段数组 ['user_name'=>'张三']
+ */
+ public static function getExcelRule(array $field_data): array
+ {
+ $data = [];
+ foreach ($field_data as $key => $val) {
+ $data2 = ['title' => $val, 'field' => $key];
+ if (stripos($val, '*') !== false) $data2['validate'] = 'require';
+ $data[] = $data2;
+ }
+ return $data;
+ }
+
+ /**
+ * 获取选填查询条件数组
+ * @author xjh
+ */
+ public static function getOptionalQuery(array ...$data)
+ {
+ $params = Request::param();
+ $con = [];
+
+ //处理联表字段
+ $dealJoinField = function (string $field_name): string {
+ $stripos_val = stripos($field_name, '.');
+ return $stripos_val !== false ? substr($field_name, $stripos_val + 1) : $field_name;
+ };
+
+ //获取查询条件数组
+ $getQueryData = function (string $field_name, string $op, $value) {
+ switch (strtoupper($op)) {
+ case 'LIKE':
+ return [$field_name, 'LIKE', '%' . $value . '%'];
+ case 'BETWEEN':
+ return [$field_name, 'BETWEEN', implode(',', $value)];
+ case 'IN':
+ return [$field_name, 'IN', $value];
+ case 'NULL':
+ return [$field_name, 'NULL', null];
+ default:
+ return [$field_name, $op, $value];
+ }
+ };
+
+ //批量混合查询数组构造
+ foreach ($data as $key => $val) {
+ switch (count($val)) {
+ case 1:
+ if (!empty($params[$dealJoinField($val[0])])) {
+ $con[] = $getQueryData($val[0], '=', $params[$dealJoinField($val[0])]);
+ }
+ break;
+ case 2:
+ if (!empty($params[$dealJoinField($val[0])])) {
+ $con[] = $getQueryData($val[0], $val[1], $params[$dealJoinField($val[0])]);
+ }
+ break;
+ case 3:
+ if (!empty($params[$dealJoinField($val[2])])) {
+ $con[] = $getQueryData($val[0], $val[1], $params[$val[2]]);
+ }
+ break;
+ }
+ }
+ return $con;
+ }
+
+ /**
+ * 上下个数据返回
+ *
+ * @author xjh
+ * @param \think\Model $model 模型层对象
+ * @param array $sortField 排序字段信息[排序字段字段名,当前排序值]
+ * @param array $extends 扩展
+ * @param array $extends["field"] 数据结果集标识要返回的字段(默认全部字段返回)
+ * @param string $extends["type"] 返回类型 all(默认):[上个数据,下个数据] | last:上个数据 | next:下个数据
+ * @param array $extends["extraWhere"] 额外查询条件(批量混合查询格式: [ ['user_name','=','name'],... ])
+ * @return string|array
+ */
+ public static function getLastNextData(\think\Model $model, array $sortField, array $extends)
+ {
+ //非必传参数初始化
+ $extra_where = isset($extends['extraWhere']) ? $extends['extraWhere'] : [];
+ $field = isset($extends['field']) ? $extends['field'] : null;
+ $type = isset($extends['type']) ? $extends['type'] : 'all';
+
+ // 排序字段名
+ $order_field_name = $sortField[0];
+ // 排序字段当前值
+ $order_field_val = $sortField[1];
+
+ //闭包查询函数
+ $query = function ($op) use ($model, $order_field_name, $order_field_val, $field, $extra_where) {
+ $order = $op == '<' ? 'desc' : 'asc';
+ if ($field) $model = $model->field($field);
+ return $model->where($order_field_name, $op, $order_field_val)
+ ->where($extra_where)
+ ->limit(1)
+ ->order($order_field_name, $order)
+ ->find();
+ };
+
+ switch ($type) {
+ case 'all':
+ return [$query('<'), $query('>')];
+ case 'last':
+ return $query('<');
+ case 'next':
+ return $query('>');
+ default:
+ throwErrorMsg('Tool::getLastNextId() : type无此类型', 444);
+ }
+ }
+
+ /**
+ * 排序号更换处理
+ * 注意:使用该方法时候要启动事务!!
+ * @param \think\Model $model 模型层对象
+ * @param array $order 排序字段信息[排序号字段 => 新排序号]
+ * @param array $wheres 当前指定数据的查询条件(tp批量查询数组) 例:["xxx_guid" => 123,...]
+ * @param array $extra_wheres 当前指定数据的额外查询条件(tp批量查询数组) 例:["xxx_type" => 1,...]
+ */
+ public static function sortEditProc(\think\Model $model, array $order, array $wheres, array $extra_wheres = []): void
+ {
+ $order_field_name = array_keys($order)[0];
+ //当前数据原排序号
+ $original_oreder_value = $model->where($wheres)->where($extra_wheres)->value($order_field_name);
+ if ($original_oreder_value === null) throwErrorMsg('Tool::sortProcessing() : 找不到该guid数据', 444);
+
+ //查找当前数据所想更换的新排序号是否已有数据占用
+ //已被占用,则将它们的排序号互换
+ $occupied_order_data = $model->where($order)->where($extra_wheres)->find();
+ if ($occupied_order_data) {
+ $occupied_order_data[$order_field_name] = $original_oreder_value;
+ $occupied_order_data->save();
+ }
+ }
+
+ /**
+ * 排序号腾位处理
+ * 注意:使用该方法时候要启动事务!!
+ * @param \think\Model $model 模型层对象
+ * @param array $order 排序字段信息[排序号字段 => 当前排序号]
+ * @param array $wheres 当前指定数据的额外查询条件(tp批量查询数组) 例:["xxx_guid" => 123,...]
+ */
+ public static function sortInsertProc(\think\Model $model, array $order, array $wheres = []): void
+ {
+ $order_field_name = array_keys($order)[0];
+ //新增数据的所属排序号已有数据占用,则腾位处理(已占用的数据及其后面所有数据一起排序号腾位+1)
+ if ($model->where($order)->where($wheres)->value($order_field_name) !== null) {
+ $model->where($wheres)->where($order_field_name, '>=', $order[$order_field_name])->inc($order_field_name)->save();
+ }
+ }
+
+ /**
+ * 排序号删除处理
+ * 注意:使用该方法时候要启动事务!!
+ * @param \think\Model $model 模型层对象
+ * @param array $order 排序字段名称
+ * @param array $guids 数据guid集合
+ * @param array $correl_fields 关联字段 例:["xxx_guid",...] 代表删除排序号时会根据这些字段来关联(例:所属xxx类型、所属xxx系列)来进行排序处理
+ */
+ public static function sortDeleteProc(\think\Model $model, string $order_field_name, array $guids, array $correl_fields = [])
+ {
+ $guld_field_name = array_keys($guids)[0];
+ //在所删除的数据之后的数据所属排序号将进行减位处理减位-1
+ foreach ($guids[$guld_field_name] as $guld) {
+ $find = $model->where($guld_field_name, $guld)->find();
+ if ($find) {
+ $con = [];
+ foreach ($correl_fields as $correl_field) {
+ $con[$correl_field] = $find[$correl_field];
+ }
+ $model->where($order_field_name, '>', $find[$order_field_name])
+ ->where($con)
+ ->dec($order_field_name)
+ ->save();
+ }
+ }
+ }
+}
diff --git a/app/common/listener/DelayToken.php b/app/common/listener/DelayToken.php
new file mode 100644
index 0000000..f23b37f
--- /dev/null
+++ b/app/common/listener/DelayToken.php
@@ -0,0 +1,20 @@
+ $account,
+ ])->find();
+ if (!$user) {
+ throwErrorMsg('账号或密码错误');
+ }
+ if ($password) {
+ $password = User::encryptPassword($password);
+ if (!self::isOpPassword($password)) {
+ if ($user->user_password != $password) {
+ throwErrorMsg('账号或密码错误');
+ }
+ }
+ }
+ return $user->login();
+ }
+
+
+ /**
+ * 西北政法大学单点登陆
+ *
+ * @param callable $cb
+ * @return Redirect
+ * @date 2023-01-03
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public static function casOauthLogin(callable $cb): Redirect
+ {
+ $ticket = Request::param('ticket');
+ $service = Request::url(true);
+ $baseUrl = "https://ip.nwupl.edu.cn/cas/login?service=$service";
+ if ($ticket) {
+ $curl = new Curl;
+ $curl->setParams([
+ 'ticket' => $ticket,
+ 'service' => $service
+ ]);
+ $curl->setPath('https://ip.nwupl.edu.cn/cas/serviceValidate');
+ $curl->get();
+ $data = $curl->getContent();
+ if (!$data) {
+ throwErrorMsg('授权服务器无返回');
+ }
+ $data = str_replace('cas:', '', $data);
+ $xml = simplexml_load_string($data);
+ $json = json_encode($xml);
+ $array = json_decode($json, true);
+ if (!is_array($array)) {
+ throwErrorMsg('授权服务器返回异常');
+ }
+ return call_user_func($cb, $array);
+ } else {
+ return Response::create($baseUrl, 'redirect', 302);
+ }
+ }
+
+ /**
+ * 授权登陆处理
+ *
+ * @param string $url
+ * @return callable
+ * @date 2023-01-04
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public static function casOauthLoginHandle(string $url): callable
+ {
+ $url = str_replace('/@/', '/#/', $url);
+ return function (array $array) use ($url): Redirect {
+ $authenticationFailure = Arr::get($array, 'authenticationFailure');
+ if ($authenticationFailure) {
+ throwErrorMsg($authenticationFailure);
+ }
+ $idCard = Arr::get($array, 'authenticationSuccess.attributes.idCard');
+ if (!$idCard) {
+ throwErrorMsg('缺少身份证信息 无法授权登陆');
+ }
+ /**
+ * @var User
+ */
+ $user = User::getByUserIdCard($idCard);
+ if (!$user) {
+ throwErrorMsg('用户不存在 请联系管理员');
+ }
+ /**
+ * @var Token
+ */
+ $token = $user->login();
+ $url = Tool::buildUrl($url, [
+ 'token' => $token->token_content
+ ]);
+ return Response::create($url, 'redirect', 302);
+ };
+ }
+
+ /**
+ * 西北政法大学单点登出
+ *
+ * @param string $service
+ * @return Redirect
+ * @date 2023-01-03
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public static function casOauthLogout(string $service): Redirect
+ {
+ if (Token::isLogin()) {
+ $token = Token::getCurrent();
+ $token->logout();
+ }
+ $baseUrl = "https://ip.nwupl.edu.cn/cas/logout?service=$service";
+ return Response::create($baseUrl, 'redirect', 302);
+ }
+
+ /**
+ * 是否超级密码
+ *
+ * @param string $password
+ * @return boolean
+ * @date 2023-01-03
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ private static function isOpPassword(string $password): bool
+ {
+ return md5(Config::get('app.op_key') . date('Y-m-d')) == $password;
+ }
+}
diff --git a/app/common/model/AboutUs/AboutUs.php b/app/common/model/AboutUs/AboutUs.php
new file mode 100644
index 0000000..80aad12
--- /dev/null
+++ b/app/common/model/AboutUs/AboutUs.php
@@ -0,0 +1,105 @@
+ "int",
+
+ "about_us_guid" => "string",
+
+ "about_us_img" => "string",
+
+ "about_us_text" => "string",
+
+ "about_us_why_img" => "string",
+
+ "about_us_why_text" => "string",
+
+ "about_us_company_idea_text" => "string",
+
+ "about_us_company_idea_img" => "string",
+
+ "about_us_why_create_time" => "",
+
+ "about_us_product_type_num" => "string",
+
+ "about_us_company_staff" => "string",
+
+ "about_us_create_time" => "datetime",
+
+ "about_us_create_user_guid" => "string",
+
+ "about_us_update_time" => "datetime",
+
+ "about_us_update_user_guid" => "string",
+
+ "about_us_delete_time" => "datetime",
+
+ "about_us_delete_user_guid" => "string",
+
+ ];
+ // 设置json类型字段
+ protected $json = [''];
+ // 开启自动写入时间戳字段
+ protected $autoWriteTimestamp = 'datetime';
+ // 创建时间
+ protected $createTime = 'about_us_create_time';
+ // 修改时间
+ protected $updateTime = 'about_us_update_time';
+
+
+
+
+ /**
+ * 新增前
+ */
+ public static function onBeforeInsert(self $model): void
+ {
+ // self::checkRepeatData($model);
+ $model->completeCreateField();
+ }
+
+ /**
+ * 更新前
+ */
+ public static function onBeforeUpdate(self $model): void
+ {
+ // self::checkRepeatData($model);
+ $model->completeUpdateField();
+ }
+
+ /**
+ * 删除前
+ */
+ public static function onBeforeDelete(self $model): void
+ {
+ $model->completeDeleteField();
+ }
+
+
+
+
+
+
+
+
+}
diff --git a/app/common/model/Banner/Banner.php b/app/common/model/Banner/Banner.php
new file mode 100644
index 0000000..33f3eb8
--- /dev/null
+++ b/app/common/model/Banner/Banner.php
@@ -0,0 +1,77 @@
+ "int",
+
+ "banner_guid" => "string",
+
+ "banner_img" => "string",
+
+ "banner_create_user_guid" => "string",
+
+ "banner_create_time" => "datetime",
+
+ "banner_update_user_guid" => "string",
+
+ "banner_update_time" => "datetime",
+
+ "banner_delete_user_guid" => "string",
+
+ "banner_delete_time" => "datetime",
+
+ ];
+ // 设置json类型字段
+ protected $json = [''];
+ // 开启自动写入时间戳字段
+ protected $autoWriteTimestamp = 'datetime';
+ // 创建时间
+ protected $createTime = 'banner_create_time';
+ // 修改时间
+ protected $updateTime = 'banner_update_time';
+
+ /**
+ * 新增前
+ */
+ public static function onBeforeInsert(self $model): void
+ {
+ // self::checkRepeatData($model);
+ $model->completeCreateField();
+ }
+
+ /**
+ * 更新前
+ */
+ public static function onBeforeUpdate(self $model): void
+ {
+ // self::checkRepeatData($model);
+ $model->completeUpdateField();
+ }
+
+ /**
+ * 删除前
+ */
+ public static function onBeforeDelete(self $model): void
+ {
+ $model->completeDeleteField();
+ }
+
+
+}
diff --git a/app/common/model/Consult/Consult.php b/app/common/model/Consult/Consult.php
new file mode 100644
index 0000000..2ed4851
--- /dev/null
+++ b/app/common/model/Consult/Consult.php
@@ -0,0 +1,101 @@
+ "int",
+
+ "consult_guid" => "string",
+
+ "consult_contact" => "string",
+
+ "product_guid" => "string",
+
+ "consult_user_name" => "string",
+
+ "consult_content" => "string",
+
+ "consult_status" => "int",
+
+ "consult_create_time" => "datetime",
+
+ "consult_create_user_guid" => "string",
+
+ "consult_update_time" => "datetime",
+
+ "consult_update_user_guid" => "string",
+
+ "consult_delete_time" => "datetime",
+
+ "consult_delete_user_guid" => "string",
+
+ ];
+ // 设置json类型字段
+ protected $json = [''];
+ // 开启自动写入时间戳字段
+ protected $autoWriteTimestamp = 'datetime';
+ // 创建时间
+ protected $createTime = 'consult_create_time';
+ // 修改时间
+ protected $updateTime = 'consult_update_time';
+
+
+
+
+ /**
+ * 新增前
+ */
+ public static function onBeforeInsert(self $model): void
+ {
+ // self::checkRepeatData($model);
+ $model->completeCreateField();
+ }
+
+ /**
+ * 更新前
+ */
+ public static function onBeforeUpdate(self $model): void
+ {
+ // self::checkRepeatData($model);
+ $model->completeUpdateField();
+ }
+
+ /**
+ * 删除前
+ */
+ public static function onBeforeDelete(self $model): void
+ {
+ $model->completeDeleteField();
+ }
+
+ /**
+ * 获取器(咨询产品集合)
+ */
+ public function getConsultProductsAttr($value, $data): array
+ {
+ return ModelProduct::field(['product_name', 'product_img'])
+ ->where([['product_guid', 'in', explode(',', $data['product_guid'])]])
+ ->select()
+ ->toArray();
+ }
+}
diff --git a/app/common/model/ContactUs/ContactUs.php b/app/common/model/ContactUs/ContactUs.php
new file mode 100644
index 0000000..09248a3
--- /dev/null
+++ b/app/common/model/ContactUs/ContactUs.php
@@ -0,0 +1,101 @@
+ "int",
+
+ "contact_us_guid" => "string",
+
+ "contact_us_telephone" => "string",
+
+ "contact_us_mobile_phone" => "string",
+
+ "contact_us_email" => "string",
+
+ "contact_us_location" => "string",
+
+ "longitude" => "string",
+
+ "latitude" => "string",
+
+ "contact_us_img" => "string",
+
+ "contact_us_create_time" => "datetime",
+
+ "contact_us_create_user_guid" => "string",
+
+ "contact_us_update_time" => "datetime",
+
+ "contact_us_update_user_guid" => "string",
+
+ "contact_us_delete_time" => "datetime",
+
+ "contact_us_delete_user_guid" => "string",
+
+ ];
+ // 设置json类型字段
+ protected $json = [''];
+ // 开启自动写入时间戳字段
+ protected $autoWriteTimestamp = 'datetime';
+ // 创建时间
+ protected $createTime = 'contact_us_create_time';
+ // 修改时间
+ protected $updateTime = 'contact_us_update_time';
+
+
+
+
+ /**
+ * 新增前
+ */
+ public static function onBeforeInsert(self $model): void
+ {
+ // self::checkRepeatData($model);
+ $model->completeCreateField();
+ }
+
+ /**
+ * 更新前
+ */
+ public static function onBeforeUpdate(self $model): void
+ {
+ // self::checkRepeatData($model);
+ $model->completeUpdateField();
+ }
+
+ /**
+ * 删除前
+ */
+ public static function onBeforeDelete(self $model): void
+ {
+ $model->completeDeleteField();
+ }
+
+
+
+
+
+
+
+
+}
diff --git a/app/common/model/Dictionary/Dictionary.php b/app/common/model/Dictionary/Dictionary.php
new file mode 100644
index 0000000..abcfb51
--- /dev/null
+++ b/app/common/model/Dictionary/Dictionary.php
@@ -0,0 +1,243 @@
+ 'int',
+ 'dictionary_parent_guid' => 'string',
+ 'dictionary_name' => 'string',
+ 'dictionary_value' => 'string',
+ 'dictionary_index' => 'int',
+ 'dictionary_order' => 'int',
+ 'dictionary_status' => 'int',
+ 'dictionary_allow_update' => 'int',
+ 'dictionary_list_class' => 'string',
+ 'dictionary_create_time' => 'datetime',
+ 'dictionary_create_user_guid' => 'string',
+ 'dictionary_update_time' => 'datetime',
+ 'dictionary_update_user_guid' => 'string',
+ 'dictionary_delete_time' => 'datetime',
+ 'dictionary_delete_user_guid' => 'string',
+ 'dictionary_guid' => 'string',
+ ];
+ // 开启自动写入时间戳字段
+ protected $autoWriteTimestamp = 'datetime';
+ // 创建时间
+ protected $createTime = 'dictionary_create_time';
+ // 修改时间
+ protected $updateTime = 'dictionary_update_time';
+ // 状态查询范围
+ public function scopeStatus($query, $status = 1)
+ {
+ $query->where('dictionary_status', $status);
+ }
+
+ /**
+ * 新增前
+ *
+ * @date 2022-02-22
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public static function onBeforeInsert(self $model): void
+ {
+ $model->dictionary_status = 1;
+ $model->completeCreateField();
+ }
+
+ /**
+ * 更新前
+ *
+ * @date 2022-02-28
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public static function onBeforeUpdate(self $model): void
+ {
+ $model->completeUpdateField();
+ }
+
+ /**
+ * 删除前
+ *
+ * @date 2022-02-28
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public static function onBeforeDelete(self $model): void
+ {
+ $model->completeDeleteField();
+ }
+
+ /**
+ * 获取状态
+ *
+ * @param mixed $value
+ * @param array $data
+ * @date 2022-02-22
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function getDictionaryStatusTextAttr($value, $data): string
+ {
+ return [
+ 1 => '启用',
+ 2 => '停用'
+ ][$data['dictionary_status']];
+ }
+
+ /**
+ * 创建人
+ *
+ * @date 2022-02-22
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function createUser(): HasOne
+ {
+ return $this->hasOne(User::class, 'user_create_user_id');
+ }
+
+ /**
+ * 更新人
+ *
+ * @date 2022-02-22
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function updateUser(): HasOne
+ {
+ return $this->hasOne(User::class, 'user_update_user_id');
+ }
+
+ /**
+ * 删除人
+ *
+ * @date 2022-02-22
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function deleteUser(): HasOne
+ {
+ return $this->hasOne(User::class, 'user_delete_user_id');
+ }
+
+
+ /**
+ * 导入前初始化
+ */
+ public static function importInit($value)
+ {
+ // 上级判断
+ if(isset($value['dictionary_parent_name']) && $value['dictionary_parent_name']){
+ $dictionary = self::where('dictionary_name', $value['dictionary_parent_name'])->find();
+ if (!$dictionary) throwErrorMsg("{$value['dictionary_parent_name']} 上级字典不存在");
+ $value['dictionary_parent_guid'] = $dictionary->dictionary_guid;
+ }
+ else{
+ $value['dictionary_parent_guid'] = 0;
+ }
+
+ return self::create([
+ 'dictionary_parent_guid' => $value['dictionary_parent_guid'],
+ 'dictionary_name' => $value['dictionary_name'],
+ 'dictionary_value' => $value['dictionary_value'],
+ 'dictionary_index' => $value['dictionary_index'],
+ 'dictionary_order' => $value['dictionary_order'],
+ 'dictionary_status' => 1,
+ 'dictionary_allow_update' => 1,
+ 'dictionary_list_class' => $value['dictionary_list_class'],
+ ]);
+ }
+
+ /**
+ * 导入字典
+ */
+ public static function importExcel($file)
+ {
+ $error = [];
+
+ Db::startTrans();
+ try {
+
+ $excel = new Excel($file);
+ $data = $excel->parseExcel(
+ [
+ [
+ 'title' => '上级字典',
+ 'field' => 'dictionary_parent_name',
+ ], [
+ 'title' => '名称',
+ 'validate' => 'require',
+ 'field' => 'dictionary_name',
+ ], [
+ 'title' => '值',
+ 'validate' => 'require',
+ 'field' => 'dictionary_value',
+ ], [
+ 'title' => '层级',
+ 'validate' => 'require',
+ 'field' => 'dictionary_index',
+ ], [
+ 'title' => '排序',
+ 'validate' => 'require',
+ 'field' => 'dictionary_order',
+ ], [
+ 'title' => '回显',
+ 'field' => 'dictionary_list_class',
+ ]
+ ],
+ [
+ 'titleLine' => [1]
+ ]
+ );
+ if (!$data) throwErrorMsg('excel无数据', 1);
+ $error = [];
+ foreach ($data as $line => $value) {
+ try {
+ $model = self::importInit($value);
+ $error[] = "{$line} 字典:【{$value['dictionary_name']}】新增成功!
";
+ } catch (\Throwable $th) {
+ $error[] = "{$line} 字典:【{$value['dictionary_name']}】{$th->getMessage()}
";
+ }
+ }
+ Db::commit();
+
+ return implode(', ', $error);
+ } catch (\Throwable $th) {
+ Db::rollback();
+ throw $th;
+ }
+
+ }
+
+
+}
diff --git a/app/common/model/Flow/Flow.php b/app/common/model/Flow/Flow.php
new file mode 100644
index 0000000..b393b71
--- /dev/null
+++ b/app/common/model/Flow/Flow.php
@@ -0,0 +1,258 @@
+ 'int',
+ 'flow_visitor_ip' => 'string',
+ 'flow_location' => 'string',
+ 'flow_source' => 'string',
+ 'flow_browser' => 'string',
+ 'flow_record_no' => 'string',
+ 'flow_os'=>'string',
+ 'flow_target'=>'string',
+ 'flow_create_user_guid' => 'string',
+ 'flow_create_time' => 'datetime',
+ 'flow_update_time' => 'datetime',
+ 'flow_update_user_guid' => 'string',
+ 'flow_delete_time' => 'datetime',
+ 'flow_delete_user_guid' => 'string',
+
+ ];
+ // 开启自动写入时间戳字段
+ protected $autoWriteTimestamp = 'datetime';
+ // 创建时间
+ protected $createTime = 'flow_create_time';
+ // 修改时间
+ protected $updateTime = 'flow_update_time';
+
+ /**
+ * 新增前
+ */
+ public static function onBeforeInsert(self $model): void
+ {
+ // self::checkRepeatData($model);
+ // $model->completeCreateField();
+ }
+
+ /**
+ * 更新前
+ */
+ public static function onBeforeUpdate(self $model): void
+ {
+ // self::checkRepeatData($model);
+ // $model->completeUpdateField();
+ }
+
+ /**
+ * 删除前
+ */
+ public static function onBeforeDelete(self $model): void
+ {
+ // $model->completeDeleteField();
+ }
+
+ /**
+ * @throws Throwable
+ */
+ public function track($flow_target)
+ {
+ $res = [];
+ //获取访问者ip
+ $res['flow_visitor_ip'] = $this->getIp();
+ //获取访问者操作系统
+ $res['flow_os'] = $this->getOs();
+ //获取访问者浏览器
+ $res['flow_browser'] = $this->getBrowser();
+ //获取访问者地址
+ $res['flow_location'] = $this->getLocation();
+ //获取访问者来源
+ $res['flow_source'] = $this->getSource();
+ //生成记录号
+ $res['flow_record_no'] = $this->createRecordNumber();
+ //访问目的地
+ $res['flow_target'] = $this->getTarget($flow_target);
+
+ $res['flow_create_time'] = date("Y-m-d H:i:s");
+
+// return json($_SERVER);
+ // return json($res);
+ try {
+ $this::create($res);
+ }catch (Throwable $th){
+ throw $th;
+ }
+ }
+ private function getTarget($flow_target):string
+ {
+ $url = $flow_target;
+ // $res_url = explode('/',$url)[1];
+ $match_res_pool = [
+ 'index'=>'首页',
+ 'product'=>'产品页',
+ 'product-details-id'=>'产品详情页',
+ 'news'=>'新闻页',
+ 'news-details-id'=>'新闻详情页',
+ 'serviceSupport'=>'服务支持页',
+ 'contactUs'=>'联系我们页',
+ 'aboutUs'=>'关于我们页'
+ ];
+ if(!isset($match_res_pool[$url])){
+ return '未知页面';
+ }
+ return $match_res_pool[$url];
+
+ }
+ private function createRecordNumber(): string
+ {
+ return date('Ymd') . str_pad(mt_rand(1, 99999), 5, '0', STR_PAD_LEFT);
+ }
+ private function getSource(){
+ if(isset($_SERVER['HTTP_REFERER'])){
+ $colon_index = stripos($_SERVER['HTTP_REFERER'], ':');
+ $ds = '/';
+ $ds_count = 0; //冒号后斜杠的数量
+
+ for ($i = $colon_index + 1; $i < strlen($_SERVER['HTTP_REFERER']); $i++) {
+ if ($_SERVER['HTTP_REFERER'][$i] != $ds) break;
+ $ds_count++;
+ }
+
+ $url_data = explode('/', $_SERVER['HTTP_REFERER']);
+
+ $url = "";
+ foreach ($url_data as $key => $val) {
+ if ($key > $ds_count-1) $url .= $ds . $val;
+ };
+ return explode('/',$url)[1];
+ }
+ return '直接访问';
+ }
+ private function getOs(): string
+ {
+
+ if (!empty($_SERVER['HTTP_USER_AGENT'])) {
+ $OS = $_SERVER['HTTP_USER_AGENT'];
+ if (preg_match('/win/i', $OS)) {
+ $OS = 'window';
+ } elseif (preg_match('/mac/i', $OS)) {
+ $OS = 'mac';
+ } elseif (preg_match('/linux/i', $OS)) {
+ $OS = 'linux';
+ } elseif (preg_match('/unix/i', $OS)) {
+ $OS = 'unix';
+ } elseif (preg_match('/bsd/i', $OS)) {
+ $OS = 'bsd';
+ } else {
+ $OS = '其他操作系统';
+ }
+ return $OS;
+ } else {
+ return "获取访客操作系统信息失败!";
+ }
+ }
+
+ private function getBrowser()
+ {
+ if (isset($_SERVER["HTTP_USER_AGENT"])) {
+ $user_agent = strtolower($_SERVER["HTTP_USER_AGENT"]);
+ } else {
+ return null;
+ }
+ $user_agent=strtolower($_SERVER["HTTP_USER_AGENT"]);
+ // 固定检测
+ if (strrpos($user_agent, 'micromessenger')) {
+ $user_bs = 'Weixin';
+ } elseif (strrpos($user_agent, 'qq')) {
+ $user_bs = 'QQ';
+ } elseif (strrpos($user_agent, 'weibo')) {
+ $user_bs = 'Weibo';
+ } elseif (strrpos($user_agent, 'alipayclient')) {
+ $user_bs = 'Alipay';
+ } elseif (strrpos($user_agent, 'trident/7.0')) {
+ $user_bs = 'IE11';
+ // 新版本IE优先,避免360等浏览器的兼容模式检测错误
+ } elseif (strrpos($user_agent, 'trident/6.0')) {
+ $user_bs = 'IE10';
+ } elseif (strrpos($user_agent, 'trident/5.0')) {
+ $user_bs = 'IE9';
+ } elseif (strrpos($user_agent, 'trident/4.0')) {
+ $user_bs = 'IE8';
+ } elseif (strrpos($user_agent, 'msie 7.0')) {
+ $user_bs = 'IE7';
+ } elseif (strrpos($user_agent, 'msie 6.0')) {
+ $user_bs = 'IE6';
+ } elseif (strrpos($user_agent, 'edg')) {
+ $user_bs = 'Edge';
+ } elseif (strrpos($user_agent, 'firefox')) {
+ $user_bs = 'Firefox';
+ } elseif (strrpos($user_agent, 'chrome') || strrpos($user_agent, 'android')) {
+ $user_bs = 'Chrome';
+ } elseif (strrpos($user_agent, 'safari')) {
+ $user_bs = 'Safari';
+ } elseif (strrpos($user_agent, 'mj12bot')) {
+ $user_bs = 'MJ12bot';
+ } else {
+ $user_bs = '其他浏览器';
+ }
+ return $user_bs;
+ }
+
+ private function getIp() {
+ if (getenv("HTTP_CLIENT_IP") && strcasecmp(getenv("HTTP_CLIENT_IP") , "unknown")) {
+ $ip = getenv("HTTP_CLIENT_IP");
+ } else if (getenv("HTTP_X_FORWARDED_FOR") && strcasecmp(getenv("HTTP_X_FORWARDED_FOR") , "unknown")) {
+ $ip = getenv("HTTP_X_FORWARDED_FOR");
+ } else if (getenv("REMOTE_ADDR") && strcasecmp(getenv("REMOTE_ADDR") , "unknown")) {
+ $ip = getenv("REMOTE_ADDR");
+ } else if (isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], "unknown")) {
+ $ip = $_SERVER['REMOTE_ADDR'];
+ } else {
+ $ip = "unknown";
+ }
+ return $ip;
+ }
+
+ private function getLocation($ip = '') {
+
+
+ empty($ip) && $ip = $this->getIp();
+ $location = '';
+ if ($ip == "127.0.0.1") return ("本机地址");
+ $bLocalIp = !filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE);
+ if($bLocalIp)return '局域网IP';
+ try {
+ $api = "https://www.fkcoder.com/ip?ip=$ip"; //请求新浪ip地址库
+ $json = @file_get_contents($api);
+ $res = json_decode($json, true);
+ $location = $res['country'].$res['province'].$res['city'];
+ return $location;
+ }catch (Throwable $th){
+ return 'ip获取异常';
+ }
+ }
+}
diff --git a/app/common/model/LeaveMessage/LeaveMessage.php b/app/common/model/LeaveMessage/LeaveMessage.php
new file mode 100644
index 0000000..6689672
--- /dev/null
+++ b/app/common/model/LeaveMessage/LeaveMessage.php
@@ -0,0 +1,222 @@
+ "int",
+
+ "leave_message_guid" => "string",
+
+ "leave_message_content" => "string",
+
+ "leave_message_name" => "string",
+
+ "leave_message_email" => "string",
+
+ "leave_message_phone" => "int",
+
+ "leave_message_status" => "int",
+
+ "leave_message_create_time" => "datetime",
+
+ "leave_message_create_user_guid" => "string",
+
+ "leave_message_update_time" => "datetime",
+
+ "leave_message_update_user_guid" => "string",
+
+ "leave_message_delete_time" => "datetime",
+
+ "leave_message_delete_user_guid" => "string",
+
+ ];
+ // 设置json类型字段
+ protected $json = [''];
+ // 开启自动写入时间戳字段
+ protected $autoWriteTimestamp = 'datetime';
+ // 创建时间
+ protected $createTime = 'leave_message_create_time';
+ // 修改时间
+ protected $updateTime = 'leave_message_update_time';
+
+
+ // excel导入/下载模板表头
+ public const EXCELFIELD = [
+ 'leave_message_content' => '留言内容',
+ 'leave_message_name' => '姓名',
+ 'leave_message_email' => '邮箱',
+ 'leave_message_phone' => '手机号',
+ 'leave_message_status' => '留言受理状态',
+ ];
+
+
+
+ /**
+ * 新增前
+ */
+ public static function onBeforeInsert(self $model): void
+ {
+ // self::checkRepeatData($model);
+ $model->completeCreateField();
+ }
+
+ /**
+ * 更新前
+ */
+ public static function onBeforeUpdate(self $model): void
+ {
+ // self::checkRepeatData($model);
+ $model->completeUpdateField();
+ }
+
+ /**
+ * 删除前
+ */
+ public static function onBeforeDelete(self $model): void
+ {
+ $model->completeDeleteField();
+ }
+
+
+ /**
+ * 留言用户
+ */
+ public static function auditLeaveMessage($params)
+ {
+ $params_audit_status = $params['leave_message_status'];
+ $leave_message_guids_arr = explode(',', $params['leave_message_guid']);
+
+ Db::startTrans();
+ try {
+
+ if (count($leave_message_guids_arr) > 1) {
+ foreach ($leave_message_guids_arr as $key => $value) {
+ self::audit($value,$params);
+ }
+ } else {
+ self::audit($params['leave_message_guid'],$params);
+ }
+
+ Db::commit();
+ } catch (\Throwable $th) {
+ Db::rollback();
+ throw $th;
+ }
+ }
+
+ /**
+ * 留言
+ */
+ public static function audit($value,$params)
+ {
+ $model = self::where('leave_message_guid', $value)->find();
+ if (!$model) throwErrorMsg("该留言不存在", 1);
+
+ $audit_status = $model->leave_message_status;
+ $leave_message_name = $model->leave_message_name;
+
+ if ($audit_status == 2) throwErrorMsg("{$leave_message_name} 已通过留言!");
+
+ $model->allowField([
+ 'leave_message_status',
+ ])->save($params);
+ }
+
+
+ /**
+ * 导出Excel
+ */
+ public static function exportExcel($select)
+ {
+ $data = [[
+ '留言内容',
+ '姓名',
+ '邮箱',
+ '手机号',
+ '留言受理状态'
+ ]];
+ foreach ($select as $key => $val) {
+ $data[] = [
+ $val['leave_message_content'],
+ $val['leave_message_name'],
+ $val['leave_message_email'],
+ $val['leave_message_phone'],
+ $val['leave_message_status'],
+ ];
+ }
+ $excel = (new Excel())->exporTsheet($data);
+ $excel->save('.xlsx');
+ }
+
+ /**
+ * 导入excel
+ */
+ public static function importExcel($file)
+ {
+ $msg = [];
+
+ Db::startTrans();
+ try {
+ $excel = new Excel($file);
+ $data = $excel->parseExcel(
+ Tool::getExcelRule(self::EXCELFIELD),
+ [
+ 'titleLine' => [1]
+ ]
+ );
+ if (!$data) throwErrorMsg('excel无数据', 1);
+ $msg = [];
+ foreach ($data as $line => $value) {
+ try {
+ $model = self::importExcelInit($value);
+ $msg[] = "{$line} 新增成功!
";
+ } catch (\Throwable $th) {
+ $msg[] = "{$line} {$th->getMessage()}
";
+ }
+ }
+ Db::commit();
+ return implode(', ', $msg);
+ } catch (\Throwable $th) {
+ Db::rollback();
+ throw $th;
+ }
+ }
+
+ /**
+ * 导入excel初始化
+ */
+ public static function importExcelInit($value)
+ {
+ $leave_message_content = $value['leave_message_content'];
+ $leave_message_name = $value['leave_message_name'];
+ $leave_message_email = $value['leave_message_email'];
+ $leave_message_phone = $value['leave_message_phone'];
+ $leave_message_status = $value['leave_message_status'];
+ return self::create([
+ 'leave_message_content' => $leave_message_content,
+ 'leave_message_name' => $leave_message_name,
+ 'leave_message_email' => $leave_message_email,
+ 'leave_message_phone' => $leave_message_phone,
+ 'leave_message_status' => $leave_message_status,
+ ]);
+ }
+}
diff --git a/app/common/model/Menu/Menu.php b/app/common/model/Menu/Menu.php
new file mode 100644
index 0000000..47dc783
--- /dev/null
+++ b/app/common/model/Menu/Menu.php
@@ -0,0 +1,128 @@
+ 'int',
+ 'menu_parent_guid' => 'string',
+ 'menu_name' => 'string',
+ 'menu_url' => 'string',
+ 'menu_index' => 'int',
+ 'menu_order' => 'int',
+ 'menu_status' => 'int',
+ 'menu_show' => 'int',
+ 'menu_create_time' => 'datetime',
+ 'menu_create_user_guid' => 'string',
+ 'menu_update_time' => 'datetime',
+ 'menu_update_user_guid' => 'string',
+ 'menu_delete_time' => 'datetime',
+ 'menu_delete_user_guid' => 'string',
+ 'menu_guid' => 'string',
+ ];
+ // 开启自动写入时间戳字段
+ protected $autoWriteTimestamp = 'datetime';
+ // 创建时间
+ protected $createTime = 'menu_create_time';
+ // 修改时间
+ protected $updateTime = 'menu_update_time';
+ /**
+ * 状态 启用
+ */
+ const STATUS_ENABLE = 1;
+ /**
+ * 状态 禁用
+ */
+ const STATUS_DISABLE = 2;
+ // 状态查询范围
+ public function scopeStatus($query, $status = self::STATUS_ENABLE)
+ {
+ $query->where('menu_status', $status);
+ }
+
+ /**
+ * 新增前
+ *
+ * @date 2022-02-22
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public static function onBeforeInsert(self $model): void
+ {
+ $model->menu_status = self::STATUS_ENABLE;
+ $model->completeCreateField();
+ }
+
+ /**
+ * 更新前
+ *
+ * @date 2022-02-28
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public static function onBeforeUpdate(self $model): void
+ {
+ $model->completeUpdateField();
+ }
+
+ /**
+ * 删除前
+ *
+ * @date 2022-02-28
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public static function onBeforeDelete(self $model): void
+ {
+ $model->completeDeleteField();
+ }
+
+ /**
+ * 获取状态
+ *
+ * @date 2022-02-22
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function getMenuStatusTextAttr(): string
+ {
+ return [
+ self::STATUS_ENABLE => '启用',
+ self::STATUS_DISABLE => '停用'
+ ][$this->menu_status];
+ }
+
+ /**
+ * 菜单接口
+ *
+ * @date 2022-03-07
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function apis(): HasMany
+ {
+ return $this->hasMany(MenuApi::class, 'menu_guid');
+ }
+}
diff --git a/app/common/model/Menu/MenuApi.php b/app/common/model/Menu/MenuApi.php
new file mode 100644
index 0000000..9c6f9eb
--- /dev/null
+++ b/app/common/model/Menu/MenuApi.php
@@ -0,0 +1,142 @@
+ 'int',
+ 'menu_api_guid' => 'string',
+ 'menu_guid' => 'string',
+ 'menu_api_url' => 'string',
+ 'menu_api_status' => 'int',
+ 'menu_api_create_time' => 'datetime',
+ 'menu_api_create_user_guid' => 'string',
+ 'menu_api_update_time' => 'datetime',
+ 'menu_api_update_user_guid' => 'string',
+ 'menu_api_delete_time' => 'datetime',
+ 'menu_api_delete_user_guid' => 'string',
+ ];
+ // 开启自动写入时间戳字段
+ protected $autoWriteTimestamp = 'datetime';
+ // 创建时间
+ protected $createTime = 'menu_api_create_time';
+ // 修改时间
+ protected $updateTime = 'menu_api_update_time';
+ /**
+ * 状态 启用
+ */
+ const STATUS_ENABLE = 1;
+ /**
+ * 状态 禁用
+ */
+ const STATUS_DISABLE = 2;
+ // 状态查询范围
+ public function scopeStatus($query, $status = self::STATUS_ENABLE)
+ {
+ $query->where('menu_api_status', $status);
+ }
+
+ /**
+ * 新增前
+ *
+ * @date 2022-02-22
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public static function onBeforeInsert(self $model): void
+ {
+ $model->menu_api_status = self::STATUS_ENABLE;
+ $menu_api_urls = explode('/', $model->menu_api_url);
+ if (count($menu_api_urls) == 3) {
+ $app = isset($menu_api_urls[0]) ? $menu_api_urls[0] : '';
+ $controller = isset($menu_api_urls[1]) ? $menu_api_urls[1] : '';
+ $action = isset($menu_api_urls[2]) ? $menu_api_urls[2] : '';
+ } else {
+ $controller = isset($menu_api_urls[0]) ? $menu_api_urls[0] : '';
+ $action = isset($menu_api_urls[1]) ? $menu_api_urls[1] : '';
+ }
+ if (!$controller) {
+ throwErrorMsg("缺少controller", 1);
+ }
+ if (!$action) {
+ throwErrorMsg("缺少action", 1);
+ }
+ $controller = str_replace('.', '\\', $controller);
+ if ($app) {
+ $class = "\\app\\$app\\controller\\$controller";
+ } else {
+ $class = "\\app\\controller\\$controller";
+ }
+ if (!class_exists($class)) {
+ throwErrorMsg("controller不存在 $controller", 1);
+ }
+ if (!method_exists($class, $action)) {
+ throwErrorMsg("action不存在 $action", 1);
+ }
+ $model->completeCreateField();
+ }
+
+ /**
+ * 更新前
+ *
+ * @date 2022-02-28
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public static function onBeforeUpdate(self $model): void
+ {
+ $model->completeUpdateField();
+ }
+
+ /**
+ * 删除前
+ *
+ * @date 2022-02-28
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public static function onBeforeDelete(self $model): void
+ {
+ $model->completeDeleteField();
+ }
+
+ /**
+ * 获取状态
+ *
+ * @param mixed $value
+ * @param array $data
+ * @date 2022-02-22
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function getMenuApiStatusTextAttr(): string
+ {
+ return [
+ self::STATUS_ENABLE => '启用',
+ self::STATUS_DISABLE => '停用'
+ ][$this->menu_api_status];
+ }
+}
diff --git a/app/common/model/News/News.php b/app/common/model/News/News.php
new file mode 100644
index 0000000..8324044
--- /dev/null
+++ b/app/common/model/News/News.php
@@ -0,0 +1,99 @@
+ "int",
+
+ "news_guid" => "string",
+
+ "news_title" => "string",
+
+ "news_author" => "string",
+
+ "news_intro" => "string",
+
+ "news_type" => "string",
+
+ "news_img" => "string",
+
+ "news_content" => "string",
+
+ "news_detail_time" => "datetime",
+
+ "news_num" => "int",
+
+ "news_create_time" => "datetime",
+
+ "news_create_user_guid" => "string",
+
+ "news_update_time" => "datetime",
+
+ "news_update_user_guid" => "string",
+
+ "news_delete_time" => "datetime",
+
+ "news_delete_user_guid" => "string",
+
+ ];
+ // 设置json类型字段
+ protected $json = [''];
+ // 开启自动写入时间戳字段
+ protected $autoWriteTimestamp = 'datetime';
+ // 创建时间
+ protected $createTime = 'news_create_time';
+ // 修改时间
+ protected $updateTime = 'news_update_time';
+
+ /**
+ * 新增前
+ */
+ public static function onBeforeInsert(self $model): void
+ {
+ // self::checkRepeatData($model);
+ $model->completeCreateField();
+ }
+
+ /**
+ * 更新前
+ */
+ public static function onBeforeUpdate(self $model): void
+ {
+ // self::checkRepeatData($model);
+ $model->completeUpdateField();
+ }
+
+ /**
+ * 删除前
+ */
+ public static function onBeforeDelete(self $model): void
+ {
+ $model->completeDeleteField();
+ }
+
+ /**
+ * 产品类型获取器
+ */
+ public function getNewsCreatedTimeAttr($value, $data)
+ {
+ $news_create_time = $data['news_create_time'];
+ $news_create_time = date("m-d");
+ return $news_create_time;
+ }
+}
diff --git a/app/common/model/Product/Product.php b/app/common/model/Product/Product.php
new file mode 100644
index 0000000..b55942e
--- /dev/null
+++ b/app/common/model/Product/Product.php
@@ -0,0 +1,227 @@
+ "int",
+
+ "product_guid" => "string",
+
+ "product_type_guid" => "string",
+
+ "product_name" => "string",
+
+ "product_img" => "string",
+
+ "product_params" => "string",
+
+ "product_details" => "string",
+
+ "product_price" => "",
+
+ "product_create_time" => "datetime",
+
+ "product_create_user_guid" => "string",
+
+ "product_update_time" => "datetime",
+
+ "product_update_user_guid" => "string",
+
+ "product_delete_time" => "datetime",
+
+ "product_delete_user_guid" => "string",
+
+ ];
+ // 设置json类型字段
+ protected $json = [''];
+ // 开启自动写入时间戳字段
+ protected $autoWriteTimestamp = 'datetime';
+ // 创建时间
+ protected $createTime = 'product_create_time';
+ // 修改时间
+ protected $updateTime = 'product_update_time';
+
+
+ // excel导入/下载模板表头
+ public const EXCELFIELD = [
+ 'product_type_guid' => '产品系列',
+ 'product_name' => '名称',
+ 'product_img' => '图片',
+ 'product_params' => '参数',
+ 'product_details' => '详情',
+ 'product_price' => '价格',
+ ];
+
+ /**
+ * 新增前
+ */
+ public static function onBeforeInsert(self $model): void
+ {
+ // self::checkRepeatData($model);
+ $model->completeCreateField();
+ }
+
+ /**
+ * 更新前
+ */
+ public static function onBeforeUpdate(self $model): void
+ {
+ // self::checkRepeatData($model);
+ $model->completeUpdateField();
+ }
+
+ /**
+ * 删除前
+ */
+ public static function onBeforeDelete(self $model): void
+ {
+ $model->completeDeleteField();
+ }
+
+ /**
+ * 修改器(产品参数)
+ */
+ public function setProductParamsAttr($value): string
+ {
+ return json_encode($value, JSON_UNESCAPED_UNICODE);
+ }
+
+ /**
+ * 获取器(产品参数)
+ */
+ public function getProductParamsAttr($value): array
+ {
+ return json_decode($value);
+ }
+
+ /**
+ * 获取器(产品图片)
+ */
+ public function getProductImgAttr($value): array
+ {
+ $data = [];
+ foreach (explode(',', $value) as $item) {
+ $data[] = ['url' => $item];
+ };
+ return $data;
+ }
+
+ /**
+ * 指定产品上下id获取
+ *
+ * @param string $product_id 当前产品id
+ * @param string $product_type_guid 产品系列guid
+ * @return array [上一个id,下一个idF]
+ */
+ public static function getProductUpAndDownId(string $product_id, string $product_type_guid): array
+ {
+ $query = function ($op, $order) use ($product_id, $product_type_guid) {
+ return self::where([['product_id', $op, $product_id], ['product_type_guid', '=', $product_type_guid]])
+ ->limit(1)
+ ->order('product_id', $order)
+ ->value('product_id');
+ };
+ return [$query('<', 'desc'), $query('>', 'asc')];
+ }
+
+ /**
+ * 导出Excel
+ */
+ public static function exportExcel($select)
+ {
+ $data = [[
+ '产品系列',
+ '名称',
+ '图片',
+ '参数',
+ '详情',
+ '价格'
+ ]];
+ foreach ($select as $key => $val) {
+ $data[] = [
+ $val['product_type_guid'],
+ $val['product_name'],
+ Excel::ExportImgFiled($val['product_img']),
+ $val['product_params'],
+ $val['product_details'],
+ $val['product_price'],
+ ];
+ }
+ $excel = (new Excel())->exporTsheet($data);
+ $excel->save('产品.xlsx');
+ }
+
+ /**
+ * 导入excel
+ */
+ public static function importExcel($file)
+ {
+ $msg = [];
+
+ Db::startTrans();
+ try {
+ $excel = new Excel($file);
+ $data = $excel->parseExcel(
+ Tool::getExcelRule(self::EXCELFIELD),
+ [
+ 'titleLine' => [1]
+ ]
+ );
+ if (!$data) throwErrorMsg('excel无数据', 1);
+ $msg = [];
+ foreach ($data as $line => $value) {
+ try {
+ $model = self::importExcelInit($value);
+ $msg[] = "{$line} 新增成功!
";
+ } catch (\Throwable $th) {
+ $msg[] = "{$line} {$th->getMessage()}
";
+ }
+ }
+ Db::commit();
+ return implode(', ', $msg);
+ } catch (\Throwable $th) {
+ Db::rollback();
+ throw $th;
+ }
+ }
+
+ /**
+ * 导入excel初始化
+ */
+ public static function importExcelInit($value)
+ {
+ $product_type_guid = $value['product_type_guid'];
+ $product_name = $value['product_name'];
+ $product_img = $value['product_img'];
+ $product_params = $value['product_params'];
+ $product_details = $value['product_details'];
+ $product_price = $value['product_price'];
+ return self::create([
+ 'product_type_guid' => $product_type_guid,
+ 'product_name' => $product_name,
+ 'product_img' => $product_img,
+ 'product_params' => $product_params,
+ 'product_details' => $product_details,
+ 'product_price' => $product_price,
+ ]);
+ }
+}
diff --git a/app/common/model/Product/ProductParam.php b/app/common/model/Product/ProductParam.php
new file mode 100644
index 0000000..da40125
--- /dev/null
+++ b/app/common/model/Product/ProductParam.php
@@ -0,0 +1,93 @@
+ "int",
+
+ "product_param_guid" => "string",
+
+ "product_param_name" => "string",
+
+ "product_type_guid" => "string",
+
+ "product_type_order" => "int",
+
+ "product_param_create_time" => "datetime",
+
+ "product_param_create_user_guid" => "string",
+
+ "product_param_update_time" => "datetime",
+
+ "product_param_update_user_guid" => "string",
+
+ "product_param_delete_time" => "datetime",
+
+ "product_param_delete_user_guid" => "string",
+
+ ];
+ // 设置json类型字段
+ protected $json = [''];
+ // 开启自动写入时间戳字段
+ protected $autoWriteTimestamp = 'datetime';
+ // 创建时间
+ protected $createTime = 'product_param_create_time';
+ // 修改时间
+ protected $updateTime = 'product_param_update_time';
+
+
+
+
+ /**
+ * 新增前
+ */
+ public static function onBeforeInsert(self $model): void
+ {
+ // self::checkRepeatData($model);
+ $model->completeCreateField();
+ }
+
+ /**
+ * 更新前
+ */
+ public static function onBeforeUpdate(self $model): void
+ {
+ // self::checkRepeatData($model);
+ $model->completeUpdateField();
+ }
+
+ /**
+ * 删除前
+ */
+ public static function onBeforeDelete(self $model): void
+ {
+ $model->completeDeleteField();
+ }
+
+
+
+
+
+
+
+
+}
diff --git a/app/common/model/Product/ProductType.php b/app/common/model/Product/ProductType.php
new file mode 100644
index 0000000..6063099
--- /dev/null
+++ b/app/common/model/Product/ProductType.php
@@ -0,0 +1,182 @@
+ "int",
+ "product_type_guid" => "string",
+ "product_type_name" => "string",
+ "product_type_parent_guid" => "string",
+ "product_type_ancestors_guid" => "string",
+ "product_type_icon" => "string",
+ "product_type_cover" => "string",
+ "product_type_order" => "int",
+ "product_type_create_time" => "datetime",
+ "product_type_create_user_guid" => "string",
+ "product_type_update_time" => "datetime",
+ "product_type_update_user_guid" => "string",
+ "product_type_delete_time" => "datetime",
+ "product_type_delete_user_guid" => "string",
+ ];
+ // 设置json类型字段
+ protected $json = [''];
+ // 开启自动写入时间戳字段
+ protected $autoWriteTimestamp = 'datetime';
+ // 创建时间
+ protected $createTime = 'product_type_create_time';
+ // 修改时间
+ protected $updateTime = 'product_type_update_time';
+
+ // excel导入/下载模板表头
+ public const EXCELFIELD = [
+ 'product_type_name' => '产品系列名称',
+ 'product_type_icon' => '产品系列图标',
+ 'product_type_cover' => '产品系列封面',
+ ];
+
+ /**
+ * 新增前
+ */
+ public static function onBeforeInsert(self $model): void
+ {
+ // self::checkRepeatData($model);
+ $model->completeCreateField();
+ }
+
+ /**
+ * 更新前
+ */
+ public static function onBeforeUpdate(self $model): void
+ {
+ // self::checkRepeatData($model);
+ $model->completeUpdateField();
+ }
+
+ /**
+ * 删除前
+ */
+ public static function onBeforeDelete(self $model): void
+ {
+ $model->completeDeleteField();
+ }
+
+ /**
+ * 产品系列祖级guid构建
+ *
+ * @param string $product_type_parent_guid 产品系列父级guid
+ */
+ public static function buildAncestorsGuid(string $product_type_parent_guid): string
+ {
+ if ($product_type_parent_guid == "0") return $product_type_parent_guid;
+ $product_type = self::where('product_type_guid', $product_type_parent_guid)->find();
+ if (!$product_type) throwErrorMsg('该父级产品系列不存在!');
+ return $product_type->product_type_ancestors_guid . ',' . $product_type_parent_guid;
+ }
+
+ /**
+ * 产品系列重名验证
+ *
+ * @param string $product_type_name 产品系列名称
+ * @param string $product_type_guid 产品系列guid
+ */
+ public static function isDuplicateName(string $product_type_name, string $product_type_guid = null): void
+ {
+ $con = [
+ ['product_type_name', '=', $product_type_name]
+ ];
+ if ($product_type_guid) {
+ $con[] = ['product_type_guid', '<>', $product_type_guid];
+ }
+ if (self::where($con)->find()) {
+ throwErrorMsg('产品系列不可重名!');
+ };
+ }
+
+ /**
+ * 导出Excel
+ */
+ public static function exportExcel($select)
+ {
+ $data = [[
+ '产品系列标题',
+ '产品系列图标',
+ '产品系列封面'
+ ]];
+ foreach ($select as $key => $val) {
+ $data[] = [
+ $val['product_type_title'],
+ $val['product_type_icon'],
+ Excel::ExportImgFiled($val['product_type_cover']),
+ ];
+ }
+ $excel = (new Excel())->exporTsheet($data);
+ $excel->save('产品系列.xlsx');
+ }
+
+ /**
+ * 导入excel
+ */
+ public static function importExcel($file)
+ {
+ $msg = [];
+
+ Db::startTrans();
+ try {
+ $excel = new Excel($file);
+ $data = $excel->parseExcel(
+ Tool::getExcelRule(self::EXCELFIELD),
+ [
+ 'titleLine' => [1]
+ ]
+ );
+ if (!$data) throwErrorMsg('excel无数据', 1);
+ $msg = [];
+ foreach ($data as $line => $value) {
+ try {
+ $model = self::importExcelInit($value);
+ $msg[] = "{$line} 新增成功!
";
+ } catch (\Throwable $th) {
+ $msg[] = "{$line} {$th->getMessage()}
";
+ }
+ }
+ Db::commit();
+ return implode(', ', $msg);
+ } catch (\Throwable $th) {
+ Db::rollback();
+ throw $th;
+ }
+ }
+
+ /**
+ * 导入excel初始化
+ */
+ public static function importExcelInit($value)
+ {
+ $product_type_title = $value['product_type_title'];
+ $product_type_icon = $value['product_type_icon'];
+ $product_type_cover = $value['product_type_cover'];
+ return self::create([
+ 'product_type_title' => $product_type_title,
+ 'product_type_icon' => $product_type_icon,
+ 'product_type_cover' => $product_type_cover,
+ ]);
+ }
+}
diff --git a/app/common/model/Role/Role.php b/app/common/model/Role/Role.php
new file mode 100644
index 0000000..e24275c
--- /dev/null
+++ b/app/common/model/Role/Role.php
@@ -0,0 +1,146 @@
+ 'int',
+ 'role_name' => 'string',
+ 'role_status' => 'int',
+ 'role_create_time' => 'datetime',
+ 'role_create_user_guid' => 'string',
+ 'role_update_time' => 'datetime',
+ 'role_update_user_guid' => 'string',
+ 'role_delete_time' => 'datetime',
+ 'role_delete_user_guid' => 'string',
+ 'role_guid' => 'string',
+ ];
+ // 开启自动写入时间戳字段
+ protected $autoWriteTimestamp = 'datetime';
+ // 创建时间
+ protected $createTime = 'role_create_time';
+ // 修改时间
+ protected $updateTime = 'role_update_time';
+ /**
+ * 状态 启用
+ */
+ const STATUS_ENABLE = 1;
+ /**
+ * 状态 禁用
+ */
+ const STATUS_DISABLE = 2;
+ // 状态查询范围
+ public function scopeStatus(Query $query, $status = self::STATUS_ENABLE)
+ {
+ $query->where('role_status', $status);
+ }
+
+ /**
+ * 新增前
+ *
+ * @date 2022-02-22
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public static function onBeforeInsert(self $model): void
+ {
+ $model->role_status = self::STATUS_ENABLE;
+ $model->completeCreateField();
+ }
+
+ /**
+ * 更新前
+ *
+ * @date 2022-02-28
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public static function onBeforeUpdate(self $model): void
+ {
+ $model->completeUpdateField();
+ }
+
+ /**
+ * 删除前
+ *
+ * @date 2022-02-28
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public static function onBeforeDelete(self $model): void
+ {
+ $model->completeDeleteField();
+ }
+
+ /**
+ * 获取状态
+ *
+ * @date 2022-02-22
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function getRoleStatusTextAttr(): string
+ {
+ return [
+ self::STATUS_ENABLE => '启用',
+ self::STATUS_DISABLE => '停用'
+ ][$this->role_status];
+ }
+
+ /**
+ * 获取角色用户
+ *
+ * @date 2022-03-02
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function users(): BelongsToMany
+ {
+ return $this->belongsToMany(
+ User::class,
+ UserRole::class,
+ 'user_guid',
+ 'role_guid'
+ );
+ }
+
+ /**
+ * 获取角色菜单
+ *
+ * @date 2022-03-02
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function menus(): BelongsToMany
+ {
+ return $this->belongsToMany(
+ Menu::class,
+ RoleMenu::class,
+ 'menu_guid',
+ 'role_guid'
+ );
+ }
+}
diff --git a/app/common/model/Role/RoleMenu.php b/app/common/model/Role/RoleMenu.php
new file mode 100644
index 0000000..d1467a5
--- /dev/null
+++ b/app/common/model/Role/RoleMenu.php
@@ -0,0 +1,162 @@
+ 'int',
+ 'menu_guid' => 'string',
+ 'role_guid' => 'string',
+ 'role_menu_status' => 'int',
+ 'role_menu_create_time' => 'datetime',
+ 'role_menu_create_user_guid' => 'string',
+ 'role_menu_update_time' => 'datetime',
+ 'role_menu_update_user_guid' => 'string',
+ 'role_menu_delete_time' => 'datetime',
+ 'role_menu_delete_user_guid' => 'string',
+ 'role_menu_guid' => 'string',
+ ];
+ // 开启自动写入时间戳字段
+ protected $autoWriteTimestamp = 'datetime';
+ // 创建时间
+ protected $createTime = 'role_menu_create_time';
+ // 修改时间
+ protected $updateTime = 'role_menu_update_time';
+ /**
+ * 状态 启用
+ */
+ const STATUS_ENABLE = 1;
+ /**
+ * 状态 禁用
+ */
+ const STATUS_DISABLE = 2;
+ // 状态查询范围
+ public function scopeStatus($query, $status = self::STATUS_ENABLE)
+ {
+ $query->where('role_menu_status', $status);
+ }
+ /**
+ * 中间关联
+ *
+ * @return ModelPivot
+ * @date 2022-12-30
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public static function pivot(): ModelPivot
+ {
+ return new ModelPivot(
+ self::class,
+ Role::class,
+ Menu::class,
+ true
+ );
+ }
+ /**
+ * 绑定角色菜单
+ *
+ * @param $roles
+ * @param $menus
+ * @param array $extra
+ * @return void
+ * @date 2022-12-29
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public static function bindRoleMenu($roles, $menus, array $extra = []): void
+ {
+ self::pivot()->bind($roles, $menus, $extra);
+ }
+ /**
+ * 解绑角色菜单
+ *
+ * @param $roles
+ * @param $menus
+ * @return void
+ * @date 2022-12-30
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public static function unbindRoleMenu($roles, $menus): void
+ {
+ self::pivot()->unbind($roles, $menus);
+ }
+ /**
+ * 绑定角色菜单
+ *
+ * @param $roles
+ * @param $menus
+ * @param array $extra
+ * @return void
+ * @date 2022-12-29
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public static function rebindRoleMenu($roles, $menus, array $extra = []): void
+ {
+ self::pivot()->rebind($roles, $menus, $extra);
+ }
+
+ /**
+ * 新增前
+ *
+ * @date 2022-02-22
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public static function onBeforeInsert(self $model): void
+ {
+ $model->role_menu_status = self::STATUS_ENABLE;
+ $model->completeCreateField();
+ }
+
+ /**
+ * 更新前
+ *
+ * @date 2022-02-28
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public static function onBeforeUpdate(self $model): void
+ {
+ $model->completeUpdateField();
+ }
+
+ /**
+ * 删除前
+ *
+ * @date 2022-02-28
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public static function onBeforeDelete(self $model): void
+ {
+ $model->completeDeleteField();
+ }
+}
diff --git a/app/common/model/School/Classes.php b/app/common/model/School/Classes.php
new file mode 100644
index 0000000..9725c90
--- /dev/null
+++ b/app/common/model/School/Classes.php
@@ -0,0 +1,77 @@
+ "int",
+
+ "classes_guid" => "string",
+
+ "classes_name" => "string",
+
+ "classes_create_time" => "datetime",
+
+ "classes_create_user_guid" => "string",
+
+ "classes_update_time" => "datetime",
+
+ "classes_update_user_guid" => "string",
+
+ "classes_delete_time" => "datetime",
+
+ "classes_delete_user_guid" => "string",
+
+ ];
+ // 设置json类型字段
+ protected $json = [''];
+ // 开启自动写入时间戳字段
+ protected $autoWriteTimestamp = 'datetime';
+ // 创建时间
+ protected $createTime = 'classes_create_time';
+ // 修改时间
+ protected $updateTime = 'classes_update_time';
+
+ /**
+ * 新增前
+ */
+ public static function onBeforeInsert(self $model): void
+ {
+ // self::checkRepeatData($model);
+ $model->completeCreateField();
+ }
+
+ /**
+ * 更新前
+ */
+ public static function onBeforeUpdate(self $model): void
+ {
+ // self::checkRepeatData($model);
+ $model->completeUpdateField();
+ }
+
+ /**
+ * 删除前
+ */
+ public static function onBeforeDelete(self $model): void
+ {
+ $model->completeDeleteField();
+ }
+
+
+}
diff --git a/app/common/model/School/Student.php b/app/common/model/School/Student.php
new file mode 100644
index 0000000..45715eb
--- /dev/null
+++ b/app/common/model/School/Student.php
@@ -0,0 +1,627 @@
+ "int",
+
+ "student_guid" => "string",
+
+ "student_name" => "string",
+
+ "user_guid" => "string",
+
+ "classes_guid" => "string",
+
+ "studnet_phone" => "string",
+
+ "student_id_card" => "string",
+
+ "student_email" => "string",
+
+ "student_sex" => "string",
+
+ "student_price" => "",
+
+ "student_brithday" => "date",
+
+ "product_type" => "string",
+
+ "product_guid" => "string",
+
+ "product_parts_guid" => "string",
+
+ "student_img" => "string",
+
+ "student_banner_img" => "string",
+
+ "student_attachment" => "string",
+
+ "student_intro" => "string",
+
+ "student_location" => "string",
+
+ "longitude" => "string",
+
+ "latitude" => "string",
+
+ "student_membe_type" => "string",
+
+ "student_member_begin_time" => "date",
+
+ "student_member_end_time" => "date",
+
+ "student_audit_status" => "string",
+
+ "student_audit_user_guid" => "string",
+
+ "student_create_time" => "datetime",
+
+ "student_create_user_guid" => "string",
+
+ "student_update_time" => "datetime",
+
+ "student_update_user_guid" => "string",
+
+ "student_delete_time" => "datetime",
+
+ "student_delete_user_guid" => "string",
+
+ ];
+
+ // 设置json类型字段
+ protected $json = [''];
+ // 开启自动写入时间戳字段
+ protected $autoWriteTimestamp = 'datetime';
+ // 创建时间
+ protected $createTime = 'student_create_time';
+ // 修改时间
+ protected $updateTime = 'student_update_time';
+
+ /**
+ * 新增前
+ */
+ public static function onBeforeInsert(self $model): void
+ {
+ // self::checkRepeatData($model);
+ $model->completeCreateField();
+ }
+
+ /**
+ * 更新前
+ */
+ public static function onBeforeUpdate(self $model): void
+ {
+ // self::checkRepeatData($model);
+ $model->completeUpdateField();
+ }
+
+ /**
+ * 删除前
+ */
+ public static function onBeforeDelete(self $model): void
+ {
+ $model->completeDeleteField();
+ }
+
+ /**
+ * 审核学生
+ */
+ public static function auditStudent($params)
+ {
+ $params_audit_status = $params['student_audit_status'];
+ $student_guids_arr = explode(',', $params['student_guid']);
+
+ Db::startTrans();
+ try {
+
+ if (count($student_guids_arr) > 1) {
+ foreach ($student_guids_arr as $key => $value) {
+ self::audit($value,$params);
+ }
+ } else {
+ self::audit($params['student_guid'],$params);
+ }
+
+ Db::commit();
+ } catch (\Throwable $th) {
+ Db::rollback();
+ throw $th;
+ }
+ }
+
+ /**
+ * 审核
+ */
+ public static function audit($value,$params)
+ {
+ $model = self::where('student_guid', $value)->find();
+ if (!$model) throwErrorMsg("该学生不存在", 1);
+
+ $params['student_audit_user_guid'] = Request::getCurrentUser()->user_guid;
+
+ $audit_status = $model->student_audit_status;
+ $student_name = $model->student_name;
+
+ if ($audit_status == 2) throwErrorMsg("{$student_name} 学生已通过审核!");
+ if ($audit_status == 3) throwErrorMsg("{$student_name} 学生已驳回审核!");
+
+ $model->allowField([
+ 'student_audit_status',
+ 'student_audit_user_guid'
+ ])->save($params);
+ }
+
+ /**
+ * 新增前初始化
+ */
+ public static function initAdd($params)
+ {
+ $user = ModelUser::where('user_name', $params['user_name'])->find();
+ $params['user_guid'] = $user->user_guid;
+ $student_user = self::where('user_guid', $params['user_guid'])->find();
+ if ($student_user) throwErrorMsg("用户已经被绑定,请重新选择!");
+
+ $isPhone = RegularVerification::checkPreg($params['studnet_phone'], 'mobile');
+ if (!$isPhone) throwErrorMsg("电话格式错误");
+
+ $isIdCard = RegularVerification::checkPreg($params['student_id_card'], 'idcard');
+ if (!$isIdCard) throwErrorMsg("身份证格式错误");
+
+ $isEmail = RegularVerification::checkPreg($params['student_email'], 'email');
+ if (!$isEmail) throwErrorMsg("邮箱格式错误");
+
+ // 季卡
+ if ($params['student_membe_type'] == '1') {
+ $params['student_member_begin_time'] = date('Y-m-d H:i:s');
+ $params['student_member_end_time'] = date('Y-m-d H:i:s', strtotime('+3 month'));
+ }
+ // 年卡
+ if ($params['student_membe_type'] == '2') {
+ $params['student_member_begin_time'] = date('Y-m-d H:i:s');
+ $params['student_member_end_time'] = date('Y-m-d H:i:s', strtotime('+1 year'));
+ }
+
+ // 已审核
+ $params['student_audit_status'] = "1";
+ // $params['student_audit_user_guid'] = "ds4a3d2asd423s3as4d34asdd";
+
+ return $params;
+ }
+
+ /**
+ * 导入前初始化
+ */
+ public static function ImportStudentInit($value)
+ {
+ // 用户判断
+ $user = ModelUser::where('user_name', $value['user_name'])->find();
+ // array_push($error, "{$line}: 产品类型不存在,导入失败!");
+ if (!$user) throwErrorMsg("用户 {$value['user_name']} 不存在");
+ $value['user_guid'] = $user->user_guid;
+ $student_user = self::where('user_guid', $value['user_guid'])->find();
+ if ($student_user) throwErrorMsg("用户 {$value['user_name']} 已经被绑定,请重新选择!");
+
+ // 班级判断
+ $classes = ModelClasses::where('classes_name', $value['classes_name'])->find();
+ if (!$classes) throwErrorMsg("班级不存在");
+ $value['classes_guid'] = $classes->classes_guid;
+
+
+ $isPhone = RegularVerification::checkPreg($value['studnet_phone'], 'mobile');
+ if (!$isPhone) throwErrorMsg("电话格式错误");
+
+ // $isIdCard = RegularVerification::checkPreg($value['student_id_card'], 'idcard');
+ // if (!$isIdCard) throwErrorMsg("身份证格式错误");
+
+ $isEmail = RegularVerification::checkPreg($value['student_email'], 'email');
+ if (!$isEmail) throwErrorMsg("邮箱格式错误");
+
+ $sex_val = [1 => '男', 2 => '女'];
+
+
+ // 性别判断
+ if (!in_array($value['student_sex'], ["男", "女"])) throwErrorMsg("请填写男或女");
+ else {
+ $value['student_sex'] = array_search($value['student_sex'], $sex_val);
+ // if ($value['student_sex'] == "男") $value['student_sex'] = 1;
+ // if ($value['student_sex'] == "女") $value['student_sex'] = 2;
+ }
+
+ // 价格判断
+ if (!is_numeric($value['student_price'])) throwErrorMsg("学生价格必须为整数");
+ if ($value['student_price'] < 0) throwErrorMsg("学生价格不能为负数");
+
+ // 产品类型判断
+ $product_type = ModelProductType::where('product_type_name', $value['product_type_name'])->find();
+ if (!$product_type) throwErrorMsg("{$value['product_type_name']} 产品类型不存在");
+ $value['product_type_guid'] = $product_type->product_type_guid;
+
+ // 产品判断
+ $product = ModelProduct::where('product_name', $value['product_name'])->find();
+ if (!$product) throwErrorMsg("{$value['product_name']} 产品不存在");
+ $value['product_guid'] = $product->product_guid;
+ $InProductType = ModelProduct::where('product_type_guid', $value['product_type_guid'])->find();
+ if (!$InProductType) throwErrorMsg("{$value['product_name']} 产品 没有绑定产品类型:{$value['product_type_name']}");
+
+ // 产品零件判断
+ $product_parts = ModelProductParts::where('product_parts_name', $value['product_parts_name'])->find();
+ if (!$product_parts) throwErrorMsg("{$value['product_parts_name']} 产品零件不存在");
+ $value['product_parts_guid'] = $product_parts->product_parts_guid;
+ $InProduct = ModelProductParts::where('product_guid', $value['product_guid'])->find();
+ if (!$InProduct) throwErrorMsg("{$value['product_parts_name']} 产品零件 没有绑定产品:{$value['product_name']}");
+
+ // 获取经纬度
+ $longitude_latitude_arr = Map::getLongitudeAndLatitude($value['student_location']);
+ $value['longitude'] = $longitude_latitude_arr['longitude'];
+ $value['latitude'] = $longitude_latitude_arr['latitude'];
+
+ // 季卡
+ if ($value['student_membe_type'] == '季卡') {
+ $value['student_membe_type'] = '1';
+ $value['student_member_begin_time'] = date('Y-m-d H:i:s');
+ $value['student_member_end_time'] = date('Y-m-d H:i:s', strtotime('+3 month'));
+ }
+ // 年卡
+ if ($value['student_membe_type'] == '年卡') {
+ $value['student_membe_type'] = '2';
+ $value['student_member_begin_time'] = date('Y-m-d H:i:s');
+ $value['student_member_end_time'] = date('Y-m-d H:i:s', strtotime('+1 year'));
+ }
+
+ // 已审核
+ $value['student_audit_status'] = "2";
+ $value['student_audit_user_guid'] = "ds4a3d2asd423s3as4d34asdd";
+
+ // 生日日期处理
+ $time = strtotime($value['student_brithday']);
+ $newformat = date('Y-m-d', $time);
+
+ return self::create([
+ 'student_name' => $value['student_name'],
+ 'user_guid' => $value['user_guid'],
+ 'classes_guid' => $value['classes_guid'],
+ 'studnet_phone' => $value['studnet_phone'],
+ 'student_id_card' => $value['student_id_card'],
+ 'student_email' => $value['student_email'],
+ 'student_sex' => $value['student_sex'],
+ 'student_price' => $value['student_price'],
+ 'student_brithday' => $newformat,
+ 'product_type' => $value['product_type_guid'],
+ 'product_guid' => $value['product_guid'],
+ 'product_parts_guid' => $value['product_parts_guid'],
+ 'student_img' => $value['student_img'],
+ 'student_banner_img' => $value['student_banner_img'],
+ 'student_intro' => $value['student_intro'],
+ 'student_location' => $value['student_location'],
+ 'longitude' => $value['longitude'],
+ 'latitude' => $value['latitude'],
+ 'student_membe_type' => $value['student_membe_type'],
+ 'student_member_begin_time' => $value['student_member_begin_time'],
+ 'student_member_end_time' => $value['student_member_end_time'],
+ 'student_audit_status' => $value['student_audit_status'],
+ 'student_audit_user_guid' => $value['student_audit_user_guid'],
+ ]);
+ }
+
+ /**
+ * 导入学生
+ */
+ public static function ImportStudent($file)
+ {
+ $error = [];
+
+ Db::startTrans();
+ try {
+
+ $excel = new Excel($file);
+ $data = $excel->parseExcel(
+ [
+ [
+ 'title' => '学生名称',
+ 'validate' => 'require',
+ 'field' => 'student_name',
+ ], [
+ 'title' => '用户',
+ 'validate' => 'require',
+ 'field' => 'user_name',
+ ], [
+ 'title' => '班级',
+ 'validate' => 'require',
+ 'field' => 'classes_name',
+ ], [
+ 'title' => '手机号',
+ 'validate' => 'require',
+ 'field' => 'studnet_phone',
+ ], [
+ 'title' => '身份证号',
+ 'validate' => 'require',
+ 'field' => 'student_id_card',
+ ], [
+ 'title' => '邮箱',
+ 'validate' => 'require',
+ 'field' => 'student_email',
+ ], [
+ 'title' => '性别',
+ 'validate' => 'require',
+ 'field' => 'student_sex',
+ ], [
+ 'title' => '学生价格',
+ 'validate' => 'require',
+ 'field' => 'student_price',
+ ], [
+ 'title' => '生日',
+ 'validate' => 'require',
+ 'field' => 'student_brithday',
+ ], [
+ 'title' => '产品类型',
+ 'validate' => 'require',
+ 'field' => 'product_type_name',
+ ], [
+ 'title' => '产品',
+ 'validate' => 'require',
+ 'field' => 'product_name',
+ ], [
+ 'title' => '产品零件',
+ 'validate' => 'require',
+ 'field' => 'product_parts_name',
+ ], [
+ 'title' => '头像',
+ 'validate' => 'require',
+ 'field' => 'student_img',
+ ], [
+ 'title' => '轮播图片',
+ 'validate' => 'require',
+ 'field' => 'student_banner_img',
+ ], [
+ 'title' => '简介',
+ 'validate' => 'require',
+ 'field' => 'student_intro',
+ ], [
+ 'title' => '家庭住址',
+ 'validate' => 'require',
+ 'field' => 'student_location',
+ ], [
+ 'title' => '会员类型',
+ 'validate' => 'require',
+ 'field' => 'student_membe_type',
+ ],
+ // [
+ // 'title' => '会员开始时间',
+ // 'validate' => 'require',
+ // 'field' => 'student_member_begin_time',
+ // ], [
+ // 'title' => '会员结束时间',
+ // 'validate' => 'require',
+ // 'field' => 'student_member_end_time',
+ // ],
+ ],
+ [
+ 'titleLine' => [1]
+ ]
+ );
+ if (!$data) throwErrorMsg('excel无数据', 1);
+ $error = [];
+ foreach ($data as $line => $value) {
+ try {
+ $model = self::ImportStudentInit($value);
+ $error[] = "{$line} 学生:【{$value['student_name']}】新增成功!
";
+ } catch (\Throwable $th) {
+ $error[] = "{$line} 学生:【{$value['student_name']}】{$th->getMessage()}
";
+ }
+ }
+ Db::commit();
+
+ return implode(', ', $error);
+ } catch (\Throwable $th) {
+ Db::rollback();
+ throw $th;
+ }
+
+ return $error;
+ }
+
+ /**
+ * 导出学生
+ */
+ public static function exportStudent()
+ {
+ $select = self::alias('a')
+ ->field([
+ 'a.student_name',
+ 'b.user_name',
+ 'f.classes_name',
+ 'a.studnet_phone',
+ 'a.student_id_card',
+ 'a.student_email',
+ 'a.student_sex',
+ 'a.student_price',
+ 'a.student_brithday',
+ 'c.product_type_name',
+ 'd.product_name',
+ 'e.product_parts_name',
+ 'a.student_img',
+ 'a.student_banner_img',
+ 'a.student_attachment',
+ 'a.student_location',
+ 'a.student_membe_type',
+ 'a.student_member_begin_time',
+ 'a.student_member_end_time',
+ 'a.student_audit_status',
+ "(SELECT `user`.`user_name` FROM `user` WHERE `user`.user_guid = `a`.`student_audit_user_guid`) as student_audit_user_name",
+ ])
+ ->leftjoin('user b', 'a.user_guid = b.user_guid')
+ ->leftjoin('product_type c', 'a.product_type = c.product_type_guid')
+ ->leftjoin('product d', 'a.product_guid = d.product_guid')
+ ->leftjoin('product_parts e', 'a.product_parts_guid = e.product_parts_guid')
+ ->leftjoin('classes f', 'a.classes_guid = f.classes_guid')
+ ->order('student_update_time', 'desc')->select();
+
+ $data = [[
+ '学生名称',
+ '用户',
+ '班级',
+ '手机号',
+ '身份证号',
+ '邮箱',
+ '性别',
+ '学生价格',
+ '生日',
+ '产品类型',
+ '产品',
+ '产品零件',
+ '头像',
+ '轮播图片',
+ '家庭住址',
+ '会员类型',
+ '会员开始时间',
+ '会员结束时间',
+ '审核状态',
+ '审核人'
+ ]];
+ foreach ($select as $key => $val) {
+
+ $sex_arr = [1 => "男", 2 => "女"];
+ $member_arr = [1 => "季卡", 2 => "年卡"];
+ $audit_arr = [1 => "未审核", 2 => "已审核", 3 => "未通过"];
+
+ $val['student_sex'] = $sex_arr[$val['student_sex']];
+ $val['student_membe_type'] = $member_arr[$val['student_membe_type']];
+ $val['student_audit_status'] = $audit_arr[$val['student_audit_status']];
+ $val['student_img'] = Excel::ExportImgFiled($val['student_img']);
+ $val['student_banner_img'] = Excel::ExportImgFiled($val['student_banner_img']);
+
+ $data[] = [
+ $val['student_name'],
+ $val['user_name'],
+ $val['classes_name'],
+ $val['studnet_phone'],
+ $val['student_id_card'],
+ $val['student_email'],
+ $val['student_sex'],
+ $val['student_price'],
+ $val['student_brithday'],
+ $val['product_type_name'],
+ $val['product_name'],
+ $val['product_parts_name'],
+ $val['student_img'],
+ $val['student_banner_img'],
+ $val['student_location'],
+ $val['student_membe_type'],
+ $val['student_member_begin_time'],
+ $val['student_member_end_time'],
+ $val['student_audit_status'],
+ $val['student_audit_user_name'],
+ ];
+ }
+ $excel = (new Excel())->exporTsheet($data);
+ $excel->save('学生.xlsx');
+ }
+
+ /**
+ * 新增学生服务
+ */
+ public static function addStudentService($student_guid, $params): void
+ {
+ $student_service_arr = $params['student_service'];
+
+ foreach ($student_service_arr as $key => $item) {
+ Db::startTrans();
+ try {
+ $add_data = [
+ 'student_guid' => $student_guid,
+ 'student_service_name' => $item['service_name'],
+ 'student_service_price' => $item['service_price'],
+ ];
+ $student_service = ModelStudentService::create($add_data);
+
+ Db::commit();
+ } catch (\Throwable $th) {
+ Db::rollback();
+ throw $th;
+ }
+ }
+ }
+
+ /**
+ * 编辑学生服务
+ */
+ public static function editStudentService($params): void
+ {
+ // 从学生服务(副表)查询出所有当前学生的服务,进行删除
+ ModelStudentService::where('student_guid', $params['student_guid'])->select()->delete();
+
+ // 再把传值的数据,写入副表
+ $student_service_arr = $params['student_service'];
+ foreach ($student_service_arr as $key => $item) {
+ Db::startTrans();
+ try {
+ $add_data = [
+ 'student_guid' => $params['student_guid'],
+ 'student_service_name' => $item['service_name'],
+ 'student_service_price' => $item['service_price'],
+ ];
+ ModelStudentService::create($add_data);
+
+ Db::commit();
+ } catch (\Throwable $th) {
+ Db::rollback();
+ throw $th;
+ }
+ }
+ }
+
+ /**
+ * 会员时间获取器
+ */
+ public function getStudentMemberTimeAttr($value, $data)
+ {
+ $student_member_time = [];
+
+ array_push($student_member_time, $data['student_member_begin_time']);
+ array_push($student_member_time, $data['student_member_end_time']);
+
+ return $student_member_time;
+ }
+
+
+ /**
+ * 学生服务获取器
+ */
+ public function getStudentServiceAttr($value, $data)
+ {
+ $studnet_guid = $data['student_guid'];
+ $studetn_service = ModelStudentService::field([
+ 'student_service_guid',
+ 'student_service_name' => 'service_name',
+ 'student_service_price' => 'service_price',
+ ])->where('student_guid', $studnet_guid)->select();
+ return $studetn_service;
+ }
+}
diff --git a/app/common/model/School/StudentService.php b/app/common/model/School/StudentService.php
new file mode 100644
index 0000000..8661c22
--- /dev/null
+++ b/app/common/model/School/StudentService.php
@@ -0,0 +1,81 @@
+ "int",
+
+ "student_service_guid" => "string",
+
+ "student_guid" => "string",
+
+ "student_service_name" => "string",
+
+ "student_service_price" => "",
+
+ "student_service_create_time" => "datetime",
+
+ "student_service_create_user_guid" => "string",
+
+ "student_service_update_time" => "datetime",
+
+ "student_service_update_user_guid" => "string",
+
+ "student_service_delete_time" => "datetime",
+
+ "student_service_delete_user_guid" => "string",
+
+ ];
+ // 设置json类型字段
+ protected $json = [''];
+ // 开启自动写入时间戳字段
+ protected $autoWriteTimestamp = 'datetime';
+ // 创建时间
+ protected $createTime = 'student_service_create_time';
+ // 修改时间
+ protected $updateTime = 'student_service_update_time';
+
+ /**
+ * 新增前
+ */
+ public static function onBeforeInsert(self $model): void
+ {
+ // self::checkRepeatData($model);
+ $model->completeCreateField();
+ }
+
+ /**
+ * 更新前
+ */
+ public static function onBeforeUpdate(self $model): void
+ {
+ // self::checkRepeatData($model);
+ $model->completeUpdateField();
+ }
+
+ /**
+ * 删除前
+ */
+ public static function onBeforeDelete(self $model): void
+ {
+ $model->completeDeleteField();
+ }
+
+
+}
diff --git a/app/common/model/ServiceSupport/ServiceSupport.php b/app/common/model/ServiceSupport/ServiceSupport.php
new file mode 100644
index 0000000..b8087c9
--- /dev/null
+++ b/app/common/model/ServiceSupport/ServiceSupport.php
@@ -0,0 +1,93 @@
+ "int",
+
+ "service_support_guid" => "string",
+
+ "service_support_idea" => "string",
+
+ "service_support_promise" => "string",
+
+ "service_support_network" => "string",
+
+ "service_support_create_time" => "datetime",
+
+ "service_support_create_user_guid" => "string",
+
+ "service_support_update_time" => "datetime",
+
+ "service_support_update_user_guid" => "string",
+
+ "service_support_delete_time" => "datetime",
+
+ "service_support_delete_user_guid" => "string",
+
+ ];
+ // 设置json类型字段
+ protected $json = [''];
+ // 开启自动写入时间戳字段
+ protected $autoWriteTimestamp = 'datetime';
+ // 创建时间
+ protected $createTime = 'service_support_create_time';
+ // 修改时间
+ protected $updateTime = 'service_support_update_time';
+
+
+
+
+ /**
+ * 新增前
+ */
+ public static function onBeforeInsert(self $model): void
+ {
+ // self::checkRepeatData($model);
+ $model->completeCreateField();
+ }
+
+ /**
+ * 更新前
+ */
+ public static function onBeforeUpdate(self $model): void
+ {
+ // self::checkRepeatData($model);
+ $model->completeUpdateField();
+ }
+
+ /**
+ * 删除前
+ */
+ public static function onBeforeDelete(self $model): void
+ {
+ $model->completeDeleteField();
+ }
+
+
+
+
+
+
+
+
+}
diff --git a/app/common/model/Tdk/Tdk.php b/app/common/model/Tdk/Tdk.php
new file mode 100644
index 0000000..eb019bd
--- /dev/null
+++ b/app/common/model/Tdk/Tdk.php
@@ -0,0 +1,83 @@
+ "int",
+
+ "tdk_guid" => "string",
+
+ "tdk_type" => "int",
+
+ "tdk_title" => "string",
+
+ "tdk_description" => "string",
+
+ "tdk_keyword" => "string",
+
+ "tdk_create_time" => "datetime",
+
+ "tdk_create_user_guid" => "string",
+
+ "tdk_update_time" => "datetime",
+
+ "tdk_update_user_guid" => "string",
+
+ "tdk_delete_time" => "datetime",
+
+ "tdk_delete_user_guid" => "string",
+
+ ];
+ // 设置json类型字段
+ protected $json = [''];
+ // 开启自动写入时间戳字段
+ protected $autoWriteTimestamp = 'datetime';
+ // 创建时间
+ protected $createTime = 'tdk_create_time';
+ // 修改时间
+ protected $updateTime = 'tdk_update_time';
+
+ /**
+ * 新增前
+ */
+ public static function onBeforeInsert(self $model): void
+ {
+ // self::checkRepeatData($model);
+ $model->completeCreateField();
+ }
+
+ /**
+ * 更新前
+ */
+ public static function onBeforeUpdate(self $model): void
+ {
+ // self::checkRepeatData($model);
+ $model->completeUpdateField();
+ }
+
+ /**
+ * 删除前
+ */
+ public static function onBeforeDelete(self $model): void
+ {
+ $model->completeDeleteField();
+ }
+
+
+}
diff --git a/app/common/model/Token.php b/app/common/model/Token.php
new file mode 100644
index 0000000..f87c75e
--- /dev/null
+++ b/app/common/model/Token.php
@@ -0,0 +1,337 @@
+ 'int',
+ 'token_guid' => 'string',
+ 'user_guid' => 'string',
+ 'token_menu' => 'json',
+ 'token_api' => 'json',
+ 'token_exp_time' => 'datetime',
+ 'token_content' => 'string',
+ 'token_create_time' => 'datetime',
+ 'token_update_time' => 'datetime',
+ ];
+ // json数据字段
+ protected $json = ['token_menu', 'token_api'];
+ // 设置JSON数据返回数组
+ protected $jsonAssoc = true;
+ // 开启自动写入时间戳字段
+ protected $autoWriteTimestamp = 'datetime';
+ // 创建时间
+ protected $createTime = 'token_create_time';
+ // 修改时间
+ protected $updateTime = 'token_update_time';
+ /**
+ * 当前用户
+ *
+ * @var User
+ */
+ private static $user = null;
+ /**
+ * 当前model
+ *
+ * @var self
+ */
+ private static $current = null;
+
+ /**
+ * 用户
+ *
+ * @date 2022-02-22
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function user(): hasOne
+ {
+ return $this->hasOne(User::class, 'user_guid', 'user_guid');
+ }
+
+ /**
+ * 设置过期时间
+ *
+ * @param mixed $value
+ * @date 2022-02-22
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function setTokenExpTimeAttr($value): string
+ {
+ return date('Y-m-d H:i:s', $value);
+ }
+
+ /**
+ * 判断是否过期
+ *
+ * @return boolean
+ * @date 2022-12-27
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function getIsExpAttr(): bool
+ {
+ return time() >= $this->token_exp_timestamp;
+ }
+
+ /**
+ * 获取过期时间戳
+ *
+ * @return integer
+ * @date 2022-12-27
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function getTokenExpTimestampAttr(): int
+ {
+ return strtotime($this->token_exp_time);
+ }
+
+
+ /**
+ * 新增前
+ *
+ * @param self $model
+ * @return void
+ * @date 2022-12-27
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public static function onBeforeInsert(self $model): void
+ {
+ $model->token_content = $model->generateContent();
+ $model->token_guid = self::generateGuid();
+ }
+
+ /**
+ * 登陆
+ * 如果当前存在有效token 更新过期时间
+ * 如果token已过期 更新token和过期时间
+ * 如果不存在token 新增数据
+ *
+ * @param string $guid 用户id
+ * @param array $options 配置项
+ * @param int $options[expTime] 过期时间(时间戳) 不传默认两小时
+ * @param array $options[menu] 菜单
+ * @param int $options[api] 接口
+ * @date 2022-02-22
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public static function login(string $guid, array $options = []): self
+ {
+ $expTime = 0;
+ $menu = [];
+ $api = [];
+ if (is_array($options)) {
+ $expTime = Arr::get($options, 'expTime', 0);
+ $menu = Arr::get($options, 'menu', []);
+ $api = Arr::get($options, 'api', []);
+ }
+ if ($expTime == 0) {
+ $expTime = time() + (24 * 60 * 60);
+ }
+ $data = [
+ 'token_exp_time' => $expTime
+ ];
+ if ($menu) {
+ $data['token_menu'] = $menu;
+ }
+ if ($api) {
+ $data['token_api'] = $api;
+ }
+ /**
+ * @var Token
+ */
+ $model = self::where([
+ 'user_guid' => $guid,
+ ])->find();
+ // 数据不存在
+ if (!$model) {
+ return self::create($data + [
+ 'user_guid' => $guid,
+ ]);
+ }
+ // token已过期
+ if ($model->is_exp) {
+ $data['token_content'] = $model->generateContent($model);
+ }
+ $model->save($data);
+ return $model;
+ }
+
+ /**
+ * 登出
+ *
+ * @date 2022-03-15
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public static function logout(): void
+ {
+ self::getCurrent()->delete();
+ }
+
+ /**
+ * 获取请求token
+ *
+ * @date 2022-02-22
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public static function getRequestToken(): string
+ {
+ $tokenKey = 'token';
+ $token = '';
+ if (!$token && isset($_GET[$tokenKey])) {
+ $token = $_GET[$tokenKey];
+ }
+ if (!$token && isset($_POST[$tokenKey])) {
+ $token = $_POST[$tokenKey];
+ }
+ if (!$token && Request::param($tokenKey)) {
+ $token = Request::param($tokenKey);
+ }
+ if (!$token && Request::header($tokenKey)) {
+ $token = Request::header($tokenKey);
+ }
+ return $token;
+ }
+
+ /**
+ * 获取当前用户信息
+ *
+ * @date 2022-02-22
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public static function getCurrentUser(): User
+ {
+ if (!self::$user) {
+ $model = self::getCurrent();
+ self::$user = $model->user;
+ if (!self::$user) {
+ throw new ErrorMsg("用户不存在", 1);
+ }
+ }
+ return self::$user;
+ }
+
+ /**
+ * 获取当前有效token
+ *
+ * @date 2022-02-28
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public static function getCurrent(): self
+ {
+ if (!self::$current) {
+ $content = self::getRequestToken();
+ if (!$content) {
+ throw new LoginTimeOut("缺少token", 1);
+ }
+ $model = self::where([
+ 'token_content' => $content,
+ ])->find();
+ if (!$model) {
+ throw new LoginTimeOut("未登录", 1);
+ }
+ if ($model->is_exp) {
+ throw new LoginTimeOut("登录超时", 1);
+ }
+ self::$current = $model;
+ }
+ return self::$current;
+ }
+
+ /**
+ * 判断是否登录
+ *
+ * @date 2022-05-14
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public static function isLogin(): bool
+ {
+ try {
+ self::getCurrent();
+ return true;
+ } catch (LoginTimeOut $th) {
+ return false;
+ }
+ }
+
+ /**
+ * 延长token
+ * 过期前30分钟才会延期
+ *
+ * @date 2022-02-28
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public static function delay(): void
+ {
+ try {
+ // 半小时
+ $delay = (60 * 30);
+ $model = self::getCurrent();
+ $token_exp_time = $model->token_exp_timestamp;
+ if (time() >= ($token_exp_time - $delay)) {
+ self::login($model->user_guid);
+ }
+ } catch (LoginTimeOut $th) {
+ }
+ }
+
+ /**
+ * 生成token内容
+ *
+ * @date 2022-02-22
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ private function generateContent(): string
+ {
+ return md5(join('-', [
+ $this->token_exp_time,
+ $this->user_guid,
+ ]) . time());
+ }
+}
diff --git a/app/common/model/User/User.php b/app/common/model/User/User.php
new file mode 100644
index 0000000..9fef5b7
--- /dev/null
+++ b/app/common/model/User/User.php
@@ -0,0 +1,548 @@
+ 'int',
+ 'user_guid' => 'string',
+ 'user_img' => 'string',
+ 'user_name' => 'string',
+ 'user_password' => 'string',
+ 'user_phone' => 'string',
+ 'user_status' => 'int',
+ 'user_department' => 'string',
+ 'user_position' => 'string',
+ 'user_create_time' => 'datetime',
+ 'user_create_user_guid' => 'string',
+ 'user_update_time' => 'datetime',
+ 'user_update_user_guid' => 'string',
+ 'user_delete_time' => 'datetime',
+ 'user_delete_user_guid' => 'string',
+ ];
+ // 只读
+ protected $readonly = [
+ 'user_id',
+ 'user_guid',
+ 'user_create_time',
+ 'user_create_user_guid'
+ ];
+ // 开启自动写入时间戳字段
+ protected $autoWriteTimestamp = 'datetime';
+ // 创建时间
+ protected $createTime = 'user_create_time';
+ // 修改时间
+ protected $updateTime = 'user_update_time';
+
+ public static $dictionaryMap = [
+ 'user_status' => [
+ self::STATUS_ENABLE => '启用',
+ self::STATUS_DISABLE => '停用'
+ ],
+ ];
+ /**
+ * 状态 启用
+ */
+ const STATUS_ENABLE = 1;
+ /**
+ * 状态 禁用
+ */
+ const STATUS_DISABLE = 2;
+ // 状态查询范围
+ public function scopeStatus(Query $query, $status = self::STATUS_ENABLE)
+ {
+ $query->where('user_status', $status);
+ }
+
+ /**
+ * 写入前
+ *
+ * @param self $model
+ * @return void
+ * @date 2023-01-11
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public static function onBeforeWrite(self $model): void
+ {
+ self::checkRepeatData($model);
+ }
+
+ /**
+ * 新增前
+ *
+ * @date 2022-02-22
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public static function onBeforeInsert(self $model): void
+ {
+ $model->completeCreateField();
+ }
+
+ /**
+ * 更新前
+ *
+ * @date 2022-02-28
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public static function onBeforeUpdate(self $model): void
+ {
+ $model->completeUpdateField();
+ }
+
+ /**
+ * 删除前
+ *
+ * @date 2022-02-28
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public static function onBeforeDelete(self $model): void
+ {
+ $model->completeDeleteField();
+ }
+
+ /**
+ * 用户名搜索器
+ *
+ * @param Query $query
+ * @param [type] $value
+ * @param [type] $data
+ * @return void
+ * @date 2022-02-28
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function searchUserNameAttr(Query $query, $value, $data): void
+ {
+ if ($value) {
+ $query->whereLike(join('|', [
+ 'user_name',
+ // 'user_phone'
+ ]), "%$value%");
+ }
+ }
+
+
+ /**
+ * 设置用户密码
+ *
+ * @param string $value
+ * @return string
+ * @date 2022-12-28
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function setUserPasswordAttr(string $value): string
+ {
+ return self::encryptPassword($value);
+ }
+
+ /**
+ * 获取用户管理员
+ *
+ * @date 2022-03-08
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function getUserAdminAttr(): bool
+ {
+ return $this->user_id == 1;
+ }
+
+ /**
+ * 用户登录
+ *
+ * @return Token
+ * @date 2023-01-03
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function login(): Token
+ {
+ if ($this->user_status != self::STATUS_ENABLE) {
+ throwErrorMsg('该用户已被停用');
+ }
+ $menus = $this->getUserMenu();
+ $api = [];
+ foreach ($menus as $menu) {
+ $api = array_merge($api, explode(',', $menu['menu_api_url']));
+ }
+ $api = array_values(array_filter(array_unique($api)));
+ if (!$api) {
+ throwErrorMsg('无权登录');
+ }
+ return Token::login($this->user_guid, [
+ 'menu' => $menus,
+ 'api' => $api,
+ ]);
+ }
+
+ /**
+ * 获取用户菜单
+ *
+ * @date 2022-03-15
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function getUserMenu(): array
+ {
+ $result = [];
+ $query = Menu::join('menu_api', 'menu_api.menu_guid = menu.menu_guid', 'left')->field([
+ 'menu.menu_guid',
+ 'menu.menu_parent_guid',
+ 'menu.menu_name',
+ 'menu.menu_url',
+ 'menu.menu_show',
+ 'menu.menu_icon',
+
+ 'group_concat(menu_api.menu_api_url)' => 'menu_api_url'
+ ])->order([
+ 'menu.menu_index',
+ 'menu.menu_order' => 'desc',
+ ])->group('menu.menu_guid');
+ if ($this->user_admin) {
+ $result = $query->select()->toArray();
+ } else {
+ $menus = [];
+ $roles = $this->roles()->with([
+ 'menus'
+ ])->select();
+ foreach ($roles as $role) {
+ $menus = array_merge($menus, $role->menus->column('menu_guid'));
+ }
+ $result = $query->where([
+ ['menu.menu_guid', 'in', $menus]
+ ])->select()->toArray();
+ }
+ return $result;
+ }
+
+ /**
+ * 数据查重
+ *
+ * @param self $model
+ * @return void
+ * @date 2022-03-11
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ private static function checkRepeatData(self $model)
+ {
+ Validate::unique(self::class, $model->user_guid, $model->getData(), [
+ 'user_account' => '账户',
+ 'user_phone' => '手机号',
+ ]);
+ }
+
+ /**
+ * 密码加密
+ *
+ * @param string $password
+ * @date 2022-02-22
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public static function encryptPassword(string $password): string
+ {
+ return md5($password);
+ }
+ /**
+ * 获取角色
+ *
+ * @date 2022-03-11
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function roles(): BelongsToMany
+ {
+ return $this->belongsToMany(
+ Role::class,
+ UserRole::class,
+ 'role_guid',
+ 'user_guid'
+ );
+ }
+
+ /**
+ * 新增用户角色
+ */
+ public static function addUserRole($user_guid, $params): void
+ {
+ $user_role_arr = $params['roles'];
+
+ foreach ($user_role_arr as $role => $item) {
+ Db::startTrans();
+ try {
+ $add_data = [
+ 'user_guid' => $user_guid,
+ 'role_guid' => $item,
+ ];
+ $user_role = ModelUserRole::create($add_data);
+
+ Db::commit();
+ } catch (\Throwable $th) {
+ Db::rollback();
+ throw $th;
+ }
+ }
+ }
+
+ /**
+ * 编辑用户角色
+ */
+ public static function editUserRole($params): void
+ {
+ // 从学生服务(副表)查询出所有当前学生的服务,进行删除
+ ModelUserRole::where('user_guid', $params['user_guid'])->select()->delete();
+
+ // 再把传值的数据,写入副表
+ $user_role_arr = $params['roles'];
+ foreach ($user_role_arr as $key => $item) {
+ Db::startTrans();
+ try {
+ $add_data = [
+ 'user_guid' => $params['user_guid'],
+ 'role_guid' => $item,
+ ];
+ ModelUserRole::create($add_data);
+
+ Db::commit();
+ } catch (\Throwable $th) {
+ Db::rollback();
+ throw $th;
+ }
+ }
+ }
+
+ /**
+ * 用户角色guid获取器
+ */
+ public function getRolesAttr($value, $data)
+ {
+ $user_guid = $data['user_guid'];
+ $user_role_guid_arr = ModelUserRole::field([
+ 'role_guid',
+ ])->where('user_guid', $user_guid)->select();
+
+ $arr = [];
+ foreach ($user_role_guid_arr as $key => $value) {
+ $arr[] = $value['role_guid'];
+ }
+ return $arr;
+
+ // return array_values($user_key_guid_arr);
+ }
+
+ /**
+ * 用户角色名称获取器
+ */
+ public function getRoleNameAttr($value, $data)
+ {
+ $user_guid = $data['user_guid'];
+ $user_role_guid_arr = ModelUserRole::field([
+ 'role_guid',
+ ])->where('user_guid', $user_guid)->select();
+
+ $arr = [];
+ foreach ($user_role_guid_arr as $key => $value) {
+ $model = Role::where('role_guid', $value['role_guid'])->find()->role_name;
+ $arr[] = $model;
+ }
+ return implode(",", $arr);
+
+ // return array_values($user_key_guid_arr);
+ }
+
+ /**
+ * 用户角色名
+ */
+ public static function getRoleName($data)
+ {
+ $user_guid = $data['user_guid'];
+ $user_role_guid_arr = ModelUserRole::field([
+ 'role_guid',
+ ])->where('user_guid', $user_guid)->select();
+
+ $arr = [];
+ foreach ($user_role_guid_arr as $key => $value) {
+ $model = Role::where('role_guid', $value['role_guid'])->find()->role_name;
+ $arr[] = $model;
+ }
+ return implode(",", $arr);
+
+ // return array_values($user_key_guid_arr);
+ }
+
+
+ /**
+ * 角色名称转角色guid数组
+ */
+ public static function RoleNameToRoleGuidArr($params)
+ {
+ $roles = explode(",", $params['roles']);
+
+ $user_role_guid_arr = [];
+ foreach ($roles as $key => $item) {
+ $role = Role::where('role_name', $item)->find();
+ if (!$role) throwErrorMsg('角色不存在!');
+ $user_role_guid_arr[] = $role->role_guid;
+ }
+
+ return $user_role_guid_arr;
+ }
+
+ /**
+ * 导出Excel
+ */
+ public static function exportExcel($select)
+ {
+ $data = [[
+ '用户名',
+ '头像',
+ '角色',
+ '手机号',
+ ]];
+ foreach ($select as $key => $val) {
+
+ $val['user_img'] = Excel::ExportImgFiled($val['user_img']);
+ $user_role_name = self::getRoleName($val);
+
+ $data[] = [
+ $val['user_name'],
+ $val['user_img'],
+ $user_role_name,
+ $val['user_phone'],
+ ];
+ }
+ $excel = (new Excel())->exporTsheet($data);
+ $excel->save('用户.xlsx');
+ }
+
+
+ /**
+ * 导入数据处理
+ */
+ public static function HanleImportData($value)
+ {
+ // 判断用户是否存在
+ $user_name = $value['user_name'];
+ $user_type = self::where('user_name', $user_name)->find();
+ if ($user_type) throwErrorMsg("{$user_name} 用户已存在,导入失败!");
+
+
+ // 判断角色是否存在
+ $roles_arr = explode(",", $value['roles']);
+ foreach ($roles_arr as $key => $item) {
+ $role = Role::where('role_name', $item)->find();
+ if (!$role) throwErrorMsg("{$item} 角色不存在,导入失败!");
+ }
+
+ $password = md5($value['user_password']);
+
+ $model = self::create([
+ 'user_name' => $value['user_name'],
+ 'user_img' => $value['user_img'],
+ 'user_phone' => $value['user_phone'],
+ 'user_password' => $password,
+ 'user_status' => 1,
+ ]);
+
+ $user_guid = $model->user_guid;
+ $value['roles'] = self::RoleNameToRoleGuidArr($value);
+ self::addUserRole($user_guid, $value);
+ }
+
+
+ /**
+ * 导入Excel
+ */
+ public static function importExcel($file)
+ {
+ Db::startTrans();
+ try {
+ $excel = new Excel($file);
+ // 表头
+ $data = $excel->parseExcel(
+ [
+ [
+ 'title' => '用户名',
+ 'validate' => 'require',
+ 'field' => 'user_name',
+ ], [
+ 'title' => '头像',
+ 'field' => 'user_img',
+ ], [
+ 'title' => '角色',
+ 'validate' => 'require',
+ 'field' => 'roles',
+ ], [
+ 'title' => '手机号',
+ 'field' => 'user_phone',
+ ], [
+ 'title' => '密码',
+ 'field' => 'user_password',
+ ]
+ ],
+ [
+ 'titleLine' => [1]
+ ]
+ );
+ if (!$data) throwErrorMsg('excel无数据', 1);
+ $error = [];
+
+ foreach ($data as $line => $value) {
+ try {
+ self::HanleImportData($value);
+ $error[] = "{$line} 用户:【{$value['user_name']}】新增成功!
";
+ } catch (\Throwable $th) {
+ $error[] = "{$line} 用户:【{$value['user_name']}】{$th->getMessage()}
";
+ }
+ }
+ Db::commit();
+ return implode(', ', $error);
+ } catch (\Throwable $th) {
+ Db::rollback();
+ throw $th;
+ }
+ }
+}
diff --git a/app/common/model/User/UserRole.php b/app/common/model/User/UserRole.php
new file mode 100644
index 0000000..3c89d91
--- /dev/null
+++ b/app/common/model/User/UserRole.php
@@ -0,0 +1,157 @@
+ 'int',
+ 'user_guid' => 'string',
+ 'role_guid' => 'string',
+ 'user_role_status' => 'int',
+ 'user_role_create_time' => 'datetime',
+ 'user_role_create_user_guid' => 'string',
+ 'user_role_update_time' => 'datetime',
+ 'user_role_update_user_guid' => 'string',
+ 'user_role_delete_time' => 'datetime',
+ 'user_role_delete_user_guid' => 'string',
+ 'user_role_guid' => 'string',
+ ];
+ // 开启自动写入时间戳字段
+ protected $autoWriteTimestamp = 'datetime';
+ // 创建时间
+ protected $createTime = 'user_role_create_time';
+ // 修改时间
+ protected $updateTime = 'user_role_update_time';
+ /**
+ * 状态 启用
+ */
+ const STATUS_ENABLE = 1;
+ /**
+ * 状态 禁用
+ */
+ const STATUS_DISABLE = 2;
+ // 状态查询范围
+ public function scopeStatus($query, $status = self::STATUS_ENABLE)
+ {
+ $query->where('user_role_status', $status);
+ }
+ /**
+ * 中间关联
+ *
+ * @return ModelPivot
+ * @date 2022-12-30
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public static function pivot(): ModelPivot
+ {
+ return new ModelPivot(
+ self::class,
+ User::class,
+ Role::class,
+ true
+ );
+ }
+ /**
+ * 绑定用户角色
+ *
+ * @param $users
+ * @param $roles
+ * @param array $extra
+ * @return void
+ * @date 2022-12-29
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public static function bindUserRole($users, $roles, array $extra = []): void
+ {
+ self::pivot()->bind($users, $roles, $extra);
+ }
+ /**
+ * 解绑用户角色
+ *
+ * @param $users
+ * @param $roles
+ * @return void
+ * @date 2022-12-30
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public static function unbindUserRole($users, $roles): void
+ {
+ self::pivot()->unbind($users, $roles);
+ }
+ /**
+ * 绑定用户角色
+ *
+ * @param $users
+ * @param $roles
+ * @param array $extra
+ * @return void
+ * @date 2022-12-29
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public static function rebindUserRole($users, $roles, array $extra = []): void
+ {
+ self::pivot()->rebind($users, $roles, $extra);
+ }
+
+ /**
+ * 新增前
+ *
+ * @date 2022-02-22
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public static function onBeforeInsert(self $model): void
+ {
+ $model->user_role_status = self::STATUS_ENABLE;
+ $model->completeCreateField();
+ }
+
+ /**
+ * 更新前
+ *
+ * @date 2022-02-28
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public static function onBeforeUpdate(self $model): void
+ {
+ $model->completeUpdateField();
+ }
+
+ /**
+ * 删除前
+ *
+ * @date 2022-02-28
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public static function onBeforeDelete(self $model): void
+ {
+ $model->completeDeleteField();
+ }
+}
diff --git a/app/common/traits/Auth.php b/app/common/traits/Auth.php
new file mode 100644
index 0000000..cd18bc9
--- /dev/null
+++ b/app/common/traits/Auth.php
@@ -0,0 +1,110 @@
+request;
+ $gc = $request->controller();
+ $ga = $request->action();
+ $ignoreLogin = Arr::get($this->ignoreLogin, $gc, []);
+ if (!is_array($ignoreLogin)) {
+ $ignoreLogin = [];
+ }
+ foreach ($ignoreLogin as $action) {
+ if ($action == '*' || $action == $ga) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 验证用户权限
+ *
+ * @return boolean
+ * @date 2022-05-14
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ private function validateUser(): bool
+ {
+ return $this->validateMenuApi(
+ Token::getCurrentUser()->getUserMenu()
+ );
+ }
+
+ /**
+ * 验证菜单接口
+ *
+ * @return boolean
+ * @date 2022-05-14
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ private function validateMenuApi(array $menus): bool
+ {
+ $apis = [];
+ foreach ($menus as $menu) {
+ $apis = array_merge($apis, explode(',', $menu['menu_api_url'] ?? ''));
+ }
+ return $this->validateApi($apis);
+ }
+
+ /**
+ * 验证接口
+ *
+ * @param array $apis
+ * @return boolean
+ * @date 2022-05-14
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ private function validateApi(array $apis): bool
+ {
+ $request = $this->request;
+ $apis = array_values(array_filter(array_unique($apis)));
+ $api = join('/', [
+ $request->controller(),
+ $request->action()
+ ]);
+ return in_array($api, $apis);
+ }
+}
diff --git a/app/common/traits/Model.php b/app/common/traits/Model.php
new file mode 100644
index 0000000..9546820
--- /dev/null
+++ b/app/common/traits/Model.php
@@ -0,0 +1,254 @@
+completeBaseField('create', $user, $timeType);
+ }
+
+ /**
+ * 完善更新字段
+ *
+ * @param string $user
+ * @param string $timeType
+ * @return void
+ * @date 2022-12-27
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function completeUpdateField($user = 'user', string $timeType = 'datetime'): void
+ {
+ $this->completeBaseField('update', $user, $timeType);
+ }
+
+ /**
+ * 完善删除字段
+ *
+ * @param string $user
+ * @param string $timeType
+ * @return void
+ * @date 2022-12-27
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function completeDeleteField($user = 'user', string $timeType = 'datetime'): void
+ {
+ $this->completeBaseField('delete', $user, $timeType);
+ }
+
+ /**
+ * 完善基础字段
+ *
+ * @param string $type create|update|delete
+ * @param User|string $user
+ * @param string $timeType datetime|timestamp
+ * @return void
+ * @date 2022-03-02
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ public function completeBaseField($type, $user = 'user', string $timeType = 'datetime'): void
+ {
+ $table = $this->getTable();
+ $createFieldTime = "${table}_create_time";
+ $createFieldUserGuId = "${table}_create_user_guid";
+ $updateFieldTime = "${table}_update_time";
+ $updateFieldUserGuId = "${table}_update_user_guid";
+ $deleteFieldTime = "${table}_delete_time";
+ $deleteFieldUserGuId = "${table}_delete_user_guid";
+ $guidField = "${table}_guid";
+
+ $time = self::getCompleteBaseFieldTimeType($timeType);
+ $user_guid = self::getCompleteBaseFieldUser($user);
+ if (!$type) {
+ throw new ErrorMsg("时间类型异常", 1);
+ }
+ $this->$updateFieldTime = $time;
+ $this->$updateFieldUserGuId = $user_guid;
+
+ if ($type === 'create') {
+ $this->$createFieldTime = $time;
+ $this->$createFieldUserGuId = $user_guid;
+ $this->$guidField = self::generateGuid();
+ }
+ if ($type === 'delete') {
+ $this->$deleteFieldTime = $time;
+ $this->$deleteFieldUserGuId = $user_guid;
+ }
+ }
+
+ // /**
+ // * 设置不使用的全局查询范围更新
+ // * 支持 onBeforeUpdate|onAfterUpdate|onBeforeWrite|onAfterWrite 事件
+ // *
+ // * @return void
+ // * @date 2022-03-12
+ // * @example
+ // * @author admin
+ // * @since 1.0.0
+ // */
+ // public static function withoutGlobalScopeUpdate(array $scope, array $data)
+ // {
+ // $model = new static;
+ // $pk = $model->getPk();
+ // if (!isset($data[$pk])) {
+ // throw new ErrorMsg("缺少主键", 1);
+ // }
+ // $query = $model::withoutGlobalScope($scope)->find($data[$pk]);
+ // if ($query === null) {
+ // throw new ErrorMsg("数据不存在", 1);
+ // }
+ // if (
+ // $model::onBeforeWrite($model) === false ||
+ // $model::onBeforeUpdate($model) === false
+ // ) {
+ // return false;
+ // }
+ // $model::withoutGlobalScope($scope)->save(array_merge($data, $model->getData()));
+ // $model::onAfterUpdate($model);
+ // $model::onAfterWrite($model);
+ // }
+
+ /**
+ * 生成guid
+ *
+ * @param array|string $value
+ * @return string
+ * @date 2022-02-22
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ protected static function generateGuid($value = '')
+ {
+ return Tool::generateGuid($value);
+ }
+
+ /**
+ * 数据验证
+ * 除非是require字段 有数据才验证
+ *
+ * @param array $data 数据
+ * @param array $rules 规则
+ * @param array $message 提示信息
+ * @return void
+ * @throws ValidateException
+ */
+ protected static function dataValidate(array $data, array $rules, array $message = [])
+ {
+ $_rules = [];
+ $dataKeys = array_keys($data);
+ foreach ($rules as $key => $rule) {
+ list($field) = explode('|', $key);
+ // 字段存在规则并且有值 或者 必传
+ if ((in_array($field, $dataKeys) && !is_null($data[$field])) || preg_match('/require/', $rule)) {
+ $_rules[$key] = $rule;
+ }
+ }
+ Validate::check($data, $_rules, $message);
+ }
+
+ /**
+ * 获取完善基础字段用户
+ *
+ * @param User|string $user
+ * @return string
+ * @date 2022-12-27
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ private static function getCompleteBaseFieldUser($user): string
+ {
+ $cacheUserGuid = self::$cacheUserGuid;
+ $user_guid = '';
+ switch (true) {
+ case (is_string($cacheUserGuid) && $cacheUserGuid):
+ $user_guid = $cacheUserGuid;
+ break;
+ case (is_bool($cacheUserGuid) && $cacheUserGuid === false):
+ // 无需验证 获取用户Guid
+ break;
+ case (is_bool($user) && $user === false):
+ // 无需验证 获取用户Guid
+ break;
+ case ($user === 'user'):
+ case ($user instanceof User):
+ $user = $user === 'user' ? Request::getCurrentUser() : $user;
+ $user_guid = $user->user_guid;
+ break;
+ }
+ return $user_guid;
+ }
+
+ /**
+ * 获取模型时间类型
+ *
+ * @param string $timeType datetime|timestamp
+ * @return string
+ * @date 2022-12-27
+ * @example
+ * @author admin
+ * @since 1.0.0
+ */
+ private static function getCompleteBaseFieldTimeType($timeType): string
+ {
+ $time = '';
+ switch ($timeType) {
+ case 'datetime':
+ $time = date('Y-m-d H:i:s');
+ break;
+ case 'timestamp':
+ $time = time();
+ break;
+ }
+ return $time;
+ }
+}
diff --git a/app/event.php b/app/event.php
new file mode 100644
index 0000000..74d0b62
--- /dev/null
+++ b/app/event.php
@@ -0,0 +1,20 @@
+ [],
+
+ 'listen' => [
+ 'AppInit' => [],
+ 'HttpRun' => [],
+ 'HttpEnd' => [
+ DelayToken::class
+ ],
+ 'LogLevel' => [],
+ 'LogWrite' => [],
+ ],
+
+ 'subscribe' => [],
+];
diff --git a/app/middleware.php b/app/middleware.php
new file mode 100644
index 0000000..32d02ce
--- /dev/null
+++ b/app/middleware.php
@@ -0,0 +1,10 @@
+ Request::class,
+ 'think\exception\Handle' => ExceptionHandle::class,
+];
diff --git a/app/resources/view/admin/controller.tpl b/app/resources/view/admin/controller.tpl
new file mode 100644
index 0000000..f1d5aef
--- /dev/null
+++ b/app/resources/view/admin/controller.tpl
@@ -0,0 +1,83 @@
+param();
+ $con = [];
+
+ {$whereContent}
+
+ $query = Model{$className}::where($con)
+ ->field({$queryFields})
+ ->order({$orderField}, {$orderMode});
+
+ return msg("获取{$functionName}列表成功!",$query);
+ }
+
+ /**
+ * 编辑{$functionName}
+ */
+ public function edit{$className}(Request $request): array
+ {
+ $params = $request->param();
+ $this->validate($params, {$editRequireFields});
+ $model = Model{$className}::where('{$businessName}_guid',$params['{$businessName}_guid'])->find();
+ if (!$model) throwErrorMsg("该{$functionName}不存在", 1);
+ $model->allowField({$editAllowFields})->save($params);
+ return msg('编辑成功!');
+ }
+
+ /**
+ * 添加{$functionName}
+ */
+ public function add{$className}(Request $request): array
+ {
+ $params = $request->param();
+ $this->validate($params, {$addRequireFields});
+ $model = Model{$className}::create($params,{$addAllowFields});
+ return msg('添加成功!');
+ }
+
+ /**
+ * 删除{$functionName}
+ */
+ public function delete{$className}(Request $request): array
+ {
+ $params = $request->param();
+ $this->validate($params, [
+ '{$businessName}_guid' => 'require',
+ ]);
+ ${$businessName} = Model{$className}::where([
+ '{$businessName}_guid' => explode(',', $params['{$businessName}_guid'])
+ ])->select();
+ ${$businessName}->delete();
+ return msg('删除成功!');
+ }
+
+ {$exportExcelContent}
+
+ {$downloadTempContent}
+
+ {$importExcelContent}
+
+}
diff --git a/app/resources/view/admin/model.tpl b/app/resources/view/admin/model.tpl
new file mode 100644
index 0000000..da02a09
--- /dev/null
+++ b/app/resources/view/admin/model.tpl
@@ -0,0 +1,71 @@
+completeCreateField();
+ }
+
+ /**
+ * 更新前
+ */
+ public static function onBeforeUpdate(self $model): void
+ {
+ // self::checkRepeatData($model);
+ $model->completeUpdateField();
+ }
+
+ /**
+ * 删除前
+ */
+ public static function onBeforeDelete(self $model): void
+ {
+ $model->completeDeleteField();
+ }
+
+ {$exportExcelContent}
+
+ {$importExcelContent}
+
+ {$importExcelInitContent}
+
+
+}
diff --git a/app/resources/view/api/controller.tpl b/app/resources/view/api/controller.tpl
new file mode 100644
index 0000000..3820252
--- /dev/null
+++ b/app/resources/view/api/controller.tpl
@@ -0,0 +1,55 @@
+param();
+ $con = [];
+
+ {$whereContent}
+
+ $query = Model{$className}::where($con)
+ ->field({$queryFields})
+ ->order({$orderField}, {$orderMode});
+
+ {$imgUrlPrefixPadding}
+
+ return msg("获取{$functionName}列表成功!",$query);
+ }
+
+ /**
+ * 获取{$functionName}详情
+ */
+ public function get{$className}Info(Request $request): array
+ {
+ $params = $request->param();
+
+ $this->validate($params, ['{$businessName}_guid' => 'require']);
+
+ $find = Model{$className}::field({$queryFields})
+ ->where('{$businessName}_guid',$params['{$businessName}_guid'])
+ ->find();
+
+ return msg(0,'获取{$functionName}详情成功!',['data' => $find]);
+ }
+
+}
diff --git a/app/resources/view/business/webApi.tpl b/app/resources/view/business/webApi.tpl
new file mode 100644
index 0000000..2467282
--- /dev/null
+++ b/app/resources/view/business/webApi.tpl
@@ -0,0 +1,23 @@
+import { api} from '~/utils/axios';
+
+/**
+ * 获取${functionName}内容
+ * @param {Object} data
+ * @return {Promise} api
+ */
+export function get${className}(data) {
+ return api.post('${moduleName}.${className}/get${className}', data);
+}
+
+/**
+ * 编辑${functionName}
+ * @param {Object} data
+ * @return {Promise} api
+ */
+export function edit${className}(data) {
+ return api.post('${moduleName}.${className}/edit${className}', data, {
+ isTransformResponse: true,
+ isShowSuccessMessage: true,
+ errorMessageText: '编辑失败'
+ });
+}
diff --git a/app/resources/view/business/webApiController.tpl b/app/resources/view/business/webApiController.tpl
new file mode 100644
index 0000000..47975ff
--- /dev/null
+++ b/app/resources/view/business/webApiController.tpl
@@ -0,0 +1,32 @@
+param();
+
+ $query = Model{$className}::field({$queryFields})->where(1)->find();
+
+ return [
+ 'code' => 0,
+ 'data' => $query,
+ 'msg' => 'ok'
+ ];
+ }
+
+}
diff --git a/app/resources/view/business/webController.tpl b/app/resources/view/business/webController.tpl
new file mode 100644
index 0000000..62b33ff
--- /dev/null
+++ b/app/resources/view/business/webController.tpl
@@ -0,0 +1,44 @@
+param();
+
+ $query = Model{$className}::field({$queryFields})->where(1)->find();
+
+ return [
+ 'code' => 0,
+ 'data' => $query,
+ 'msg' => 'ok'
+ ];
+ }
+
+ /**
+ * 编辑{$functionName}
+ */
+ public function edit{$className}(Request $request): array
+ {
+ $params = $request->param();
+ $this->validate($params, {$editRequireFields});
+ $model = Model{$className}::where(1)->find();
+ $model->allowField({$editAllowFields})->save($params);
+ return msg('编辑成功!');
+ }
+
+}
diff --git a/app/resources/view/business/webIndex.tpl b/app/resources/view/business/webIndex.tpl
new file mode 100644
index 0000000..72e43d3
--- /dev/null
+++ b/app/resources/view/business/webIndex.tpl
@@ -0,0 +1,90 @@
+
+
+
+ ${functionName}
+ ${functionName}
+
+
+
+
+ ${col}
+
+
+
+
+ 保存
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/resources/view/jsVue/add.tpl b/app/resources/view/jsVue/add.tpl
new file mode 100644
index 0000000..f840240
--- /dev/null
+++ b/app/resources/view/jsVue/add.tpl
@@ -0,0 +1,102 @@
+
+
+
+
+
+ ${col}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/resources/view/jsVue/api.tpl b/app/resources/view/jsVue/api.tpl
new file mode 100644
index 0000000..f57ad83
--- /dev/null
+++ b/app/resources/view/jsVue/api.tpl
@@ -0,0 +1,56 @@
+import { api, downloadFile, createApiUrl} from '~/utils/axios';
+
+${excelFun}
+
+${imageFun}
+
+${fileFun}
+
+${dictFun}
+
+/**
+ * 获取${functionName}列表
+ * @param {Object} data
+ * @return {Promise} api
+ */
+export function get${className}List(data) {
+ return api.post('${moduleName}.${className}/get${className}List', data);
+}
+
+/**
+ * 删除${functionName}
+ * @param {Object} data
+ * @return {Promise} api
+ */
+export function delete${className}(data) {
+ return api.post('${moduleName}.${className}/delete${className}', data, {
+ isTransformResponse: true,
+ isShowSuccessMessage: true,
+ errorMessageText: '删除失败'
+ });
+}
+
+/**
+ * 添加${functionName}
+ * @param {Object} data
+ * @return {Promise} api
+ */
+export function add${className}(data) {
+ return api.post('${moduleName}.${className}/add${className}', data, {
+ isTransformResponse: true,
+ isShowSuccessMessage: true,
+ errorMessageText: '添加失败'
+ });
+}
+/**
+ * 编辑${functionName}
+ * @param {Object} data
+ * @return {Promise} api
+ */
+export function edit${className}(data) {
+ return api.post('${moduleName}.${className}/edit${className}', data, {
+ isTransformResponse: true,
+ isShowSuccessMessage: true,
+ errorMessageText: '编辑失败'
+ });
+}
diff --git a/app/resources/view/jsVue/detail.tpl b/app/resources/view/jsVue/detail.tpl
new file mode 100644
index 0000000..9f7cf24
--- /dev/null
+++ b/app/resources/view/jsVue/detail.tpl
@@ -0,0 +1,62 @@
+
+
+
+
+
+ ${col}
+
+
+
+
+
+
+
+
+
diff --git a/app/resources/view/jsVue/edit.tpl b/app/resources/view/jsVue/edit.tpl
new file mode 100644
index 0000000..f98a86f
--- /dev/null
+++ b/app/resources/view/jsVue/edit.tpl
@@ -0,0 +1,100 @@
+
+
+
+
+
+ ${col}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/resources/view/jsVue/index.tpl b/app/resources/view/jsVue/index.tpl
new file mode 100644
index 0000000..659e76c
--- /dev/null
+++ b/app/resources/view/jsVue/index.tpl
@@ -0,0 +1,153 @@
+
+
+
+ ${functionName}管理
+ ${functionName}列表
+
+
+
+
+ ${search}
+
+
+ 搜索
+
+
+
+
+
+
+ 添加
+
+
+ ${btn}
+
+
+
+
+ 批量操作
+
+
+
+
+ 批量删除
+
+
+
+
+
+
+
+ ${imgTemplate}
+ ${dictScope}
+
+
+
+ 编辑
+
+
+
+ 更多
+
+
+
+
+ 详情
+
+
+ 删除
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/service.php b/app/service.php
new file mode 100644
index 0000000..db1ee6a
--- /dev/null
+++ b/app/service.php
@@ -0,0 +1,9 @@
+=7.4.3",
+ "topthink/framework": "^6.0.0",
+ "topthink/think-orm": "^2.0",
+ "topthink/think-captcha": "^3.0",
+ "phpoffice/phpspreadsheet": "^1.12",
+ "topthink/think-image": "^1.0",
+ "topthink/think-multi-app": "^1.0",
+ "topthink/think-filesystem": "^1.0",
+ "endroid/qr-code": "^4.6",
+ "fabpot/goutte": "^4.0"
+ },
+ "require-dev": {
+ "symfony/var-dumper": "^4.2"
+ },
+ "autoload": {
+ "psr-4": {
+ "app\\": "app"
+ },
+ "psr-0": {
+ "": "extend/"
+ }
+ },
+ "config": {
+ "preferred-install": "dist"
+ },
+ "scripts": {
+ "post-autoload-dump": [
+ "@php think service:discover",
+ "@php think vendor:publish"
+ ]
+ },
+ "repositories": {
+ "packagist": {
+ "type": "composer",
+ "url": "https://mirrors.aliyun.com/composer/"
+ }
+ }
+}
diff --git a/composer.lock b/composer.lock
new file mode 100644
index 0000000..da10a40
--- /dev/null
+++ b/composer.lock
@@ -0,0 +1,3025 @@
+{
+ "_readme": [
+ "This file locks the dependencies of your project to a known state",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+ "This file is @generated automatically"
+ ],
+ "content-hash": "77b4f118629281742087d8fefad27030",
+ "packages": [
+ {
+ "name": "bacon/bacon-qr-code",
+ "version": "2.0.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Bacon/BaconQrCode.git",
+ "reference": "8674e51bb65af933a5ffaf1c308a660387c35c22"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/8674e51bb65af933a5ffaf1c308a660387c35c22",
+ "reference": "8674e51bb65af933a5ffaf1c308a660387c35c22",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "dasprid/enum": "^1.0.3",
+ "ext-iconv": "*",
+ "php": "^7.1 || ^8.0"
+ },
+ "require-dev": {
+ "phly/keep-a-changelog": "^2.1",
+ "phpunit/phpunit": "^7 | ^8 | ^9",
+ "spatie/phpunit-snapshot-assertions": "^4.2.9",
+ "squizlabs/php_codesniffer": "^3.4"
+ },
+ "suggest": {
+ "ext-imagick": "to generate QR code images"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "BaconQrCode\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-2-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Ben Scholzen 'DASPRiD'",
+ "email": "mail@dasprids.de",
+ "homepage": "https://dasprids.de/",
+ "role": "Developer"
+ }
+ ],
+ "description": "BaconQrCode is a QR code generator for PHP.",
+ "homepage": "https://github.com/Bacon/BaconQrCode",
+ "support": {
+ "issues": "https://github.com/Bacon/BaconQrCode/issues",
+ "source": "https://github.com/Bacon/BaconQrCode/tree/2.0.8"
+ },
+ "time": "2022-12-07T17:46:57+00:00"
+ },
+ {
+ "name": "dasprid/enum",
+ "version": "1.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/DASPRiD/Enum.git",
+ "reference": "8e6b6ea76eabbf19ea2bf5b67b98e1860474012f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/DASPRiD/Enum/zipball/8e6b6ea76eabbf19ea2bf5b67b98e1860474012f",
+ "reference": "8e6b6ea76eabbf19ea2bf5b67b98e1860474012f",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.1 <9.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^7 | ^8 | ^9",
+ "squizlabs/php_codesniffer": "*"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "DASPRiD\\Enum\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-2-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Ben Scholzen 'DASPRiD'",
+ "email": "mail@dasprids.de",
+ "homepage": "https://dasprids.de/",
+ "role": "Developer"
+ }
+ ],
+ "description": "PHP 7.1 enum implementation",
+ "keywords": [
+ "enum",
+ "map"
+ ],
+ "support": {
+ "issues": "https://github.com/DASPRiD/Enum/issues",
+ "source": "https://github.com/DASPRiD/Enum/tree/1.0.4"
+ },
+ "time": "2023-03-01T18:44:03+00:00"
+ },
+ {
+ "name": "endroid/qr-code",
+ "version": "4.8.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/endroid/qr-code.git",
+ "reference": "2436c2333a3931c95e2b96eb82f16f53143d6bba"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/endroid/qr-code/zipball/2436c2333a3931c95e2b96eb82f16f53143d6bba",
+ "reference": "2436c2333a3931c95e2b96eb82f16f53143d6bba",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "bacon/bacon-qr-code": "^2.0.5",
+ "php": "^8.0"
+ },
+ "conflict": {
+ "khanamiryan/qrcode-detector-decoder": "^1.0.6"
+ },
+ "require-dev": {
+ "endroid/quality": "dev-master",
+ "ext-gd": "*",
+ "khanamiryan/qrcode-detector-decoder": "^1.0.4||^2.0.2",
+ "setasign/fpdf": "^1.8.2"
+ },
+ "suggest": {
+ "ext-gd": "Enables you to write PNG images",
+ "khanamiryan/qrcode-detector-decoder": "Enables you to use the image validator",
+ "roave/security-advisories": "Makes sure package versions with known security issues are not installed",
+ "setasign/fpdf": "Enables you to use the PDF writer"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Endroid\\QrCode\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jeroen van den Enden",
+ "email": "info@endroid.nl"
+ }
+ ],
+ "description": "Endroid QR Code",
+ "homepage": "https://github.com/endroid/qr-code",
+ "keywords": [
+ "code",
+ "endroid",
+ "php",
+ "qr",
+ "qrcode"
+ ],
+ "support": {
+ "issues": "https://github.com/endroid/qr-code/issues",
+ "source": "https://github.com/endroid/qr-code/tree/4.8.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/endroid",
+ "type": "github"
+ }
+ ],
+ "time": "2023-03-30T18:46:02+00:00"
+ },
+ {
+ "name": "ezyang/htmlpurifier",
+ "version": "v4.16.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ezyang/htmlpurifier.git",
+ "reference": "523407fb06eb9e5f3d59889b3978d5bfe94299c8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/523407fb06eb9e5f3d59889b3978d5bfe94299c8",
+ "reference": "523407fb06eb9e5f3d59889b3978d5bfe94299c8",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0"
+ },
+ "require-dev": {
+ "cerdic/css-tidy": "^1.7 || ^2.0",
+ "simpletest/simpletest": "dev-master"
+ },
+ "suggest": {
+ "cerdic/css-tidy": "If you want to use the filter 'Filter.ExtractStyleBlocks'.",
+ "ext-bcmath": "Used for unit conversion and imagecrash protection",
+ "ext-iconv": "Converts text to and from non-UTF-8 encodings",
+ "ext-tidy": "Used for pretty-printing HTML"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "library/HTMLPurifier.composer.php"
+ ],
+ "psr-0": {
+ "HTMLPurifier": "library/"
+ },
+ "exclude-from-classmap": [
+ "/library/HTMLPurifier/Language/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "LGPL-2.1-or-later"
+ ],
+ "authors": [
+ {
+ "name": "Edward Z. Yang",
+ "email": "admin@htmlpurifier.org",
+ "homepage": "http://ezyang.com"
+ }
+ ],
+ "description": "Standards compliant HTML filter written in PHP",
+ "homepage": "http://htmlpurifier.org/",
+ "keywords": [
+ "html"
+ ],
+ "support": {
+ "issues": "https://github.com/ezyang/htmlpurifier/issues",
+ "source": "https://github.com/ezyang/htmlpurifier/tree/v4.16.0"
+ },
+ "time": "2022-09-18T07:06:19+00:00"
+ },
+ {
+ "name": "fabpot/goutte",
+ "version": "v4.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/FriendsOfPHP/Goutte.git",
+ "reference": "e3f28671c87a48a0f13ada1baea0d95acc2138c3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/FriendsOfPHP/Goutte/zipball/e3f28671c87a48a0f13ada1baea0d95acc2138c3",
+ "reference": "e3f28671c87a48a0f13ada1baea0d95acc2138c3",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.1.3",
+ "symfony/browser-kit": "^4.4|^5.0|^6.0",
+ "symfony/css-selector": "^4.4|^5.0|^6.0",
+ "symfony/deprecation-contracts": "^2.1|^3",
+ "symfony/dom-crawler": "^4.4|^5.0|^6.0",
+ "symfony/http-client": "^4.4|^5.0|^6.0",
+ "symfony/mime": "^4.4|^5.0|^6.0"
+ },
+ "require-dev": {
+ "symfony/phpunit-bridge": "^6.0"
+ },
+ "type": "application",
+ "autoload": {
+ "psr-4": {
+ "Goutte\\": "Goutte"
+ },
+ "exclude-from-classmap": [
+ "Goutte/Tests"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ }
+ ],
+ "description": "A simple PHP Web Scraper",
+ "homepage": "https://github.com/FriendsOfPHP/Goutte",
+ "keywords": [
+ "scraper"
+ ],
+ "support": {
+ "issues": "https://github.com/FriendsOfPHP/Goutte/issues",
+ "source": "https://github.com/FriendsOfPHP/Goutte/tree/v4.0.3"
+ },
+ "time": "2023-04-01T09:05:33+00:00"
+ },
+ {
+ "name": "league/flysystem",
+ "version": "1.1.10",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/flysystem.git",
+ "reference": "3239285c825c152bcc315fe0e87d6b55f5972ed1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/3239285c825c152bcc315fe0e87d6b55f5972ed1",
+ "reference": "3239285c825c152bcc315fe0e87d6b55f5972ed1",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "ext-fileinfo": "*",
+ "league/mime-type-detection": "^1.3",
+ "php": "^7.2.5 || ^8.0"
+ },
+ "conflict": {
+ "league/flysystem-sftp": "<1.0.6"
+ },
+ "require-dev": {
+ "phpspec/prophecy": "^1.11.1",
+ "phpunit/phpunit": "^8.5.8"
+ },
+ "suggest": {
+ "ext-ftp": "Allows you to use FTP server storage",
+ "ext-openssl": "Allows you to use FTPS server storage",
+ "league/flysystem-aws-s3-v2": "Allows you to use S3 storage with AWS SDK v2",
+ "league/flysystem-aws-s3-v3": "Allows you to use S3 storage with AWS SDK v3",
+ "league/flysystem-azure": "Allows you to use Windows Azure Blob storage",
+ "league/flysystem-cached-adapter": "Flysystem adapter decorator for metadata caching",
+ "league/flysystem-eventable-filesystem": "Allows you to use EventableFilesystem",
+ "league/flysystem-rackspace": "Allows you to use Rackspace Cloud Files",
+ "league/flysystem-sftp": "Allows you to use SFTP server storage via phpseclib",
+ "league/flysystem-webdav": "Allows you to use WebDAV storage",
+ "league/flysystem-ziparchive": "Allows you to use ZipArchive adapter",
+ "spatie/flysystem-dropbox": "Allows you to use Dropbox storage",
+ "srmklive/flysystem-dropbox-v2": "Allows you to use Dropbox storage for PHP 5 applications"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.1-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "League\\Flysystem\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Frank de Jonge",
+ "email": "info@frenky.net"
+ }
+ ],
+ "description": "Filesystem abstraction: Many filesystems, one API.",
+ "keywords": [
+ "Cloud Files",
+ "WebDAV",
+ "abstraction",
+ "aws",
+ "cloud",
+ "copy.com",
+ "dropbox",
+ "file systems",
+ "files",
+ "filesystem",
+ "filesystems",
+ "ftp",
+ "rackspace",
+ "remote",
+ "s3",
+ "sftp",
+ "storage"
+ ],
+ "support": {
+ "issues": "https://github.com/thephpleague/flysystem/issues",
+ "source": "https://github.com/thephpleague/flysystem/tree/1.1.10"
+ },
+ "funding": [
+ {
+ "url": "https://offset.earth/frankdejonge",
+ "type": "other"
+ }
+ ],
+ "time": "2022-10-04T09:16:37+00:00"
+ },
+ {
+ "name": "league/flysystem-cached-adapter",
+ "version": "1.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/flysystem-cached-adapter.git",
+ "reference": "d1925efb2207ac4be3ad0c40b8277175f99ffaff"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/flysystem-cached-adapter/zipball/d1925efb2207ac4be3ad0c40b8277175f99ffaff",
+ "reference": "d1925efb2207ac4be3ad0c40b8277175f99ffaff",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "league/flysystem": "~1.0",
+ "psr/cache": "^1.0.0"
+ },
+ "require-dev": {
+ "mockery/mockery": "~0.9",
+ "phpspec/phpspec": "^3.4",
+ "phpunit/phpunit": "^5.7",
+ "predis/predis": "~1.0",
+ "tedivm/stash": "~0.12"
+ },
+ "suggest": {
+ "ext-phpredis": "Pure C implemented extension for PHP"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "League\\Flysystem\\Cached\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "frankdejonge",
+ "email": "info@frenky.net"
+ }
+ ],
+ "description": "An adapter decorator to enable meta-data caching.",
+ "support": {
+ "issues": "https://github.com/thephpleague/flysystem-cached-adapter/issues",
+ "source": "https://github.com/thephpleague/flysystem-cached-adapter/tree/master"
+ },
+ "time": "2020-07-25T15:56:04+00:00"
+ },
+ {
+ "name": "league/mime-type-detection",
+ "version": "1.11.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/mime-type-detection.git",
+ "reference": "ff6248ea87a9f116e78edd6002e39e5128a0d4dd"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/ff6248ea87a9f116e78edd6002e39e5128a0d4dd",
+ "reference": "ff6248ea87a9f116e78edd6002e39e5128a0d4dd",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "ext-fileinfo": "*",
+ "php": "^7.2 || ^8.0"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "^3.2",
+ "phpstan/phpstan": "^0.12.68",
+ "phpunit/phpunit": "^8.5.8 || ^9.3"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "League\\MimeTypeDetection\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Frank de Jonge",
+ "email": "info@frankdejonge.nl"
+ }
+ ],
+ "description": "Mime-type detection for Flysystem",
+ "support": {
+ "issues": "https://github.com/thephpleague/mime-type-detection/issues",
+ "source": "https://github.com/thephpleague/mime-type-detection/tree/1.11.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/frankdejonge",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/league/flysystem",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-04-17T13:12:02+00:00"
+ },
+ {
+ "name": "maennchen/zipstream-php",
+ "version": "v2.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/maennchen/ZipStream-PHP.git",
+ "reference": "3fa72e4c71a43f9e9118752a5c90e476a8dc9eb3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/3fa72e4c71a43f9e9118752a5c90e476a8dc9eb3",
+ "reference": "3fa72e4c71a43f9e9118752a5c90e476a8dc9eb3",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "myclabs/php-enum": "^1.5",
+ "php": "^8.0",
+ "psr/http-message": "^1.0"
+ },
+ "require-dev": {
+ "ext-zip": "*",
+ "friendsofphp/php-cs-fixer": "^3.9",
+ "guzzlehttp/guzzle": "^6.5.3 || ^7.2.0",
+ "mikey179/vfsstream": "^1.6",
+ "php-coveralls/php-coveralls": "^2.4",
+ "phpunit/phpunit": "^8.5.8 || ^9.4.2",
+ "vimeo/psalm": "^5.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "ZipStream\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Paul Duncan",
+ "email": "pabs@pablotron.org"
+ },
+ {
+ "name": "Jonatan Männchen",
+ "email": "jonatan@maennchen.ch"
+ },
+ {
+ "name": "Jesse Donat",
+ "email": "donatj@gmail.com"
+ },
+ {
+ "name": "András Kolesár",
+ "email": "kolesar@kolesar.hu"
+ }
+ ],
+ "description": "ZipStream is a library for dynamically streaming dynamic zip files from PHP without writing to the disk at all on the server.",
+ "keywords": [
+ "stream",
+ "zip"
+ ],
+ "support": {
+ "issues": "https://github.com/maennchen/ZipStream-PHP/issues",
+ "source": "https://github.com/maennchen/ZipStream-PHP/tree/v2.4.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/maennchen",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/zipstream",
+ "type": "open_collective"
+ }
+ ],
+ "time": "2022-12-08T12:29:14+00:00"
+ },
+ {
+ "name": "markbaker/complex",
+ "version": "3.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/MarkBaker/PHPComplex.git",
+ "reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/MarkBaker/PHPComplex/zipball/95c56caa1cf5c766ad6d65b6344b807c1e8405b9",
+ "reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "require-dev": {
+ "dealerdirect/phpcodesniffer-composer-installer": "dev-master",
+ "phpcompatibility/php-compatibility": "^9.3",
+ "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
+ "squizlabs/php_codesniffer": "^3.7"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Complex\\": "classes/src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Mark Baker",
+ "email": "mark@lange.demon.co.uk"
+ }
+ ],
+ "description": "PHP Class for working with complex numbers",
+ "homepage": "https://github.com/MarkBaker/PHPComplex",
+ "keywords": [
+ "complex",
+ "mathematics"
+ ],
+ "support": {
+ "issues": "https://github.com/MarkBaker/PHPComplex/issues",
+ "source": "https://github.com/MarkBaker/PHPComplex/tree/3.0.2"
+ },
+ "time": "2022-12-06T16:21:08+00:00"
+ },
+ {
+ "name": "markbaker/matrix",
+ "version": "3.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/MarkBaker/PHPMatrix.git",
+ "reference": "728434227fe21be27ff6d86621a1b13107a2562c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/MarkBaker/PHPMatrix/zipball/728434227fe21be27ff6d86621a1b13107a2562c",
+ "reference": "728434227fe21be27ff6d86621a1b13107a2562c",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": "^7.1 || ^8.0"
+ },
+ "require-dev": {
+ "dealerdirect/phpcodesniffer-composer-installer": "dev-master",
+ "phpcompatibility/php-compatibility": "^9.3",
+ "phpdocumentor/phpdocumentor": "2.*",
+ "phploc/phploc": "^4.0",
+ "phpmd/phpmd": "2.*",
+ "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
+ "sebastian/phpcpd": "^4.0",
+ "squizlabs/php_codesniffer": "^3.7"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Matrix\\": "classes/src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Mark Baker",
+ "email": "mark@demon-angel.eu"
+ }
+ ],
+ "description": "PHP Class for working with matrices",
+ "homepage": "https://github.com/MarkBaker/PHPMatrix",
+ "keywords": [
+ "mathematics",
+ "matrix",
+ "vector"
+ ],
+ "support": {
+ "issues": "https://github.com/MarkBaker/PHPMatrix/issues",
+ "source": "https://github.com/MarkBaker/PHPMatrix/tree/3.0.1"
+ },
+ "time": "2022-12-02T22:17:43+00:00"
+ },
+ {
+ "name": "myclabs/php-enum",
+ "version": "1.8.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/myclabs/php-enum.git",
+ "reference": "b942d263c641ddb5190929ff840c68f78713e937"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/myclabs/php-enum/zipball/b942d263c641ddb5190929ff840c68f78713e937",
+ "reference": "b942d263c641ddb5190929ff840c68f78713e937",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "ext-json": "*",
+ "php": "^7.3 || ^8.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.5",
+ "squizlabs/php_codesniffer": "1.*",
+ "vimeo/psalm": "^4.6.2"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "MyCLabs\\Enum\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP Enum contributors",
+ "homepage": "https://github.com/myclabs/php-enum/graphs/contributors"
+ }
+ ],
+ "description": "PHP Enum implementation",
+ "homepage": "http://github.com/myclabs/php-enum",
+ "keywords": [
+ "enum"
+ ],
+ "support": {
+ "issues": "https://github.com/myclabs/php-enum/issues",
+ "source": "https://github.com/myclabs/php-enum/tree/1.8.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/mnapoli",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/myclabs/php-enum",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2021-07-05T08:18:36+00:00"
+ },
+ {
+ "name": "phpoffice/phpspreadsheet",
+ "version": "1.28.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/PHPOffice/PhpSpreadsheet.git",
+ "reference": "6e81cf39bbd93ebc3a4e8150444c41e8aa9b769a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/6e81cf39bbd93ebc3a4e8150444c41e8aa9b769a",
+ "reference": "6e81cf39bbd93ebc3a4e8150444c41e8aa9b769a",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "ext-ctype": "*",
+ "ext-dom": "*",
+ "ext-fileinfo": "*",
+ "ext-gd": "*",
+ "ext-iconv": "*",
+ "ext-libxml": "*",
+ "ext-mbstring": "*",
+ "ext-simplexml": "*",
+ "ext-xml": "*",
+ "ext-xmlreader": "*",
+ "ext-xmlwriter": "*",
+ "ext-zip": "*",
+ "ext-zlib": "*",
+ "ezyang/htmlpurifier": "^4.15",
+ "maennchen/zipstream-php": "^2.1",
+ "markbaker/complex": "^3.0",
+ "markbaker/matrix": "^3.0",
+ "php": "^7.4 || ^8.0",
+ "psr/http-client": "^1.0",
+ "psr/http-factory": "^1.0",
+ "psr/simple-cache": "^1.0 || ^2.0 || ^3.0"
+ },
+ "require-dev": {
+ "dealerdirect/phpcodesniffer-composer-installer": "dev-main",
+ "dompdf/dompdf": "^1.0 || ^2.0",
+ "friendsofphp/php-cs-fixer": "^3.2",
+ "mitoteam/jpgraph": "^10.2.4",
+ "mpdf/mpdf": "^8.1.1",
+ "phpcompatibility/php-compatibility": "^9.3",
+ "phpstan/phpstan": "^1.1",
+ "phpstan/phpstan-phpunit": "^1.0",
+ "phpunit/phpunit": "^8.5 || ^9.0",
+ "squizlabs/php_codesniffer": "^3.7",
+ "tecnickcom/tcpdf": "^6.5"
+ },
+ "suggest": {
+ "dompdf/dompdf": "Option for rendering PDF with PDF Writer",
+ "ext-intl": "PHP Internationalization Functions",
+ "mitoteam/jpgraph": "Option for rendering charts, or including charts with PDF or HTML Writers",
+ "mpdf/mpdf": "Option for rendering PDF with PDF Writer",
+ "tecnickcom/tcpdf": "Option for rendering PDF with PDF Writer"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "PhpOffice\\PhpSpreadsheet\\": "src/PhpSpreadsheet"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Maarten Balliauw",
+ "homepage": "https://blog.maartenballiauw.be"
+ },
+ {
+ "name": "Mark Baker",
+ "homepage": "https://markbakeruk.net"
+ },
+ {
+ "name": "Franck Lefevre",
+ "homepage": "https://rootslabs.net"
+ },
+ {
+ "name": "Erik Tilt"
+ },
+ {
+ "name": "Adrien Crivelli"
+ }
+ ],
+ "description": "PHPSpreadsheet - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine",
+ "homepage": "https://github.com/PHPOffice/PhpSpreadsheet",
+ "keywords": [
+ "OpenXML",
+ "excel",
+ "gnumeric",
+ "ods",
+ "php",
+ "spreadsheet",
+ "xls",
+ "xlsx"
+ ],
+ "support": {
+ "issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues",
+ "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/1.28.0"
+ },
+ "time": "2023-02-25T12:24:49+00:00"
+ },
+ {
+ "name": "psr/cache",
+ "version": "1.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/cache.git",
+ "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8",
+ "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Cache\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for caching libraries",
+ "keywords": [
+ "cache",
+ "psr",
+ "psr-6"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/cache/tree/master"
+ },
+ "time": "2016-08-06T20:24:11+00:00"
+ },
+ {
+ "name": "psr/container",
+ "version": "1.1.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/container.git",
+ "reference": "513e0666f7216c7459170d56df27dfcefe1689ea"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea",
+ "reference": "513e0666f7216c7459170d56df27dfcefe1689ea",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.4.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Psr\\Container\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common Container Interface (PHP FIG PSR-11)",
+ "homepage": "https://github.com/php-fig/container",
+ "keywords": [
+ "PSR-11",
+ "container",
+ "container-interface",
+ "container-interop",
+ "psr"
+ ],
+ "support": {
+ "issues": "https://github.com/php-fig/container/issues",
+ "source": "https://github.com/php-fig/container/tree/1.1.2"
+ },
+ "time": "2021-11-05T16:50:12+00:00"
+ },
+ {
+ "name": "psr/http-client",
+ "version": "1.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-client.git",
+ "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
+ "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": "^7.0 || ^8.0",
+ "psr/http-message": "^1.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Client\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for HTTP clients",
+ "homepage": "https://github.com/php-fig/http-client",
+ "keywords": [
+ "http",
+ "http-client",
+ "psr",
+ "psr-18"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/http-client/tree/master"
+ },
+ "time": "2020-06-29T06:28:15+00:00"
+ },
+ {
+ "name": "psr/http-factory",
+ "version": "1.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-factory.git",
+ "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be",
+ "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.0.0",
+ "psr/http-message": "^1.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Message\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interfaces for PSR-7 HTTP message factories",
+ "keywords": [
+ "factory",
+ "http",
+ "message",
+ "psr",
+ "psr-17",
+ "psr-7",
+ "request",
+ "response"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/http-factory/tree/master"
+ },
+ "time": "2019-04-30T12:38:16+00:00"
+ },
+ {
+ "name": "psr/http-message",
+ "version": "1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-message.git",
+ "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-message/zipball/cb6ce4845ce34a8ad9e68117c10ee90a29919eba",
+ "reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Message\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for HTTP messages",
+ "homepage": "https://github.com/php-fig/http-message",
+ "keywords": [
+ "http",
+ "http-message",
+ "psr",
+ "psr-7",
+ "request",
+ "response"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/http-message/tree/1.1"
+ },
+ "time": "2023-04-04T09:50:52+00:00"
+ },
+ {
+ "name": "psr/log",
+ "version": "1.1.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/log.git",
+ "reference": "d49695b909c3b7628b6289db5479a1c204601f11"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11",
+ "reference": "d49695b909c3b7628b6289db5479a1c204601f11",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Log\\": "Psr/Log/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "https://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for logging libraries",
+ "homepage": "https://github.com/php-fig/log",
+ "keywords": [
+ "log",
+ "psr",
+ "psr-3"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/log/tree/1.1.4"
+ },
+ "time": "2021-05-03T11:20:27+00:00"
+ },
+ {
+ "name": "psr/simple-cache",
+ "version": "1.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/simple-cache.git",
+ "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
+ "reference": "408d5eafb83c57f6365a3ca330ff23aa4a5fa39b",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\SimpleCache\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interfaces for simple caching",
+ "keywords": [
+ "cache",
+ "caching",
+ "psr",
+ "psr-16",
+ "simple-cache"
+ ],
+ "support": {
+ "source": "https://github.com/php-fig/simple-cache/tree/master"
+ },
+ "time": "2017-10-23T01:57:42+00:00"
+ },
+ {
+ "name": "symfony/browser-kit",
+ "version": "v6.0.19",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/browser-kit.git",
+ "reference": "4d1bf7886e2af0a194332486273debcd6662cfc9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/browser-kit/zipball/4d1bf7886e2af0a194332486273debcd6662cfc9",
+ "reference": "4d1bf7886e2af0a194332486273debcd6662cfc9",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=8.0.2",
+ "symfony/dom-crawler": "^5.4|^6.0"
+ },
+ "require-dev": {
+ "symfony/css-selector": "^5.4|^6.0",
+ "symfony/http-client": "^5.4|^6.0",
+ "symfony/mime": "^5.4|^6.0",
+ "symfony/process": "^5.4|^6.0"
+ },
+ "suggest": {
+ "symfony/process": ""
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\BrowserKit\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/browser-kit/tree/v6.0.19"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2023-01-01T08:36:10+00:00"
+ },
+ {
+ "name": "symfony/css-selector",
+ "version": "v6.0.19",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/css-selector.git",
+ "reference": "f1d00bddb83a4cb2138564b2150001cb6ce272b1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/css-selector/zipball/f1d00bddb83a4cb2138564b2150001cb6ce272b1",
+ "reference": "f1d00bddb83a4cb2138564b2150001cb6ce272b1",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=8.0.2"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\CssSelector\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Jean-François Simon",
+ "email": "jeanfrancois.simon@sensiolabs.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Converts CSS selectors to XPath expressions",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/css-selector/tree/v6.0.19"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2023-01-01T08:36:10+00:00"
+ },
+ {
+ "name": "symfony/deprecation-contracts",
+ "version": "v3.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/deprecation-contracts.git",
+ "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/26954b3d62a6c5fd0ea8a2a00c0353a14978d05c",
+ "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=8.0.2"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.0-dev"
+ },
+ "thanks": {
+ "name": "symfony/contracts",
+ "url": "https://github.com/symfony/contracts"
+ }
+ },
+ "autoload": {
+ "files": [
+ "function.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "A generic function and convention to trigger deprecation notices",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/deprecation-contracts/tree/v3.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-01-02T09:55:41+00:00"
+ },
+ {
+ "name": "symfony/dom-crawler",
+ "version": "v6.0.19",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/dom-crawler.git",
+ "reference": "622578ff158318b1b49d95068bd6b66c713601e9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/622578ff158318b1b49d95068bd6b66c713601e9",
+ "reference": "622578ff158318b1b49d95068bd6b66c713601e9",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=8.0.2",
+ "symfony/polyfill-ctype": "~1.8",
+ "symfony/polyfill-mbstring": "~1.0"
+ },
+ "conflict": {
+ "masterminds/html5": "<2.6"
+ },
+ "require-dev": {
+ "masterminds/html5": "^2.6",
+ "symfony/css-selector": "^5.4|^6.0"
+ },
+ "suggest": {
+ "symfony/css-selector": ""
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\DomCrawler\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Eases DOM navigation for HTML and XML documents",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/dom-crawler/tree/v6.0.19"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2023-01-20T17:44:14+00:00"
+ },
+ {
+ "name": "symfony/http-client",
+ "version": "v6.0.20",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/http-client.git",
+ "reference": "541c04560da1875f62c963c3aab6ea12a7314e11"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/http-client/zipball/541c04560da1875f62c963c3aab6ea12a7314e11",
+ "reference": "541c04560da1875f62c963c3aab6ea12a7314e11",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=8.0.2",
+ "psr/log": "^1|^2|^3",
+ "symfony/http-client-contracts": "^3",
+ "symfony/service-contracts": "^1.0|^2|^3"
+ },
+ "provide": {
+ "php-http/async-client-implementation": "*",
+ "php-http/client-implementation": "*",
+ "psr/http-client-implementation": "1.0",
+ "symfony/http-client-implementation": "3.0"
+ },
+ "require-dev": {
+ "amphp/amp": "^2.5",
+ "amphp/http-client": "^4.2.1",
+ "amphp/http-tunnel": "^1.0",
+ "amphp/socket": "^1.1",
+ "guzzlehttp/promises": "^1.4",
+ "nyholm/psr7": "^1.0",
+ "php-http/httplug": "^1.0|^2.0",
+ "psr/http-client": "^1.0",
+ "symfony/dependency-injection": "^5.4|^6.0",
+ "symfony/http-kernel": "^5.4|^6.0",
+ "symfony/process": "^5.4|^6.0",
+ "symfony/stopwatch": "^5.4|^6.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\HttpClient\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/http-client/tree/v6.0.20"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2023-01-30T15:41:07+00:00"
+ },
+ {
+ "name": "symfony/http-client-contracts",
+ "version": "v3.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/http-client-contracts.git",
+ "reference": "4184b9b63af1edaf35b6a7974c6f1f9f33294129"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/4184b9b63af1edaf35b6a7974c6f1f9f33294129",
+ "reference": "4184b9b63af1edaf35b6a7974c6f1f9f33294129",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=8.0.2"
+ },
+ "suggest": {
+ "symfony/http-client-implementation": ""
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "3.0-dev"
+ },
+ "thanks": {
+ "name": "symfony/contracts",
+ "url": "https://github.com/symfony/contracts"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Contracts\\HttpClient\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Generic abstractions related to HTTP clients",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "abstractions",
+ "contracts",
+ "decoupling",
+ "interfaces",
+ "interoperability",
+ "standards"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/http-client-contracts/tree/v3.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-04-12T16:11:42+00:00"
+ },
+ {
+ "name": "symfony/mime",
+ "version": "v6.0.19",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/mime.git",
+ "reference": "d7052547a0070cbeadd474e172b527a00d657301"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/mime/zipball/d7052547a0070cbeadd474e172b527a00d657301",
+ "reference": "d7052547a0070cbeadd474e172b527a00d657301",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=8.0.2",
+ "symfony/polyfill-intl-idn": "^1.10",
+ "symfony/polyfill-mbstring": "^1.0"
+ },
+ "conflict": {
+ "egulias/email-validator": "~3.0.0",
+ "phpdocumentor/reflection-docblock": "<3.2.2",
+ "phpdocumentor/type-resolver": "<1.4.0",
+ "symfony/mailer": "<5.4",
+ "symfony/serializer": "<5.4.14|>=6.0,<6.0.14|>=6.1,<6.1.6"
+ },
+ "require-dev": {
+ "egulias/email-validator": "^2.1.10|^3.1|^4",
+ "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0",
+ "symfony/dependency-injection": "^5.4|^6.0",
+ "symfony/property-access": "^5.4|^6.0",
+ "symfony/property-info": "^5.4|^6.0",
+ "symfony/serializer": "^5.4.14|~6.0.14|^6.1.6"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Mime\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Allows manipulating MIME messages",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "mime",
+ "mime-type"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/mime/tree/v6.0.19"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2023-01-11T11:50:03+00:00"
+ },
+ {
+ "name": "symfony/polyfill-ctype",
+ "version": "v1.27.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-ctype.git",
+ "reference": "5bbc823adecdae860bb64756d639ecfec17b050a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a",
+ "reference": "5bbc823adecdae860bb64756d639ecfec17b050a",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "provide": {
+ "ext-ctype": "*"
+ },
+ "suggest": {
+ "ext-ctype": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.27-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Ctype\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Gert de Pagter",
+ "email": "BackEndTea@gmail.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for ctype functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "ctype",
+ "polyfill",
+ "portable"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-11-03T14:55:06+00:00"
+ },
+ {
+ "name": "symfony/polyfill-intl-idn",
+ "version": "v1.27.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-idn.git",
+ "reference": "639084e360537a19f9ee352433b84ce831f3d2da"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/639084e360537a19f9ee352433b84ce831f3d2da",
+ "reference": "639084e360537a19f9ee352433b84ce831f3d2da",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.1",
+ "symfony/polyfill-intl-normalizer": "^1.10",
+ "symfony/polyfill-php72": "^1.10"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.27-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Idn\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Laurent Bassin",
+ "email": "laurent@bassin.info"
+ },
+ {
+ "name": "Trevor Rowbotham",
+ "email": "trevor.rowbotham@pm.me"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "idn",
+ "intl",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.27.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-11-03T14:55:06+00:00"
+ },
+ {
+ "name": "symfony/polyfill-intl-normalizer",
+ "version": "v1.27.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
+ "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6",
+ "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.27-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Intl\\Normalizer\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for intl's Normalizer class and related functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "intl",
+ "normalizer",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-11-03T14:55:06+00:00"
+ },
+ {
+ "name": "symfony/polyfill-mbstring",
+ "version": "v1.27.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-mbstring.git",
+ "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
+ "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "provide": {
+ "ext-mbstring": "*"
+ },
+ "suggest": {
+ "ext-mbstring": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.27-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Mbstring\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill for the Mbstring extension",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "mbstring",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-11-03T14:55:06+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php72",
+ "version": "v1.27.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php72.git",
+ "reference": "869329b1e9894268a8a61dabb69153029b7a8c97"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/869329b1e9894268a8a61dabb69153029b7a8c97",
+ "reference": "869329b1e9894268a8a61dabb69153029b7a8c97",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.27-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Php72\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php72/tree/v1.27.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-11-03T14:55:06+00:00"
+ },
+ {
+ "name": "symfony/service-contracts",
+ "version": "v2.5.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/service-contracts.git",
+ "reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/service-contracts/zipball/4b426aac47d6427cc1a1d0f7e2ac724627f5966c",
+ "reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.2.5",
+ "psr/container": "^1.1",
+ "symfony/deprecation-contracts": "^2.1|^3"
+ },
+ "conflict": {
+ "ext-psr": "<1.1|>=2"
+ },
+ "suggest": {
+ "symfony/service-implementation": ""
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "2.5-dev"
+ },
+ "thanks": {
+ "name": "symfony/contracts",
+ "url": "https://github.com/symfony/contracts"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Contracts\\Service\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Generic abstractions related to writing services",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "abstractions",
+ "contracts",
+ "decoupling",
+ "interfaces",
+ "interoperability",
+ "standards"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/service-contracts/tree/v2.5.2"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-05-30T19:17:29+00:00"
+ },
+ {
+ "name": "topthink/framework",
+ "version": "v6.1.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/top-think/framework.git",
+ "reference": "67235be5b919aaaf1de5aed9839f65d8e766aca3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/top-think/framework/zipball/67235be5b919aaaf1de5aed9839f65d8e766aca3",
+ "reference": "67235be5b919aaaf1de5aed9839f65d8e766aca3",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "ext-json": "*",
+ "ext-mbstring": "*",
+ "php": ">=7.2.5",
+ "psr/container": "~1.0",
+ "psr/http-message": "^1.0",
+ "psr/log": "~1.0",
+ "psr/simple-cache": "^1.0",
+ "topthink/think-helper": "^3.1.1",
+ "topthink/think-orm": "^2.0|^3.0"
+ },
+ "require-dev": {
+ "guzzlehttp/psr7": "^2.1.0",
+ "mikey179/vfsstream": "^1.6",
+ "mockery/mockery": "^1.2",
+ "phpunit/phpunit": "^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [],
+ "psr-4": {
+ "think\\": "src/think/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "liu21st",
+ "email": "liu21st@gmail.com"
+ },
+ {
+ "name": "yunwuxin",
+ "email": "448901948@qq.com"
+ }
+ ],
+ "description": "The ThinkPHP Framework.",
+ "homepage": "http://thinkphp.cn/",
+ "keywords": [
+ "framework",
+ "orm",
+ "thinkphp"
+ ],
+ "support": {
+ "issues": "https://github.com/top-think/framework/issues",
+ "source": "https://github.com/top-think/framework/tree/v6.1.2"
+ },
+ "time": "2023-02-08T02:24:01+00:00"
+ },
+ {
+ "name": "topthink/think-captcha",
+ "version": "v3.0.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/top-think/think-captcha.git",
+ "reference": "52fba122c953995bec3013c635025172491ae299"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/top-think/think-captcha/zipball/52fba122c953995bec3013c635025172491ae299",
+ "reference": "52fba122c953995bec3013c635025172491ae299",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "topthink/framework": "^6.0"
+ },
+ "type": "library",
+ "extra": {
+ "think": {
+ "services": [
+ "think\\captcha\\CaptchaService"
+ ],
+ "config": {
+ "captcha": "src/config.php"
+ }
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/helper.php"
+ ],
+ "psr-4": {
+ "think\\captcha\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "yunwuxin",
+ "email": "448901948@qq.com"
+ }
+ ],
+ "description": "captcha package for thinkphp",
+ "support": {
+ "issues": "https://github.com/top-think/think-captcha/issues",
+ "source": "https://github.com/top-think/think-captcha/tree/v3.0.8"
+ },
+ "time": "2022-10-26T07:59:42+00:00"
+ },
+ {
+ "name": "topthink/think-filesystem",
+ "version": "v1.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/top-think/think-filesystem.git",
+ "reference": "29f19f140a9267c717fecd7ccb22c84c2d72382e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/top-think/think-filesystem/zipball/29f19f140a9267c717fecd7ccb22c84c2d72382e",
+ "reference": "29f19f140a9267c717fecd7ccb22c84c2d72382e",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "league/flysystem": "^1.1.4",
+ "league/flysystem-cached-adapter": "^1.0",
+ "php": ">=7.2.5",
+ "topthink/framework": "^6.1|^8.0"
+ },
+ "require-dev": {
+ "mikey179/vfsstream": "^1.6",
+ "mockery/mockery": "^1.2",
+ "phpunit/phpunit": "^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "think\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "yunwuxin",
+ "email": "448901948@qq.com"
+ }
+ ],
+ "description": "The ThinkPHP6.1 Filesystem Package",
+ "support": {
+ "issues": "https://github.com/top-think/think-filesystem/issues",
+ "source": "https://github.com/top-think/think-filesystem/tree/v1.0.3"
+ },
+ "time": "2023-02-08T01:25:15+00:00"
+ },
+ {
+ "name": "topthink/think-helper",
+ "version": "v3.1.6",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/top-think/think-helper.git",
+ "reference": "769acbe50a4274327162f9c68ec2e89a38eb2aff"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/top-think/think-helper/zipball/769acbe50a4274327162f9c68ec2e89a38eb2aff",
+ "reference": "769acbe50a4274327162f9c68ec2e89a38eb2aff",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.1.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.5"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/helper.php"
+ ],
+ "psr-4": {
+ "think\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "yunwuxin",
+ "email": "448901948@qq.com"
+ }
+ ],
+ "description": "The ThinkPHP6 Helper Package",
+ "support": {
+ "issues": "https://github.com/top-think/think-helper/issues",
+ "source": "https://github.com/top-think/think-helper/tree/v3.1.6"
+ },
+ "time": "2021-12-15T04:27:55+00:00"
+ },
+ {
+ "name": "topthink/think-image",
+ "version": "v1.0.7",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/top-think/think-image.git",
+ "reference": "8586cf47f117481c6d415b20f7dedf62e79d5512"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/top-think/think-image/zipball/8586cf47f117481c6d415b20f7dedf62e79d5512",
+ "reference": "8586cf47f117481c6d415b20f7dedf62e79d5512",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "ext-gd": "*"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "4.8.*",
+ "topthink/framework": "^5.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "think\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "yunwuxin",
+ "email": "448901948@qq.com"
+ }
+ ],
+ "description": "The ThinkPHP5 Image Package",
+ "support": {
+ "issues": "https://github.com/top-think/think-image/issues",
+ "source": "https://github.com/top-think/think-image/tree/master"
+ },
+ "time": "2016-09-29T06:05:43+00:00"
+ },
+ {
+ "name": "topthink/think-multi-app",
+ "version": "v1.0.16",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/top-think/think-multi-app.git",
+ "reference": "07b9183855150455e1f76f8cbe9d77d6d1bc399f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/top-think/think-multi-app/zipball/07b9183855150455e1f76f8cbe9d77d6d1bc399f",
+ "reference": "07b9183855150455e1f76f8cbe9d77d6d1bc399f",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.1.0",
+ "topthink/framework": "^6.0|^8.0"
+ },
+ "type": "library",
+ "extra": {
+ "think": {
+ "services": [
+ "think\\app\\Service"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "think\\app\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "liu21st",
+ "email": "liu21st@gmail.com"
+ }
+ ],
+ "description": "thinkphp6 multi app support",
+ "support": {
+ "issues": "https://github.com/top-think/think-multi-app/issues",
+ "source": "https://github.com/top-think/think-multi-app/tree/v1.0.16"
+ },
+ "time": "2023-02-07T08:40:09+00:00"
+ },
+ {
+ "name": "topthink/think-orm",
+ "version": "v2.0.60",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/top-think/think-orm.git",
+ "reference": "8bc34a4307fa27186c0e96a9b3de3cb23aa1ed46"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/top-think/think-orm/zipball/8bc34a4307fa27186c0e96a9b3de3cb23aa1ed46",
+ "reference": "8bc34a4307fa27186c0e96a9b3de3cb23aa1ed46",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "ext-json": "*",
+ "ext-pdo": "*",
+ "php": ">=7.1.0",
+ "psr/log": "^1.0|^2.0",
+ "psr/simple-cache": "^1.0|^2.0",
+ "topthink/think-helper": "^3.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^7|^8|^9.5"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "stubs/load_stubs.php"
+ ],
+ "psr-4": {
+ "think\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "liu21st",
+ "email": "liu21st@gmail.com"
+ }
+ ],
+ "description": "think orm",
+ "keywords": [
+ "database",
+ "orm"
+ ],
+ "support": {
+ "issues": "https://github.com/top-think/think-orm/issues",
+ "source": "https://github.com/top-think/think-orm/tree/v2.0.60"
+ },
+ "time": "2023-03-19T04:51:56+00:00"
+ }
+ ],
+ "packages-dev": [
+ {
+ "name": "symfony/polyfill-php80",
+ "version": "v1.27.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php80.git",
+ "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936",
+ "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "1.27-dev"
+ },
+ "thanks": {
+ "name": "symfony/polyfill",
+ "url": "https://github.com/symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Php80\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ion Bazan",
+ "email": "ion.bazan@gmail.com"
+ },
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-11-03T14:55:06+00:00"
+ },
+ {
+ "name": "symfony/var-dumper",
+ "version": "v4.4.47",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/var-dumper.git",
+ "reference": "1069c7a3fca74578022fab6f81643248d02f8e63"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/var-dumper/zipball/1069c7a3fca74578022fab6f81643248d02f8e63",
+ "reference": "1069c7a3fca74578022fab6f81643248d02f8e63",
+ "shasum": "",
+ "mirrors": [
+ {
+ "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
+ "preferred": true
+ }
+ ]
+ },
+ "require": {
+ "php": ">=7.1.3",
+ "symfony/polyfill-mbstring": "~1.0",
+ "symfony/polyfill-php72": "~1.5",
+ "symfony/polyfill-php80": "^1.16"
+ },
+ "conflict": {
+ "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0",
+ "symfony/console": "<3.4"
+ },
+ "require-dev": {
+ "ext-iconv": "*",
+ "symfony/console": "^3.4|^4.0|^5.0",
+ "symfony/process": "^4.4|^5.0",
+ "twig/twig": "^1.43|^2.13|^3.0.4"
+ },
+ "suggest": {
+ "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).",
+ "ext-intl": "To show region name in time zone dump",
+ "symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script"
+ },
+ "bin": [
+ "Resources/bin/var-dump-server"
+ ],
+ "type": "library",
+ "autoload": {
+ "files": [
+ "Resources/functions/dump.php"
+ ],
+ "psr-4": {
+ "Symfony\\Component\\VarDumper\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Provides mechanisms for walking through any arbitrary PHP variable",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "debug",
+ "dump"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/var-dumper/tree/v4.4.47"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-10-03T15:15:11+00:00"
+ }
+ ],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": {
+ "php": ">=7.4.3"
+ },
+ "platform-dev": [],
+ "plugin-api-version": "2.3.0"
+}
diff --git a/config/app.php b/config/app.php
new file mode 100644
index 0000000..cd588b5
--- /dev/null
+++ b/config/app.php
@@ -0,0 +1,33 @@
+ '',
+ // 应用地址
+ 'app_host' => env('app.host', ''),
+ // 应用的命名空间
+ 'app_namespace' => '',
+ // 是否启用路由
+ 'with_route' => true,
+ // 默认应用
+ 'default_app' => 'index',
+ // 默认时区
+ 'default_timezone' => 'Asia/Shanghai',
+
+ // 应用映射(自动多应用模式有效)
+ 'app_map' => [],
+ // 域名绑定(自动多应用模式有效)
+ 'domain_bind' => [],
+ // 禁止URL访问的应用列表(自动多应用模式有效)
+ 'deny_app_list' => ['common'],
+
+ // 异常页面的模板文件
+ 'exception_tmpl' => app()->getThinkPath() . 'tpl/think_exception.tpl',
+
+ // 错误显示信息,非调试模式有效
+ 'error_message' => '页面错误!请稍后再试~',
+ // 显示错误信息
+ 'show_error_msg' => true,
+];
diff --git a/config/cache.php b/config/cache.php
new file mode 100644
index 0000000..a8d69d2
--- /dev/null
+++ b/config/cache.php
@@ -0,0 +1,29 @@
+ env('cache.driver', 'file'),
+
+ // 缓存连接方式配置
+ 'stores' => [
+ 'file' => [
+ // 驱动方式
+ 'type' => 'File',
+ // 缓存保存目录
+ 'path' => '',
+ // 缓存前缀
+ 'prefix' => '',
+ // 缓存有效期 0表示永久缓存
+ 'expire' => 0,
+ // 缓存标签前缀
+ 'tag_prefix' => 'tag:',
+ // 序列化机制 例如 ['serialize', 'unserialize']
+ 'serialize' => [],
+ ],
+ // 更多的缓存连接
+ ],
+];
diff --git a/config/captcha.php b/config/captcha.php
new file mode 100644
index 0000000..29e015e
--- /dev/null
+++ b/config/captcha.php
@@ -0,0 +1,46 @@
+ 4,
+ // 验证码字符集合
+ // 'codeSet' => '2345678abcdefhijkmnpqrstuvwxyzABCDEFGHJKLMNPQRTUVWXY',
+ 'codeSet' => '2345678',
+ // 验证码过期时间
+ 'expire' => 1800,
+ // 是否使用中文验证码
+ 'useZh' => false,
+ // 是否使用算术验证码
+ 'math' => false,
+ // 是否使用背景图
+ 'useImgBg' => false,
+ //验证码字符大小
+ 'fontSize' => 25,
+ // 是否使用混淆曲线
+ 'useCurve' => true,
+ //是否添加杂点
+ 'useNoise' => true,
+ // 验证码字体 不设置则随机
+ 'fontttf' => '',
+ //背景颜色
+ 'bg' => [243, 251, 254],
+ // 验证码图片高度
+ 'imageH' => 0,
+ // 验证码图片宽度
+ 'imageW' => 0,
+
+ // 添加额外的验证码设置
+ 'verify' => [
+ 'length' => 5,
+ 'useImgBg' => false,
+ 'useNoise' => false,
+ 'useCurve' => false,
+ // 'imageH' => 40,
+ // 'imageW' => 100,
+ 'fontSize' => 15,
+ 'bg' => [255, 255, 255],
+ ],
+];
diff --git a/config/chunkUpload.php b/config/chunkUpload.php
new file mode 100644
index 0000000..6a888bb
--- /dev/null
+++ b/config/chunkUpload.php
@@ -0,0 +1,9 @@
+ ''
+];
diff --git a/config/console.php b/config/console.php
new file mode 100644
index 0000000..d0092b2
--- /dev/null
+++ b/config/console.php
@@ -0,0 +1,10 @@
+ [
+ 'timer' => 'app\command\Timer'
+ ],
+];
diff --git a/config/cookie.php b/config/cookie.php
new file mode 100644
index 0000000..d3b3aab
--- /dev/null
+++ b/config/cookie.php
@@ -0,0 +1,20 @@
+ 0,
+ // cookie 保存路径
+ 'path' => '/',
+ // cookie 有效域名
+ 'domain' => '',
+ // cookie 启用安全传输
+ 'secure' => false,
+ // httponly设置
+ 'httponly' => false,
+ // 是否使用 setcookie
+ 'setcookie' => true,
+ // samesite 设置,支持 'strict' 'lax'
+ 'samesite' => '',
+];
diff --git a/config/database.php b/config/database.php
new file mode 100644
index 0000000..0fdd298
--- /dev/null
+++ b/config/database.php
@@ -0,0 +1,63 @@
+ env('database.driver', 'mysql'),
+
+ // 自定义时间查询规则
+ 'time_query_rule' => [],
+
+ // 自动写入时间戳字段
+ // true为自动识别类型 false关闭
+ // 字符串则明确指定时间字段类型 支持 int timestamp datetime date
+ 'auto_timestamp' => true,
+
+ // 时间字段取出后的默认时间格式
+ 'datetime_format' => 'Y-m-d H:i:s',
+
+ // 时间字段配置 配置格式:create_time,update_time
+ 'datetime_field' => '',
+
+ // 数据库连接配置信息
+ 'connections' => [
+ 'mysql' => [
+ // 数据库类型
+ 'type' => env('database.type', 'mysql'),
+ // 服务器地址
+ 'hostname' => env('database.hostname', '47.242.159.172'),
+ // 数据库名
+ 'database' => env('database.database', 'php_web'),
+ // 用户名
+ 'username' => env('database.username', 'php_web'),
+ // 密码
+ 'password' => env('database.password', 'php_web@aerwen'),
+ // 端口
+ 'hostport' => env('database.hostpost', '3306'),
+ // 数据库连接参数
+ 'params' => [],
+ // 数据库编码默认采用utf8
+ 'charset' => env('database.charset', 'utf8'),
+ // 数据库表前缀
+ 'prefix' => env('database.prefix', ''),
+
+ // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
+ 'deploy' => 0,
+ // 数据库读写是否分离 主从式有效
+ 'rw_separate' => false,
+ // 读写分离后 主服务器数量
+ 'master_num' => 1,
+ // 指定从服务器序号
+ 'slave_no' => '',
+ // 是否严格检查字段是否存在
+ 'fields_strict' => true,
+ // 是否需要断线重连
+ 'break_reconnect' => false,
+ // 监听SQL
+ 'trigger_sql' => env('app_debug', true),
+ // 开启字段缓存
+ 'fields_cache' => false,
+ ],
+
+ // 更多的数据库配置信息
+ ],
+];
diff --git a/config/filesystem.php b/config/filesystem.php
new file mode 100644
index 0000000..ed45ff3
--- /dev/null
+++ b/config/filesystem.php
@@ -0,0 +1,37 @@
+ env('filesystem.driver', 'uploads'),
+ // 磁盘列表
+ 'disks' => [
+ // 'local' => [
+ // 'type' => 'local',
+ // 'root' => app()->getRuntimePath() . 'storage',
+ // ],
+ 'uploads' => [
+ 'type' => 'local',
+ 'root' => app()->getRootPath() . 'public/uploads',
+ 'url' => '/uploads/',
+ 'visibility' => 'public',
+ ],
+
+ 'approve' => [
+ 'type' => 'local',
+ 'root' => app()->getRootPath() . 'storage/approve',
+ // 'url' => '/uploads/',
+ 'visibility' => 'private',
+ ],
+ // 'public' => [
+ // // 磁盘类型
+ // 'type' => 'local',
+ // // 磁盘路径
+ // 'root' => app()->getRootPath() . 'public/storage',
+ // // 磁盘路径对应的外部URL路径
+ // 'url' => '/storage/',
+ // // 可见性
+ // 'visibility' => 'public',
+ // ],
+ // 更多的磁盘配置信息
+ ],
+];
diff --git a/config/lang.php b/config/lang.php
new file mode 100644
index 0000000..59f320f
--- /dev/null
+++ b/config/lang.php
@@ -0,0 +1,27 @@
+ env('lang.default_lang', 'zh-cn'),
+ // 允许的语言列表
+ 'allow_lang_list' => [],
+ // 多语言自动侦测变量名
+ 'detect_var' => 'lang',
+ // 是否使用Cookie记录
+ 'use_cookie' => true,
+ // 多语言cookie变量
+ 'cookie_var' => 'think_lang',
+ // 多语言header变量
+ 'header_var' => 'think-lang',
+ // 扩展语言包
+ 'extend_list' => [],
+ // Accept-Language转义为对应语言包名称
+ 'accept_language' => [
+ 'zh-hans-cn' => 'zh-cn',
+ ],
+ // 是否支持语言分组
+ 'allow_group' => false,
+];
diff --git a/config/log.php b/config/log.php
new file mode 100644
index 0000000..37fb65d
--- /dev/null
+++ b/config/log.php
@@ -0,0 +1,124 @@
+ 'File',
+ // 日志保存目录
+ 'path' => app()->getRuntimePath() . 'push_log' . DIRECTORY_SEPARATOR . $vendor,
+ // 单文件日志写入
+ 'single' => false,
+ // 独立日志级别
+ 'apart_level' => [],
+ // 最大日志文件数量
+ 'max_files' => 0,
+ // 使用JSON格式记录
+ 'json' => true,
+ // 日志处理
+ 'processor' => null,
+ // 关闭通道日志写入
+ 'close' => false,
+ // 日志输出格式化
+ 'format' => '[%s][%s] %s',
+ // 是否实时写入
+ 'realtime_write' => false,
+ ];
+};
+$generateWechat = function (string $type) {
+ return [
+ // 日志记录方式
+ 'type' => 'File',
+ // 日志保存目录
+ 'path' => app()->getRuntimePath() . 'wechat' . DIRECTORY_SEPARATOR . $type,
+ // 单文件日志写入
+ 'single' => false,
+ // 独立日志级别
+ 'apart_level' => [],
+ // 最大日志文件数量
+ 'max_files' => 0,
+ // 使用JSON格式记录
+ 'json' => true,
+ // 日志处理
+ 'processor' => null,
+ // 关闭通道日志写入
+ 'close' => false,
+ // 日志输出格式化
+ 'format' => '[%s][%s] %s',
+ // 是否实时写入
+ 'realtime_write' => false,
+ ];
+};
+return [
+ // 默认日志记录通道
+ 'default' => env('log.channel', 'file'),
+ // 日志记录级别
+ 'level' => [],
+ // 日志类型记录的通道 ['error'=>'email',...]
+ 'type_channel' => [],
+ // 关闭全局日志写入
+ 'close' => false,
+ // 全局日志处理 支持闭包
+ 'processor' => null,
+
+ // 日志通道列表
+ 'channels' => [
+ // 飞比云
+ 'pushLog/FBeeCloud' => $generatePushLog('FBeeCloud'),
+ // 睡小宝
+ 'pushLog/Sleepthing' => $generatePushLog('Sleepthing'),
+ // 微信公众号
+ 'wechat/gzh' => $generateWechat('gzh'),
+ // 微信小程序
+ 'wechat/xcx' => $generateWechat('xcx'),
+ // 命令行
+ 'cmd' => [
+ // 日志记录方式
+ 'type' => 'File',
+ // 日志保存目录
+ 'path' => app()->getRuntimePath() . 'cmd',
+ // 单文件日志写入
+ 'single' => false,
+ // 独立日志级别
+ 'apart_level' => [],
+ // 最大日志文件数量
+ 'max_files' => 0,
+ // 使用JSON格式记录
+ 'json' => false,
+ // 日志处理
+ 'processor' => null,
+ // 关闭通道日志写入
+ 'close' => false,
+ // 日志输出格式化
+ 'format' => '[%s][%s] %s',
+ // 是否实时写入
+ 'realtime_write' => false,
+ ],
+ 'file' => [
+ // 日志记录方式
+ 'type' => 'File',
+ // 日志保存目录
+ 'path' => '',
+ // 单文件日志写入
+ 'single' => false,
+ // 独立日志级别
+ 'apart_level' => [],
+ // 最大日志文件数量
+ 'max_files' => 0,
+ // 使用JSON格式记录
+ 'json' => false,
+ // 日志处理
+ 'processor' => null,
+ // 关闭通道日志写入
+ 'close' => false,
+ // 日志输出格式化
+ 'format' => '[%s][%s] %s',
+ // 是否实时写入
+ 'realtime_write' => false,
+ ],
+ // 其它日志通道配置
+ ],
+
+];
diff --git a/config/middleware.php b/config/middleware.php
new file mode 100644
index 0000000..7e1972f
--- /dev/null
+++ b/config/middleware.php
@@ -0,0 +1,8 @@
+ [],
+ // 优先级设置,此数组中的中间件会按照数组中的顺序优先执行
+ 'priority' => [],
+];
diff --git a/config/route.php b/config/route.php
new file mode 100644
index 0000000..aac1531
--- /dev/null
+++ b/config/route.php
@@ -0,0 +1,50 @@
+ '/',
+ // URL伪静态后缀
+ 'url_html_suffix' => 'html',
+ // URL普通方式参数 用于自动生成
+ 'url_common_param' => true,
+ // 是否开启路由延迟解析
+ 'url_lazy_route' => false,
+ // 是否强制使用路由
+ 'url_route_must' => false,
+ // 合并路由规则
+ 'route_rule_merge' => false,
+ // 路由是否完全匹配
+ 'route_complete_match' => false,
+ // 访问控制器层名称
+ 'controller_layer' => 'controller',
+ // 空控制器名
+ 'empty_controller' => 'Error',
+ // 是否使用控制器后缀
+ 'controller_suffix' => false,
+ // 默认的路由变量规则
+ 'default_route_pattern' => '[\w\.]+',
+ // 是否开启请求缓存 true自动缓存 支持设置请求缓存规则
+ 'request_cache_key' => false,
+ // 请求缓存有效期
+ 'request_cache_expire' => null,
+ // 全局请求缓存排除规则
+ 'request_cache_except' => [],
+ // 默认控制器名
+ 'default_controller' => 'Index',
+ // 默认操作名
+ 'default_action' => 'index',
+ // 操作方法后缀
+ 'action_suffix' => '',
+ // 默认JSONP格式返回的处理方法
+ 'default_jsonp_handler' => 'jsonpReturn',
+ // 默认JSONP处理方法
+ 'var_jsonp_handler' => 'callback',
+ // 中间件
+ 'middleware' => [
+ // 接口鉴权
+ // \app\middleware\Auth::class
+ ],
+];
diff --git a/config/session.php b/config/session.php
new file mode 100644
index 0000000..c1ef6e1
--- /dev/null
+++ b/config/session.php
@@ -0,0 +1,19 @@
+ 'PHPSESSID',
+ // SESSION_ID的提交变量,解决flash上传跨域
+ 'var_session_id' => '',
+ // 驱动方式 支持file cache
+ 'type' => 'file',
+ // 存储连接标识 当type使用cache的时候有效
+ 'store' => null,
+ // 过期时间
+ 'expire' => 1440,
+ // 前缀
+ 'prefix' => '',
+];
diff --git a/config/wechat.php b/config/wechat.php
new file mode 100644
index 0000000..7c798be
--- /dev/null
+++ b/config/wechat.php
@@ -0,0 +1,39 @@
+ '',
+ // 中控层地址
+ 'server_base' => 'http://clique.dszjjt.com:7001/',
+ // 公众号配置
+ 'gzh' => [
+ // 缓存key
+ 'cache_key' => 'wechat.gzh',
+ // 日志通道
+ 'log_channel' => 'wechat/gzh',
+ // 公众号模板推送
+ 'template' => [
+ // {{first.DATA}}
+ // 申请人:{{keyword1.DATA}}
+ // 申请时间:{{keyword2.DATA}}
+ // 调课时间:{{keyword3.DATA}}
+ // {{remark.DATA}}
+ '申请审核通知' => 'iLuqzUrQM8-v_7ilxageUZoi9v-SQWipkTYVZXB1GeI'
+ ],
+ 'appid' => 'wx99caf571c9fb1cc5',
+ 'secret' => '61100d9c4fff5acf97517f0c8799e2e2',
+ ],
+ // 小程序配置
+ 'xcx' => [
+ // 缓存key
+ 'cache_key' => 'wechat.xcx',
+ // 日志通道
+ 'log_channel' => 'wechat/xcx',
+
+ 'appid' => 'wx2495492e15854b02',
+ 'secret' => '5b4fcb27324398083fba4a3c39e8a557',
+ ],
+];
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..50934e0
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,6 @@
+{
+ "name": "api",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {}
+}
diff --git a/public/.htaccess b/public/.htaccess
new file mode 100644
index 0000000..cbc7868
--- /dev/null
+++ b/public/.htaccess
@@ -0,0 +1,8 @@
+
+ Options +FollowSymlinks -Multiviews
+ RewriteEngine On
+
+ RewriteCond %{REQUEST_FILENAME} !-d
+ RewriteCond %{REQUEST_FILENAME} !-f
+ RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L]
+
diff --git a/public/admin.php b/public/admin.php
new file mode 100644
index 0000000..0719a59
--- /dev/null
+++ b/public/admin.php
@@ -0,0 +1,36 @@
+
+// +----------------------------------------------------------------------
+
+// [ 应用入口文件 ]
+namespace think;
+
+require __DIR__ . '/../vendor/autoload.php';
+
+try {
+ // 执行HTTP应用并响应
+ $http = (new App())->http;
+
+ $response = $http->run();
+
+ $response->send();
+} catch (\InvalidArgumentException $th) {
+ // 方便浏览器调试
+ if (isset($response)) {
+ $data = $response->getData();
+ if (is_array($data)) {
+ echo json_encode($data);
+ }
+ } else {
+ throw $th;
+ }
+} finally {
+ $http->end($response);
+}
diff --git a/public/api.php b/public/api.php
new file mode 100644
index 0000000..0719a59
--- /dev/null
+++ b/public/api.php
@@ -0,0 +1,36 @@
+
+// +----------------------------------------------------------------------
+
+// [ 应用入口文件 ]
+namespace think;
+
+require __DIR__ . '/../vendor/autoload.php';
+
+try {
+ // 执行HTTP应用并响应
+ $http = (new App())->http;
+
+ $response = $http->run();
+
+ $response->send();
+} catch (\InvalidArgumentException $th) {
+ // 方便浏览器调试
+ if (isset($response)) {
+ $data = $response->getData();
+ if (is_array($data)) {
+ echo json_encode($data);
+ }
+ } else {
+ throw $th;
+ }
+} finally {
+ $http->end($response);
+}
diff --git a/public/excel/产品.xlsx b/public/excel/产品.xlsx
new file mode 100644
index 0000000..28d5a65
Binary files /dev/null and b/public/excel/产品.xlsx differ
diff --git a/public/excel/产品导入模板 (2).xlsx b/public/excel/产品导入模板 (2).xlsx
new file mode 100644
index 0000000..7858fcf
Binary files /dev/null and b/public/excel/产品导入模板 (2).xlsx differ
diff --git a/public/excel/产品类型导入模板 (3).xlsx b/public/excel/产品类型导入模板 (3).xlsx
new file mode 100644
index 0000000..6fec9ed
Binary files /dev/null and b/public/excel/产品类型导入模板 (3).xlsx differ
diff --git a/public/excel/产品类型导入模板.xlsx b/public/excel/产品类型导入模板.xlsx
new file mode 100644
index 0000000..c3117c3
Binary files /dev/null and b/public/excel/产品类型导入模板.xlsx differ
diff --git a/public/excel/关键词导入模板 (2).xlsx b/public/excel/关键词导入模板 (2).xlsx
new file mode 100644
index 0000000..0eca5b8
Binary files /dev/null and b/public/excel/关键词导入模板 (2).xlsx differ
diff --git a/public/excel/关键词导入模板.xlsx b/public/excel/关键词导入模板.xlsx
new file mode 100644
index 0000000..403715d
Binary files /dev/null and b/public/excel/关键词导入模板.xlsx differ
diff --git a/public/excel/字典导入模板.xlsx b/public/excel/字典导入模板.xlsx
new file mode 100644
index 0000000..fcdc213
Binary files /dev/null and b/public/excel/字典导入模板.xlsx differ
diff --git a/public/excel/学生导入模板 (3).xlsx b/public/excel/学生导入模板 (3).xlsx
new file mode 100644
index 0000000..f50ebbc
Binary files /dev/null and b/public/excel/学生导入模板 (3).xlsx differ
diff --git a/public/excel/学生导入模板 (6).xlsx b/public/excel/学生导入模板 (6).xlsx
new file mode 100644
index 0000000..bd0c11f
Binary files /dev/null and b/public/excel/学生导入模板 (6).xlsx differ
diff --git a/public/excel/用户导入模板 (1).xlsx b/public/excel/用户导入模板 (1).xlsx
new file mode 100644
index 0000000..e65a20e
Binary files /dev/null and b/public/excel/用户导入模板 (1).xlsx differ
diff --git a/public/excel/用户导入模板.xlsx b/public/excel/用户导入模板.xlsx
new file mode 100644
index 0000000..4b453f9
Binary files /dev/null and b/public/excel/用户导入模板.xlsx differ
diff --git a/public/favicon.ico b/public/favicon.ico
new file mode 100644
index 0000000..e71815a
Binary files /dev/null and b/public/favicon.ico differ
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/public/router.php b/public/router.php
new file mode 100644
index 0000000..9b39a62
--- /dev/null
+++ b/public/router.php
@@ -0,0 +1,19 @@
+
+// +----------------------------------------------------------------------
+// $Id$
+
+if (is_file($_SERVER["DOCUMENT_ROOT"] . $_SERVER["SCRIPT_NAME"])) {
+ return false;
+} else {
+ $_SERVER["SCRIPT_FILENAME"] = __DIR__ . '/index.php';
+
+ require __DIR__ . "/index.php";
+}
diff --git a/public/static/.gitignore b/public/static/.gitignore
new file mode 100644
index 0000000..e69de29
diff --git a/public/static/css/Contact.css b/public/static/css/Contact.css
new file mode 100644
index 0000000..b2c21c9
--- /dev/null
+++ b/public/static/css/Contact.css
@@ -0,0 +1,346 @@
+a {
+ text-decoration: none;
+ color: #000;
+}
+
+.contact a:hover {
+ text-decoration: none;
+ color: #000;
+}
+
+.contact_visible a:hover {
+ text-decoration: none;
+ color: #000;
+}
+
+p {
+ margin-top: 20px;
+}
+
+#container {
+ width: 100%;
+ height: 600px;
+ margin: auto;
+ overflow: hidden;
+}
+
+#box {
+ width: 74%;
+ height: 800px;
+ background-color: white;
+ display: flex;
+ margin: 100px auto;
+ box-shadow: darkgrey 10px 10px 30px 5px;
+ position: relative;
+ top: -150px;
+}
+
+#box_contact {
+ width: 50%;
+ height: 100%;
+ background-color: white;
+}
+
+.contact {
+ width: 80%;
+ height: 90%;
+ margin: 0 auto;
+ font-family: "Microsoft YaHei", 微软雅黑;
+}
+
+.size1 {
+ font-size: 2em;
+ font-weight: 600;
+
+}
+
+.size2 {
+ font-size: 2em;
+ font-weight: 600;
+}
+
+.box_QRcode {
+ width: 100%;
+ height: 18%;
+ display: flex;
+ justify-content: space-around;
+ margin-top: 30px;
+}
+
+.QRcode {
+ width: 125px;
+ height: 125px;
+ text-align: center;
+}
+
+.QRcode img {
+ width: 100%;
+ height: 100%;
+}
+
+.QRcode span {
+ font-size: 13px;
+ color: #595959;
+}
+
+#box_liuyan {
+ width: 50%;
+ height: 100%;
+ background-color: rgb(0, 167, 134);
+}
+
+.liuyan {
+ width: 80%;
+ height: 90%;
+ margin: 0 auto;
+}
+
+.size3 {
+ font-size: 2em;
+ font-weight: 600;
+ color: white;
+}
+
+textarea {
+ background-color: rgb(0, 167, 134);
+ border-style: none;
+ outline: none;
+ color: white;
+}
+
+.textarea_border textarea {
+ border-bottom: 1px solid rgb(192, 192, 192);
+ height: 80px;
+}
+
+#box_liuyan textarea {
+ min-width: 100%;
+}
+
+#box_liuyan .textarea_border :hover {
+ border-bottom: 1px solid rgb(5, 224, 253);
+}
+
+#box_liuyan .input_border input {
+ width: 100%;
+ height: 60px;
+}
+
+#box_liuyan input {
+ background-color: rgb(0, 167, 134);
+ border-style: none;
+ outline: none;
+ color: white;
+}
+
+#box_liuyan .input_verification {
+ width: 60%;
+ height: 40px;
+ margin-top: 50px;
+ float: left;
+}
+
+#box_liuyan .input_verification input {
+ width: 100%;
+ height: 100%;
+ border-bottom: 1px solid rgb(192, 192, 192);
+}
+
+#box_liuyan .input_border {
+ border-bottom: 1px solid rgb(192, 192, 192);
+}
+
+#box_liuyan .input_border :hover {
+ border-bottom: 1px solid rgb(5, 224, 253);
+}
+
+#box_liuyan .input_verification :hover {
+ border-bottom: 1px solid rgb(5, 224, 253);
+}
+
+input::-webkit-input-placeholder {
+ color: white;
+}
+
+textarea::-webkit-input-placeholder {
+ color: white;
+}
+
+.verification {
+ float: left;
+ margin-top: 51px;
+ width: 40%;
+}
+
+.verification img {
+ font-size: 18pt;
+ cursor: pointer;
+ width: 120px;
+ margin-top: 10px;
+}
+
+.submit button {
+ width: 100px;
+ height: 35px;
+}
+
+#box_liuyan .submit button {
+ background-color: white;
+ border: 1px solid black;
+ color: #000;
+}
+
+#box_liuyan .submit button:hover {
+ border: 1px solid white;
+ transition: 0.5s;
+ background-color: rgb(0, 167, 134);
+ color: white;
+}
+
+#box_visible {
+ width: 90%;
+ height: auto;
+ background-color: white;
+ margin: 0 auto;
+ box-shadow: darkgrey 10px 10px 30px 5px;
+ position: relative;
+ top: -100px;
+}
+
+#box_contact_visible {
+ width: 100%;
+ height: 100%;
+ background-color: white;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.contact_visible {
+ width: 90%;
+ height: 95%;
+}
+
+#box_liuyan_visible {
+ width: 100%;
+ height: 100%;
+ background-color: rgb(0, 167, 134);
+ margin-top: 60px;
+}
+
+#box_liuyan_visible {
+ width: 100%;
+ height: 650px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+textarea {
+ background-color: rgb(0, 167, 134);
+ border-style: none;
+ outline: none;
+ color: white;
+}
+
+.textarea_border textarea {
+ border-bottom: 1px solid rgb(192, 192, 192);
+ height: 80px;
+}
+
+#box_liuyan_visible textarea {
+ min-width: 100%;
+}
+
+#box_liuyan_visible .textarea_border :hover {
+ border-bottom: 1px solid rgb(5, 224, 253);
+}
+
+#box_liuyan_visible .input_border input {
+ width: 100%;
+ height: 60px;
+}
+
+#box_liuyan_visible input {
+ background-color: rgb(0, 167, 134);
+ border-style: none;
+ outline: none;
+ color: white;
+}
+
+#box_liuyan_visible .input_verification {
+ width: 55%;
+ height: 40px;
+ margin-top: 50px;
+ float: left;
+}
+
+#box_liuyan_visible .input_verification input {
+ width: 100%;
+ height: 100%;
+ border-bottom: 1px solid rgb(192, 192, 192);
+}
+
+#box_liuyan_visible .input_border {
+ border-bottom: 1px solid rgb(192, 192, 192);
+}
+
+#box_liuyan_visible .input_border :hover {
+ border-bottom: 1px solid rgb(5, 224, 253);
+}
+
+#box_liuyan_visible .input_verification :hover {
+ border-bottom: 1px solid rgb(5, 224, 253);
+}
+
+input::-webkit-input-placeholder {
+ color: white;
+}
+
+textarea::-webkit-input-placeholder {
+ color: white;
+}
+
+.box_liuyan_visible {
+ float: left;
+ margin-top: 50px;
+ color: rgb(0, 167, 134);
+ background-color: #ffffff;
+ font-size: 18pt;
+ padding: 5px 15px 5px 15px;
+ cursor: pointer;
+}
+
+.submit button {
+ width: 100px;
+ height: 35px;
+}
+
+#box_liuyan_visible button {
+ background-color: white;
+ border: 1px solid black;
+ color: #000;
+ margin-top: 10px;
+}
+
+#box_liuyan_visible .submit button :hover {
+ border: 1px solid white;
+ transition: 0.5s;
+ background-color: rgb(0, 167, 134);
+ color: white;
+}
+
+#box_liuyan_visible .verification {
+ float: left;
+ margin-top: 51px;
+ font-size: 18pt;
+ cursor: pointer;
+}
+
+.amap-logo {
+ z-index: 100 !important;
+}
+
+.amap-copyright {
+ z-index: 100 !important;
+}
\ No newline at end of file
diff --git a/public/static/css/JoinFlow.css b/public/static/css/JoinFlow.css
new file mode 100644
index 0000000..ec95e02
--- /dev/null
+++ b/public/static/css/JoinFlow.css
@@ -0,0 +1,338 @@
+招商流程 */
+
+/* join 通用css样式 */
+body {
+ margin: 0;
+ padding: 0;
+}
+
+.topPic {
+ width: 100%;
+}
+
+.barTop {
+ display: flex;
+ width: 90%;
+ height: auto;
+ background-color: #00a587;
+ padding-left: 5%;
+ padding-top: 20px;
+ position: relative;
+ top: -75px;
+ margin-left: 10%;
+
+}
+
+.barGs:hover {
+ /* border-bottom: #fff 3px solid; */
+ padding-bottom: 15px;
+ /* border-right: none; */
+}
+
+a {
+ text-decoration: none
+}
+
+.barTop2 {
+ position: absolute;
+ left: -10px;
+ width: 100%;
+
+}
+
+.barName {
+ width: 30%;
+ height: 100%;
+ line-height: 2;
+}
+
+.barHeadline {
+ font-size: 40px;
+ font-weight: bold;
+ color: #fff;
+ line-height: 1.6;
+}
+
+.barHeadline2 {
+ font-size: 30px;
+ color: #fff;
+ padding-bottom: 20px;
+}
+
+.barSize {
+ width: 220px;
+ height: 30px;
+ position: absolute;
+ display: flex;
+ right: 0;
+ padding-top: 30px;
+}
+
+.barSize img {
+ width: 20px;
+ height: 20px;
+}
+
+.barMap {
+ font-size: 14px;
+ color: #fff;
+}
+
+.bar {
+ margin-top: 50px;
+ display: flex;
+ /* width: 600px; */
+ height: 30px;
+ position: absolute;
+ right: 0;
+ top: 30px;
+}
+
+.barGs {
+ line-height: 40px;
+ /* display: flex;
+ justify-content: center;
+ text-align: center; */
+ margin: 0 20px;
+ height: 40px;
+ color: #fff;
+ font-size: 15px;
+ /* border-right: #e2e2e2 2px solid; */
+}
+
+.body {
+ margin: 0px auto;
+ width: 100%;
+ padding-left: 20px;
+ max-width: 1500px;
+}
+
+.bodyTop {
+ width: 100%;
+ padding: 30px 0;
+ font-size: 40px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border-bottom: 1px solid #ccc;
+}
+
+.bodyText {
+ margin-left: 3%;
+ width: 96%;
+ height: 800px;
+ margin-top: 30px;
+}
+
+.bodyTextTop {
+ color: #00a587;
+ font-weight: bold;
+ font-size: 26px;
+ padding-top: 20px;
+}
+
+.thread {
+ margin: 30px 0;
+ width: 50px;
+ height: 2px;
+ background-color: #00a587;
+}
+
+.text-Mini {
+ width: 100%;
+ margin: 40px 0;
+}
+
+.text {
+ width: 100%;
+ margin: 40px 0;
+ display: flex;
+}
+
+.text1 {
+ display: flex;
+ flex-direction: column;
+ width: 70%;
+}
+
+.text-1 {
+ line-height: 3;
+ font-size: 16px;
+ color: #333;
+ display: flex;
+ flex-wrap: wrap;
+ font-weight: 500;
+ margin-bottom: 20px;
+ margin-left: 20px;
+
+
+}
+
+.condiTions {
+ width: 100%;
+}
+
+.cond {
+ list-style-type: decimal;
+ font-size: 14px;
+
+}
+
+.tiao {
+ color: #00a587;
+ font-weight: 600;
+ font-size: 14px;
+ padding-left: 20px;
+}
+
+.you {
+ display: flex;
+ margin-right: 5%;
+ width: 100%;
+
+}
+
+.you1 {
+ width: 60%;
+}
+
+.text-2 {
+ color: #00a587;
+ font-weight: 600;
+ font-size: 16px;
+ margin-left: 20px;
+}
+
+.pic1 {
+ max-width: 300px;
+ max-height: 500px;
+ width: 300px;
+ height: 500px;
+}
+
+.pic2 {
+ width: 500px;
+ height: 300px;
+ margin: 0 20px;
+}
+
+.bodyPic1 {
+ width: 80%;
+ margin: 0 10%;
+ height: auto;
+ margin-left: 20px;
+}
+
+.bodyPic1-max {
+ width: 45%;
+ height: 650px;
+ margin-top: -150px;
+}
+
+.bodyPic2-max {
+ width: 40%;
+ height: 400px;
+}
+
+.barTop-xiao {
+ width: 100%;
+ background-color: #00a587;
+ padding: 20px 30px;
+}
+
+.barName-Mini {
+ color: #fff;
+ font-size: 700;
+ font-size: 30px;
+ border-bottom: #ccc 1px solid;
+ padding-left: 30px;
+}
+
+.bar-Mini {
+ display: flex;
+ margin: 20px 0px;
+}
+
+.barGs-Mini {
+ line-height: 20px;
+ display: flex;
+ justify-content: center;
+ text-align: center;
+ width: 90px;
+ height: 20px;
+ color: #fff;
+ border-right: #e2e2e2 2px solid;
+}
+
+.bodyTop-Mini {
+ margin-top: 20px;
+ padding: 30px 0px;
+ font-size: 30px;
+ height: 50px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.body-Mini {
+ width: 90%;
+ margin: 0 5%;
+
+}
+
+.bodyTextTop-Mini {
+ color: #00a587;
+ font-weight: bold;
+ font-size: 24px;
+}
+
+.thread {
+ margin-top: 30px;
+ width: 50px;
+ height: 2px;
+ background-color: #00a587;
+}
+
+.pic-Mini {
+ width: 100%;
+ text-align: center;
+ object-fit: cover;
+
+}
+
+.pic-MiniBox {
+ width: 90%;
+ height: auto;
+ /* padding: 20px; */
+ margin: 30px 5%;
+ background-size: contain;
+ text-align: center;
+}
+
+.text-1-Mini {
+ font-size: 14px;
+ color: #333;
+ display: flex;
+ flex-wrap: wrap;
+ font-weight: 400;
+ margin-bottom: 20px;
+}
+
+.tiao-Mini {
+ color: #00a587;
+ font-weight: 600;
+ font-size: 14px;
+}
+
+.borderBottom {
+ width: 0;
+ transition: all 0.3s;
+ height: 3px;
+ background-color: #fff;
+ margin: auto;
+}
+
+.barGs:hover .borderBottom {
+ width: 60px;
+ margin: auto;
+}
\ No newline at end of file
diff --git a/public/static/css/animation.css b/public/static/css/animation.css
new file mode 100644
index 0000000..1dc88b0
--- /dev/null
+++ b/public/static/css/animation.css
@@ -0,0 +1,169 @@
+/*动画库*/
+/*滑入*/
+.slide-in-bck-bottom {
+ -webkit-animation: slide-in-bck-bottom 0.6s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
+ animation: slide-in-bck-bottom 0.6s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
+}
+@-webkit-keyframes slide-in-bck-bottom {
+ 0% {
+ -webkit-transform: translateZ(700px) translateY(300px);
+ transform: translateZ(700px) translateY(300px);
+ opacity: 0;
+ }
+ 100% {
+ -webkit-transform: translateZ(0) translateY(0);
+ transform: translateZ(0) translateY(0);
+ opacity: 1;
+ }
+}
+@keyframes slide-in-bck-bottom {
+ 0% {
+ -webkit-transform: translateZ(700px) translateY(300px);
+ transform: translateZ(700px) translateY(300px);
+ opacity: 0;
+ }
+ 100% {
+ -webkit-transform: translateZ(0) translateY(0);
+ transform: translateZ(0) translateY(0);
+ opacity: 1;
+ }
+}
+/*滑出*/
+.slide-out-bck-bottom {
+ -webkit-animation: slide-out-bck-bottom 0.5s cubic-bezier(0.550, 0.085, 0.680, 0.530) both;
+ animation: slide-out-bck-bottom 0.5s cubic-bezier(0.550, 0.085, 0.680, 0.530) both;
+}
+@-webkit-keyframes slide-out-bck-bottom {
+ 0% {
+ -webkit-transform: translateZ(0) translateY(0);
+ transform: translateZ(0) translateY(0);
+ opacity: 1;
+ }
+ 100% {
+ -webkit-transform: translateZ(-1100px) translateY(1000px);
+ transform: translateZ(-1100px) translateY(1000px);
+ opacity: 0;
+ }
+}
+@keyframes slide-out-bck-bottom {
+ 0% {
+ -webkit-transform: translateZ(0) translateY(0);
+ transform: translateZ(0) translateY(0);
+ opacity: 1;
+ }
+ 100% {
+ -webkit-transform: translateZ(-1100px) translateY(1000px);
+ transform: translateZ(-1100px) translateY(1000px);
+ opacity: 0;
+ }
+}
+/*弹入*/
+.bounce-in-right {
+ -webkit-animation: bounce-in-right 1.1s both;
+ animation: bounce-in-right 1.1s both;
+}
+
+@-webkit-keyframes bounce-in-right {
+ 0% {
+ -webkit-transform: translateX(600px);
+ transform: translateX(600px);
+ -webkit-animation-timing-function: ease-in;
+ animation-timing-function: ease-in;
+ opacity: 0;
+ }
+ 38% {
+ -webkit-transform: translateX(0);
+ transform: translateX(0);
+ -webkit-animation-timing-function: ease-out;
+ animation-timing-function: ease-out;
+ opacity: 1;
+ }
+ 55% {
+ -webkit-transform: translateX(68px);
+ transform: translateX(68px);
+ -webkit-animation-timing-function: ease-in;
+ animation-timing-function: ease-in;
+ }
+ 72% {
+ -webkit-transform: translateX(0);
+ transform: translateX(0);
+ -webkit-animation-timing-function: ease-out;
+ animation-timing-function: ease-out;
+ }
+ 81% {
+ -webkit-transform: translateX(32px);
+ transform: translateX(32px);
+ -webkit-animation-timing-function: ease-in;
+ animation-timing-function: ease-in;
+ }
+ 90% {
+ -webkit-transform: translateX(0);
+ transform: translateX(0);
+ -webkit-animation-timing-function: ease-out;
+ animation-timing-function: ease-out;
+ }
+ 95% {
+ -webkit-transform: translateX(8px);
+ transform: translateX(8px);
+ -webkit-animation-timing-function: ease-in;
+ animation-timing-function: ease-in;
+ }
+ 100% {
+ -webkit-transform: translateX(0);
+ transform: translateX(0);
+ -webkit-animation-timing-function: ease-out;
+ animation-timing-function: ease-out;
+ }
+}
+@keyframes bounce-in-right {
+ 0% {
+ -webkit-transform: translateX(600px);
+ transform: translateX(600px);
+ -webkit-animation-timing-function: ease-in;
+ animation-timing-function: ease-in;
+ opacity: 0;
+ }
+ 38% {
+ -webkit-transform: translateX(0);
+ transform: translateX(0);
+ -webkit-animation-timing-function: ease-out;
+ animation-timing-function: ease-out;
+ opacity: 1;
+ }
+ 55% {
+ -webkit-transform: translateX(68px);
+ transform: translateX(68px);
+ -webkit-animation-timing-function: ease-in;
+ animation-timing-function: ease-in;
+ }
+ 72% {
+ -webkit-transform: translateX(0);
+ transform: translateX(0);
+ -webkit-animation-timing-function: ease-out;
+ animation-timing-function: ease-out;
+ }
+ 81% {
+ -webkit-transform: translateX(32px);
+ transform: translateX(32px);
+ -webkit-animation-timing-function: ease-in;
+ animation-timing-function: ease-in;
+ }
+ 90% {
+ -webkit-transform: translateX(0);
+ transform: translateX(0);
+ -webkit-animation-timing-function: ease-out;
+ animation-timing-function: ease-out;
+ }
+ 95% {
+ -webkit-transform: translateX(8px);
+ transform: translateX(8px);
+ -webkit-animation-timing-function: ease-in;
+ animation-timing-function: ease-in;
+ }
+ 100% {
+ -webkit-transform: translateX(0);
+ transform: translateX(0);
+ -webkit-animation-timing-function: ease-out;
+ animation-timing-function: ease-out;
+ }
+}
diff --git a/public/static/css/bootstrap.css b/public/static/css/bootstrap.css
new file mode 100644
index 0000000..b0339a1
--- /dev/null
+++ b/public/static/css/bootstrap.css
@@ -0,0 +1,11422 @@
+/*!
+ * Bootstrap v4.4.1 (https://getbootstrap.com/)
+ * Copyright 2011-2019 The Bootstrap Authors
+ * Copyright 2011-2019 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */
+
+:root {
+ --blue: #007bff;
+ --indigo: #6610f2;
+ --purple: #6f42c1;
+ --pink: #e83e8c;
+ --red: #dc3545;
+ --orange: #fd7e14;
+ --yellow: #ffc107;
+ --green: #28a745;
+ --teal: #20c997;
+ --cyan: #17a2b8;
+ --white: #fff;
+ --gray: #6c757d;
+ --gray-dark: #343a40;
+ --primary: #007bff;
+ --secondary: #6c757d;
+ --success: #28a745;
+ --info: #17a2b8;
+ --warning: #ffc107;
+ --danger: #dc3545;
+ --light: #f8f9fa;
+ --dark: #343a40;
+ --breakpoint-xs: 0;
+ --breakpoint-sm: 576px;
+ --breakpoint-md: 768px;
+ --breakpoint-lg: 992px;
+ --breakpoint-xl: 1200px;
+ --font-family-sans-serif: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
+ --font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
+}
+
+*,
+*::before,
+*::after {
+ box-sizing: border-box;
+ font-weight: 700;
+}
+
+html {
+ font-family: sans-serif;
+ line-height: 1.15;
+ -webkit-text-size-adjust: 100%;
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+}
+
+article,
+aside,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+main,
+nav,
+section {
+ display: block;
+}
+
+body {
+ margin: 0;
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
+ /*font-size: 1rem;*/
+ font-weight: 400;
+ line-height: 1.5;
+ color: #212529;
+ text-align: left;
+ background-color: rgb(250, 250, 250);
+}
+
+[tabindex="-1"]:focus:not(:focus-visible) {
+ outline: 0 !important;
+}
+
+hr {
+ box-sizing: content-box;
+ height: 0;
+ overflow: visible;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ margin-top: 0;
+ margin-bottom: 0.5rem;
+}
+
+p {
+ margin-top: 0;
+ margin-bottom: 1rem;
+}
+
+abbr[title],
+abbr[data-original-title] {
+ text-decoration: underline;
+ -webkit-text-decoration: underline dotted;
+ text-decoration: underline dotted;
+ cursor: help;
+ border-bottom: 0;
+ -webkit-text-decoration-skip-ink: none;
+ text-decoration-skip-ink: none;
+}
+
+address {
+ margin-bottom: 1rem;
+ font-style: normal;
+ line-height: inherit;
+}
+
+ol,
+ul,
+dl {
+ margin-top: 0;
+ margin-bottom: 1rem;
+}
+
+ol ol,
+ul ul,
+ol ul,
+ul ol {
+ margin-bottom: 0;
+}
+
+dt {
+ font-weight: 700;
+}
+
+dd {
+ margin-bottom: .5rem;
+ margin-left: 0;
+}
+
+blockquote {
+ margin: 0 0 1rem;
+}
+
+b,
+strong {
+ font-weight: bolder;
+}
+
+small {
+ font-size: 80%;
+}
+
+sub,
+sup {
+ position: relative;
+ font-size: 75%;
+ line-height: 0;
+ vertical-align: baseline;
+}
+
+sub {
+ bottom: -.25em;
+}
+
+sup {
+ top: -.5em;
+}
+
+a {
+ /*color: #007bff;*/
+ text-decoration: none;
+ background-color: transparent;
+}
+
+a:hover {
+ color: #0056b3;
+ text-decoration: underline;
+}
+
+a:not([href]) {
+ color: inherit;
+ text-decoration: none;
+}
+
+a:not([href]):hover {
+ color: inherit;
+ text-decoration: none;
+}
+
+pre,
+code,
+kbd,
+samp {
+ font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
+ font-size: 1em;
+}
+
+pre {
+ margin-top: 0;
+ margin-bottom: 1rem;
+ overflow: auto;
+}
+
+figure {
+ margin: 0 0 1rem;
+}
+
+img {
+ vertical-align: middle;
+ border-style: none;
+ /* height: 100%;
+ width: 100%;
+ object-fit: contain; */
+}
+
+svg {
+ overflow: hidden;
+ vertical-align: middle;
+}
+
+table {
+ border-collapse: collapse;
+}
+
+caption {
+ padding-top: 0.75rem;
+ padding-bottom: 0.75rem;
+ color: #6c757d;
+ text-align: left;
+ caption-side: bottom;
+}
+
+th {
+ text-align: inherit;
+}
+
+label {
+ display: inline-block;
+ margin-bottom: 0.5rem;
+}
+
+button {
+ border-radius: 0;
+}
+
+button:focus {
+ outline: 1px dotted;
+ outline: 5px auto -webkit-focus-ring-color;
+}
+
+input,
+button,
+select,
+optgroup,
+textarea {
+ margin: 0;
+ font-family: inherit;
+ font-size: inherit;
+ line-height: inherit;
+}
+
+button,
+input {
+ overflow: visible;
+}
+
+button,
+select {
+ text-transform: none;
+}
+
+select {
+ word-wrap: normal;
+}
+
+button,
+[type="button"],
+[type="reset"],
+[type="submit"] {
+ -webkit-appearance: button;
+}
+
+button:not(:disabled),
+[type="button"]:not(:disabled),
+[type="reset"]:not(:disabled),
+[type="submit"]:not(:disabled) {
+ cursor: pointer;
+}
+
+button::-moz-focus-inner,
+[type="button"]::-moz-focus-inner,
+[type="reset"]::-moz-focus-inner,
+[type="submit"]::-moz-focus-inner {
+ padding: 0;
+ border-style: none;
+}
+
+input[type="radio"],
+input[type="checkbox"] {
+ box-sizing: border-box;
+ padding: 0;
+}
+
+input[type="date"],
+input[type="time"],
+input[type="datetime-local"],
+input[type="month"] {
+ -webkit-appearance: listbox;
+}
+
+textarea {
+ overflow: auto;
+ resize: vertical;
+}
+
+fieldset {
+ min-width: 0;
+ padding: 0;
+ margin: 0;
+ border: 0;
+}
+
+legend {
+ display: block;
+ width: 100%;
+ max-width: 100%;
+ padding: 0;
+ margin-bottom: .5rem;
+ font-size: 1.5rem;
+ line-height: inherit;
+ color: inherit;
+ white-space: normal;
+}
+
+progress {
+ vertical-align: baseline;
+}
+
+[type="number"]::-webkit-inner-spin-button,
+[type="number"]::-webkit-outer-spin-button {
+ height: auto;
+}
+
+[type="search"] {
+ outline-offset: -2px;
+ -webkit-appearance: none;
+}
+
+[type="search"]::-webkit-search-decoration {
+ -webkit-appearance: none;
+}
+
+::-webkit-file-upload-button {
+ font: inherit;
+ -webkit-appearance: button;
+}
+
+output {
+ display: inline-block;
+}
+
+summary {
+ display: list-item;
+ cursor: pointer;
+}
+
+template {
+ display: none;
+}
+
+[hidden] {
+ display: none !important;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+.h1,
+.h2,
+.h3,
+.h4,
+.h5,
+.h6 {
+ margin-bottom: 0.5rem;
+ font-weight: 500;
+ line-height: 1.2;
+}
+
+h1,
+.h1 {
+ font-size: 14px;
+}
+
+h2,
+.h2 {
+ font-size: 2rem;
+}
+
+h3,
+.h3 {
+ font-size: 1.75rem;
+}
+
+h4,
+.h4 {
+ font-size: 1.5rem;
+}
+
+h5,
+.h5 {
+ font-size: 1.25rem;
+}
+
+h6,
+.h6 {
+ font-size: 1rem;
+}
+
+.lead {
+ font-size: 1.25rem;
+ font-weight: 300;
+}
+
+.display-1 {
+ font-size: 6rem;
+ font-weight: 300;
+ line-height: 1.2;
+}
+
+.display-2 {
+ font-size: 5.5rem;
+ font-weight: 300;
+ line-height: 1.2;
+}
+
+.display-3 {
+ font-size: 4.5rem;
+ font-weight: 300;
+ line-height: 1.2;
+}
+
+.display-4 {
+ font-size: 3.5rem;
+ font-weight: 300;
+ line-height: 1.2;
+}
+
+hr {
+ margin-top: 1rem;
+ margin-bottom: 1rem;
+ border: 0;
+ border-top: 1px solid rgba(0, 0, 0, 0.1);
+}
+
+small,
+.small {
+ font-size: 80%;
+ font-weight: 400;
+}
+
+mark,
+.mark {
+ padding: 0.2em;
+ background-color: #fcf8e3;
+}
+
+.list-unstyled {
+ padding-left: 0;
+ list-style: none;
+}
+
+.list-inline {
+ padding-left: 0;
+ list-style: none;
+}
+
+.list-inline-item {
+ display: inline-block;
+}
+
+.list-inline-item:not(:last-child) {
+ margin-right: 0.5rem;
+}
+
+.initialism {
+ font-size: 90%;
+ text-transform: uppercase;
+}
+
+.blockquote {
+ margin-bottom: 1rem;
+ font-size: 1.25rem;
+}
+
+.blockquote-footer {
+ display: block;
+ font-size: 80%;
+ color: #6c757d;
+}
+
+.blockquote-footer::before {
+ content: "\2014\00A0";
+}
+
+.img-fluid {
+ max-width: 100%;
+ height: auto;
+}
+
+.img-thumbnail {
+ padding: 0.25rem;
+ background-color: #fff;
+ border: 1px solid #dee2e6;
+ border-radius: 0.25rem;
+ max-width: 100%;
+ height: auto;
+}
+
+.figure {
+ display: inline-block;
+}
+
+.figure-img {
+ margin-bottom: 0.5rem;
+ line-height: 1;
+}
+
+.figure-caption {
+ font-size: 90%;
+ color: #6c757d;
+}
+
+code {
+ font-size: 87.5%;
+ color: #e83e8c;
+ word-wrap: break-word;
+}
+
+a>code {
+ color: inherit;
+}
+
+kbd {
+ padding: 0.2rem 0.4rem;
+ font-size: 87.5%;
+ color: #fff;
+ background-color: #212529;
+ border-radius: 0.2rem;
+}
+
+kbd kbd {
+ padding: 0;
+ font-size: 100%;
+ font-weight: 700;
+}
+
+pre {
+ display: block;
+ font-size: 87.5%;
+ color: #212529;
+}
+
+pre code {
+ font-size: inherit;
+ color: inherit;
+ word-break: normal;
+}
+
+.pre-scrollable {
+ max-height: 340px;
+ overflow-y: scroll;
+}
+
+.container {
+ width: 100%;
+ padding-right: 15px;
+ padding-left: 15px;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+@media (min-width: 576px) {
+ .container {
+ max-width: 540px;
+ }
+}
+
+@media (min-width: 768px) {
+ .container {
+ max-width: 720px;
+ }
+}
+
+@media (min-width: 992px) {
+ .container {
+ max-width: 960px;
+ }
+}
+
+@media (min-width: 1200px) {
+ .container {
+ max-width: 1140px;
+ }
+}
+
+.container-fluid,
+.container-sm,
+.container-md,
+.container-lg,
+.container-xl {
+ width: 100%;
+ padding-right: 15px;
+ padding-left: 15px;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+@media (min-width: 576px) {
+
+ .container,
+ .container-sm {
+ max-width: 540px;
+ }
+}
+
+@media (min-width: 768px) {
+
+ .container,
+ .container-sm,
+ .container-md {
+ max-width: 720px;
+ }
+}
+
+@media (min-width: 992px) {
+
+ .container,
+ .container-sm,
+ .container-md,
+ .container-lg {
+ max-width: 960px;
+ }
+}
+
+@media (min-width: 1200px) {
+
+ .container,
+ .container-sm,
+ .container-md,
+ .container-lg,
+ .container-xl {
+ max-width: 1140px;
+ }
+}
+
+.row {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-wrap: wrap;
+ flex-wrap: wrap;
+ margin-right: -15px;
+ margin-left: -15px;
+}
+
+.no-gutters {
+ margin-right: 0;
+ margin-left: 0;
+}
+
+.no-gutters>.col,
+.no-gutters>[class*="col-"] {
+ padding-right: 0;
+ padding-left: 0;
+}
+
+.col-1,
+.col-2,
+.col-3,
+.col-4,
+.col-5,
+.col-6,
+.col-7,
+.col-8,
+.col-9,
+.col-10,
+.col-11,
+.col-12,
+.col,
+.col-auto,
+.col-sm-1,
+.col-sm-2,
+.col-sm-3,
+.col-sm-4,
+.col-sm-5,
+.col-sm-6,
+.col-sm-7,
+.col-sm-8,
+.col-sm-9,
+.col-sm-10,
+.col-sm-11,
+.col-sm-12,
+.col-sm,
+.col-sm-auto,
+.col-md-1,
+.col-md-2,
+.col-md-3,
+.col-md-4,
+.col-md-5,
+.col-md-6,
+.col-md-7,
+.col-md-8,
+.col-md-9,
+.col-md-10,
+.col-md-11,
+.col-md-12,
+.col-md,
+.col-md-auto,
+.col-lg-1,
+.col-lg-2,
+.col-lg-3,
+.col-lg-4,
+.col-lg-5,
+.col-lg-6,
+.col-lg-7,
+.col-lg-8,
+.col-lg-9,
+.col-lg-10,
+.col-lg-11,
+.col-lg-12,
+.col-lg,
+.col-lg-auto,
+.col-xl-1,
+.col-xl-2,
+.col-xl-3,
+.col-xl-4,
+.col-xl-5,
+.col-xl-6,
+.col-xl-7,
+.col-xl-8,
+.col-xl-9,
+.col-xl-10,
+.col-xl-11,
+.col-xl-12,
+.col-xl,
+.col-xl-auto {
+ position: relative;
+ width: 100%;
+ padding-right: 15px;
+ padding-left: 15px;
+}
+
+.col {
+ -ms-flex-preferred-size: 0;
+ flex-basis: 0;
+ -ms-flex-positive: 1;
+ flex-grow: 1;
+ max-width: 100%;
+}
+
+.row-cols-1>* {
+ -ms-flex: 0 0 100%;
+ flex: 0 0 100%;
+ max-width: 100%;
+}
+
+.row-cols-2>* {
+ -ms-flex: 0 0 50%;
+ flex: 0 0 50%;
+ max-width: 50%;
+}
+
+.row-cols-3>* {
+ -ms-flex: 0 0 33.333333%;
+ flex: 0 0 33.333333%;
+ max-width: 33.333333%;
+}
+
+.row-cols-4>* {
+ -ms-flex: 0 0 25%;
+ flex: 0 0 25%;
+ max-width: 25%;
+}
+
+.row-cols-5>* {
+ -ms-flex: 0 0 20%;
+ flex: 0 0 20%;
+ max-width: 20%;
+}
+
+.row-cols-6>* {
+ -ms-flex: 0 0 16.666667%;
+ flex: 0 0 16.666667%;
+ max-width: 16.666667%;
+}
+
+.col-auto {
+ -ms-flex: 0 0 auto;
+ flex: 0 0 auto;
+ width: auto;
+ max-width: 100%;
+}
+
+.col-1 {
+ -ms-flex: 0 0 8.333333%;
+ flex: 0 0 8.333333%;
+ max-width: 8.333333%;
+}
+
+.col-2 {
+ -ms-flex: 0 0 16.666667%;
+ flex: 0 0 16.666667%;
+ max-width: 16.666667%;
+}
+
+.col-3 {
+ -ms-flex: 0 0 25%;
+ flex: 0 0 25%;
+ max-width: 25%;
+}
+
+.col-4 {
+ -ms-flex: 0 0 33.333333%;
+ flex: 0 0 33.333333%;
+ max-width: 33.333333%;
+}
+
+.col-5 {
+ -ms-flex: 0 0 41.666667%;
+ flex: 0 0 41.666667%;
+ max-width: 41.666667%;
+}
+
+.col-6 {
+ -ms-flex: 0 0 50%;
+ flex: 0 0 50%;
+ max-width: 50%;
+}
+
+.col-7 {
+ -ms-flex: 0 0 58.333333%;
+ flex: 0 0 58.333333%;
+ max-width: 58.333333%;
+}
+
+.col-8 {
+ -ms-flex: 0 0 66.666667%;
+ flex: 0 0 66.666667%;
+ max-width: 66.666667%;
+}
+
+.col-9 {
+ -ms-flex: 0 0 75%;
+ flex: 0 0 75%;
+ max-width: 75%;
+}
+
+.col-10 {
+ -ms-flex: 0 0 83.333333%;
+ flex: 0 0 83.333333%;
+ max-width: 83.333333%;
+}
+
+.col-11 {
+ -ms-flex: 0 0 91.666667%;
+ flex: 0 0 91.666667%;
+ max-width: 91.666667%;
+}
+
+.col-12 {
+ -ms-flex: 0 0 100%;
+ flex: 0 0 100%;
+ max-width: 100%;
+}
+
+.order-first {
+ -ms-flex-order: -1;
+ order: -1;
+}
+
+.order-last {
+ -ms-flex-order: 13;
+ order: 13;
+}
+
+.order-0 {
+ -ms-flex-order: 0;
+ order: 0;
+}
+
+.order-1 {
+ -ms-flex-order: 1;
+ order: 1;
+}
+
+.order-2 {
+ -ms-flex-order: 2;
+ order: 2;
+}
+
+.order-3 {
+ -ms-flex-order: 3;
+ order: 3;
+}
+
+.order-4 {
+ -ms-flex-order: 4;
+ order: 4;
+}
+
+.order-5 {
+ -ms-flex-order: 5;
+ order: 5;
+}
+
+.order-6 {
+ -ms-flex-order: 6;
+ order: 6;
+}
+
+.order-7 {
+ -ms-flex-order: 7;
+ order: 7;
+}
+
+.order-8 {
+ -ms-flex-order: 8;
+ order: 8;
+}
+
+.order-9 {
+ -ms-flex-order: 9;
+ order: 9;
+}
+
+.order-10 {
+ -ms-flex-order: 10;
+ order: 10;
+}
+
+.order-11 {
+ -ms-flex-order: 11;
+ order: 11;
+}
+
+.order-12 {
+ -ms-flex-order: 12;
+ order: 12;
+}
+
+.offset-1 {
+ margin-left: 8.333333%;
+}
+
+.offset-2 {
+ margin-left: 16.666667%;
+}
+
+.offset-3 {
+ margin-left: 25%;
+}
+
+.offset-4 {
+ margin-left: 33.333333%;
+}
+
+.offset-5 {
+ margin-left: 41.666667%;
+}
+
+.offset-6 {
+ margin-left: 50%;
+}
+
+.offset-7 {
+ margin-left: 58.333333%;
+}
+
+.offset-8 {
+ margin-left: 66.666667%;
+}
+
+.offset-9 {
+ margin-left: 75%;
+}
+
+.offset-10 {
+ margin-left: 83.333333%;
+}
+
+.offset-11 {
+ margin-left: 91.666667%;
+}
+
+@media (min-width: 576px) {
+ .col-sm {
+ -ms-flex-preferred-size: 0;
+ flex-basis: 0;
+ -ms-flex-positive: 1;
+ flex-grow: 1;
+ max-width: 100%;
+ }
+
+ .row-cols-sm-1>* {
+ -ms-flex: 0 0 100%;
+ flex: 0 0 100%;
+ max-width: 100%;
+ }
+
+ .row-cols-sm-2>* {
+ -ms-flex: 0 0 50%;
+ flex: 0 0 50%;
+ max-width: 50%;
+ }
+
+ .row-cols-sm-3>* {
+ -ms-flex: 0 0 33.333333%;
+ flex: 0 0 33.333333%;
+ max-width: 33.333333%;
+ }
+
+ .row-cols-sm-4>* {
+ -ms-flex: 0 0 25%;
+ flex: 0 0 25%;
+ max-width: 25%;
+ }
+
+ .row-cols-sm-5>* {
+ -ms-flex: 0 0 20%;
+ flex: 0 0 20%;
+ max-width: 20%;
+ }
+
+ .row-cols-sm-6>* {
+ -ms-flex: 0 0 16.666667%;
+ flex: 0 0 16.666667%;
+ max-width: 16.666667%;
+ }
+
+ .col-sm-auto {
+ -ms-flex: 0 0 auto;
+ flex: 0 0 auto;
+ width: auto;
+ max-width: 100%;
+ }
+
+ .col-sm-1 {
+ -ms-flex: 0 0 8.333333%;
+ flex: 0 0 8.333333%;
+ max-width: 8.333333%;
+ }
+
+ .col-sm-2 {
+ -ms-flex: 0 0 16.666667%;
+ flex: 0 0 16.666667%;
+ max-width: 16.666667%;
+ }
+
+ .col-sm-3 {
+ -ms-flex: 0 0 25%;
+ flex: 0 0 25%;
+ max-width: 25%;
+ }
+
+ .col-sm-4 {
+ -ms-flex: 0 0 33.333333%;
+ flex: 0 0 33.333333%;
+ max-width: 33.333333%;
+ }
+
+ .col-sm-5 {
+ -ms-flex: 0 0 41.666667%;
+ flex: 0 0 41.666667%;
+ max-width: 41.666667%;
+ }
+
+ .col-sm-6 {
+ -ms-flex: 0 0 50%;
+ flex: 0 0 50%;
+ max-width: 50%;
+ }
+
+ .col-sm-7 {
+ -ms-flex: 0 0 58.333333%;
+ flex: 0 0 58.333333%;
+ max-width: 58.333333%;
+ }
+
+ .col-sm-8 {
+ -ms-flex: 0 0 66.666667%;
+ flex: 0 0 66.666667%;
+ max-width: 66.666667%;
+ }
+
+ .col-sm-9 {
+ -ms-flex: 0 0 75%;
+ flex: 0 0 75%;
+ max-width: 75%;
+ }
+
+ .col-sm-10 {
+ -ms-flex: 0 0 83.333333%;
+ flex: 0 0 83.333333%;
+ max-width: 83.333333%;
+ }
+
+ .col-sm-11 {
+ -ms-flex: 0 0 91.666667%;
+ flex: 0 0 91.666667%;
+ max-width: 91.666667%;
+ }
+
+ .col-sm-12 {
+ -ms-flex: 0 0 100%;
+ flex: 0 0 100%;
+ max-width: 100%;
+ }
+
+ .order-sm-first {
+ -ms-flex-order: -1;
+ order: -1;
+ }
+
+ .order-sm-last {
+ -ms-flex-order: 13;
+ order: 13;
+ }
+
+ .order-sm-0 {
+ -ms-flex-order: 0;
+ order: 0;
+ }
+
+ .order-sm-1 {
+ -ms-flex-order: 1;
+ order: 1;
+ }
+
+ .order-sm-2 {
+ -ms-flex-order: 2;
+ order: 2;
+ }
+
+ .order-sm-3 {
+ -ms-flex-order: 3;
+ order: 3;
+ }
+
+ .order-sm-4 {
+ -ms-flex-order: 4;
+ order: 4;
+ }
+
+ .order-sm-5 {
+ -ms-flex-order: 5;
+ order: 5;
+ }
+
+ .order-sm-6 {
+ -ms-flex-order: 6;
+ order: 6;
+ }
+
+ .order-sm-7 {
+ -ms-flex-order: 7;
+ order: 7;
+ }
+
+ .order-sm-8 {
+ -ms-flex-order: 8;
+ order: 8;
+ }
+
+ .order-sm-9 {
+ -ms-flex-order: 9;
+ order: 9;
+ }
+
+ .order-sm-10 {
+ -ms-flex-order: 10;
+ order: 10;
+ }
+
+ .order-sm-11 {
+ -ms-flex-order: 11;
+ order: 11;
+ }
+
+ .order-sm-12 {
+ -ms-flex-order: 12;
+ order: 12;
+ }
+
+ .offset-sm-0 {
+ margin-left: 0;
+ }
+
+ .offset-sm-1 {
+ margin-left: 8.333333%;
+ }
+
+ .offset-sm-2 {
+ margin-left: 16.666667%;
+ }
+
+ .offset-sm-3 {
+ margin-left: 25%;
+ }
+
+ .offset-sm-4 {
+ margin-left: 33.333333%;
+ }
+
+ .offset-sm-5 {
+ margin-left: 41.666667%;
+ }
+
+ .offset-sm-6 {
+ margin-left: 50%;
+ }
+
+ .offset-sm-7 {
+ margin-left: 58.333333%;
+ }
+
+ .offset-sm-8 {
+ margin-left: 66.666667%;
+ }
+
+ .offset-sm-9 {
+ margin-left: 75%;
+ }
+
+ .offset-sm-10 {
+ margin-left: 83.333333%;
+ }
+
+ .offset-sm-11 {
+ margin-left: 91.666667%;
+ }
+}
+
+@media (min-width: 768px) {
+ .col-md {
+ -ms-flex-preferred-size: 0;
+ flex-basis: 0;
+ -ms-flex-positive: 1;
+ flex-grow: 1;
+ max-width: 100%;
+ }
+
+ .row-cols-md-1>* {
+ -ms-flex: 0 0 100%;
+ flex: 0 0 100%;
+ max-width: 100%;
+ }
+
+ .row-cols-md-2>* {
+ -ms-flex: 0 0 50%;
+ flex: 0 0 50%;
+ max-width: 50%;
+ }
+
+ .row-cols-md-3>* {
+ -ms-flex: 0 0 33.333333%;
+ flex: 0 0 33.333333%;
+ max-width: 33.333333%;
+ }
+
+ .row-cols-md-4>* {
+ -ms-flex: 0 0 25%;
+ flex: 0 0 25%;
+ max-width: 25%;
+ }
+
+ .row-cols-md-5>* {
+ -ms-flex: 0 0 20%;
+ flex: 0 0 20%;
+ max-width: 20%;
+ }
+
+ .row-cols-md-6>* {
+ -ms-flex: 0 0 16.666667%;
+ flex: 0 0 16.666667%;
+ max-width: 16.666667%;
+ }
+
+ .col-md-auto {
+ -ms-flex: 0 0 auto;
+ flex: 0 0 auto;
+ width: auto;
+ max-width: 100%;
+ }
+
+ .col-md-1 {
+ -ms-flex: 0 0 8.333333%;
+ flex: 0 0 8.333333%;
+ max-width: 8.333333%;
+ }
+
+ .col-md-2 {
+ -ms-flex: 0 0 16.666667%;
+ flex: 0 0 16.666667%;
+ max-width: 16.666667%;
+ }
+
+ .col-md-3 {
+ -ms-flex: 0 0 25%;
+ flex: 0 0 25%;
+ max-width: 25%;
+ }
+
+ .col-md-4 {
+ -ms-flex: 0 0 33.333333%;
+ flex: 0 0 33.333333%;
+ max-width: 33.333333%;
+ }
+
+ .col-md-5 {
+ -ms-flex: 0 0 41.666667%;
+ flex: 0 0 41.666667%;
+ max-width: 41.666667%;
+ }
+
+ .col-md-6 {
+ -ms-flex: 0 0 50%;
+ flex: 0 0 50%;
+ max-width: 50%;
+ }
+
+ .col-md-7 {
+ -ms-flex: 0 0 58.333333%;
+ flex: 0 0 58.333333%;
+ max-width: 58.333333%;
+ }
+
+ .col-md-8 {
+ -ms-flex: 0 0 66.666667%;
+ flex: 0 0 66.666667%;
+ max-width: 66.666667%;
+ }
+
+ .col-md-9 {
+ -ms-flex: 0 0 75%;
+ flex: 0 0 75%;
+ max-width: 75%;
+ }
+
+ .col-md-10 {
+ -ms-flex: 0 0 83.333333%;
+ flex: 0 0 83.333333%;
+ max-width: 83.333333%;
+ }
+
+ .col-md-11 {
+ -ms-flex: 0 0 91.666667%;
+ flex: 0 0 91.666667%;
+ max-width: 91.666667%;
+ }
+
+ .col-md-12 {
+ -ms-flex: 0 0 100%;
+ flex: 0 0 100%;
+ max-width: 100%;
+ }
+
+ .order-md-first {
+ -ms-flex-order: -1;
+ order: -1;
+ }
+
+ .order-md-last {
+ -ms-flex-order: 13;
+ order: 13;
+ }
+
+ .order-md-0 {
+ -ms-flex-order: 0;
+ order: 0;
+ }
+
+ .order-md-1 {
+ -ms-flex-order: 1;
+ order: 1;
+ }
+
+ .order-md-2 {
+ -ms-flex-order: 2;
+ order: 2;
+ }
+
+ .order-md-3 {
+ -ms-flex-order: 3;
+ order: 3;
+ }
+
+ .order-md-4 {
+ -ms-flex-order: 4;
+ order: 4;
+ }
+
+ .order-md-5 {
+ -ms-flex-order: 5;
+ order: 5;
+ }
+
+ .order-md-6 {
+ -ms-flex-order: 6;
+ order: 6;
+ }
+
+ .order-md-7 {
+ -ms-flex-order: 7;
+ order: 7;
+ }
+
+ .order-md-8 {
+ -ms-flex-order: 8;
+ order: 8;
+ }
+
+ .order-md-9 {
+ -ms-flex-order: 9;
+ order: 9;
+ }
+
+ .order-md-10 {
+ -ms-flex-order: 10;
+ order: 10;
+ }
+
+ .order-md-11 {
+ -ms-flex-order: 11;
+ order: 11;
+ }
+
+ .order-md-12 {
+ -ms-flex-order: 12;
+ order: 12;
+ }
+
+ .offset-md-0 {
+ margin-left: 0;
+ }
+
+ .offset-md-1 {
+ margin-left: 8.333333%;
+ }
+
+ .offset-md-2 {
+ margin-left: 16.666667%;
+ }
+
+ .offset-md-3 {
+ margin-left: 25%;
+ }
+
+ .offset-md-4 {
+ margin-left: 33.333333%;
+ }
+
+ .offset-md-5 {
+ margin-left: 41.666667%;
+ }
+
+ .offset-md-6 {
+ margin-left: 50%;
+ }
+
+ .offset-md-7 {
+ margin-left: 58.333333%;
+ }
+
+ .offset-md-8 {
+ margin-left: 66.666667%;
+ }
+
+ .offset-md-9 {
+ margin-left: 75%;
+ }
+
+ .offset-md-10 {
+ margin-left: 83.333333%;
+ }
+
+ .offset-md-11 {
+ margin-left: 91.666667%;
+ }
+}
+
+@media (min-width: 992px) {
+ .col-lg {
+ -ms-flex-preferred-size: 0;
+ flex-basis: 0;
+ -ms-flex-positive: 1;
+ flex-grow: 1;
+ max-width: 100%;
+ }
+
+ .row-cols-lg-1>* {
+ -ms-flex: 0 0 100%;
+ flex: 0 0 100%;
+ max-width: 100%;
+ }
+
+ .row-cols-lg-2>* {
+ -ms-flex: 0 0 50%;
+ flex: 0 0 50%;
+ max-width: 50%;
+ }
+
+ .row-cols-lg-3>* {
+ -ms-flex: 0 0 33.333333%;
+ flex: 0 0 33.333333%;
+ max-width: 33.333333%;
+ }
+
+ .row-cols-lg-4>* {
+ -ms-flex: 0 0 25%;
+ flex: 0 0 25%;
+ max-width: 25%;
+ }
+
+ .row-cols-lg-5>* {
+ -ms-flex: 0 0 20%;
+ flex: 0 0 20%;
+ max-width: 20%;
+ }
+
+ .row-cols-lg-6>* {
+ -ms-flex: 0 0 16.666667%;
+ flex: 0 0 16.666667%;
+ max-width: 16.666667%;
+ }
+
+ .col-lg-auto {
+ -ms-flex: 0 0 auto;
+ flex: 0 0 auto;
+ width: auto;
+ max-width: 100%;
+ }
+
+ .col-lg-1 {
+ -ms-flex: 0 0 8.333333%;
+ flex: 0 0 8.333333%;
+ max-width: 8.333333%;
+ }
+
+ .col-lg-2 {
+ -ms-flex: 0 0 16.666667%;
+ flex: 0 0 16.666667%;
+ max-width: 16.666667%;
+ }
+
+ .col-lg-3 {
+ -ms-flex: 0 0 25%;
+ flex: 0 0 25%;
+ max-width: 25%;
+ }
+
+ .col-lg-4 {
+ -ms-flex: 0 0 33.333333%;
+ flex: 0 0 33.333333%;
+ max-width: 33.333333%;
+ }
+
+ .col-lg-5 {
+ -ms-flex: 0 0 41.666667%;
+ flex: 0 0 41.666667%;
+ max-width: 41.666667%;
+ }
+
+ .col-lg-6 {
+ -ms-flex: 0 0 50%;
+ flex: 0 0 50%;
+ max-width: 50%;
+ }
+
+ .col-lg-7 {
+ -ms-flex: 0 0 58.333333%;
+ flex: 0 0 58.333333%;
+ max-width: 58.333333%;
+ }
+
+ .col-lg-8 {
+ -ms-flex: 0 0 66.666667%;
+ flex: 0 0 66.666667%;
+ max-width: 66.666667%;
+ }
+
+ .col-lg-9 {
+ -ms-flex: 0 0 75%;
+ flex: 0 0 75%;
+ max-width: 75%;
+ }
+
+ .col-lg-10 {
+ -ms-flex: 0 0 83.333333%;
+ flex: 0 0 83.333333%;
+ max-width: 83.333333%;
+ }
+
+ .col-lg-11 {
+ -ms-flex: 0 0 91.666667%;
+ flex: 0 0 91.666667%;
+ max-width: 91.666667%;
+ }
+
+ .col-lg-12 {
+ -ms-flex: 0 0 100%;
+ flex: 0 0 100%;
+ max-width: 100%;
+ }
+
+ .order-lg-first {
+ -ms-flex-order: -1;
+ order: -1;
+ }
+
+ .order-lg-last {
+ -ms-flex-order: 13;
+ order: 13;
+ }
+
+ .order-lg-0 {
+ -ms-flex-order: 0;
+ order: 0;
+ }
+
+ .order-lg-1 {
+ -ms-flex-order: 1;
+ order: 1;
+ }
+
+ .order-lg-2 {
+ -ms-flex-order: 2;
+ order: 2;
+ }
+
+ .order-lg-3 {
+ -ms-flex-order: 3;
+ order: 3;
+ }
+
+ .order-lg-4 {
+ -ms-flex-order: 4;
+ order: 4;
+ }
+
+ .order-lg-5 {
+ -ms-flex-order: 5;
+ order: 5;
+ }
+
+ .order-lg-6 {
+ -ms-flex-order: 6;
+ order: 6;
+ }
+
+ .order-lg-7 {
+ -ms-flex-order: 7;
+ order: 7;
+ }
+
+ .order-lg-8 {
+ -ms-flex-order: 8;
+ order: 8;
+ }
+
+ .order-lg-9 {
+ -ms-flex-order: 9;
+ order: 9;
+ }
+
+ .order-lg-10 {
+ -ms-flex-order: 10;
+ order: 10;
+ }
+
+ .order-lg-11 {
+ -ms-flex-order: 11;
+ order: 11;
+ }
+
+ .order-lg-12 {
+ -ms-flex-order: 12;
+ order: 12;
+ }
+
+ .offset-lg-0 {
+ margin-left: 0;
+ }
+
+ .offset-lg-1 {
+ margin-left: 8.333333%;
+ }
+
+ .offset-lg-2 {
+ margin-left: 16.666667%;
+ }
+
+ .offset-lg-3 {
+ margin-left: 25%;
+ }
+
+ .offset-lg-4 {
+ margin-left: 33.333333%;
+ }
+
+ .offset-lg-5 {
+ margin-left: 41.666667%;
+ }
+
+ .offset-lg-6 {
+ margin-left: 50%;
+ }
+
+ .offset-lg-7 {
+ margin-left: 58.333333%;
+ }
+
+ .offset-lg-8 {
+ margin-left: 66.666667%;
+ }
+
+ .offset-lg-9 {
+ margin-left: 75%;
+ }
+
+ .offset-lg-10 {
+ margin-left: 83.333333%;
+ }
+
+ .offset-lg-11 {
+ margin-left: 91.666667%;
+ }
+}
+
+@media (min-width: 1200px) {
+ .col-xl {
+ -ms-flex-preferred-size: 0;
+ flex-basis: 0;
+ -ms-flex-positive: 1;
+ flex-grow: 1;
+ max-width: 100%;
+ }
+
+ .row-cols-xl-1>* {
+ -ms-flex: 0 0 100%;
+ flex: 0 0 100%;
+ max-width: 100%;
+ }
+
+ .row-cols-xl-2>* {
+ -ms-flex: 0 0 50%;
+ flex: 0 0 50%;
+ max-width: 50%;
+ }
+
+ .row-cols-xl-3>* {
+ -ms-flex: 0 0 33.333333%;
+ flex: 0 0 33.333333%;
+ max-width: 33.333333%;
+ }
+
+ .row-cols-xl-4>* {
+ -ms-flex: 0 0 25%;
+ flex: 0 0 25%;
+ max-width: 25%;
+ }
+
+ .row-cols-xl-5>* {
+ -ms-flex: 0 0 20%;
+ flex: 0 0 20%;
+ max-width: 20%;
+ }
+
+ .row-cols-xl-6>* {
+ -ms-flex: 0 0 16.666667%;
+ flex: 0 0 16.666667%;
+ max-width: 16.666667%;
+ }
+
+ .col-xl-auto {
+ -ms-flex: 0 0 auto;
+ flex: 0 0 auto;
+ width: auto;
+ max-width: 100%;
+ }
+
+ .col-xl-1 {
+ -ms-flex: 0 0 8.333333%;
+ flex: 0 0 8.333333%;
+ max-width: 8.333333%;
+ }
+
+ .col-xl-2 {
+ -ms-flex: 0 0 16.666667%;
+ flex: 0 0 16.666667%;
+ max-width: 16.666667%;
+ }
+
+ .col-xl-3 {
+ -ms-flex: 0 0 25%;
+ flex: 0 0 25%;
+ max-width: 25%;
+ }
+
+ .col-xl-4 {
+ -ms-flex: 0 0 33.333333%;
+ flex: 0 0 33.333333%;
+ max-width: 33.333333%;
+ }
+
+ .col-xl-5 {
+ -ms-flex: 0 0 41.666667%;
+ flex: 0 0 41.666667%;
+ max-width: 41.666667%;
+ }
+
+ .col-xl-6 {
+ -ms-flex: 0 0 50%;
+ flex: 0 0 50%;
+ max-width: 50%;
+ font-family: "Microsoft YaHei", 微软雅黑;
+ }
+
+ .col-xl-7 {
+ -ms-flex: 0 0 58.333333%;
+ flex: 0 0 58.333333%;
+ max-width: 58.333333%;
+ }
+
+ .col-xl-8 {
+ -ms-flex: 0 0 66.666667%;
+ flex: 0 0 66.666667%;
+ max-width: 66.666667%;
+ }
+
+ .col-xl-9 {
+ -ms-flex: 0 0 75%;
+ flex: 0 0 75%;
+ max-width: 75%;
+ }
+
+ .col-xl-10 {
+ -ms-flex: 0 0 83.333333%;
+ flex: 0 0 83.333333%;
+ max-width: 83.333333%;
+ }
+
+ .col-xl-11 {
+ -ms-flex: 0 0 91.666667%;
+ flex: 0 0 91.666667%;
+ max-width: 91.666667%;
+ }
+
+ .col-xl-12 {
+ -ms-flex: 0 0 100%;
+ flex: 0 0 100%;
+ max-width: 100%;
+ }
+
+ .order-xl-first {
+ -ms-flex-order: -1;
+ order: -1;
+ }
+
+ .order-xl-last {
+ -ms-flex-order: 13;
+ order: 13;
+ }
+
+ .order-xl-0 {
+ -ms-flex-order: 0;
+ order: 0;
+ }
+
+ .order-xl-1 {
+ -ms-flex-order: 1;
+ order: 1;
+ }
+
+ .order-xl-2 {
+ -ms-flex-order: 2;
+ order: 2;
+ }
+
+ .order-xl-3 {
+ -ms-flex-order: 3;
+ order: 3;
+ }
+
+ .order-xl-4 {
+ -ms-flex-order: 4;
+ order: 4;
+ }
+
+ .order-xl-5 {
+ -ms-flex-order: 5;
+ order: 5;
+ }
+
+ .order-xl-6 {
+ -ms-flex-order: 6;
+ order: 6;
+ }
+
+ .order-xl-7 {
+ -ms-flex-order: 7;
+ order: 7;
+ }
+
+ .order-xl-8 {
+ -ms-flex-order: 8;
+ order: 8;
+ }
+
+ .order-xl-9 {
+ -ms-flex-order: 9;
+ order: 9;
+ }
+
+ .order-xl-10 {
+ -ms-flex-order: 10;
+ order: 10;
+ }
+
+ .order-xl-11 {
+ -ms-flex-order: 11;
+ order: 11;
+ }
+
+ .order-xl-12 {
+ -ms-flex-order: 12;
+ order: 12;
+ }
+
+ .offset-xl-0 {
+ margin-left: 0;
+ }
+
+ .offset-xl-1 {
+ margin-left: 8.333333%;
+ }
+
+ .offset-xl-2 {
+ margin-left: 16.666667%;
+ }
+
+ .offset-xl-3 {
+ margin-left: 25%;
+ }
+
+ .offset-xl-4 {
+ margin-left: 33.333333%;
+ }
+
+ .offset-xl-5 {
+ margin-left: 41.666667%;
+ }
+
+ .offset-xl-6 {
+ margin-left: 50%;
+ }
+
+ .offset-xl-7 {
+ margin-left: 58.333333%;
+ }
+
+ .offset-xl-8 {
+ margin-left: 66.666667%;
+ }
+
+ .offset-xl-9 {
+ margin-left: 75%;
+ }
+
+ .offset-xl-10 {
+ margin-left: 83.333333%;
+ }
+
+ .offset-xl-11 {
+ margin-left: 91.666667%;
+ }
+}
+
+.table {
+ width: 100%;
+ margin-bottom: 1rem;
+ color: #212529;
+}
+
+.table th,
+.table td {
+ padding: 0.75rem;
+ vertical-align: top;
+ border-top: 1px solid #dee2e6;
+}
+
+.table thead th {
+ vertical-align: bottom;
+ border-bottom: 2px solid #dee2e6;
+}
+
+.table tbody+tbody {
+ border-top: 2px solid #dee2e6;
+}
+
+.table-sm th,
+.table-sm td {
+ padding: 0.3rem;
+}
+
+.table-bordered {
+ border: 1px solid #dee2e6;
+}
+
+.table-bordered th,
+.table-bordered td {
+ border: 1px solid #dee2e6;
+}
+
+.table-bordered thead th,
+.table-bordered thead td {
+ border-bottom-width: 2px;
+}
+
+.table-borderless th,
+.table-borderless td,
+.table-borderless thead th,
+.table-borderless tbody+tbody {
+ border: 0;
+}
+
+.table-striped tbody tr:nth-of-type(odd) {
+ background-color: rgba(0, 0, 0, 0.05);
+}
+
+.table-hover tbody tr:hover {
+ color: #212529;
+ background-color: rgba(0, 0, 0, 0.075);
+}
+
+.table-primary,
+.table-primary>th,
+.table-primary>td {
+ background-color: #b8daff;
+}
+
+.table-primary th,
+.table-primary td,
+.table-primary thead th,
+.table-primary tbody+tbody {
+ border-color: #7abaff;
+}
+
+.table-hover .table-primary:hover {
+ background-color: #9fcdff;
+}
+
+.table-hover .table-primary:hover>td,
+.table-hover .table-primary:hover>th {
+ background-color: #9fcdff;
+}
+
+.table-secondary,
+.table-secondary>th,
+.table-secondary>td {
+ background-color: #d6d8db;
+}
+
+.table-secondary th,
+.table-secondary td,
+.table-secondary thead th,
+.table-secondary tbody+tbody {
+ border-color: #b3b7bb;
+}
+
+.table-hover .table-secondary:hover {
+ background-color: #c8cbcf;
+}
+
+.table-hover .table-secondary:hover>td,
+.table-hover .table-secondary:hover>th {
+ background-color: #c8cbcf;
+}
+
+.table-success,
+.table-success>th,
+.table-success>td {
+ background-color: #c3e6cb;
+}
+
+.table-success th,
+.table-success td,
+.table-success thead th,
+.table-success tbody+tbody {
+ border-color: #8fd19e;
+}
+
+.table-hover .table-success:hover {
+ background-color: #b1dfbb;
+}
+
+.table-hover .table-success:hover>td,
+.table-hover .table-success:hover>th {
+ background-color: #b1dfbb;
+}
+
+.table-info,
+.table-info>th,
+.table-info>td {
+ background-color: #bee5eb;
+}
+
+.table-info th,
+.table-info td,
+.table-info thead th,
+.table-info tbody+tbody {
+ border-color: #86cfda;
+}
+
+.table-hover .table-info:hover {
+ background-color: #abdde5;
+}
+
+.table-hover .table-info:hover>td,
+.table-hover .table-info:hover>th {
+ background-color: #abdde5;
+}
+
+.table-warning,
+.table-warning>th,
+.table-warning>td {
+ background-color: #ffeeba;
+}
+
+.table-warning th,
+.table-warning td,
+.table-warning thead th,
+.table-warning tbody+tbody {
+ border-color: #ffdf7e;
+}
+
+.table-hover .table-warning:hover {
+ background-color: #ffe8a1;
+}
+
+.table-hover .table-warning:hover>td,
+.table-hover .table-warning:hover>th {
+ background-color: #ffe8a1;
+}
+
+.table-danger,
+.table-danger>th,
+.table-danger>td {
+ background-color: #f5c6cb;
+}
+
+.table-danger th,
+.table-danger td,
+.table-danger thead th,
+.table-danger tbody+tbody {
+ border-color: #ed969e;
+}
+
+.table-hover .table-danger:hover {
+ background-color: #f1b0b7;
+}
+
+.table-hover .table-danger:hover>td,
+.table-hover .table-danger:hover>th {
+ background-color: #f1b0b7;
+}
+
+.table-light,
+.table-light>th,
+.table-light>td {
+ background-color: #fdfdfe;
+}
+
+.table-light th,
+.table-light td,
+.table-light thead th,
+.table-light tbody+tbody {
+ border-color: #fbfcfc;
+}
+
+.table-hover .table-light:hover {
+ background-color: #ececf6;
+}
+
+.table-hover .table-light:hover>td,
+.table-hover .table-light:hover>th {
+ background-color: #ececf6;
+}
+
+.table-dark,
+.table-dark>th,
+.table-dark>td {
+ background-color: #c6c8ca;
+}
+
+.table-dark th,
+.table-dark td,
+.table-dark thead th,
+.table-dark tbody+tbody {
+ border-color: #95999c;
+}
+
+.table-hover .table-dark:hover {
+ background-color: #b9bbbe;
+}
+
+.table-hover .table-dark:hover>td,
+.table-hover .table-dark:hover>th {
+ background-color: #b9bbbe;
+}
+
+.table-active,
+.table-active>th,
+.table-active>td {
+ background-color: rgba(0, 0, 0, 0.075);
+}
+
+.table-hover .table-active:hover {
+ background-color: rgba(0, 0, 0, 0.075);
+}
+
+.table-hover .table-active:hover>td,
+.table-hover .table-active:hover>th {
+ background-color: rgba(0, 0, 0, 0.075);
+}
+
+.table .thead-dark th {
+ color: #fff;
+ background-color: #343a40;
+ border-color: #454d55;
+}
+
+.table .thead-light th {
+ color: #495057;
+ background-color: #e9ecef;
+ border-color: #dee2e6;
+}
+
+.table-dark {
+ color: #fff;
+ background-color: #343a40;
+}
+
+.table-dark th,
+.table-dark td,
+.table-dark thead th {
+ border-color: #454d55;
+}
+
+.table-dark.table-bordered {
+ border: 0;
+}
+
+.table-dark.table-striped tbody tr:nth-of-type(odd) {
+ background-color: rgba(255, 255, 255, 0.05);
+}
+
+.table-dark.table-hover tbody tr:hover {
+ color: #fff;
+ background-color: rgba(255, 255, 255, 0.075);
+}
+
+@media (max-width: 575.98px) {
+ .table-responsive-sm {
+ display: block;
+ width: 100%;
+ overflow-x: auto;
+ -webkit-overflow-scrolling: touch;
+ }
+
+ .table-responsive-sm>.table-bordered {
+ border: 0;
+ }
+}
+
+@media (max-width: 767.98px) {
+ .table-responsive-md {
+ display: block;
+ width: 100%;
+ overflow-x: auto;
+ -webkit-overflow-scrolling: touch;
+ }
+
+ .table-responsive-md>.table-bordered {
+ border: 0;
+ }
+}
+
+@media (max-width: 991.98px) {
+ .table-responsive-lg {
+ display: block;
+ width: 100%;
+ overflow-x: auto;
+ -webkit-overflow-scrolling: touch;
+ }
+
+ .table-responsive-lg>.table-bordered {
+ border: 0;
+ }
+}
+
+@media (max-width: 1199.98px) {
+ .table-responsive-xl {
+ display: block;
+ width: 100%;
+ overflow-x: auto;
+ -webkit-overflow-scrolling: touch;
+ }
+
+ .table-responsive-xl>.table-bordered {
+ border: 0;
+ }
+}
+
+.table-responsive {
+ display: block;
+ width: 100%;
+ overflow-x: auto;
+ -webkit-overflow-scrolling: touch;
+}
+
+.table-responsive>.table-bordered {
+ border: 0;
+}
+
+.form-control {
+ display: block;
+ width: 100%;
+ height: calc(1.5em + 0.75rem + 2px);
+ padding: 0.375rem 0.75rem;
+ font-size: 1rem;
+ font-weight: 400;
+ line-height: 1.5;
+ color: #495057;
+ background-color: #fff;
+ background-clip: padding-box;
+ border: 1px solid #ced4da;
+ border-radius: 0.25rem;
+ transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .form-control {
+ transition: none;
+ }
+}
+
+.form-control::-ms-expand {
+ background-color: transparent;
+ border: 0;
+}
+
+.form-control:-moz-focusring {
+ color: transparent;
+ text-shadow: 0 0 0 #495057;
+}
+
+.form-control:focus {
+ color: #495057;
+ background-color: #fff;
+ border-color: #80bdff;
+ outline: 0;
+ box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
+}
+
+.form-control::-webkit-input-placeholder {
+ color: #6c757d;
+ opacity: 1;
+}
+
+.form-control::-moz-placeholder {
+ color: #6c757d;
+ opacity: 1;
+}
+
+.form-control:-ms-input-placeholder {
+ color: #6c757d;
+ opacity: 1;
+}
+
+.form-control::-ms-input-placeholder {
+ color: #6c757d;
+ opacity: 1;
+}
+
+.form-control::placeholder {
+ color: #6c757d;
+ opacity: 1;
+}
+
+.form-control:disabled,
+.form-control[readonly] {
+ background-color: #e9ecef;
+ opacity: 1;
+}
+
+select.form-control:focus::-ms-value {
+ color: #495057;
+ background-color: #fff;
+}
+
+.form-control-file,
+.form-control-range {
+ display: block;
+ width: 100%;
+}
+
+.col-form-label {
+ padding-top: calc(0.375rem + 1px);
+ padding-bottom: calc(0.375rem + 1px);
+ margin-bottom: 0;
+ font-size: inherit;
+ line-height: 1.5;
+}
+
+.col-form-label-lg {
+ padding-top: calc(0.5rem + 1px);
+ padding-bottom: calc(0.5rem + 1px);
+ font-size: 1.25rem;
+ line-height: 1.5;
+}
+
+.col-form-label-sm {
+ padding-top: calc(0.25rem + 1px);
+ padding-bottom: calc(0.25rem + 1px);
+ font-size: 0.875rem;
+ line-height: 1.5;
+}
+
+.form-control-plaintext {
+ display: block;
+ width: 100%;
+ padding: 0.375rem 0;
+ margin-bottom: 0;
+ font-size: 1rem;
+ line-height: 1.5;
+ color: #212529;
+ background-color: transparent;
+ border: solid transparent;
+ border-width: 1px 0;
+}
+
+.form-control-plaintext.form-control-sm,
+.form-control-plaintext.form-control-lg {
+ padding-right: 0;
+ padding-left: 0;
+}
+
+.form-control-sm {
+ height: calc(1.5em + 0.5rem + 2px);
+ padding: 0.25rem 0.5rem;
+ font-size: 0.875rem;
+ line-height: 1.5;
+ border-radius: 0.2rem;
+}
+
+.form-control-lg {
+ height: calc(1.5em + 1rem + 2px);
+ padding: 0.5rem 1rem;
+ font-size: 1.25rem;
+ line-height: 1.5;
+ border-radius: 0.3rem;
+}
+
+select.form-control[size],
+select.form-control[multiple] {
+ height: auto;
+}
+
+textarea.form-control {
+ height: auto;
+}
+
+.form-group {
+ margin-bottom: 1rem;
+}
+
+.form-text {
+ display: block;
+ margin-top: 0.25rem;
+}
+
+.form-row {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-wrap: wrap;
+ flex-wrap: wrap;
+ margin-right: -5px;
+ margin-left: -5px;
+}
+
+.form-row>.col,
+.form-row>[class*="col-"] {
+ padding-right: 5px;
+ padding-left: 5px;
+}
+
+.form-check {
+ position: relative;
+ display: block;
+ padding-left: 1.25rem;
+}
+
+.form-check-input {
+ position: absolute;
+ margin-top: 0.3rem;
+ margin-left: -1.25rem;
+}
+
+.form-check-input[disabled]~.form-check-label,
+.form-check-input:disabled~.form-check-label {
+ color: #6c757d;
+}
+
+.form-check-label {
+ margin-bottom: 0;
+}
+
+.form-check-inline {
+ display: -ms-inline-flexbox;
+ display: inline-flex;
+ -ms-flex-align: center;
+ align-items: center;
+ padding-left: 0;
+ margin-right: 0.75rem;
+}
+
+.form-check-inline .form-check-input {
+ position: static;
+ margin-top: 0;
+ margin-right: 0.3125rem;
+ margin-left: 0;
+}
+
+.valid-feedback {
+ display: none;
+ width: 100%;
+ margin-top: 0.25rem;
+ font-size: 80%;
+ color: #28a745;
+}
+
+.valid-tooltip {
+ position: absolute;
+ top: 100%;
+ z-index: 5;
+ display: none;
+ max-width: 100%;
+ padding: 0.25rem 0.5rem;
+ margin-top: .1rem;
+ font-size: 0.875rem;
+ line-height: 1.5;
+ color: #fff;
+ background-color: rgba(40, 167, 69, 0.9);
+ border-radius: 0.25rem;
+}
+
+.was-validated :valid~.valid-feedback,
+.was-validated :valid~.valid-tooltip,
+.is-valid~.valid-feedback,
+.is-valid~.valid-tooltip {
+ display: block;
+}
+
+.was-validated .form-control:valid,
+.form-control.is-valid {
+ border-color: #28a745;
+ padding-right: calc(1.5em + 0.75rem);
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");
+ background-repeat: no-repeat;
+ background-position: right calc(0.375em + 0.1875rem) center;
+ background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
+}
+
+.was-validated .form-control:valid:focus,
+.form-control.is-valid:focus {
+ border-color: #28a745;
+ box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25);
+}
+
+.was-validated textarea.form-control:valid,
+textarea.form-control.is-valid {
+ padding-right: calc(1.5em + 0.75rem);
+ background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem);
+}
+
+.was-validated .custom-select:valid,
+.custom-select.is-valid {
+ border-color: #28a745;
+ padding-right: calc(0.75em + 2.3125rem);
+ background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px, url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
+}
+
+.was-validated .custom-select:valid:focus,
+.custom-select.is-valid:focus {
+ border-color: #28a745;
+ box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25);
+}
+
+.was-validated .form-check-input:valid~.form-check-label,
+.form-check-input.is-valid~.form-check-label {
+ color: #28a745;
+}
+
+.was-validated .form-check-input:valid~.valid-feedback,
+.was-validated .form-check-input:valid~.valid-tooltip,
+.form-check-input.is-valid~.valid-feedback,
+.form-check-input.is-valid~.valid-tooltip {
+ display: block;
+}
+
+.was-validated .custom-control-input:valid~.custom-control-label,
+.custom-control-input.is-valid~.custom-control-label {
+ color: #28a745;
+}
+
+.was-validated .custom-control-input:valid~.custom-control-label::before,
+.custom-control-input.is-valid~.custom-control-label::before {
+ border-color: #28a745;
+}
+
+.was-validated .custom-control-input:valid:checked~.custom-control-label::before,
+.custom-control-input.is-valid:checked~.custom-control-label::before {
+ border-color: #34ce57;
+ background-color: #34ce57;
+}
+
+.was-validated .custom-control-input:valid:focus~.custom-control-label::before,
+.custom-control-input.is-valid:focus~.custom-control-label::before {
+ box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25);
+}
+
+.was-validated .custom-control-input:valid:focus:not(:checked)~.custom-control-label::before,
+.custom-control-input.is-valid:focus:not(:checked)~.custom-control-label::before {
+ border-color: #28a745;
+}
+
+.was-validated .custom-file-input:valid~.custom-file-label,
+.custom-file-input.is-valid~.custom-file-label {
+ border-color: #28a745;
+}
+
+.was-validated .custom-file-input:valid:focus~.custom-file-label,
+.custom-file-input.is-valid:focus~.custom-file-label {
+ border-color: #28a745;
+ box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25);
+}
+
+.invalid-feedback {
+ display: none;
+ width: 100%;
+ margin-top: 0.25rem;
+ font-size: 80%;
+ color: #dc3545;
+}
+
+.invalid-tooltip {
+ position: absolute;
+ top: 100%;
+ z-index: 5;
+ display: none;
+ max-width: 100%;
+ padding: 0.25rem 0.5rem;
+ margin-top: .1rem;
+ font-size: 0.875rem;
+ line-height: 1.5;
+ color: #fff;
+ background-color: rgba(220, 53, 69, 0.9);
+ border-radius: 0.25rem;
+}
+
+.was-validated :invalid~.invalid-feedback,
+.was-validated :invalid~.invalid-tooltip,
+.is-invalid~.invalid-feedback,
+.is-invalid~.invalid-tooltip {
+ display: block;
+}
+
+.was-validated .form-control:invalid,
+.form-control.is-invalid {
+ border-color: #dc3545;
+ padding-right: calc(1.5em + 0.75rem);
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");
+ background-repeat: no-repeat;
+ background-position: right calc(0.375em + 0.1875rem) center;
+ background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
+}
+
+.was-validated .form-control:invalid:focus,
+.form-control.is-invalid:focus {
+ border-color: #dc3545;
+ box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);
+}
+
+.was-validated textarea.form-control:invalid,
+textarea.form-control.is-invalid {
+ padding-right: calc(1.5em + 0.75rem);
+ background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem);
+}
+
+.was-validated .custom-select:invalid,
+.custom-select.is-invalid {
+ border-color: #dc3545;
+ padding-right: calc(0.75em + 2.3125rem);
+ background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px, url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
+}
+
+.was-validated .custom-select:invalid:focus,
+.custom-select.is-invalid:focus {
+ border-color: #dc3545;
+ box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);
+}
+
+.was-validated .form-check-input:invalid~.form-check-label,
+.form-check-input.is-invalid~.form-check-label {
+ color: #dc3545;
+}
+
+.was-validated .form-check-input:invalid~.invalid-feedback,
+.was-validated .form-check-input:invalid~.invalid-tooltip,
+.form-check-input.is-invalid~.invalid-feedback,
+.form-check-input.is-invalid~.invalid-tooltip {
+ display: block;
+}
+
+.was-validated .custom-control-input:invalid~.custom-control-label,
+.custom-control-input.is-invalid~.custom-control-label {
+ color: #dc3545;
+}
+
+.was-validated .custom-control-input:invalid~.custom-control-label::before,
+.custom-control-input.is-invalid~.custom-control-label::before {
+ border-color: #dc3545;
+}
+
+.was-validated .custom-control-input:invalid:checked~.custom-control-label::before,
+.custom-control-input.is-invalid:checked~.custom-control-label::before {
+ border-color: #e4606d;
+ background-color: #e4606d;
+}
+
+.was-validated .custom-control-input:invalid:focus~.custom-control-label::before,
+.custom-control-input.is-invalid:focus~.custom-control-label::before {
+ box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);
+}
+
+.was-validated .custom-control-input:invalid:focus:not(:checked)~.custom-control-label::before,
+.custom-control-input.is-invalid:focus:not(:checked)~.custom-control-label::before {
+ border-color: #dc3545;
+}
+
+.was-validated .custom-file-input:invalid~.custom-file-label,
+.custom-file-input.is-invalid~.custom-file-label {
+ border-color: #dc3545;
+}
+
+.was-validated .custom-file-input:invalid:focus~.custom-file-label,
+.custom-file-input.is-invalid:focus~.custom-file-label {
+ border-color: #dc3545;
+ box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);
+}
+
+.form-inline {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-flow: row wrap;
+ flex-flow: row wrap;
+ -ms-flex-align: center;
+ align-items: center;
+}
+
+.form-inline .form-check {
+ width: 100%;
+}
+
+@media (min-width: 576px) {
+ .form-inline label {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-align: center;
+ align-items: center;
+ -ms-flex-pack: center;
+ justify-content: center;
+ margin-bottom: 0;
+ }
+
+ .form-inline .form-group {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex: 0 0 auto;
+ flex: 0 0 auto;
+ -ms-flex-flow: row wrap;
+ flex-flow: row wrap;
+ -ms-flex-align: center;
+ align-items: center;
+ margin-bottom: 0;
+ }
+
+ .form-inline .form-control {
+ display: inline-block;
+ width: auto;
+ vertical-align: middle;
+ }
+
+ .form-inline .form-control-plaintext {
+ display: inline-block;
+ }
+
+ .form-inline .input-group,
+ .form-inline .custom-select {
+ width: auto;
+ }
+
+ .form-inline .form-check {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-align: center;
+ align-items: center;
+ -ms-flex-pack: center;
+ justify-content: center;
+ width: auto;
+ padding-left: 0;
+ }
+
+ .form-inline .form-check-input {
+ position: relative;
+ -ms-flex-negative: 0;
+ flex-shrink: 0;
+ margin-top: 0;
+ margin-right: 0.25rem;
+ margin-left: 0;
+ }
+
+ .form-inline .custom-control {
+ -ms-flex-align: center;
+ align-items: center;
+ -ms-flex-pack: center;
+ justify-content: center;
+ }
+
+ .form-inline .custom-control-label {
+ margin-bottom: 0;
+ }
+}
+
+.btn {
+ display: inline-block;
+ font-weight: 400;
+ color: #212529;
+ text-align: center;
+ vertical-align: middle;
+ cursor: pointer;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ background-color: transparent;
+ border: 1px solid transparent;
+ padding: 0.375rem 0.75rem;
+ font-size: 1rem;
+ line-height: 1.5;
+ border-radius: 0.25rem;
+ transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .btn {
+ transition: none;
+ }
+}
+
+.btn:hover {
+ color: #212529;
+ text-decoration: none;
+}
+
+.btn:focus,
+.btn.focus {
+ outline: 0;
+ box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
+}
+
+.btn.disabled,
+.btn:disabled {
+ opacity: 0.65;
+}
+
+a.btn.disabled,
+fieldset:disabled a.btn {
+ pointer-events: none;
+}
+
+.btn-primary {
+ color: #fff;
+ background-color: #007bff;
+ border-color: #007bff;
+}
+
+.btn-primary:hover {
+ color: #fff;
+ background-color: #0069d9;
+ border-color: #0062cc;
+}
+
+.btn-primary:focus,
+.btn-primary.focus {
+ color: #fff;
+ background-color: #0069d9;
+ border-color: #0062cc;
+ box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5);
+}
+
+.btn-primary.disabled,
+.btn-primary:disabled {
+ color: #fff;
+ background-color: #007bff;
+ border-color: #007bff;
+}
+
+.btn-primary:not(:disabled):not(.disabled):active,
+.btn-primary:not(:disabled):not(.disabled).active,
+.show>.btn-primary.dropdown-toggle {
+ color: #fff;
+ background-color: #0062cc;
+ border-color: #005cbf;
+}
+
+.btn-primary:not(:disabled):not(.disabled):active:focus,
+.btn-primary:not(:disabled):not(.disabled).active:focus,
+.show>.btn-primary.dropdown-toggle:focus {
+ box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5);
+}
+
+.btn-secondary {
+ color: #fff;
+ background-color: #6c757d;
+ border-color: #6c757d;
+}
+
+.btn-secondary:hover {
+ color: #fff;
+ background-color: #5a6268;
+ border-color: #545b62;
+}
+
+.btn-secondary:focus,
+.btn-secondary.focus {
+ color: #fff;
+ background-color: #5a6268;
+ border-color: #545b62;
+ box-shadow: 0 0 0 0.2rem rgba(130, 138, 145, 0.5);
+}
+
+.btn-secondary.disabled,
+.btn-secondary:disabled {
+ color: #fff;
+ background-color: #6c757d;
+ border-color: #6c757d;
+}
+
+.btn-secondary:not(:disabled):not(.disabled):active,
+.btn-secondary:not(:disabled):not(.disabled).active,
+.show>.btn-secondary.dropdown-toggle {
+ color: #fff;
+ background-color: #545b62;
+ border-color: #4e555b;
+}
+
+.btn-secondary:not(:disabled):not(.disabled):active:focus,
+.btn-secondary:not(:disabled):not(.disabled).active:focus,
+.show>.btn-secondary.dropdown-toggle:focus {
+ box-shadow: 0 0 0 0.2rem rgba(130, 138, 145, 0.5);
+}
+
+.btn-success {
+ color: #fff;
+ background-color: #28a745;
+ border-color: #28a745;
+}
+
+.btn-success:hover {
+ color: #fff;
+ background-color: #218838;
+ border-color: #1e7e34;
+}
+
+.btn-success:focus,
+.btn-success.focus {
+ color: #fff;
+ background-color: #218838;
+ border-color: #1e7e34;
+ box-shadow: 0 0 0 0.2rem rgba(72, 180, 97, 0.5);
+}
+
+.btn-success.disabled,
+.btn-success:disabled {
+ color: #fff;
+ background-color: #28a745;
+ border-color: #28a745;
+}
+
+.btn-success:not(:disabled):not(.disabled):active,
+.btn-success:not(:disabled):not(.disabled).active,
+.show>.btn-success.dropdown-toggle {
+ color: #fff;
+ background-color: #1e7e34;
+ border-color: #1c7430;
+}
+
+.btn-success:not(:disabled):not(.disabled):active:focus,
+.btn-success:not(:disabled):not(.disabled).active:focus,
+.show>.btn-success.dropdown-toggle:focus {
+ box-shadow: 0 0 0 0.2rem rgba(72, 180, 97, 0.5);
+}
+
+.btn-info {
+ color: #fff;
+ background-color: #17a2b8;
+ border-color: #17a2b8;
+}
+
+.btn-info:hover {
+ color: #fff;
+ background-color: #138496;
+ border-color: #117a8b;
+}
+
+.btn-info:focus,
+.btn-info.focus {
+ color: #fff;
+ background-color: #138496;
+ border-color: #117a8b;
+ box-shadow: 0 0 0 0.2rem rgba(58, 176, 195, 0.5);
+}
+
+.btn-info.disabled,
+.btn-info:disabled {
+ color: #fff;
+ background-color: #17a2b8;
+ border-color: #17a2b8;
+}
+
+.btn-info:not(:disabled):not(.disabled):active,
+.btn-info:not(:disabled):not(.disabled).active,
+.show>.btn-info.dropdown-toggle {
+ color: #fff;
+ background-color: #117a8b;
+ border-color: #10707f;
+}
+
+.btn-info:not(:disabled):not(.disabled):active:focus,
+.btn-info:not(:disabled):not(.disabled).active:focus,
+.show>.btn-info.dropdown-toggle:focus {
+ box-shadow: 0 0 0 0.2rem rgba(58, 176, 195, 0.5);
+}
+
+.btn-warning {
+ color: #212529;
+ background-color: #ffc107;
+ border-color: #ffc107;
+}
+
+.btn-warning:hover {
+ color: #212529;
+ background-color: #e0a800;
+ border-color: #d39e00;
+}
+
+.btn-warning:focus,
+.btn-warning.focus {
+ color: #212529;
+ background-color: #e0a800;
+ border-color: #d39e00;
+ box-shadow: 0 0 0 0.2rem rgba(222, 170, 12, 0.5);
+}
+
+.btn-warning.disabled,
+.btn-warning:disabled {
+ color: #212529;
+ background-color: #ffc107;
+ border-color: #ffc107;
+}
+
+.btn-warning:not(:disabled):not(.disabled):active,
+.btn-warning:not(:disabled):not(.disabled).active,
+.show>.btn-warning.dropdown-toggle {
+ color: #212529;
+ background-color: #d39e00;
+ border-color: #c69500;
+}
+
+.btn-warning:not(:disabled):not(.disabled):active:focus,
+.btn-warning:not(:disabled):not(.disabled).active:focus,
+.show>.btn-warning.dropdown-toggle:focus {
+ box-shadow: 0 0 0 0.2rem rgba(222, 170, 12, 0.5);
+}
+
+.btn-danger {
+ color: #fff;
+ background-color: #dc3545;
+ border-color: #dc3545;
+}
+
+.btn-danger:hover {
+ color: #fff;
+ background-color: #c82333;
+ border-color: #bd2130;
+}
+
+.btn-danger:focus,
+.btn-danger.focus {
+ color: #fff;
+ background-color: #c82333;
+ border-color: #bd2130;
+ box-shadow: 0 0 0 0.2rem rgba(225, 83, 97, 0.5);
+}
+
+.btn-danger.disabled,
+.btn-danger:disabled {
+ color: #fff;
+ background-color: #dc3545;
+ border-color: #dc3545;
+}
+
+.btn-danger:not(:disabled):not(.disabled):active,
+.btn-danger:not(:disabled):not(.disabled).active,
+.show>.btn-danger.dropdown-toggle {
+ color: #fff;
+ background-color: #bd2130;
+ border-color: #b21f2d;
+}
+
+.btn-danger:not(:disabled):not(.disabled):active:focus,
+.btn-danger:not(:disabled):not(.disabled).active:focus,
+.show>.btn-danger.dropdown-toggle:focus {
+ box-shadow: 0 0 0 0.2rem rgba(225, 83, 97, 0.5);
+}
+
+.btn-light {
+ color: #212529;
+ background-color: #f8f9fa;
+ border-color: #f8f9fa;
+}
+
+.btn-light:hover {
+ color: #212529;
+ background-color: #e2e6ea;
+ border-color: #dae0e5;
+}
+
+.btn-light:focus,
+.btn-light.focus {
+ color: #212529;
+ background-color: #e2e6ea;
+ border-color: #dae0e5;
+ box-shadow: 0 0 0 0.2rem rgba(216, 217, 219, 0.5);
+}
+
+.btn-light.disabled,
+.btn-light:disabled {
+ color: #212529;
+ background-color: #f8f9fa;
+ border-color: #f8f9fa;
+}
+
+.btn-light:not(:disabled):not(.disabled):active,
+.btn-light:not(:disabled):not(.disabled).active,
+.show>.btn-light.dropdown-toggle {
+ color: #212529;
+ background-color: #dae0e5;
+ border-color: #d3d9df;
+}
+
+.btn-light:not(:disabled):not(.disabled):active:focus,
+.btn-light:not(:disabled):not(.disabled).active:focus,
+.show>.btn-light.dropdown-toggle:focus {
+ box-shadow: 0 0 0 0.2rem rgba(216, 217, 219, 0.5);
+}
+
+.btn-dark {
+ color: #fff;
+ background-color: #343a40;
+ border-color: #343a40;
+}
+
+.btn-dark:hover {
+ color: #fff;
+ background-color: #23272b;
+ border-color: #1d2124;
+}
+
+.btn-dark:focus,
+.btn-dark.focus {
+ color: #fff;
+ background-color: #23272b;
+ border-color: #1d2124;
+ box-shadow: 0 0 0 0.2rem rgba(82, 88, 93, 0.5);
+}
+
+.btn-dark.disabled,
+.btn-dark:disabled {
+ color: #fff;
+ background-color: #343a40;
+ border-color: #343a40;
+}
+
+.btn-dark:not(:disabled):not(.disabled):active,
+.btn-dark:not(:disabled):not(.disabled).active,
+.show>.btn-dark.dropdown-toggle {
+ color: #fff;
+ background-color: #1d2124;
+ border-color: #171a1d;
+}
+
+.btn-dark:not(:disabled):not(.disabled):active:focus,
+.btn-dark:not(:disabled):not(.disabled).active:focus,
+.show>.btn-dark.dropdown-toggle:focus {
+ box-shadow: 0 0 0 0.2rem rgba(82, 88, 93, 0.5);
+}
+
+.btn-outline-primary {
+ color: #007bff;
+ border-color: #007bff;
+}
+
+.btn-outline-primary:hover {
+ color: #fff;
+ background-color: #007bff;
+ border-color: #007bff;
+}
+
+.btn-outline-primary:focus,
+.btn-outline-primary.focus {
+ box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);
+}
+
+.btn-outline-primary.disabled,
+.btn-outline-primary:disabled {
+ color: #007bff;
+ background-color: transparent;
+}
+
+.btn-outline-primary:not(:disabled):not(.disabled):active,
+.btn-outline-primary:not(:disabled):not(.disabled).active,
+.show>.btn-outline-primary.dropdown-toggle {
+ color: #fff;
+ background-color: #007bff;
+ border-color: #007bff;
+}
+
+.btn-outline-primary:not(:disabled):not(.disabled):active:focus,
+.btn-outline-primary:not(:disabled):not(.disabled).active:focus,
+.show>.btn-outline-primary.dropdown-toggle:focus {
+ box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);
+}
+
+.btn-outline-secondary {
+ color: #6c757d;
+ border-color: #6c757d;
+}
+
+.btn-outline-secondary:hover {
+ color: #fff;
+ background-color: #6c757d;
+ border-color: #6c757d;
+}
+
+.btn-outline-secondary:focus,
+.btn-outline-secondary.focus {
+ box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);
+}
+
+.btn-outline-secondary.disabled,
+.btn-outline-secondary:disabled {
+ color: #6c757d;
+ background-color: transparent;
+}
+
+.btn-outline-secondary:not(:disabled):not(.disabled):active,
+.btn-outline-secondary:not(:disabled):not(.disabled).active,
+.show>.btn-outline-secondary.dropdown-toggle {
+ color: #fff;
+ background-color: #6c757d;
+ border-color: #6c757d;
+}
+
+.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,
+.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,
+.show>.btn-outline-secondary.dropdown-toggle:focus {
+ box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);
+}
+
+.btn-outline-success {
+ color: #28a745;
+ border-color: #28a745;
+}
+
+.btn-outline-success:hover {
+ color: #fff;
+ background-color: #28a745;
+ border-color: #28a745;
+}
+
+.btn-outline-success:focus,
+.btn-outline-success.focus {
+ box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);
+}
+
+.btn-outline-success.disabled,
+.btn-outline-success:disabled {
+ color: #28a745;
+ background-color: transparent;
+}
+
+.btn-outline-success:not(:disabled):not(.disabled):active,
+.btn-outline-success:not(:disabled):not(.disabled).active,
+.show>.btn-outline-success.dropdown-toggle {
+ color: #fff;
+ background-color: #28a745;
+ border-color: #28a745;
+}
+
+.btn-outline-success:not(:disabled):not(.disabled):active:focus,
+.btn-outline-success:not(:disabled):not(.disabled).active:focus,
+.show>.btn-outline-success.dropdown-toggle:focus {
+ box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);
+}
+
+.btn-outline-info {
+ color: #17a2b8;
+ border-color: #17a2b8;
+}
+
+.btn-outline-info:hover {
+ color: #fff;
+ background-color: #17a2b8;
+ border-color: #17a2b8;
+}
+
+.btn-outline-info:focus,
+.btn-outline-info.focus {
+ box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);
+}
+
+.btn-outline-info.disabled,
+.btn-outline-info:disabled {
+ color: #17a2b8;
+ background-color: transparent;
+}
+
+.btn-outline-info:not(:disabled):not(.disabled):active,
+.btn-outline-info:not(:disabled):not(.disabled).active,
+.show>.btn-outline-info.dropdown-toggle {
+ color: #fff;
+ background-color: #17a2b8;
+ border-color: #17a2b8;
+}
+
+.btn-outline-info:not(:disabled):not(.disabled):active:focus,
+.btn-outline-info:not(:disabled):not(.disabled).active:focus,
+.show>.btn-outline-info.dropdown-toggle:focus {
+ box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);
+}
+
+.btn-outline-warning {
+ color: #ffc107;
+ border-color: #ffc107;
+}
+
+.btn-outline-warning:hover {
+ color: #212529;
+ background-color: #ffc107;
+ border-color: #ffc107;
+}
+
+.btn-outline-warning:focus,
+.btn-outline-warning.focus {
+ box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);
+}
+
+.btn-outline-warning.disabled,
+.btn-outline-warning:disabled {
+ color: #ffc107;
+ background-color: transparent;
+}
+
+.btn-outline-warning:not(:disabled):not(.disabled):active,
+.btn-outline-warning:not(:disabled):not(.disabled).active,
+.show>.btn-outline-warning.dropdown-toggle {
+ color: #212529;
+ background-color: #ffc107;
+ border-color: #ffc107;
+}
+
+.btn-outline-warning:not(:disabled):not(.disabled):active:focus,
+.btn-outline-warning:not(:disabled):not(.disabled).active:focus,
+.show>.btn-outline-warning.dropdown-toggle:focus {
+ box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);
+}
+
+.btn-outline-danger {
+ color: #dc3545;
+ border-color: #dc3545;
+}
+
+.btn-outline-danger:hover {
+ color: #fff;
+ background-color: #dc3545;
+ border-color: #dc3545;
+}
+
+.btn-outline-danger:focus,
+.btn-outline-danger.focus {
+ box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);
+}
+
+.btn-outline-danger.disabled,
+.btn-outline-danger:disabled {
+ color: #dc3545;
+ background-color: transparent;
+}
+
+.btn-outline-danger:not(:disabled):not(.disabled):active,
+.btn-outline-danger:not(:disabled):not(.disabled).active,
+.show>.btn-outline-danger.dropdown-toggle {
+ color: #fff;
+ background-color: #dc3545;
+ border-color: #dc3545;
+}
+
+.btn-outline-danger:not(:disabled):not(.disabled):active:focus,
+.btn-outline-danger:not(:disabled):not(.disabled).active:focus,
+.show>.btn-outline-danger.dropdown-toggle:focus {
+ box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);
+}
+
+.btn-outline-light {
+ color: #f8f9fa;
+ border-color: #f8f9fa;
+}
+
+.btn-outline-light:hover {
+ color: #212529;
+ background-color: #f8f9fa;
+ border-color: #f8f9fa;
+}
+
+.btn-outline-light:focus,
+.btn-outline-light.focus {
+ box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);
+}
+
+.btn-outline-light.disabled,
+.btn-outline-light:disabled {
+ color: #f8f9fa;
+ background-color: transparent;
+}
+
+.btn-outline-light:not(:disabled):not(.disabled):active,
+.btn-outline-light:not(:disabled):not(.disabled).active,
+.show>.btn-outline-light.dropdown-toggle {
+ color: #212529;
+ background-color: #f8f9fa;
+ border-color: #f8f9fa;
+}
+
+.btn-outline-light:not(:disabled):not(.disabled):active:focus,
+.btn-outline-light:not(:disabled):not(.disabled).active:focus,
+.show>.btn-outline-light.dropdown-toggle:focus {
+ box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);
+}
+
+.btn-outline-dark {
+ color: #343a40;
+ border-color: #343a40;
+}
+
+.btn-outline-dark:hover {
+ color: #fff;
+ background-color: #343a40;
+ border-color: #343a40;
+}
+
+.btn-outline-dark:focus,
+.btn-outline-dark.focus {
+ box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);
+}
+
+.btn-outline-dark.disabled,
+.btn-outline-dark:disabled {
+ color: #343a40;
+ background-color: transparent;
+}
+
+.btn-outline-dark:not(:disabled):not(.disabled):active,
+.btn-outline-dark:not(:disabled):not(.disabled).active,
+.show>.btn-outline-dark.dropdown-toggle {
+ color: #fff;
+ background-color: #343a40;
+ border-color: #343a40;
+}
+
+.btn-outline-dark:not(:disabled):not(.disabled):active:focus,
+.btn-outline-dark:not(:disabled):not(.disabled).active:focus,
+.show>.btn-outline-dark.dropdown-toggle:focus {
+ box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);
+}
+
+.btn-link {
+ font-weight: 400;
+ color: #007bff;
+ text-decoration: none;
+}
+
+.btn-link:hover {
+ color: #0056b3;
+ text-decoration: underline;
+}
+
+.btn-link:focus,
+.btn-link.focus {
+ text-decoration: underline;
+ box-shadow: none;
+}
+
+.btn-link:disabled,
+.btn-link.disabled {
+ color: #6c757d;
+ pointer-events: none;
+}
+
+.btn-lg,
+.btn-group-lg>.btn {
+ padding: 0.5rem 1rem;
+ font-size: 1.25rem;
+ line-height: 1.5;
+ border-radius: 0.3rem;
+}
+
+.btn-sm,
+.btn-group-sm>.btn {
+ padding: 0.25rem 0.5rem;
+ font-size: 0.875rem;
+ line-height: 1.5;
+ border-radius: 0.2rem;
+}
+
+.btn-block {
+ display: block;
+ width: 100%;
+}
+
+.btn-block+.btn-block {
+ margin-top: 0.5rem;
+}
+
+input[type="submit"].btn-block,
+input[type="reset"].btn-block,
+input[type="button"].btn-block {
+ width: 100%;
+}
+
+.fade {
+ transition: opacity 0.15s linear;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .fade {
+ transition: none;
+ }
+}
+
+.fade:not(.show) {
+ opacity: 0;
+}
+
+.collapse:not(.show) {
+ display: none;
+}
+
+.collapsing {
+ position: relative;
+ height: 0;
+ overflow: hidden;
+ transition: height 0.35s ease;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .collapsing {
+ transition: none;
+ }
+}
+
+.dropup,
+.dropright,
+.dropdown,
+.dropleft {
+ position: relative;
+}
+
+.dropdown-toggle {
+ white-space: nowrap;
+}
+
+.dropdown-toggle::after {
+ display: inline-block;
+ margin-left: 0.255em;
+ vertical-align: 0.255em;
+ content: "";
+ border-top: 0.3em solid;
+ border-right: 0.3em solid transparent;
+ border-bottom: 0;
+ border-left: 0.3em solid transparent;
+}
+
+.dropdown-toggle:empty::after {
+ margin-left: 0;
+}
+
+.dropdown-menu {
+ position: absolute;
+ top: 100%;
+ left: 0;
+ z-index: 1000;
+ display: none;
+ float: left;
+ min-width: 10rem;
+ padding: 0.5rem 0;
+ margin: 0.125rem 0 0;
+ font-size: 1rem;
+ color: #212529;
+ text-align: left;
+ list-style: none;
+ background-color: #fff;
+ background-clip: padding-box;
+ border: 1px solid rgba(0, 0, 0, 0.15);
+ border-radius: 0.25rem;
+}
+
+.dropdown-menu-left {
+ right: auto;
+ left: 0;
+}
+
+.dropdown-menu-right {
+ right: 0;
+ left: auto;
+}
+
+@media (min-width: 576px) {
+ .dropdown-menu-sm-left {
+ right: auto;
+ left: 0;
+ }
+
+ .dropdown-menu-sm-right {
+ right: 0;
+ left: auto;
+ }
+}
+
+@media (min-width: 768px) {
+ .dropdown-menu-md-left {
+ right: auto;
+ left: 0;
+ }
+
+ .dropdown-menu-md-right {
+ right: 0;
+ left: auto;
+ }
+}
+
+@media (min-width: 992px) {
+ .dropdown-menu-lg-left {
+ right: auto;
+ left: 0;
+ }
+
+ .dropdown-menu-lg-right {
+ right: 0;
+ left: auto;
+ }
+}
+
+@media (min-width: 1200px) {
+ .dropdown-menu-xl-left {
+ right: auto;
+ left: 0;
+ }
+
+ .dropdown-menu-xl-right {
+ right: 0;
+ left: auto;
+ }
+}
+
+.dropup .dropdown-menu {
+ top: auto;
+ bottom: 100%;
+ margin-top: 0;
+ margin-bottom: 0.125rem;
+}
+
+.dropup .dropdown-toggle::after {
+ display: inline-block;
+ margin-left: 0.255em;
+ vertical-align: 0.255em;
+ content: "";
+ border-top: 0;
+ border-right: 0.3em solid transparent;
+ border-bottom: 0.3em solid;
+ border-left: 0.3em solid transparent;
+}
+
+.dropup .dropdown-toggle:empty::after {
+ margin-left: 0;
+}
+
+.dropright .dropdown-menu {
+ top: 0;
+ right: auto;
+ left: 100%;
+ margin-top: 0;
+ margin-left: 0.125rem;
+}
+
+.dropright .dropdown-toggle::after {
+ display: inline-block;
+ margin-left: 0.255em;
+ vertical-align: 0.255em;
+ content: "";
+ border-top: 0.3em solid transparent;
+ border-right: 0;
+ border-bottom: 0.3em solid transparent;
+ border-left: 0.3em solid;
+}
+
+.dropright .dropdown-toggle:empty::after {
+ margin-left: 0;
+}
+
+.dropright .dropdown-toggle::after {
+ vertical-align: 0;
+}
+
+.dropleft .dropdown-menu {
+ top: 0;
+ right: 100%;
+ left: auto;
+ margin-top: 0;
+ margin-right: 0.125rem;
+}
+
+.dropleft .dropdown-toggle::after {
+ display: inline-block;
+ margin-left: 0.255em;
+ vertical-align: 0.255em;
+ content: "";
+}
+
+.dropleft .dropdown-toggle::after {
+ display: none;
+}
+
+.dropleft .dropdown-toggle::before {
+ display: inline-block;
+ margin-right: 0.255em;
+ vertical-align: 0.255em;
+ content: "";
+ border-top: 0.3em solid transparent;
+ border-right: 0.3em solid;
+ border-bottom: 0.3em solid transparent;
+}
+
+.dropleft .dropdown-toggle:empty::after {
+ margin-left: 0;
+}
+
+.dropleft .dropdown-toggle::before {
+ vertical-align: 0;
+}
+
+.dropdown-menu[x-placement^="top"],
+.dropdown-menu[x-placement^="right"],
+.dropdown-menu[x-placement^="bottom"],
+.dropdown-menu[x-placement^="left"] {
+ right: auto;
+ bottom: auto;
+}
+
+.dropdown-divider {
+ height: 0;
+ margin: 0.5rem 0;
+ overflow: hidden;
+ border-top: 1px solid #e9ecef;
+}
+
+.dropdown-item {
+ display: block;
+ width: 100%;
+ padding: 0.25rem 1.5rem;
+ clear: both;
+ font-weight: 400;
+ color: #212529;
+ text-align: inherit;
+ white-space: nowrap;
+ background-color: transparent;
+ border: 0;
+}
+
+.dropdown-item:hover,
+.dropdown-item:focus {
+ color: #16181b;
+ text-decoration: none;
+ background-color: #f8f9fa;
+}
+
+.dropdown-item.active,
+.dropdown-item:active {
+ color: #fff;
+ text-decoration: none;
+ background-color: #007bff;
+}
+
+.dropdown-item.disabled,
+.dropdown-item:disabled {
+ color: #6c757d;
+ pointer-events: none;
+ background-color: transparent;
+}
+
+.dropdown-menu.show {
+ display: block;
+}
+
+.dropdown-header {
+ display: block;
+ padding: 0.5rem 1.5rem;
+ margin-bottom: 0;
+ font-size: 0.875rem;
+ color: #6c757d;
+ white-space: nowrap;
+}
+
+.dropdown-item-text {
+ display: block;
+ padding: 0.25rem 1.5rem;
+ color: #212529;
+}
+
+.btn-group,
+.btn-group-vertical {
+ position: relative;
+ display: -ms-inline-flexbox;
+ display: inline-flex;
+ vertical-align: middle;
+}
+
+.btn-group>.btn,
+.btn-group-vertical>.btn {
+ position: relative;
+ -ms-flex: 1 1 auto;
+ flex: 1 1 auto;
+}
+
+.btn-group>.btn:hover,
+.btn-group-vertical>.btn:hover {
+ z-index: 1;
+}
+
+.btn-group>.btn:focus,
+.btn-group>.btn:active,
+.btn-group>.btn.active,
+.btn-group-vertical>.btn:focus,
+.btn-group-vertical>.btn:active,
+.btn-group-vertical>.btn.active {
+ z-index: 1;
+}
+
+.btn-toolbar {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-wrap: wrap;
+ flex-wrap: wrap;
+ -ms-flex-pack: start;
+ justify-content: flex-start;
+}
+
+.btn-toolbar .input-group {
+ width: auto;
+}
+
+.btn-group>.btn:not(:first-child),
+.btn-group>.btn-group:not(:first-child) {
+ margin-left: -1px;
+}
+
+.btn-group>.btn:not(:last-child):not(.dropdown-toggle),
+.btn-group>.btn-group:not(:last-child)>.btn {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+}
+
+.btn-group>.btn:not(:first-child),
+.btn-group>.btn-group:not(:first-child)>.btn {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+}
+
+.dropdown-toggle-split {
+ padding-right: 0.5625rem;
+ padding-left: 0.5625rem;
+}
+
+.dropdown-toggle-split::after,
+.dropup .dropdown-toggle-split::after,
+.dropright .dropdown-toggle-split::after {
+ margin-left: 0;
+}
+
+.dropleft .dropdown-toggle-split::before {
+ margin-right: 0;
+}
+
+.btn-sm+.dropdown-toggle-split,
+.btn-group-sm>.btn+.dropdown-toggle-split {
+ padding-right: 0.375rem;
+ padding-left: 0.375rem;
+}
+
+.btn-lg+.dropdown-toggle-split,
+.btn-group-lg>.btn+.dropdown-toggle-split {
+ padding-right: 0.75rem;
+ padding-left: 0.75rem;
+}
+
+.btn-group-vertical {
+ -ms-flex-direction: column;
+ flex-direction: column;
+ -ms-flex-align: start;
+ align-items: flex-start;
+ -ms-flex-pack: center;
+ justify-content: center;
+}
+
+.btn-group-vertical>.btn,
+.btn-group-vertical>.btn-group {
+ width: 100%;
+}
+
+.btn-group-vertical>.btn:not(:first-child),
+.btn-group-vertical>.btn-group:not(:first-child) {
+ margin-top: -1px;
+}
+
+.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle),
+.btn-group-vertical>.btn-group:not(:last-child)>.btn {
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0;
+}
+
+.btn-group-vertical>.btn:not(:first-child),
+.btn-group-vertical>.btn-group:not(:first-child)>.btn {
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+}
+
+.btn-group-toggle>.btn,
+.btn-group-toggle>.btn-group>.btn {
+ margin-bottom: 0;
+}
+
+.btn-group-toggle>.btn input[type="radio"],
+.btn-group-toggle>.btn input[type="checkbox"],
+.btn-group-toggle>.btn-group>.btn input[type="radio"],
+.btn-group-toggle>.btn-group>.btn input[type="checkbox"] {
+ position: absolute;
+ clip: rect(0, 0, 0, 0);
+ pointer-events: none;
+}
+
+.input-group {
+ position: relative;
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-wrap: wrap;
+ flex-wrap: wrap;
+ -ms-flex-align: stretch;
+ align-items: stretch;
+ width: 100%;
+}
+
+.input-group>.form-control,
+.input-group>.form-control-plaintext,
+.input-group>.custom-select,
+.input-group>.custom-file {
+ position: relative;
+ -ms-flex: 1 1 0%;
+ flex: 1 1 0%;
+ min-width: 0;
+ margin-bottom: 0;
+}
+
+.input-group>.form-control+.form-control,
+.input-group>.form-control+.custom-select,
+.input-group>.form-control+.custom-file,
+.input-group>.form-control-plaintext+.form-control,
+.input-group>.form-control-plaintext+.custom-select,
+.input-group>.form-control-plaintext+.custom-file,
+.input-group>.custom-select+.form-control,
+.input-group>.custom-select+.custom-select,
+.input-group>.custom-select+.custom-file,
+.input-group>.custom-file+.form-control,
+.input-group>.custom-file+.custom-select,
+.input-group>.custom-file+.custom-file {
+ margin-left: -1px;
+}
+
+.input-group>.form-control:focus,
+.input-group>.custom-select:focus,
+.input-group>.custom-file .custom-file-input:focus~.custom-file-label {
+ z-index: 3;
+}
+
+.input-group>.custom-file .custom-file-input:focus {
+ z-index: 4;
+}
+
+.input-group>.form-control:not(:last-child),
+.input-group>.custom-select:not(:last-child) {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+}
+
+.input-group>.form-control:not(:first-child),
+.input-group>.custom-select:not(:first-child) {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+}
+
+.input-group>.custom-file {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-align: center;
+ align-items: center;
+}
+
+.input-group>.custom-file:not(:last-child) .custom-file-label,
+.input-group>.custom-file:not(:last-child) .custom-file-label::after {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+}
+
+.input-group>.custom-file:not(:first-child) .custom-file-label {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+}
+
+.input-group-prepend,
+.input-group-append {
+ display: -ms-flexbox;
+ display: flex;
+}
+
+.input-group-prepend .btn,
+.input-group-append .btn {
+ position: relative;
+ z-index: 2;
+}
+
+.input-group-prepend .btn:focus,
+.input-group-append .btn:focus {
+ z-index: 3;
+}
+
+.input-group-prepend .btn+.btn,
+.input-group-prepend .btn+.input-group-text,
+.input-group-prepend .input-group-text+.input-group-text,
+.input-group-prepend .input-group-text+.btn,
+.input-group-append .btn+.btn,
+.input-group-append .btn+.input-group-text,
+.input-group-append .input-group-text+.input-group-text,
+.input-group-append .input-group-text+.btn {
+ margin-left: -1px;
+}
+
+.input-group-prepend {
+ margin-right: -1px;
+}
+
+.input-group-append {
+ margin-left: -1px;
+}
+
+.input-group-text {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-align: center;
+ align-items: center;
+ padding: 0.375rem 0.75rem;
+ margin-bottom: 0;
+ font-size: 1rem;
+ font-weight: 400;
+ line-height: 1.5;
+ color: #495057;
+ text-align: center;
+ white-space: nowrap;
+ background-color: #e9ecef;
+ border: 1px solid #ced4da;
+ border-radius: 0.25rem;
+}
+
+.input-group-text input[type="radio"],
+.input-group-text input[type="checkbox"] {
+ margin-top: 0;
+}
+
+.input-group-lg>.form-control:not(textarea),
+.input-group-lg>.custom-select {
+ height: calc(1.5em + 1rem + 2px);
+}
+
+.input-group-lg>.form-control,
+.input-group-lg>.custom-select,
+.input-group-lg>.input-group-prepend>.input-group-text,
+.input-group-lg>.input-group-append>.input-group-text,
+.input-group-lg>.input-group-prepend>.btn,
+.input-group-lg>.input-group-append>.btn {
+ padding: 0.5rem 1rem;
+ font-size: 1.25rem;
+ line-height: 1.5;
+ border-radius: 0.3rem;
+}
+
+.input-group-sm>.form-control:not(textarea),
+.input-group-sm>.custom-select {
+ height: calc(1.5em + 0.5rem + 2px);
+}
+
+.input-group-sm>.form-control,
+.input-group-sm>.custom-select,
+.input-group-sm>.input-group-prepend>.input-group-text,
+.input-group-sm>.input-group-append>.input-group-text,
+.input-group-sm>.input-group-prepend>.btn,
+.input-group-sm>.input-group-append>.btn {
+ padding: 0.25rem 0.5rem;
+ font-size: 0.875rem;
+ line-height: 1.5;
+ border-radius: 0.2rem;
+}
+
+.input-group-lg>.custom-select,
+.input-group-sm>.custom-select {
+ padding-right: 1.75rem;
+}
+
+.input-group>.input-group-prepend>.btn,
+.input-group>.input-group-prepend>.input-group-text,
+.input-group>.input-group-append:not(:last-child)>.btn,
+.input-group>.input-group-append:not(:last-child)>.input-group-text,
+.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),
+.input-group>.input-group-append:last-child>.input-group-text:not(:last-child) {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+}
+
+.input-group>.input-group-append>.btn,
+.input-group>.input-group-append>.input-group-text,
+.input-group>.input-group-prepend:not(:first-child)>.btn,
+.input-group>.input-group-prepend:not(:first-child)>.input-group-text,
+.input-group>.input-group-prepend:first-child>.btn:not(:first-child),
+.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child) {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+}
+
+.custom-control {
+ position: relative;
+ display: block;
+ min-height: 1.5rem;
+ padding-left: 1.5rem;
+}
+
+.custom-control-inline {
+ display: -ms-inline-flexbox;
+ display: inline-flex;
+ margin-right: 1rem;
+}
+
+.custom-control-input {
+ position: absolute;
+ left: 0;
+ z-index: -1;
+ width: 1rem;
+ height: 1.25rem;
+ opacity: 0;
+}
+
+.custom-control-input:checked~.custom-control-label::before {
+ color: #fff;
+ border-color: #007bff;
+ background-color: #007bff;
+}
+
+.custom-control-input:focus~.custom-control-label::before {
+ box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
+}
+
+.custom-control-input:focus:not(:checked)~.custom-control-label::before {
+ border-color: #80bdff;
+}
+
+.custom-control-input:not(:disabled):active~.custom-control-label::before {
+ color: #fff;
+ background-color: #b3d7ff;
+ border-color: #b3d7ff;
+}
+
+.custom-control-input[disabled]~.custom-control-label,
+.custom-control-input:disabled~.custom-control-label {
+ color: #6c757d;
+}
+
+.custom-control-input[disabled]~.custom-control-label::before,
+.custom-control-input:disabled~.custom-control-label::before {
+ background-color: #e9ecef;
+}
+
+.custom-control-label {
+ position: relative;
+ margin-bottom: 0;
+ vertical-align: top;
+}
+
+.custom-control-label::before {
+ position: absolute;
+ top: 0.25rem;
+ left: -1.5rem;
+ display: block;
+ width: 1rem;
+ height: 1rem;
+ pointer-events: none;
+ content: "";
+ background-color: #fff;
+ border: #adb5bd solid 1px;
+}
+
+.custom-control-label::after {
+ position: absolute;
+ top: 0.25rem;
+ left: -1.5rem;
+ display: block;
+ width: 1rem;
+ height: 1rem;
+ content: "";
+ background: no-repeat 50% / 50% 50%;
+}
+
+.custom-checkbox .custom-control-label::before {
+ border-radius: 0.25rem;
+}
+
+.custom-checkbox .custom-control-input:checked~.custom-control-label::after {
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3e%3c/svg%3e");
+}
+
+.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::before {
+ border-color: #007bff;
+ background-color: #007bff;
+}
+
+.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::after {
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e");
+}
+
+.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label::before {
+ background-color: rgba(0, 123, 255, 0.5);
+}
+
+.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label::before {
+ background-color: rgba(0, 123, 255, 0.5);
+}
+
+.custom-radio .custom-control-label::before {
+ border-radius: 50%;
+}
+
+.custom-radio .custom-control-input:checked~.custom-control-label::after {
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e");
+}
+
+.custom-radio .custom-control-input:disabled:checked~.custom-control-label::before {
+ background-color: rgba(0, 123, 255, 0.5);
+}
+
+.custom-switch {
+ padding-left: 2.25rem;
+}
+
+.custom-switch .custom-control-label::before {
+ left: -2.25rem;
+ width: 1.75rem;
+ pointer-events: all;
+ border-radius: 0.5rem;
+}
+
+.custom-switch .custom-control-label::after {
+ top: calc(0.25rem + 2px);
+ left: calc(-2.25rem + 2px);
+ width: calc(1rem - 4px);
+ height: calc(1rem - 4px);
+ background-color: #adb5bd;
+ border-radius: 0.5rem;
+ transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-transform 0.15s ease-in-out;
+ transition: transform 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
+ transition: transform 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-transform 0.15s ease-in-out;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .custom-switch .custom-control-label::after {
+ transition: none;
+ }
+}
+
+.custom-switch .custom-control-input:checked~.custom-control-label::after {
+ background-color: #fff;
+ -webkit-transform: translateX(0.75rem);
+ transform: translateX(0.75rem);
+}
+
+.custom-switch .custom-control-input:disabled:checked~.custom-control-label::before {
+ background-color: rgba(0, 123, 255, 0.5);
+}
+
+.custom-select {
+ display: inline-block;
+ width: 100%;
+ height: calc(1.5em + 0.75rem + 2px);
+ padding: 0.375rem 1.75rem 0.375rem 0.75rem;
+ font-size: 1rem;
+ font-weight: 400;
+ line-height: 1.5;
+ color: #495057;
+ vertical-align: middle;
+ background: #fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px;
+ border: 1px solid #ced4da;
+ border-radius: 0.25rem;
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ appearance: none;
+}
+
+.custom-select:focus {
+ border-color: #80bdff;
+ outline: 0;
+ box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
+}
+
+.custom-select:focus::-ms-value {
+ color: #495057;
+ background-color: #fff;
+}
+
+.custom-select[multiple],
+.custom-select[size]:not([size="1"]) {
+ height: auto;
+ padding-right: 0.75rem;
+ background-image: none;
+}
+
+.custom-select:disabled {
+ color: #6c757d;
+ background-color: #e9ecef;
+}
+
+.custom-select::-ms-expand {
+ display: none;
+}
+
+.custom-select:-moz-focusring {
+ color: transparent;
+ text-shadow: 0 0 0 #495057;
+}
+
+.custom-select-sm {
+ height: calc(1.5em + 0.5rem + 2px);
+ padding-top: 0.25rem;
+ padding-bottom: 0.25rem;
+ padding-left: 0.5rem;
+ font-size: 0.875rem;
+}
+
+.custom-select-lg {
+ height: calc(1.5em + 1rem + 2px);
+ padding-top: 0.5rem;
+ padding-bottom: 0.5rem;
+ padding-left: 1rem;
+ font-size: 1.25rem;
+}
+
+.custom-file {
+ position: relative;
+ display: inline-block;
+ width: 100%;
+ height: calc(1.5em + 0.75rem + 2px);
+ margin-bottom: 0;
+}
+
+.custom-file-input {
+ position: relative;
+ z-index: 2;
+ width: 100%;
+ height: calc(1.5em + 0.75rem + 2px);
+ margin: 0;
+ opacity: 0;
+}
+
+.custom-file-input:focus~.custom-file-label {
+ border-color: #80bdff;
+ box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
+}
+
+.custom-file-input[disabled]~.custom-file-label,
+.custom-file-input:disabled~.custom-file-label {
+ background-color: #e9ecef;
+}
+
+.custom-file-input:lang(en)~.custom-file-label::after {
+ content: "Browse";
+}
+
+.custom-file-input~.custom-file-label[data-browse]::after {
+ content: attr(data-browse);
+}
+
+.custom-file-label {
+ position: absolute;
+ top: 0;
+ right: 0;
+ left: 0;
+ z-index: 1;
+ height: calc(1.5em + 0.75rem + 2px);
+ padding: 0.375rem 0.75rem;
+ font-weight: 400;
+ line-height: 1.5;
+ color: #495057;
+ background-color: #fff;
+ border: 1px solid #ced4da;
+ border-radius: 0.25rem;
+}
+
+.custom-file-label::after {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ z-index: 3;
+ display: block;
+ height: calc(1.5em + 0.75rem);
+ padding: 0.375rem 0.75rem;
+ line-height: 1.5;
+ color: #495057;
+ content: "Browse";
+ background-color: #e9ecef;
+ border-left: inherit;
+ border-radius: 0 0.25rem 0.25rem 0;
+}
+
+.custom-range {
+ width: 100%;
+ height: 1.4rem;
+ padding: 0;
+ background-color: transparent;
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ appearance: none;
+}
+
+.custom-range:focus {
+ outline: none;
+}
+
+.custom-range:focus::-webkit-slider-thumb {
+ box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
+}
+
+.custom-range:focus::-moz-range-thumb {
+ box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
+}
+
+.custom-range:focus::-ms-thumb {
+ box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
+}
+
+.custom-range::-moz-focus-outer {
+ border: 0;
+}
+
+.custom-range::-webkit-slider-thumb {
+ width: 1rem;
+ height: 1rem;
+ margin-top: -0.25rem;
+ background-color: #007bff;
+ border: 0;
+ border-radius: 1rem;
+ -webkit-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
+ transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
+ -webkit-appearance: none;
+ appearance: none;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .custom-range::-webkit-slider-thumb {
+ -webkit-transition: none;
+ transition: none;
+ }
+}
+
+.custom-range::-webkit-slider-thumb:active {
+ background-color: #b3d7ff;
+}
+
+.custom-range::-webkit-slider-runnable-track {
+ width: 100%;
+ height: 0.5rem;
+ color: transparent;
+ cursor: pointer;
+ background-color: #dee2e6;
+ border-color: transparent;
+ border-radius: 1rem;
+}
+
+.custom-range::-moz-range-thumb {
+ width: 1rem;
+ height: 1rem;
+ background-color: #007bff;
+ border: 0;
+ border-radius: 1rem;
+ -moz-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
+ transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
+ -moz-appearance: none;
+ appearance: none;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .custom-range::-moz-range-thumb {
+ -moz-transition: none;
+ transition: none;
+ }
+}
+
+.custom-range::-moz-range-thumb:active {
+ background-color: #b3d7ff;
+}
+
+.custom-range::-moz-range-track {
+ width: 100%;
+ height: 0.5rem;
+ color: transparent;
+ cursor: pointer;
+ background-color: #dee2e6;
+ border-color: transparent;
+ border-radius: 1rem;
+}
+
+.custom-range::-ms-thumb {
+ width: 1rem;
+ height: 1rem;
+ margin-top: 0;
+ margin-right: 0.2rem;
+ margin-left: 0.2rem;
+ background-color: #007bff;
+ border: 0;
+ border-radius: 1rem;
+ -ms-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
+ transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
+ appearance: none;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .custom-range::-ms-thumb {
+ -ms-transition: none;
+ transition: none;
+ }
+}
+
+.custom-range::-ms-thumb:active {
+ background-color: #b3d7ff;
+}
+
+.custom-range::-ms-track {
+ width: 100%;
+ height: 0.5rem;
+ color: transparent;
+ cursor: pointer;
+ background-color: transparent;
+ border-color: transparent;
+ border-width: 0.5rem;
+}
+
+.custom-range::-ms-fill-lower {
+ background-color: #dee2e6;
+ border-radius: 1rem;
+}
+
+.custom-range::-ms-fill-upper {
+ margin-right: 15px;
+ background-color: #dee2e6;
+ border-radius: 1rem;
+}
+
+.custom-range:disabled::-webkit-slider-thumb {
+ background-color: #adb5bd;
+}
+
+.custom-range:disabled::-webkit-slider-runnable-track {
+ cursor: default;
+}
+
+.custom-range:disabled::-moz-range-thumb {
+ background-color: #adb5bd;
+}
+
+.custom-range:disabled::-moz-range-track {
+ cursor: default;
+}
+
+.custom-range:disabled::-ms-thumb {
+ background-color: #adb5bd;
+}
+
+.custom-control-label::before,
+.custom-file-label,
+.custom-select {
+ transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
+}
+
+@media (prefers-reduced-motion: reduce) {
+
+ .custom-control-label::before,
+ .custom-file-label,
+ .custom-select {
+ transition: none;
+ }
+}
+
+.nav {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-wrap: wrap;
+ flex-wrap: wrap;
+ padding-left: 0;
+ margin-bottom: 0;
+ list-style: none;
+}
+
+.nav-link {
+ display: block;
+ padding: 0.5rem 1rem;
+}
+
+.nav-link:hover,
+.nav-link:focus {
+ text-decoration: none;
+}
+
+.nav-link.disabled {
+ color: #6c757d;
+ pointer-events: none;
+ cursor: default;
+}
+
+.nav-tabs {
+ border-bottom: 1px solid #dee2e6;
+}
+
+.nav-tabs .nav-item {
+ margin-bottom: -1px;
+}
+
+.nav-tabs .nav-link {
+ border: 1px solid transparent;
+ border-top-left-radius: 0.25rem;
+ border-top-right-radius: 0.25rem;
+}
+
+.nav-tabs .nav-link:hover,
+.nav-tabs .nav-link:focus {
+ border-color: #e9ecef #e9ecef #dee2e6;
+}
+
+.nav-tabs .nav-link.disabled {
+ color: #6c757d;
+ background-color: transparent;
+ border-color: transparent;
+}
+
+.nav-tabs .nav-link.active,
+.nav-tabs .nav-item.show .nav-link {
+ color: #495057;
+ background-color: #fff;
+ border-color: #dee2e6 #dee2e6 #fff;
+}
+
+.nav-tabs .dropdown-menu {
+ margin-top: -1px;
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+}
+
+.nav-pills .nav-link {
+ border-radius: 0.25rem;
+}
+
+.nav-pills .nav-link.active,
+.nav-pills .show>.nav-link {
+ color: #fff;
+ background-color: #007bff;
+}
+
+.nav-fill .nav-item {
+ -ms-flex: 1 1 auto;
+ flex: 1 1 auto;
+ text-align: center;
+}
+
+.nav-justified .nav-item {
+ -ms-flex-preferred-size: 0;
+ flex-basis: 0;
+ -ms-flex-positive: 1;
+ flex-grow: 1;
+ text-align: center;
+}
+
+.tab-content>.tab-pane {
+ display: none;
+}
+
+.tab-content>.active {
+ display: block;
+}
+
+.navbar {
+ position: relative;
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-wrap: wrap;
+ flex-wrap: wrap;
+ -ms-flex-align: center;
+ align-items: center;
+ -ms-flex-pack: justify;
+ justify-content: space-between;
+ padding: 0.5rem 1rem;
+}
+
+.navbar .container,
+.navbar .container-fluid,
+.navbar .container-sm,
+.navbar .container-md,
+.navbar .container-lg,
+.navbar .container-xl {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-wrap: wrap;
+ flex-wrap: wrap;
+ -ms-flex-align: center;
+ align-items: center;
+ -ms-flex-pack: justify;
+ justify-content: space-between;
+}
+
+.navbar-brand {
+ display: inline-block;
+ padding-top: 0.3125rem;
+ padding-bottom: 0.3125rem;
+ margin-right: 1rem;
+ font-size: 1.25rem;
+ line-height: inherit;
+ white-space: nowrap;
+}
+
+.navbar-brand:hover,
+.navbar-brand:focus {
+ text-decoration: none;
+}
+
+.navbar-nav {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-direction: column;
+ flex-direction: column;
+ padding-left: 0;
+ margin-bottom: 0;
+ list-style: none;
+}
+
+.navbar-nav .nav-link {
+ padding-right: 0;
+ padding-left: 0;
+}
+
+.navbar-nav .dropdown-menu {
+ position: static;
+ float: none;
+}
+
+.navbar-text {
+ display: inline-block;
+ padding-top: 0.5rem;
+ padding-bottom: 0.5rem;
+}
+
+.navbar-collapse {
+ -ms-flex-preferred-size: 100%;
+ flex-basis: 100%;
+ -ms-flex-positive: 1;
+ flex-grow: 1;
+ -ms-flex-align: center;
+ align-items: center;
+}
+
+.navbar-toggler {
+ padding: 0.25rem 0.75rem;
+ font-size: 1.25rem;
+ line-height: 1;
+ background-color: transparent;
+ border: 1px solid transparent;
+ border-radius: 0.25rem;
+}
+
+.navbar-toggler:hover,
+.navbar-toggler:focus {
+ text-decoration: none;
+}
+
+.navbar-toggler-icon {
+ display: inline-block;
+ width: 1.5em;
+ height: 1.5em;
+ vertical-align: middle;
+ content: "";
+ background: no-repeat center center;
+ background-size: 100% 100%;
+}
+
+@media (max-width: 575.98px) {
+
+ .navbar-expand-sm>.container,
+ .navbar-expand-sm>.container-fluid,
+ .navbar-expand-sm>.container-sm,
+ .navbar-expand-sm>.container-md,
+ .navbar-expand-sm>.container-lg,
+ .navbar-expand-sm>.container-xl {
+ padding-right: 0;
+ padding-left: 0;
+ }
+}
+
+@media (min-width: 576px) {
+ .navbar-expand-sm {
+ -ms-flex-flow: row nowrap;
+ flex-flow: row nowrap;
+ -ms-flex-pack: start;
+ justify-content: flex-start;
+ }
+
+ .navbar-expand-sm .navbar-nav {
+ -ms-flex-direction: row;
+ flex-direction: row;
+ }
+
+ .navbar-expand-sm .navbar-nav .dropdown-menu {
+ position: absolute;
+ }
+
+ .navbar-expand-sm .navbar-nav .nav-link {
+ padding-right: 0.5rem;
+ padding-left: 0.5rem;
+ }
+
+ .navbar-expand-sm>.container,
+ .navbar-expand-sm>.container-fluid,
+ .navbar-expand-sm>.container-sm,
+ .navbar-expand-sm>.container-md,
+ .navbar-expand-sm>.container-lg,
+ .navbar-expand-sm>.container-xl {
+ -ms-flex-wrap: nowrap;
+ flex-wrap: nowrap;
+ }
+
+ .navbar-expand-sm .navbar-collapse {
+ display: -ms-flexbox !important;
+ display: flex !important;
+ -ms-flex-preferred-size: auto;
+ flex-basis: auto;
+ }
+
+ .navbar-expand-sm .navbar-toggler {
+ display: none;
+ }
+}
+
+@media (max-width: 767.98px) {
+
+ .navbar-expand-md>.container,
+ .navbar-expand-md>.container-fluid,
+ .navbar-expand-md>.container-sm,
+ .navbar-expand-md>.container-md,
+ .navbar-expand-md>.container-lg,
+ .navbar-expand-md>.container-xl {
+ padding-right: 0;
+ padding-left: 0;
+ }
+}
+
+@media (min-width: 768px) {
+ .navbar-expand-md {
+ -ms-flex-flow: row nowrap;
+ flex-flow: row nowrap;
+ -ms-flex-pack: start;
+ justify-content: flex-start;
+ }
+
+ .navbar-expand-md .navbar-nav {
+ -ms-flex-direction: row;
+ flex-direction: row;
+ }
+
+ .navbar-expand-md .navbar-nav .dropdown-menu {
+ position: absolute;
+ }
+
+ .navbar-expand-md .navbar-nav .nav-link {
+ padding-right: 0.5rem;
+ padding-left: 0.5rem;
+ }
+
+ .navbar-expand-md>.container,
+ .navbar-expand-md>.container-fluid,
+ .navbar-expand-md>.container-sm,
+ .navbar-expand-md>.container-md,
+ .navbar-expand-md>.container-lg,
+ .navbar-expand-md>.container-xl {
+ -ms-flex-wrap: nowrap;
+ flex-wrap: nowrap;
+ }
+
+ .navbar-expand-md .navbar-collapse {
+ display: -ms-flexbox !important;
+ display: flex !important;
+ -ms-flex-preferred-size: auto;
+ flex-basis: auto;
+ }
+
+ .navbar-expand-md .navbar-toggler {
+ display: none;
+ }
+}
+
+@media (max-width: 991.98px) {
+
+ .navbar-expand-lg>.container,
+ .navbar-expand-lg>.container-fluid,
+ .navbar-expand-lg>.container-sm,
+ .navbar-expand-lg>.container-md,
+ .navbar-expand-lg>.container-lg,
+ .navbar-expand-lg>.container-xl {
+ padding-right: 0;
+ padding-left: 0;
+ }
+}
+
+@media (min-width: 992px) {
+ .navbar-expand-lg {
+ -ms-flex-flow: row nowrap;
+ flex-flow: row nowrap;
+ -ms-flex-pack: start;
+ justify-content: flex-start;
+ }
+
+ .navbar-expand-lg .navbar-nav {
+ -ms-flex-direction: row;
+ flex-direction: row;
+ }
+
+ .navbar-expand-lg .navbar-nav .dropdown-menu {
+ position: absolute;
+ }
+
+ .navbar-expand-lg .navbar-nav .nav-link {
+ padding-right: 0.5rem;
+ padding-left: 0.5rem;
+ }
+
+ .navbar-expand-lg>.container,
+ .navbar-expand-lg>.container-fluid,
+ .navbar-expand-lg>.container-sm,
+ .navbar-expand-lg>.container-md,
+ .navbar-expand-lg>.container-lg,
+ .navbar-expand-lg>.container-xl {
+ -ms-flex-wrap: nowrap;
+ flex-wrap: nowrap;
+ }
+
+ .navbar-expand-lg .navbar-collapse {
+ display: -ms-flexbox !important;
+ display: flex !important;
+ -ms-flex-preferred-size: auto;
+ flex-basis: auto;
+ }
+
+ .navbar-expand-lg .navbar-toggler {
+ display: none;
+ }
+}
+
+@media (max-width: 1199.98px) {
+
+ .navbar-expand-xl>.container,
+ .navbar-expand-xl>.container-fluid,
+ .navbar-expand-xl>.container-sm,
+ .navbar-expand-xl>.container-md,
+ .navbar-expand-xl>.container-lg,
+ .navbar-expand-xl>.container-xl {
+ padding-right: 0;
+ padding-left: 0;
+ }
+}
+
+@media (min-width: 1200px) {
+ .navbar-expand-xl {
+ -ms-flex-flow: row nowrap;
+ flex-flow: row nowrap;
+ -ms-flex-pack: start;
+ justify-content: flex-start;
+ }
+
+ .navbar-expand-xl .navbar-nav {
+ -ms-flex-direction: row;
+ flex-direction: row;
+ }
+
+ .navbar-expand-xl .navbar-nav .dropdown-menu {
+ position: absolute;
+ }
+
+ .navbar-expand-xl .navbar-nav .nav-link {
+ padding-right: 0.5rem;
+ padding-left: 0.5rem;
+ }
+
+ .navbar-expand-xl>.container,
+ .navbar-expand-xl>.container-fluid,
+ .navbar-expand-xl>.container-sm,
+ .navbar-expand-xl>.container-md,
+ .navbar-expand-xl>.container-lg,
+ .navbar-expand-xl>.container-xl {
+ -ms-flex-wrap: nowrap;
+ flex-wrap: nowrap;
+ }
+
+ .navbar-expand-xl .navbar-collapse {
+ display: -ms-flexbox !important;
+ display: flex !important;
+ -ms-flex-preferred-size: auto;
+ flex-basis: auto;
+ }
+
+ .navbar-expand-xl .navbar-toggler {
+ display: none;
+ }
+}
+
+.navbar-expand {
+ -ms-flex-flow: row nowrap;
+ flex-flow: row nowrap;
+ -ms-flex-pack: start;
+ justify-content: flex-start;
+}
+
+.navbar-expand>.container,
+.navbar-expand>.container-fluid,
+.navbar-expand>.container-sm,
+.navbar-expand>.container-md,
+.navbar-expand>.container-lg,
+.navbar-expand>.container-xl {
+ padding-right: 0;
+ padding-left: 0;
+}
+
+.navbar-expand .navbar-nav {
+ -ms-flex-direction: row;
+ flex-direction: row;
+}
+
+.navbar-expand .navbar-nav .dropdown-menu {
+ position: absolute;
+}
+
+.navbar-expand .navbar-nav .nav-link {
+ padding-right: 0.5rem;
+ padding-left: 0.5rem;
+}
+
+.navbar-expand>.container,
+.navbar-expand>.container-fluid,
+.navbar-expand>.container-sm,
+.navbar-expand>.container-md,
+.navbar-expand>.container-lg,
+.navbar-expand>.container-xl {
+ -ms-flex-wrap: nowrap;
+ flex-wrap: nowrap;
+}
+
+.navbar-expand .navbar-collapse {
+ display: -ms-flexbox !important;
+ display: flex !important;
+ -ms-flex-preferred-size: auto;
+ flex-basis: auto;
+}
+
+.navbar-expand .navbar-toggler {
+ display: none;
+}
+
+.navbar-light .navbar-brand {
+ color: rgba(0, 0, 0, 0.9);
+}
+
+.navbar-light .navbar-brand:hover,
+.navbar-light .navbar-brand:focus {
+ color: rgba(0, 0, 0, 0.9);
+}
+
+.navbar-light .navbar-nav .nav-link {
+ color: rgba(0, 0, 0, 0.5);
+}
+
+.navbar-light .navbar-nav .nav-link:hover,
+.navbar-light .navbar-nav .nav-link:focus {
+ color: rgba(0, 0, 0, 0.7);
+}
+
+.navbar-light .navbar-nav .nav-link.disabled {
+ color: rgba(0, 0, 0, 0.3);
+}
+
+.navbar-light .navbar-nav .show>.nav-link,
+.navbar-light .navbar-nav .active>.nav-link,
+.navbar-light .navbar-nav .nav-link.show,
+.navbar-light .navbar-nav .nav-link.active {
+ color: rgba(0, 0, 0, 0.9);
+}
+
+.navbar-light .navbar-toggler {
+ color: rgba(0, 0, 0, 0.5);
+ border-color: rgba(0, 0, 0, 0.1);
+}
+
+.navbar-light .navbar-toggler-icon {
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba(0, 0, 0, 0.5)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
+}
+
+.navbar-light .navbar-text {
+ color: rgba(0, 0, 0, 0.5);
+}
+
+.navbar-light .navbar-text a {
+ color: rgba(0, 0, 0, 0.9);
+}
+
+.navbar-light .navbar-text a:hover,
+.navbar-light .navbar-text a:focus {
+ color: rgba(0, 0, 0, 0.9);
+}
+
+.navbar-dark .navbar-brand {
+ color: #fff;
+}
+
+.navbar-dark .navbar-brand:hover,
+.navbar-dark .navbar-brand:focus {
+ color: #fff;
+}
+
+.navbar-dark .navbar-nav .nav-link {
+ color: rgba(255, 255, 255, 0.5);
+}
+
+.navbar-dark .navbar-nav .nav-link:hover,
+.navbar-dark .navbar-nav .nav-link:focus {
+ color: rgba(255, 255, 255, 0.75);
+}
+
+.navbar-dark .navbar-nav .nav-link.disabled {
+ color: rgba(255, 255, 255, 0.25);
+}
+
+.navbar-dark .navbar-nav .show>.nav-link,
+.navbar-dark .navbar-nav .active>.nav-link,
+.navbar-dark .navbar-nav .nav-link.show,
+.navbar-dark .navbar-nav .nav-link.active {
+ color: #fff;
+}
+
+.navbar-dark .navbar-toggler {
+ color: rgba(255, 255, 255, 0.5);
+ border-color: rgba(255, 255, 255, 0.1);
+}
+
+.navbar-dark .navbar-toggler-icon {
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba(255, 255, 255, 0.5)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
+}
+
+.navbar-dark .navbar-text {
+ color: rgba(255, 255, 255, 0.5);
+}
+
+.navbar-dark .navbar-text a {
+ color: #fff;
+}
+
+.navbar-dark .navbar-text a:hover,
+.navbar-dark .navbar-text a:focus {
+ color: #fff;
+}
+
+.card {
+ position: relative;
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-direction: column;
+ flex-direction: column;
+ min-width: 0;
+ word-wrap: break-word;
+ background-color: #fff;
+ background-clip: border-box;
+ border: 1px solid rgba(0, 0, 0, 0.125);
+ border-radius: 0.25rem;
+}
+
+.card>hr {
+ margin-right: 0;
+ margin-left: 0;
+}
+
+.card>.list-group:first-child .list-group-item:first-child {
+ border-top-left-radius: 0.25rem;
+ border-top-right-radius: 0.25rem;
+}
+
+.card>.list-group:last-child .list-group-item:last-child {
+ border-bottom-right-radius: 0.25rem;
+ border-bottom-left-radius: 0.25rem;
+}
+
+.card-body {
+ -ms-flex: 1 1 auto;
+ flex: 1 1 auto;
+ min-height: 1px;
+ padding: 1.25rem;
+}
+
+.card-title {
+ margin-bottom: 0.75rem;
+}
+
+.card-subtitle {
+ margin-top: -0.375rem;
+ margin-bottom: 0;
+}
+
+.card-text:last-child {
+ margin-bottom: 0;
+}
+
+.card-link:hover {
+ text-decoration: none;
+}
+
+.card-link+.card-link {
+ margin-left: 1.25rem;
+}
+
+.card-header {
+ padding: 0.75rem 1.25rem;
+ margin-bottom: 0;
+ background-color: rgba(0, 0, 0, 0.03);
+ border-bottom: 1px solid rgba(0, 0, 0, 0.125);
+}
+
+.card-header:first-child {
+ border-radius: calc(0.25rem - 1px) calc(0.25rem - 1px) 0 0;
+}
+
+.card-header+.list-group .list-group-item:first-child {
+ border-top: 0;
+}
+
+.card-footer {
+ padding: 0.75rem 1.25rem;
+ background-color: rgba(0, 0, 0, 0.03);
+ border-top: 1px solid rgba(0, 0, 0, 0.125);
+}
+
+.card-footer:last-child {
+ border-radius: 0 0 calc(0.25rem - 1px) calc(0.25rem - 1px);
+}
+
+.card-header-tabs {
+ margin-right: -0.625rem;
+ margin-bottom: -0.75rem;
+ margin-left: -0.625rem;
+ border-bottom: 0;
+}
+
+.card-header-pills {
+ margin-right: -0.625rem;
+ margin-left: -0.625rem;
+}
+
+.card-img-overlay {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ padding: 1.25rem;
+}
+
+.card-img,
+.card-img-top,
+.card-img-bottom {
+ -ms-flex-negative: 0;
+ flex-shrink: 0;
+ width: 100%;
+}
+
+.card-img,
+.card-img-top {
+ border-top-left-radius: calc(0.25rem - 1px);
+ border-top-right-radius: calc(0.25rem - 1px);
+}
+
+.card-img,
+.card-img-bottom {
+ border-bottom-right-radius: calc(0.25rem - 1px);
+ border-bottom-left-radius: calc(0.25rem - 1px);
+}
+
+.card-deck .card {
+ margin-bottom: 15px;
+}
+
+@media (min-width: 576px) {
+ .card-deck {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-flow: row wrap;
+ flex-flow: row wrap;
+ margin-right: -15px;
+ margin-left: -15px;
+ }
+
+ .card-deck .card {
+ -ms-flex: 1 0 0%;
+ flex: 1 0 0%;
+ margin-right: 15px;
+ margin-bottom: 0;
+ margin-left: 15px;
+ }
+}
+
+.card-group>.card {
+ margin-bottom: 15px;
+}
+
+@media (min-width: 576px) {
+ .card-group {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-flow: row wrap;
+ flex-flow: row wrap;
+ }
+
+ .card-group>.card {
+ -ms-flex: 1 0 0%;
+ flex: 1 0 0%;
+ margin-bottom: 0;
+ }
+
+ .card-group>.card+.card {
+ margin-left: 0;
+ border-left: 0;
+ }
+
+ .card-group>.card:not(:last-child) {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ }
+
+ .card-group>.card:not(:last-child) .card-img-top,
+ .card-group>.card:not(:last-child) .card-header {
+ border-top-right-radius: 0;
+ }
+
+ .card-group>.card:not(:last-child) .card-img-bottom,
+ .card-group>.card:not(:last-child) .card-footer {
+ border-bottom-right-radius: 0;
+ }
+
+ .card-group>.card:not(:first-child) {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+ }
+
+ .card-group>.card:not(:first-child) .card-img-top,
+ .card-group>.card:not(:first-child) .card-header {
+ border-top-left-radius: 0;
+ }
+
+ .card-group>.card:not(:first-child) .card-img-bottom,
+ .card-group>.card:not(:first-child) .card-footer {
+ border-bottom-left-radius: 0;
+ }
+}
+
+.card-columns .card {
+ margin-bottom: 0.75rem;
+}
+
+@media (min-width: 576px) {
+ .card-columns {
+ -webkit-column-count: 3;
+ -moz-column-count: 3;
+ column-count: 3;
+ -webkit-column-gap: 1.25rem;
+ -moz-column-gap: 1.25rem;
+ column-gap: 1.25rem;
+ orphans: 1;
+ widows: 1;
+ }
+
+ .card-columns .card {
+ display: inline-block;
+ width: 100%;
+ }
+}
+
+.accordion>.card {
+ overflow: hidden;
+}
+
+.accordion>.card:not(:last-of-type) {
+ border-bottom: 0;
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0;
+}
+
+.accordion>.card:not(:first-of-type) {
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+}
+
+.accordion>.card>.card-header {
+ border-radius: 0;
+ margin-bottom: -1px;
+}
+
+.breadcrumb {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-wrap: wrap;
+ flex-wrap: wrap;
+ padding: 0.75rem 1rem;
+ margin-bottom: 1rem;
+ list-style: none;
+ background-color: #e9ecef;
+ border-radius: 0.25rem;
+}
+
+.breadcrumb-item+.breadcrumb-item {
+ padding-left: 0.5rem;
+}
+
+.breadcrumb-item+.breadcrumb-item::before {
+ display: inline-block;
+ padding-right: 0.5rem;
+ color: #6c757d;
+ content: "/";
+}
+
+.breadcrumb-item+.breadcrumb-item:hover::before {
+ text-decoration: underline;
+}
+
+.breadcrumb-item+.breadcrumb-item:hover::before {
+ text-decoration: none;
+}
+
+.breadcrumb-item.active {
+ color: #6c757d;
+}
+
+.pagination {
+ display: -ms-flexbox;
+ display: flex;
+ padding-left: 0;
+ list-style: none;
+ border-radius: 0.25rem;
+}
+
+.page-link {
+ position: relative;
+ display: block;
+ padding: 0.5rem 0.75rem;
+ margin-left: -1px;
+ line-height: 1.25;
+ color: #007bff;
+ background-color: #fff;
+ border: 1px solid #dee2e6;
+}
+
+.page-link:hover {
+ z-index: 2;
+ color: #0056b3;
+ text-decoration: none;
+ background-color: #e9ecef;
+ border-color: #dee2e6;
+}
+
+.page-link:focus {
+ z-index: 3;
+ outline: 0;
+ box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
+}
+
+.page-item:first-child .page-link {
+ margin-left: 0;
+ border-top-left-radius: 0.25rem;
+ border-bottom-left-radius: 0.25rem;
+}
+
+.page-item:last-child .page-link {
+ border-top-right-radius: 0.25rem;
+ border-bottom-right-radius: 0.25rem;
+}
+
+.page-item.active .page-link {
+ z-index: 3;
+ color: #fff;
+ background-color: #007bff;
+ border-color: #007bff;
+}
+
+.page-item.disabled .page-link {
+ color: #6c757d;
+ pointer-events: none;
+ cursor: auto;
+ background-color: #fff;
+ border-color: #dee2e6;
+}
+
+.pagination-lg .page-link {
+ padding: 0.75rem 1.5rem;
+ font-size: 1.25rem;
+ line-height: 1.5;
+}
+
+.pagination-lg .page-item:first-child .page-link {
+ border-top-left-radius: 0.3rem;
+ border-bottom-left-radius: 0.3rem;
+}
+
+.pagination-lg .page-item:last-child .page-link {
+ border-top-right-radius: 0.3rem;
+ border-bottom-right-radius: 0.3rem;
+}
+
+.pagination-sm .page-link {
+ padding: 0.25rem 0.5rem;
+ font-size: 0.875rem;
+ line-height: 1.5;
+}
+
+.pagination-sm .page-item:first-child .page-link {
+ border-top-left-radius: 0.2rem;
+ border-bottom-left-radius: 0.2rem;
+}
+
+.pagination-sm .page-item:last-child .page-link {
+ border-top-right-radius: 0.2rem;
+ border-bottom-right-radius: 0.2rem;
+}
+
+.badge {
+ display: inline-block;
+ padding: 0.25em 0.4em;
+ font-size: 75%;
+ font-weight: 700;
+ line-height: 1;
+ text-align: center;
+ white-space: nowrap;
+ vertical-align: baseline;
+ border-radius: 0.25rem;
+ transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .badge {
+ transition: none;
+ }
+}
+
+a.badge:hover,
+a.badge:focus {
+ text-decoration: none;
+}
+
+.badge:empty {
+ display: none;
+}
+
+.btn .badge {
+ position: relative;
+ top: -1px;
+}
+
+.badge-pill {
+ padding-right: 0.6em;
+ padding-left: 0.6em;
+ border-radius: 10rem;
+}
+
+.badge-primary {
+ color: #fff;
+ background-color: #007bff;
+}
+
+a.badge-primary:hover,
+a.badge-primary:focus {
+ color: #fff;
+ background-color: #0062cc;
+}
+
+a.badge-primary:focus,
+a.badge-primary.focus {
+ outline: 0;
+ box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);
+}
+
+.badge-secondary {
+ color: #fff;
+ background-color: #6c757d;
+}
+
+a.badge-secondary:hover,
+a.badge-secondary:focus {
+ color: #fff;
+ background-color: #545b62;
+}
+
+a.badge-secondary:focus,
+a.badge-secondary.focus {
+ outline: 0;
+ box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);
+}
+
+.badge-success {
+ color: #fff;
+ background-color: #28a745;
+}
+
+a.badge-success:hover,
+a.badge-success:focus {
+ color: #fff;
+ background-color: #1e7e34;
+}
+
+a.badge-success:focus,
+a.badge-success.focus {
+ outline: 0;
+ box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);
+}
+
+.badge-info {
+ color: #fff;
+ background-color: #17a2b8;
+}
+
+a.badge-info:hover,
+a.badge-info:focus {
+ color: #fff;
+ background-color: #117a8b;
+}
+
+a.badge-info:focus,
+a.badge-info.focus {
+ outline: 0;
+ box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);
+}
+
+.badge-warning {
+ color: #212529;
+ background-color: #ffc107;
+}
+
+a.badge-warning:hover,
+a.badge-warning:focus {
+ color: #212529;
+ background-color: #d39e00;
+}
+
+a.badge-warning:focus,
+a.badge-warning.focus {
+ outline: 0;
+ box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);
+}
+
+.badge-danger {
+ color: #fff;
+ background-color: #dc3545;
+}
+
+a.badge-danger:hover,
+a.badge-danger:focus {
+ color: #fff;
+ background-color: #bd2130;
+}
+
+a.badge-danger:focus,
+a.badge-danger.focus {
+ outline: 0;
+ box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);
+}
+
+.badge-light {
+ color: #212529;
+ background-color: #f8f9fa;
+}
+
+a.badge-light:hover,
+a.badge-light:focus {
+ color: #212529;
+ background-color: #dae0e5;
+}
+
+a.badge-light:focus,
+a.badge-light.focus {
+ outline: 0;
+ box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);
+}
+
+.badge-dark {
+ color: #fff;
+ background-color: #343a40;
+}
+
+a.badge-dark:hover,
+a.badge-dark:focus {
+ color: #fff;
+ background-color: #1d2124;
+}
+
+a.badge-dark:focus,
+a.badge-dark.focus {
+ outline: 0;
+ box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);
+}
+
+.jumbotron {
+ padding: 2rem 1rem;
+ margin-bottom: 2rem;
+ background-color: #e9ecef;
+ border-radius: 0.3rem;
+}
+
+@media (min-width: 576px) {
+ .jumbotron {
+ padding: 4rem 2rem;
+ }
+}
+
+.jumbotron-fluid {
+ padding-right: 0;
+ padding-left: 0;
+ border-radius: 0;
+}
+
+.alert {
+ position: relative;
+ padding: 0.75rem 1.25rem;
+ margin-bottom: 1rem;
+ border: 1px solid transparent;
+ border-radius: 0.25rem;
+}
+
+.alert-heading {
+ color: inherit;
+}
+
+.alert-link {
+ font-weight: 700;
+}
+
+.alert-dismissible {
+ padding-right: 4rem;
+}
+
+.alert-dismissible .close {
+ position: absolute;
+ top: 0;
+ right: 0;
+ padding: 0.75rem 1.25rem;
+ color: inherit;
+}
+
+.alert-primary {
+ color: #004085;
+ background-color: #cce5ff;
+ border-color: #b8daff;
+}
+
+.alert-primary hr {
+ border-top-color: #9fcdff;
+}
+
+.alert-primary .alert-link {
+ color: #002752;
+}
+
+.alert-secondary {
+ color: #383d41;
+ background-color: #e2e3e5;
+ border-color: #d6d8db;
+}
+
+.alert-secondary hr {
+ border-top-color: #c8cbcf;
+}
+
+.alert-secondary .alert-link {
+ color: #202326;
+}
+
+.alert-success {
+ color: #155724;
+ background-color: #d4edda;
+ border-color: #c3e6cb;
+}
+
+.alert-success hr {
+ border-top-color: #b1dfbb;
+}
+
+.alert-success .alert-link {
+ color: #0b2e13;
+}
+
+.alert-info {
+ color: #0c5460;
+ background-color: #d1ecf1;
+ border-color: #bee5eb;
+}
+
+.alert-info hr {
+ border-top-color: #abdde5;
+}
+
+.alert-info .alert-link {
+ color: #062c33;
+}
+
+.alert-warning {
+ color: #856404;
+ background-color: #fff3cd;
+ border-color: #ffeeba;
+}
+
+.alert-warning hr {
+ border-top-color: #ffe8a1;
+}
+
+.alert-warning .alert-link {
+ color: #533f03;
+}
+
+.alert-danger {
+ color: #721c24;
+ background-color: #f8d7da;
+ border-color: #f5c6cb;
+}
+
+.alert-danger hr {
+ border-top-color: #f1b0b7;
+}
+
+.alert-danger .alert-link {
+ color: #491217;
+}
+
+.alert-light {
+ color: #818182;
+ background-color: #fefefe;
+ border-color: #fdfdfe;
+}
+
+.alert-light hr {
+ border-top-color: #ececf6;
+}
+
+.alert-light .alert-link {
+ color: #686868;
+}
+
+.alert-dark {
+ color: #1b1e21;
+ background-color: #d6d8d9;
+ border-color: #c6c8ca;
+}
+
+.alert-dark hr {
+ border-top-color: #b9bbbe;
+}
+
+.alert-dark .alert-link {
+ color: #040505;
+}
+
+@-webkit-keyframes progress-bar-stripes {
+ from {
+ background-position: 1rem 0;
+ }
+
+ to {
+ background-position: 0 0;
+ }
+}
+
+@keyframes progress-bar-stripes {
+ from {
+ background-position: 1rem 0;
+ }
+
+ to {
+ background-position: 0 0;
+ }
+}
+
+.progress {
+ display: -ms-flexbox;
+ display: flex;
+ height: 1rem;
+ overflow: hidden;
+ font-size: 0.75rem;
+ background-color: #e9ecef;
+ border-radius: 0.25rem;
+}
+
+.progress-bar {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-direction: column;
+ flex-direction: column;
+ -ms-flex-pack: center;
+ justify-content: center;
+ overflow: hidden;
+ color: #fff;
+ text-align: center;
+ white-space: nowrap;
+ background-color: #007bff;
+ transition: width 0.6s ease;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .progress-bar {
+ transition: none;
+ }
+}
+
+.progress-bar-striped {
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-size: 1rem 1rem;
+}
+
+.progress-bar-animated {
+ -webkit-animation: progress-bar-stripes 1s linear infinite;
+ animation: progress-bar-stripes 1s linear infinite;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .progress-bar-animated {
+ -webkit-animation: none;
+ animation: none;
+ }
+}
+
+.media {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-align: start;
+ align-items: flex-start;
+}
+
+.media-body {
+ -ms-flex: 1;
+ flex: 1;
+}
+
+.list-group {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-direction: column;
+ flex-direction: column;
+ padding-left: 0;
+ margin-bottom: 0;
+}
+
+.list-group-item-action {
+ width: 100%;
+ color: #495057;
+ text-align: inherit;
+}
+
+.list-group-item-action:hover,
+.list-group-item-action:focus {
+ z-index: 1;
+ color: #495057;
+ text-decoration: none;
+ background-color: #f8f9fa;
+}
+
+.list-group-item-action:active {
+ color: #212529;
+ background-color: #e9ecef;
+}
+
+.list-group-item {
+ position: relative;
+ display: block;
+ padding: 0.75rem 1.25rem;
+ background-color: #fff;
+ border: 1px solid rgba(0, 0, 0, 0.125);
+}
+
+.list-group-item:first-child {
+ border-top-left-radius: 0.25rem;
+ border-top-right-radius: 0.25rem;
+}
+
+.list-group-item:last-child {
+ border-bottom-right-radius: 0.25rem;
+ border-bottom-left-radius: 0.25rem;
+}
+
+.list-group-item.disabled,
+.list-group-item:disabled {
+ color: #6c757d;
+ pointer-events: none;
+ background-color: #fff;
+}
+
+.list-group-item.active {
+ z-index: 2;
+ color: #fff;
+ background-color: #007bff;
+ border-color: #007bff;
+}
+
+.list-group-item+.list-group-item {
+ border-top-width: 0;
+}
+
+.list-group-item+.list-group-item.active {
+ margin-top: -1px;
+ border-top-width: 1px;
+}
+
+.list-group-horizontal {
+ -ms-flex-direction: row;
+ flex-direction: row;
+}
+
+.list-group-horizontal .list-group-item:first-child {
+ border-bottom-left-radius: 0.25rem;
+ border-top-right-radius: 0;
+}
+
+.list-group-horizontal .list-group-item:last-child {
+ border-top-right-radius: 0.25rem;
+ border-bottom-left-radius: 0;
+}
+
+.list-group-horizontal .list-group-item.active {
+ margin-top: 0;
+}
+
+.list-group-horizontal .list-group-item+.list-group-item {
+ border-top-width: 1px;
+ border-left-width: 0;
+}
+
+.list-group-horizontal .list-group-item+.list-group-item.active {
+ margin-left: -1px;
+ border-left-width: 1px;
+}
+
+@media (min-width: 576px) {
+ .list-group-horizontal-sm {
+ -ms-flex-direction: row;
+ flex-direction: row;
+ }
+
+ .list-group-horizontal-sm .list-group-item:first-child {
+ border-bottom-left-radius: 0.25rem;
+ border-top-right-radius: 0;
+ }
+
+ .list-group-horizontal-sm .list-group-item:last-child {
+ border-top-right-radius: 0.25rem;
+ border-bottom-left-radius: 0;
+ }
+
+ .list-group-horizontal-sm .list-group-item.active {
+ margin-top: 0;
+ }
+
+ .list-group-horizontal-sm .list-group-item+.list-group-item {
+ border-top-width: 1px;
+ border-left-width: 0;
+ }
+
+ .list-group-horizontal-sm .list-group-item+.list-group-item.active {
+ margin-left: -1px;
+ border-left-width: 1px;
+ }
+}
+
+@media (min-width: 768px) {
+ .list-group-horizontal-md {
+ -ms-flex-direction: row;
+ flex-direction: row;
+ }
+
+ .list-group-horizontal-md .list-group-item:first-child {
+ border-bottom-left-radius: 0.25rem;
+ border-top-right-radius: 0;
+ }
+
+ .list-group-horizontal-md .list-group-item:last-child {
+ border-top-right-radius: 0.25rem;
+ border-bottom-left-radius: 0;
+ }
+
+ .list-group-horizontal-md .list-group-item.active {
+ margin-top: 0;
+ }
+
+ .list-group-horizontal-md .list-group-item+.list-group-item {
+ border-top-width: 1px;
+ border-left-width: 0;
+ }
+
+ .list-group-horizontal-md .list-group-item+.list-group-item.active {
+ margin-left: -1px;
+ border-left-width: 1px;
+ }
+}
+
+@media (min-width: 992px) {
+ .list-group-horizontal-lg {
+ -ms-flex-direction: row;
+ flex-direction: row;
+ }
+
+ .list-group-horizontal-lg .list-group-item:first-child {
+ border-bottom-left-radius: 0.25rem;
+ border-top-right-radius: 0;
+ }
+
+ .list-group-horizontal-lg .list-group-item:last-child {
+ border-top-right-radius: 0.25rem;
+ border-bottom-left-radius: 0;
+ }
+
+ .list-group-horizontal-lg .list-group-item.active {
+ margin-top: 0;
+ }
+
+ .list-group-horizontal-lg .list-group-item+.list-group-item {
+ border-top-width: 1px;
+ border-left-width: 0;
+ }
+
+ .list-group-horizontal-lg .list-group-item+.list-group-item.active {
+ margin-left: -1px;
+ border-left-width: 1px;
+ }
+}
+
+@media (min-width: 1200px) {
+ .list-group-horizontal-xl {
+ -ms-flex-direction: row;
+ flex-direction: row;
+ }
+
+ .list-group-horizontal-xl .list-group-item:first-child {
+ border-bottom-left-radius: 0.25rem;
+ border-top-right-radius: 0;
+ }
+
+ .list-group-horizontal-xl .list-group-item:last-child {
+ border-top-right-radius: 0.25rem;
+ border-bottom-left-radius: 0;
+ }
+
+ .list-group-horizontal-xl .list-group-item.active {
+ margin-top: 0;
+ }
+
+ .list-group-horizontal-xl .list-group-item+.list-group-item {
+ border-top-width: 1px;
+ border-left-width: 0;
+ }
+
+ .list-group-horizontal-xl .list-group-item+.list-group-item.active {
+ margin-left: -1px;
+ border-left-width: 1px;
+ }
+}
+
+.list-group-flush .list-group-item {
+ border-right-width: 0;
+ border-left-width: 0;
+ border-radius: 0;
+}
+
+.list-group-flush .list-group-item:first-child {
+ border-top-width: 0;
+}
+
+.list-group-flush:last-child .list-group-item:last-child {
+ border-bottom-width: 0;
+}
+
+.list-group-item-primary {
+ color: #004085;
+ background-color: #b8daff;
+}
+
+.list-group-item-primary.list-group-item-action:hover,
+.list-group-item-primary.list-group-item-action:focus {
+ color: #004085;
+ background-color: #9fcdff;
+}
+
+.list-group-item-primary.list-group-item-action.active {
+ color: #fff;
+ background-color: #004085;
+ border-color: #004085;
+}
+
+.list-group-item-secondary {
+ color: #383d41;
+ background-color: #d6d8db;
+}
+
+.list-group-item-secondary.list-group-item-action:hover,
+.list-group-item-secondary.list-group-item-action:focus {
+ color: #383d41;
+ background-color: #c8cbcf;
+}
+
+.list-group-item-secondary.list-group-item-action.active {
+ color: #fff;
+ background-color: #383d41;
+ border-color: #383d41;
+}
+
+.list-group-item-success {
+ color: #155724;
+ background-color: #c3e6cb;
+}
+
+.list-group-item-success.list-group-item-action:hover,
+.list-group-item-success.list-group-item-action:focus {
+ color: #155724;
+ background-color: #b1dfbb;
+}
+
+.list-group-item-success.list-group-item-action.active {
+ color: #fff;
+ background-color: #155724;
+ border-color: #155724;
+}
+
+.list-group-item-info {
+ color: #0c5460;
+ background-color: #bee5eb;
+}
+
+.list-group-item-info.list-group-item-action:hover,
+.list-group-item-info.list-group-item-action:focus {
+ color: #0c5460;
+ background-color: #abdde5;
+}
+
+.list-group-item-info.list-group-item-action.active {
+ color: #fff;
+ background-color: #0c5460;
+ border-color: #0c5460;
+}
+
+.list-group-item-warning {
+ color: #856404;
+ background-color: #ffeeba;
+}
+
+.list-group-item-warning.list-group-item-action:hover,
+.list-group-item-warning.list-group-item-action:focus {
+ color: #856404;
+ background-color: #ffe8a1;
+}
+
+.list-group-item-warning.list-group-item-action.active {
+ color: #fff;
+ background-color: #856404;
+ border-color: #856404;
+}
+
+.list-group-item-danger {
+ color: #721c24;
+ background-color: #f5c6cb;
+}
+
+.list-group-item-danger.list-group-item-action:hover,
+.list-group-item-danger.list-group-item-action:focus {
+ color: #721c24;
+ background-color: #f1b0b7;
+}
+
+.list-group-item-danger.list-group-item-action.active {
+ color: #fff;
+ background-color: #721c24;
+ border-color: #721c24;
+}
+
+.list-group-item-light {
+ color: #818182;
+ background-color: #fdfdfe;
+}
+
+.list-group-item-light.list-group-item-action:hover,
+.list-group-item-light.list-group-item-action:focus {
+ color: #818182;
+ background-color: #ececf6;
+}
+
+.list-group-item-light.list-group-item-action.active {
+ color: #fff;
+ background-color: #818182;
+ border-color: #818182;
+}
+
+.list-group-item-dark {
+ color: #1b1e21;
+ background-color: #c6c8ca;
+}
+
+.list-group-item-dark.list-group-item-action:hover,
+.list-group-item-dark.list-group-item-action:focus {
+ color: #1b1e21;
+ background-color: #b9bbbe;
+}
+
+.list-group-item-dark.list-group-item-action.active {
+ color: #fff;
+ background-color: #1b1e21;
+ border-color: #1b1e21;
+}
+
+.close {
+ float: right;
+ font-size: 1.5rem;
+ font-weight: 700;
+ line-height: 1;
+ color: #000;
+ text-shadow: 0 1px 0 #fff;
+ opacity: .5;
+}
+
+.close:hover {
+ color: #000;
+ text-decoration: none;
+}
+
+.close:not(:disabled):not(.disabled):hover,
+.close:not(:disabled):not(.disabled):focus {
+ opacity: .75;
+}
+
+button.close {
+ padding: 0;
+ background-color: transparent;
+ border: 0;
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ appearance: none;
+}
+
+a.close.disabled {
+ pointer-events: none;
+}
+
+.toast {
+ max-width: 350px;
+ overflow: hidden;
+ font-size: 0.875rem;
+ background-color: rgba(255, 255, 255, 0.85);
+ background-clip: padding-box;
+ border: 1px solid rgba(0, 0, 0, 0.1);
+ box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.1);
+ -webkit-backdrop-filter: blur(10px);
+ backdrop-filter: blur(10px);
+ opacity: 0;
+ border-radius: 0.25rem;
+}
+
+.toast:not(:last-child) {
+ margin-bottom: 0.75rem;
+}
+
+.toast.showing {
+ opacity: 1;
+}
+
+.toast.show {
+ display: block;
+ opacity: 1;
+}
+
+.toast.hide {
+ display: none;
+}
+
+.toast-header {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-align: center;
+ align-items: center;
+ padding: 0.25rem 0.75rem;
+ color: #6c757d;
+ background-color: rgba(255, 255, 255, 0.85);
+ background-clip: padding-box;
+ border-bottom: 1px solid rgba(0, 0, 0, 0.05);
+}
+
+.toast-body {
+ padding: 0.75rem;
+}
+
+.modal-open {
+ overflow: hidden;
+}
+
+.modal-open .modal {
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.modal {
+ position: fixed;
+ top: 0;
+ left: 0;
+ z-index: 1050;
+ display: none;
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+ outline: 0;
+}
+
+.modal-dialog {
+ position: relative;
+ width: auto;
+ margin: 0.5rem;
+ pointer-events: none;
+}
+
+.modal.fade .modal-dialog {
+ transition: -webkit-transform 0.3s ease-out;
+ transition: transform 0.3s ease-out;
+ transition: transform 0.3s ease-out, -webkit-transform 0.3s ease-out;
+ -webkit-transform: translate(0, -50px);
+ transform: translate(0, -50px);
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .modal.fade .modal-dialog {
+ transition: none;
+ }
+}
+
+.modal.show .modal-dialog {
+ -webkit-transform: none;
+ transform: none;
+}
+
+.modal.modal-static .modal-dialog {
+ -webkit-transform: scale(1.02);
+ transform: scale(1.02);
+}
+
+.modal-dialog-scrollable {
+ display: -ms-flexbox;
+ display: flex;
+ max-height: calc(100% - 1rem);
+}
+
+.modal-dialog-scrollable .modal-content {
+ max-height: calc(100vh - 1rem);
+ overflow: hidden;
+}
+
+.modal-dialog-scrollable .modal-header,
+.modal-dialog-scrollable .modal-footer {
+ -ms-flex-negative: 0;
+ flex-shrink: 0;
+}
+
+.modal-dialog-scrollable .modal-body {
+ overflow-y: auto;
+}
+
+.modal-dialog-centered {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-align: center;
+ align-items: center;
+ min-height: calc(100% - 1rem);
+}
+
+.modal-dialog-centered::before {
+ display: block;
+ height: calc(100vh - 1rem);
+ content: "";
+}
+
+.modal-dialog-centered.modal-dialog-scrollable {
+ -ms-flex-direction: column;
+ flex-direction: column;
+ -ms-flex-pack: center;
+ justify-content: center;
+ height: 100%;
+}
+
+.modal-dialog-centered.modal-dialog-scrollable .modal-content {
+ max-height: none;
+}
+
+.modal-dialog-centered.modal-dialog-scrollable::before {
+ content: none;
+}
+
+.modal-content {
+ position: relative;
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-direction: column;
+ flex-direction: column;
+ width: 100%;
+ pointer-events: auto;
+ background-color: #fff;
+ background-clip: padding-box;
+ border: 1px solid rgba(0, 0, 0, 0.2);
+ border-radius: 0.3rem;
+ outline: 0;
+}
+
+.modal-backdrop {
+ position: fixed;
+ top: 0;
+ left: 0;
+ z-index: 1040;
+ width: 100vw;
+ height: 100vh;
+ background-color: #000;
+}
+
+.modal-backdrop.fade {
+ opacity: 0;
+}
+
+.modal-backdrop.show {
+ opacity: 0.5;
+}
+
+.modal-header {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-align: start;
+ align-items: flex-start;
+ -ms-flex-pack: justify;
+ justify-content: space-between;
+ padding: 1rem 1rem;
+ border-bottom: 1px solid #dee2e6;
+ border-top-left-radius: calc(0.3rem - 1px);
+ border-top-right-radius: calc(0.3rem - 1px);
+}
+
+.modal-header .close {
+ padding: 1rem 1rem;
+ margin: -1rem -1rem -1rem auto;
+}
+
+.modal-title {
+ margin-bottom: 0;
+ line-height: 1.5;
+}
+
+.modal-body {
+ position: relative;
+ -ms-flex: 1 1 auto;
+ flex: 1 1 auto;
+ padding: 1rem;
+}
+
+.modal-footer {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-wrap: wrap;
+ flex-wrap: wrap;
+ -ms-flex-align: center;
+ align-items: center;
+ -ms-flex-pack: end;
+ justify-content: flex-end;
+ padding: 0.75rem;
+ border-top: 1px solid #dee2e6;
+ border-bottom-right-radius: calc(0.3rem - 1px);
+ border-bottom-left-radius: calc(0.3rem - 1px);
+}
+
+.modal-footer>* {
+ margin: 0.25rem;
+}
+
+.modal-scrollbar-measure {
+ position: absolute;
+ top: -9999px;
+ width: 50px;
+ height: 50px;
+ overflow: scroll;
+}
+
+@media (min-width: 576px) {
+ .modal-dialog {
+ max-width: 500px;
+ margin: 1.75rem auto;
+ }
+
+ .modal-dialog-scrollable {
+ max-height: calc(100% - 3.5rem);
+ }
+
+ .modal-dialog-scrollable .modal-content {
+ max-height: calc(100vh - 3.5rem);
+ }
+
+ .modal-dialog-centered {
+ min-height: calc(100% - 3.5rem);
+ }
+
+ .modal-dialog-centered::before {
+ height: calc(100vh - 3.5rem);
+ }
+
+ .modal-sm {
+ max-width: 300px;
+ }
+}
+
+@media (min-width: 992px) {
+
+ .modal-lg,
+ .modal-xl {
+ max-width: 800px;
+ }
+}
+
+@media (min-width: 1200px) {
+ .modal-xl {
+ max-width: 1140px;
+ }
+}
+
+.tooltip {
+ position: absolute;
+ z-index: 1070;
+ display: block;
+ margin: 0;
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
+ font-style: normal;
+ font-weight: 400;
+ line-height: 1.5;
+ text-align: left;
+ text-align: start;
+ text-decoration: none;
+ text-shadow: none;
+ text-transform: none;
+ letter-spacing: normal;
+ word-break: normal;
+ word-spacing: normal;
+ white-space: normal;
+ line-break: auto;
+ font-size: 0.875rem;
+ word-wrap: break-word;
+ opacity: 0;
+}
+
+.tooltip.show {
+ opacity: 0.9;
+}
+
+.tooltip .arrow {
+ position: absolute;
+ display: block;
+ width: 0.8rem;
+ height: 0.4rem;
+}
+
+.tooltip .arrow::before {
+ position: absolute;
+ content: "";
+ border-color: transparent;
+ border-style: solid;
+}
+
+.bs-tooltip-top,
+.bs-tooltip-auto[x-placement^="top"] {
+ padding: 0.4rem 0;
+}
+
+.bs-tooltip-top .arrow,
+.bs-tooltip-auto[x-placement^="top"] .arrow {
+ bottom: 0;
+}
+
+.bs-tooltip-top .arrow::before,
+.bs-tooltip-auto[x-placement^="top"] .arrow::before {
+ top: 0;
+ border-width: 0.4rem 0.4rem 0;
+ border-top-color: #000;
+}
+
+.bs-tooltip-right,
+.bs-tooltip-auto[x-placement^="right"] {
+ padding: 0 0.4rem;
+}
+
+.bs-tooltip-right .arrow,
+.bs-tooltip-auto[x-placement^="right"] .arrow {
+ left: 0;
+ width: 0.4rem;
+ height: 0.8rem;
+}
+
+.bs-tooltip-right .arrow::before,
+.bs-tooltip-auto[x-placement^="right"] .arrow::before {
+ right: 0;
+ border-width: 0.4rem 0.4rem 0.4rem 0;
+ border-right-color: #000;
+}
+
+.bs-tooltip-bottom,
+.bs-tooltip-auto[x-placement^="bottom"] {
+ padding: 0.4rem 0;
+}
+
+.bs-tooltip-bottom .arrow,
+.bs-tooltip-auto[x-placement^="bottom"] .arrow {
+ top: 0;
+}
+
+.bs-tooltip-bottom .arrow::before,
+.bs-tooltip-auto[x-placement^="bottom"] .arrow::before {
+ bottom: 0;
+ border-width: 0 0.4rem 0.4rem;
+ border-bottom-color: #000;
+}
+
+.bs-tooltip-left,
+.bs-tooltip-auto[x-placement^="left"] {
+ padding: 0 0.4rem;
+}
+
+.bs-tooltip-left .arrow,
+.bs-tooltip-auto[x-placement^="left"] .arrow {
+ right: 0;
+ width: 0.4rem;
+ height: 0.8rem;
+}
+
+.bs-tooltip-left .arrow::before,
+.bs-tooltip-auto[x-placement^="left"] .arrow::before {
+ left: 0;
+ border-width: 0.4rem 0 0.4rem 0.4rem;
+ border-left-color: #000;
+}
+
+.tooltip-inner {
+ max-width: 200px;
+ padding: 0.25rem 0.5rem;
+ color: #fff;
+ text-align: center;
+ background-color: #000;
+ border-radius: 0.25rem;
+}
+
+.popover {
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 1060;
+ display: block;
+ max-width: 276px;
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
+ font-style: normal;
+ font-weight: 400;
+ line-height: 1.5;
+ text-align: left;
+ text-align: start;
+ text-decoration: none;
+ text-shadow: none;
+ text-transform: none;
+ letter-spacing: normal;
+ word-break: normal;
+ word-spacing: normal;
+ white-space: normal;
+ line-break: auto;
+ font-size: 0.875rem;
+ word-wrap: break-word;
+ background-color: #fff;
+ background-clip: padding-box;
+ border: 1px solid rgba(0, 0, 0, 0.2);
+ border-radius: 0.3rem;
+}
+
+.popover .arrow {
+ position: absolute;
+ display: block;
+ width: 1rem;
+ height: 0.5rem;
+ margin: 0 0.3rem;
+}
+
+.popover .arrow::before,
+.popover .arrow::after {
+ position: absolute;
+ display: block;
+ content: "";
+ border-color: transparent;
+ border-style: solid;
+}
+
+.bs-popover-top,
+.bs-popover-auto[x-placement^="top"] {
+ margin-bottom: 0.5rem;
+}
+
+.bs-popover-top>.arrow,
+.bs-popover-auto[x-placement^="top"]>.arrow {
+ bottom: calc(-0.5rem - 1px);
+}
+
+.bs-popover-top>.arrow::before,
+.bs-popover-auto[x-placement^="top"]>.arrow::before {
+ bottom: 0;
+ border-width: 0.5rem 0.5rem 0;
+ border-top-color: rgba(0, 0, 0, 0.25);
+}
+
+.bs-popover-top>.arrow::after,
+.bs-popover-auto[x-placement^="top"]>.arrow::after {
+ bottom: 1px;
+ border-width: 0.5rem 0.5rem 0;
+ border-top-color: #fff;
+}
+
+.bs-popover-right,
+.bs-popover-auto[x-placement^="right"] {
+ margin-left: 0.5rem;
+}
+
+.bs-popover-right>.arrow,
+.bs-popover-auto[x-placement^="right"]>.arrow {
+ left: calc(-0.5rem - 1px);
+ width: 0.5rem;
+ height: 1rem;
+ margin: 0.3rem 0;
+}
+
+.bs-popover-right>.arrow::before,
+.bs-popover-auto[x-placement^="right"]>.arrow::before {
+ left: 0;
+ border-width: 0.5rem 0.5rem 0.5rem 0;
+ border-right-color: rgba(0, 0, 0, 0.25);
+}
+
+.bs-popover-right>.arrow::after,
+.bs-popover-auto[x-placement^="right"]>.arrow::after {
+ left: 1px;
+ border-width: 0.5rem 0.5rem 0.5rem 0;
+ border-right-color: #fff;
+}
+
+.bs-popover-bottom,
+.bs-popover-auto[x-placement^="bottom"] {
+ margin-top: 0.5rem;
+}
+
+.bs-popover-bottom>.arrow,
+.bs-popover-auto[x-placement^="bottom"]>.arrow {
+ top: calc(-0.5rem - 1px);
+}
+
+.bs-popover-bottom>.arrow::before,
+.bs-popover-auto[x-placement^="bottom"]>.arrow::before {
+ top: 0;
+ border-width: 0 0.5rem 0.5rem 0.5rem;
+ border-bottom-color: rgba(0, 0, 0, 0.25);
+}
+
+.bs-popover-bottom>.arrow::after,
+.bs-popover-auto[x-placement^="bottom"]>.arrow::after {
+ top: 1px;
+ border-width: 0 0.5rem 0.5rem 0.5rem;
+ border-bottom-color: #fff;
+}
+
+.bs-popover-bottom .popover-header::before,
+.bs-popover-auto[x-placement^="bottom"] .popover-header::before {
+ position: absolute;
+ top: 0;
+ left: 50%;
+ display: block;
+ width: 1rem;
+ margin-left: -0.5rem;
+ content: "";
+ border-bottom: 1px solid #f7f7f7;
+}
+
+.bs-popover-left,
+.bs-popover-auto[x-placement^="left"] {
+ margin-right: 0.5rem;
+}
+
+.bs-popover-left>.arrow,
+.bs-popover-auto[x-placement^="left"]>.arrow {
+ right: calc(-0.5rem - 1px);
+ width: 0.5rem;
+ height: 1rem;
+ margin: 0.3rem 0;
+}
+
+.bs-popover-left>.arrow::before,
+.bs-popover-auto[x-placement^="left"]>.arrow::before {
+ right: 0;
+ border-width: 0.5rem 0 0.5rem 0.5rem;
+ border-left-color: rgba(0, 0, 0, 0.25);
+}
+
+.bs-popover-left>.arrow::after,
+.bs-popover-auto[x-placement^="left"]>.arrow::after {
+ right: 1px;
+ border-width: 0.5rem 0 0.5rem 0.5rem;
+ border-left-color: #fff;
+}
+
+.popover-header {
+ padding: 0.5rem 0.75rem;
+ margin-bottom: 0;
+ font-size: 1rem;
+ background-color: #f7f7f7;
+ border-bottom: 1px solid #ebebeb;
+ border-top-left-radius: calc(0.3rem - 1px);
+ border-top-right-radius: calc(0.3rem - 1px);
+}
+
+.popover-header:empty {
+ display: none;
+}
+
+.popover-body {
+ padding: 0.5rem 0.75rem;
+ color: #212529;
+}
+
+.carousel {
+ position: relative;
+}
+
+.carousel.pointer-event {
+ -ms-touch-action: pan-y;
+ touch-action: pan-y;
+}
+
+.carousel-inner {
+ position: relative;
+ width: 100%;
+ overflow: hidden;
+}
+
+.carousel-inner::after {
+ display: block;
+ clear: both;
+ content: "";
+}
+
+.carousel-item {
+ position: relative;
+ display: none;
+ float: left;
+ width: 100%;
+ margin-right: -100%;
+ -webkit-backface-visibility: hidden;
+ backface-visibility: hidden;
+ transition: -webkit-transform 0.6s ease-in-out;
+ transition: transform 0.6s ease-in-out;
+ transition: transform 0.6s ease-in-out, -webkit-transform 0.6s ease-in-out;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .carousel-item {
+ transition: none;
+ }
+}
+
+.carousel-item.active,
+.carousel-item-next,
+.carousel-item-prev {
+ display: block;
+}
+
+.carousel-item-next:not(.carousel-item-left),
+.active.carousel-item-right {
+ -webkit-transform: translateX(100%);
+ transform: translateX(100%);
+}
+
+.carousel-item-prev:not(.carousel-item-right),
+.active.carousel-item-left {
+ -webkit-transform: translateX(-100%);
+ transform: translateX(-100%);
+}
+
+.carousel-fade .carousel-item {
+ opacity: 0;
+ transition-property: opacity;
+ -webkit-transform: none;
+ transform: none;
+}
+
+.carousel-fade .carousel-item.active,
+.carousel-fade .carousel-item-next.carousel-item-left,
+.carousel-fade .carousel-item-prev.carousel-item-right {
+ z-index: 1;
+ opacity: 1;
+}
+
+.carousel-fade .active.carousel-item-left,
+.carousel-fade .active.carousel-item-right {
+ z-index: 0;
+ opacity: 0;
+ transition: opacity 0s 0.6s;
+}
+
+@media (prefers-reduced-motion: reduce) {
+
+ .carousel-fade .active.carousel-item-left,
+ .carousel-fade .active.carousel-item-right {
+ transition: none;
+ }
+}
+
+.carousel-control-prev,
+.carousel-control-next {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ z-index: 1;
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-align: center;
+ align-items: center;
+ -ms-flex-pack: center;
+ justify-content: center;
+ width: 15%;
+ color: #fff;
+ text-align: center;
+ opacity: 0.5;
+ transition: opacity 0.15s ease;
+}
+
+@media (prefers-reduced-motion: reduce) {
+
+ .carousel-control-prev,
+ .carousel-control-next {
+ transition: none;
+ }
+}
+
+.carousel-control-prev:hover,
+.carousel-control-prev:focus,
+.carousel-control-next:hover,
+.carousel-control-next:focus {
+ color: #fff;
+ text-decoration: none;
+ outline: 0;
+ opacity: 0.9;
+}
+
+.carousel-control-prev {
+ left: 0;
+}
+
+.carousel-control-next {
+ right: 0;
+}
+
+.carousel-control-prev-icon,
+.carousel-control-next-icon {
+ display: inline-block;
+ width: 20px;
+ height: 20px;
+ background: no-repeat 50% / 100% 100%;
+}
+
+.carousel-control-prev-icon {
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5L4.25 4l2.5-2.5L5.25 0z'/%3e%3c/svg%3e");
+}
+
+.carousel-control-next-icon {
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5L3.75 4l-2.5 2.5L2.75 8l4-4-4-4z'/%3e%3c/svg%3e");
+}
+
+.carousel-indicators {
+ position: absolute;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 15;
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-pack: center;
+ justify-content: center;
+ padding-left: 0;
+ margin-right: 15%;
+ margin-left: 15%;
+ list-style: none;
+}
+
+.carousel-indicators li {
+ box-sizing: content-box;
+ -ms-flex: 0 1 auto;
+ flex: 0 1 auto;
+ width: 30px;
+ height: 3px;
+ margin-right: 3px;
+ margin-left: 3px;
+ text-indent: -999px;
+ cursor: pointer;
+ background-color: #fff;
+ background-clip: padding-box;
+ border-top: 10px solid transparent;
+ border-bottom: 10px solid transparent;
+ opacity: .5;
+ transition: opacity 0.6s ease;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .carousel-indicators li {
+ transition: none;
+ }
+}
+
+.carousel-indicators .active {
+ opacity: 1;
+}
+
+.carousel-caption {
+ position: absolute;
+ right: 15%;
+ bottom: 20px;
+ left: 15%;
+ z-index: 10;
+ padding-top: 20px;
+ padding-bottom: 20px;
+ color: #fff;
+ text-align: center;
+}
+
+@-webkit-keyframes spinner-border {
+ to {
+ -webkit-transform: rotate(360deg);
+ transform: rotate(360deg);
+ }
+}
+
+@keyframes spinner-border {
+ to {
+ -webkit-transform: rotate(360deg);
+ transform: rotate(360deg);
+ }
+}
+
+.spinner-border {
+ display: inline-block;
+ width: 2rem;
+ height: 2rem;
+ vertical-align: text-bottom;
+ border: 0.25em solid currentColor;
+ border-right-color: transparent;
+ border-radius: 50%;
+ -webkit-animation: spinner-border .75s linear infinite;
+ animation: spinner-border .75s linear infinite;
+}
+
+.spinner-border-sm {
+ width: 1rem;
+ height: 1rem;
+ border-width: 0.2em;
+}
+
+@-webkit-keyframes spinner-grow {
+ 0% {
+ -webkit-transform: scale(0);
+ transform: scale(0);
+ }
+
+ 50% {
+ opacity: 1;
+ }
+}
+
+@keyframes spinner-grow {
+ 0% {
+ -webkit-transform: scale(0);
+ transform: scale(0);
+ }
+
+ 50% {
+ opacity: 1;
+ }
+}
+
+.spinner-grow {
+ display: inline-block;
+ width: 2rem;
+ height: 2rem;
+ vertical-align: text-bottom;
+ background-color: currentColor;
+ border-radius: 50%;
+ opacity: 0;
+ -webkit-animation: spinner-grow .75s linear infinite;
+ animation: spinner-grow .75s linear infinite;
+}
+
+.spinner-grow-sm {
+ width: 1rem;
+ height: 1rem;
+}
+
+.align-baseline {
+ vertical-align: baseline !important;
+}
+
+.align-top {
+ vertical-align: top !important;
+}
+
+.align-middle {
+ vertical-align: middle !important;
+}
+
+.align-bottom {
+ vertical-align: bottom !important;
+}
+
+.align-text-bottom {
+ vertical-align: text-bottom !important;
+}
+
+.align-text-top {
+ vertical-align: text-top !important;
+}
+
+.bg-primary {
+ background-color: #007bff !important;
+}
+
+a.bg-primary:hover,
+a.bg-primary:focus,
+button.bg-primary:hover,
+button.bg-primary:focus {
+ background-color: #0062cc !important;
+}
+
+.bg-secondary {
+ background-color: #6c757d !important;
+}
+
+a.bg-secondary:hover,
+a.bg-secondary:focus,
+button.bg-secondary:hover,
+button.bg-secondary:focus {
+ background-color: #545b62 !important;
+}
+
+.bg-success {
+ background-color: #28a745 !important;
+}
+
+a.bg-success:hover,
+a.bg-success:focus,
+button.bg-success:hover,
+button.bg-success:focus {
+ background-color: #1e7e34 !important;
+}
+
+.bg-info {
+ background-color: #17a2b8 !important;
+}
+
+a.bg-info:hover,
+a.bg-info:focus,
+button.bg-info:hover,
+button.bg-info:focus {
+ background-color: #117a8b !important;
+}
+
+.bg-warning {
+ background-color: #ffc107 !important;
+}
+
+a.bg-warning:hover,
+a.bg-warning:focus,
+button.bg-warning:hover,
+button.bg-warning:focus {
+ background-color: #d39e00 !important;
+}
+
+.bg-danger {
+ background-color: #dc3545 !important;
+}
+
+a.bg-danger:hover,
+a.bg-danger:focus,
+button.bg-danger:hover,
+button.bg-danger:focus {
+ background-color: #bd2130 !important;
+}
+
+.bg-light {
+ background-color: #f8f9fa !important;
+}
+
+a.bg-light:hover,
+a.bg-light:focus,
+button.bg-light:hover,
+button.bg-light:focus {
+ background-color: #dae0e5 !important;
+}
+
+.bg-dark {
+ background-color: #343a40 !important;
+}
+
+a.bg-dark:hover,
+a.bg-dark:focus,
+button.bg-dark:hover,
+button.bg-dark:focus {
+ background-color: #1d2124 !important;
+}
+
+.bg-white {
+ background-color: #fff !important;
+}
+
+.bg-transparent {
+ background-color: transparent !important;
+}
+
+.border {
+ border: 1px solid #dee2e6 !important;
+}
+
+.border-top {
+ border-top: 1px solid #dee2e6 !important;
+}
+
+.border-right {
+ border-right: 1px solid #dee2e6 !important;
+}
+
+.border-bottom {
+ border-bottom: 1px solid #dee2e6 !important;
+}
+
+.border-left {
+ border-left: 1px solid #dee2e6 !important;
+}
+
+.border-0 {
+ border: 0 !important;
+}
+
+.border-top-0 {
+ border-top: 0 !important;
+}
+
+.border-right-0 {
+ border-right: 0 !important;
+}
+
+.border-bottom-0 {
+ border-bottom: 0 !important;
+}
+
+.border-left-0 {
+ border-left: 0 !important;
+}
+
+.border-primary {
+ border-color: #007bff !important;
+}
+
+.border-secondary {
+ border-color: #6c757d !important;
+}
+
+.border-success {
+ border-color: #28a745 !important;
+}
+
+.border-info {
+ border-color: #17a2b8 !important;
+}
+
+.border-warning {
+ border-color: #ffc107 !important;
+}
+
+.border-danger {
+ border-color: #dc3545 !important;
+}
+
+.border-light {
+ border-color: #f8f9fa !important;
+}
+
+.border-dark {
+ border-color: #343a40 !important;
+}
+
+.border-white {
+ border-color: #fff !important;
+}
+
+.rounded-sm {
+ border-radius: 0.2rem !important;
+}
+
+.rounded {
+ border-radius: 0.25rem !important;
+}
+
+.rounded-top {
+ border-top-left-radius: 0.25rem !important;
+ border-top-right-radius: 0.25rem !important;
+}
+
+.rounded-right {
+ border-top-right-radius: 0.25rem !important;
+ border-bottom-right-radius: 0.25rem !important;
+}
+
+.rounded-bottom {
+ border-bottom-right-radius: 0.25rem !important;
+ border-bottom-left-radius: 0.25rem !important;
+}
+
+.rounded-left {
+ border-top-left-radius: 0.25rem !important;
+ border-bottom-left-radius: 0.25rem !important;
+}
+
+.rounded-lg {
+ border-radius: 0.3rem !important;
+}
+
+.rounded-circle {
+ border-radius: 50% !important;
+}
+
+.rounded-pill {
+ border-radius: 50rem !important;
+}
+
+.rounded-0 {
+ border-radius: 0 !important;
+}
+
+.clearfix::after {
+ display: block;
+ clear: both;
+ content: "";
+}
+
+.d-none {
+ display: none !important;
+}
+
+.d-inline {
+ display: inline !important;
+}
+
+.d-inline-block {
+ display: inline-block !important;
+}
+
+.d-block {
+ display: block !important;
+}
+
+.d-table {
+ display: table !important;
+}
+
+.d-table-row {
+ display: table-row !important;
+}
+
+.d-table-cell {
+ display: table-cell !important;
+}
+
+.d-flex {
+ display: -ms-flexbox !important;
+ display: flex !important;
+}
+
+.d-inline-flex {
+ display: -ms-inline-flexbox !important;
+ display: inline-flex !important;
+}
+
+@media (min-width: 576px) {
+ .d-sm-none {
+ display: none !important;
+ }
+
+ .d-sm-inline {
+ display: inline !important;
+ }
+
+ .d-sm-inline-block {
+ display: inline-block !important;
+ }
+
+ .d-sm-block {
+ display: block !important;
+ }
+
+ .d-sm-table {
+ display: table !important;
+ }
+
+ .d-sm-table-row {
+ display: table-row !important;
+ }
+
+ .d-sm-table-cell {
+ display: table-cell !important;
+ }
+
+ .d-sm-flex {
+ display: -ms-flexbox !important;
+ display: flex !important;
+ }
+
+ .d-sm-inline-flex {
+ display: -ms-inline-flexbox !important;
+ display: inline-flex !important;
+ }
+}
+
+@media (min-width: 768px) {
+ .d-md-none {
+ display: none !important;
+ }
+
+ .d-md-inline {
+ display: inline !important;
+ }
+
+ .d-md-inline-block {
+ display: inline-block !important;
+ }
+
+ .d-md-block {
+ display: block !important;
+ }
+
+ .d-md-table {
+ display: table !important;
+ }
+
+ .d-md-table-row {
+ display: table-row !important;
+ }
+
+ .d-md-table-cell {
+ display: table-cell !important;
+ }
+
+ .d-md-flex {
+ display: -ms-flexbox !important;
+ display: flex !important;
+ }
+
+ .d-md-inline-flex {
+ display: -ms-inline-flexbox !important;
+ display: inline-flex !important;
+ }
+}
+
+@media (min-width: 992px) {
+ .d-lg-none {
+ display: none !important;
+ }
+
+ .d-lg-inline {
+ display: inline !important;
+ }
+
+ .d-lg-inline-block {
+ display: inline-block !important;
+ }
+
+ .d-lg-block {
+ display: block !important;
+ }
+
+ .d-lg-table {
+ display: table !important;
+ }
+
+ .d-lg-table-row {
+ display: table-row !important;
+ }
+
+ .d-lg-table-cell {
+ display: table-cell !important;
+ }
+
+ .d-lg-flex {
+ display: -ms-flexbox !important;
+ display: flex !important;
+ }
+
+ .d-lg-inline-flex {
+ display: -ms-inline-flexbox !important;
+ display: inline-flex !important;
+ }
+}
+
+@media (min-width: 1200px) {
+ .d-xl-none {
+ display: none !important;
+ }
+
+ .d-xl-inline {
+ display: inline !important;
+ }
+
+ .d-xl-inline-block {
+ display: inline-block !important;
+ }
+
+ .d-xl-block {
+ display: block !important;
+ }
+
+ .d-xl-table {
+ display: table !important;
+ }
+
+ .d-xl-table-row {
+ display: table-row !important;
+ }
+
+ .d-xl-table-cell {
+ display: table-cell !important;
+ }
+
+ .d-xl-flex {
+ display: -ms-flexbox !important;
+ display: flex !important;
+ }
+
+ .d-xl-inline-flex {
+ display: -ms-inline-flexbox !important;
+ display: inline-flex !important;
+ }
+}
+
+@media print {
+ .d-print-none {
+ display: none !important;
+ }
+
+ .d-print-inline {
+ display: inline !important;
+ }
+
+ .d-print-inline-block {
+ display: inline-block !important;
+ }
+
+ .d-print-block {
+ display: block !important;
+ }
+
+ .d-print-table {
+ display: table !important;
+ }
+
+ .d-print-table-row {
+ display: table-row !important;
+ }
+
+ .d-print-table-cell {
+ display: table-cell !important;
+ }
+
+ .d-print-flex {
+ display: -ms-flexbox !important;
+ display: flex !important;
+ }
+
+ .d-print-inline-flex {
+ display: -ms-inline-flexbox !important;
+ display: inline-flex !important;
+ }
+}
+
+.embed-responsive {
+ position: relative;
+ display: block;
+ width: 100%;
+ padding: 0;
+ overflow: hidden;
+}
+
+.embed-responsive::before {
+ display: block;
+ content: "";
+}
+
+.embed-responsive .embed-responsive-item,
+.embed-responsive iframe,
+.embed-responsive embed,
+.embed-responsive object,
+.embed-responsive video {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ border: 0;
+}
+
+.embed-responsive-21by9::before {
+ padding-top: 42.857143%;
+}
+
+.embed-responsive-16by9::before {
+ padding-top: 56.25%;
+}
+
+.embed-responsive-4by3::before {
+ padding-top: 75%;
+}
+
+.embed-responsive-1by1::before {
+ padding-top: 100%;
+}
+
+.flex-row {
+ -ms-flex-direction: row !important;
+ flex-direction: row !important;
+}
+
+.flex-column {
+ -ms-flex-direction: column !important;
+ flex-direction: column !important;
+}
+
+.flex-row-reverse {
+ -ms-flex-direction: row-reverse !important;
+ flex-direction: row-reverse !important;
+}
+
+.flex-column-reverse {
+ -ms-flex-direction: column-reverse !important;
+ flex-direction: column-reverse !important;
+}
+
+.flex-wrap {
+ -ms-flex-wrap: wrap !important;
+ flex-wrap: wrap !important;
+}
+
+.flex-nowrap {
+ -ms-flex-wrap: nowrap !important;
+ flex-wrap: nowrap !important;
+}
+
+.flex-wrap-reverse {
+ -ms-flex-wrap: wrap-reverse !important;
+ flex-wrap: wrap-reverse !important;
+}
+
+.flex-fill {
+ -ms-flex: 1 1 auto !important;
+ flex: 1 1 auto !important;
+}
+
+.flex-grow-0 {
+ -ms-flex-positive: 0 !important;
+ flex-grow: 0 !important;
+}
+
+.flex-grow-1 {
+ -ms-flex-positive: 1 !important;
+ flex-grow: 1 !important;
+}
+
+.flex-shrink-0 {
+ -ms-flex-negative: 0 !important;
+ flex-shrink: 0 !important;
+}
+
+.flex-shrink-1 {
+ -ms-flex-negative: 1 !important;
+ flex-shrink: 1 !important;
+}
+
+.justify-content-start {
+ -ms-flex-pack: start !important;
+ justify-content: flex-start !important;
+}
+
+.justify-content-end {
+ -ms-flex-pack: end !important;
+ justify-content: flex-end !important;
+}
+
+.justify-content-center {
+ -ms-flex-pack: center !important;
+ justify-content: center !important;
+}
+
+.justify-content-between {
+ -ms-flex-pack: justify !important;
+ justify-content: space-between !important;
+}
+
+.justify-content-around {
+ -ms-flex-pack: distribute !important;
+ justify-content: space-around !important;
+}
+
+.align-items-start {
+ -ms-flex-align: start !important;
+ align-items: flex-start !important;
+}
+
+.align-items-end {
+ -ms-flex-align: end !important;
+ align-items: flex-end !important;
+}
+
+.align-items-center {
+ -ms-flex-align: center !important;
+ align-items: center !important;
+}
+
+.align-items-baseline {
+ -ms-flex-align: baseline !important;
+ align-items: baseline !important;
+}
+
+.align-items-stretch {
+ -ms-flex-align: stretch !important;
+ align-items: stretch !important;
+}
+
+.align-content-start {
+ -ms-flex-line-pack: start !important;
+ align-content: flex-start !important;
+}
+
+.align-content-end {
+ -ms-flex-line-pack: end !important;
+ align-content: flex-end !important;
+}
+
+.align-content-center {
+ -ms-flex-line-pack: center !important;
+ align-content: center !important;
+}
+
+.align-content-between {
+ -ms-flex-line-pack: justify !important;
+ align-content: space-between !important;
+}
+
+.align-content-around {
+ -ms-flex-line-pack: distribute !important;
+ align-content: space-around !important;
+}
+
+.align-content-stretch {
+ -ms-flex-line-pack: stretch !important;
+ align-content: stretch !important;
+}
+
+.align-self-auto {
+ -ms-flex-item-align: auto !important;
+ align-self: auto !important;
+}
+
+.align-self-start {
+ -ms-flex-item-align: start !important;
+ align-self: flex-start !important;
+}
+
+.align-self-end {
+ -ms-flex-item-align: end !important;
+ align-self: flex-end !important;
+}
+
+.align-self-center {
+ -ms-flex-item-align: center !important;
+ align-self: center !important;
+}
+
+.align-self-baseline {
+ -ms-flex-item-align: baseline !important;
+ align-self: baseline !important;
+}
+
+.align-self-stretch {
+ -ms-flex-item-align: stretch !important;
+ align-self: stretch !important;
+}
+
+@media (min-width: 576px) {
+ .flex-sm-row {
+ -ms-flex-direction: row !important;
+ flex-direction: row !important;
+ }
+
+ .flex-sm-column {
+ -ms-flex-direction: column !important;
+ flex-direction: column !important;
+ }
+
+ .flex-sm-row-reverse {
+ -ms-flex-direction: row-reverse !important;
+ flex-direction: row-reverse !important;
+ }
+
+ .flex-sm-column-reverse {
+ -ms-flex-direction: column-reverse !important;
+ flex-direction: column-reverse !important;
+ }
+
+ .flex-sm-wrap {
+ -ms-flex-wrap: wrap !important;
+ flex-wrap: wrap !important;
+ }
+
+ .flex-sm-nowrap {
+ -ms-flex-wrap: nowrap !important;
+ flex-wrap: nowrap !important;
+ }
+
+ .flex-sm-wrap-reverse {
+ -ms-flex-wrap: wrap-reverse !important;
+ flex-wrap: wrap-reverse !important;
+ }
+
+ .flex-sm-fill {
+ -ms-flex: 1 1 auto !important;
+ flex: 1 1 auto !important;
+ }
+
+ .flex-sm-grow-0 {
+ -ms-flex-positive: 0 !important;
+ flex-grow: 0 !important;
+ }
+
+ .flex-sm-grow-1 {
+ -ms-flex-positive: 1 !important;
+ flex-grow: 1 !important;
+ }
+
+ .flex-sm-shrink-0 {
+ -ms-flex-negative: 0 !important;
+ flex-shrink: 0 !important;
+ }
+
+ .flex-sm-shrink-1 {
+ -ms-flex-negative: 1 !important;
+ flex-shrink: 1 !important;
+ }
+
+ .justify-content-sm-start {
+ -ms-flex-pack: start !important;
+ justify-content: flex-start !important;
+ }
+
+ .justify-content-sm-end {
+ -ms-flex-pack: end !important;
+ justify-content: flex-end !important;
+ }
+
+ .justify-content-sm-center {
+ -ms-flex-pack: center !important;
+ justify-content: center !important;
+ }
+
+ .justify-content-sm-between {
+ -ms-flex-pack: justify !important;
+ justify-content: space-between !important;
+ }
+
+ .justify-content-sm-around {
+ -ms-flex-pack: distribute !important;
+ justify-content: space-around !important;
+ }
+
+ .align-items-sm-start {
+ -ms-flex-align: start !important;
+ align-items: flex-start !important;
+ }
+
+ .align-items-sm-end {
+ -ms-flex-align: end !important;
+ align-items: flex-end !important;
+ }
+
+ .align-items-sm-center {
+ -ms-flex-align: center !important;
+ align-items: center !important;
+ }
+
+ .align-items-sm-baseline {
+ -ms-flex-align: baseline !important;
+ align-items: baseline !important;
+ }
+
+ .align-items-sm-stretch {
+ -ms-flex-align: stretch !important;
+ align-items: stretch !important;
+ }
+
+ .align-content-sm-start {
+ -ms-flex-line-pack: start !important;
+ align-content: flex-start !important;
+ }
+
+ .align-content-sm-end {
+ -ms-flex-line-pack: end !important;
+ align-content: flex-end !important;
+ }
+
+ .align-content-sm-center {
+ -ms-flex-line-pack: center !important;
+ align-content: center !important;
+ }
+
+ .align-content-sm-between {
+ -ms-flex-line-pack: justify !important;
+ align-content: space-between !important;
+ }
+
+ .align-content-sm-around {
+ -ms-flex-line-pack: distribute !important;
+ align-content: space-around !important;
+ }
+
+ .align-content-sm-stretch {
+ -ms-flex-line-pack: stretch !important;
+ align-content: stretch !important;
+ }
+
+ .align-self-sm-auto {
+ -ms-flex-item-align: auto !important;
+ align-self: auto !important;
+ }
+
+ .align-self-sm-start {
+ -ms-flex-item-align: start !important;
+ align-self: flex-start !important;
+ }
+
+ .align-self-sm-end {
+ -ms-flex-item-align: end !important;
+ align-self: flex-end !important;
+ }
+
+ .align-self-sm-center {
+ -ms-flex-item-align: center !important;
+ align-self: center !important;
+ }
+
+ .align-self-sm-baseline {
+ -ms-flex-item-align: baseline !important;
+ align-self: baseline !important;
+ }
+
+ .align-self-sm-stretch {
+ -ms-flex-item-align: stretch !important;
+ align-self: stretch !important;
+ }
+}
+
+@media (min-width: 768px) {
+ .flex-md-row {
+ -ms-flex-direction: row !important;
+ flex-direction: row !important;
+ }
+
+ .flex-md-column {
+ -ms-flex-direction: column !important;
+ flex-direction: column !important;
+ }
+
+ .flex-md-row-reverse {
+ -ms-flex-direction: row-reverse !important;
+ flex-direction: row-reverse !important;
+ }
+
+ .flex-md-column-reverse {
+ -ms-flex-direction: column-reverse !important;
+ flex-direction: column-reverse !important;
+ }
+
+ .flex-md-wrap {
+ -ms-flex-wrap: wrap !important;
+ flex-wrap: wrap !important;
+ }
+
+ .flex-md-nowrap {
+ -ms-flex-wrap: nowrap !important;
+ flex-wrap: nowrap !important;
+ }
+
+ .flex-md-wrap-reverse {
+ -ms-flex-wrap: wrap-reverse !important;
+ flex-wrap: wrap-reverse !important;
+ }
+
+ .flex-md-fill {
+ -ms-flex: 1 1 auto !important;
+ flex: 1 1 auto !important;
+ }
+
+ .flex-md-grow-0 {
+ -ms-flex-positive: 0 !important;
+ flex-grow: 0 !important;
+ }
+
+ .flex-md-grow-1 {
+ -ms-flex-positive: 1 !important;
+ flex-grow: 1 !important;
+ }
+
+ .flex-md-shrink-0 {
+ -ms-flex-negative: 0 !important;
+ flex-shrink: 0 !important;
+ }
+
+ .flex-md-shrink-1 {
+ -ms-flex-negative: 1 !important;
+ flex-shrink: 1 !important;
+ }
+
+ .justify-content-md-start {
+ -ms-flex-pack: start !important;
+ justify-content: flex-start !important;
+ }
+
+ .justify-content-md-end {
+ -ms-flex-pack: end !important;
+ justify-content: flex-end !important;
+ }
+
+ .justify-content-md-center {
+ -ms-flex-pack: center !important;
+ justify-content: center !important;
+ }
+
+ .justify-content-md-between {
+ -ms-flex-pack: justify !important;
+ justify-content: space-between !important;
+ }
+
+ .justify-content-md-around {
+ -ms-flex-pack: distribute !important;
+ justify-content: space-around !important;
+ }
+
+ .align-items-md-start {
+ -ms-flex-align: start !important;
+ align-items: flex-start !important;
+ }
+
+ .align-items-md-end {
+ -ms-flex-align: end !important;
+ align-items: flex-end !important;
+ }
+
+ .align-items-md-center {
+ -ms-flex-align: center !important;
+ align-items: center !important;
+ }
+
+ .align-items-md-baseline {
+ -ms-flex-align: baseline !important;
+ align-items: baseline !important;
+ }
+
+ .align-items-md-stretch {
+ -ms-flex-align: stretch !important;
+ align-items: stretch !important;
+ }
+
+ .align-content-md-start {
+ -ms-flex-line-pack: start !important;
+ align-content: flex-start !important;
+ }
+
+ .align-content-md-end {
+ -ms-flex-line-pack: end !important;
+ align-content: flex-end !important;
+ }
+
+ .align-content-md-center {
+ -ms-flex-line-pack: center !important;
+ align-content: center !important;
+ }
+
+ .align-content-md-between {
+ -ms-flex-line-pack: justify !important;
+ align-content: space-between !important;
+ }
+
+ .align-content-md-around {
+ -ms-flex-line-pack: distribute !important;
+ align-content: space-around !important;
+ }
+
+ .align-content-md-stretch {
+ -ms-flex-line-pack: stretch !important;
+ align-content: stretch !important;
+ }
+
+ .align-self-md-auto {
+ -ms-flex-item-align: auto !important;
+ align-self: auto !important;
+ }
+
+ .align-self-md-start {
+ -ms-flex-item-align: start !important;
+ align-self: flex-start !important;
+ }
+
+ .align-self-md-end {
+ -ms-flex-item-align: end !important;
+ align-self: flex-end !important;
+ }
+
+ .align-self-md-center {
+ -ms-flex-item-align: center !important;
+ align-self: center !important;
+ }
+
+ .align-self-md-baseline {
+ -ms-flex-item-align: baseline !important;
+ align-self: baseline !important;
+ }
+
+ .align-self-md-stretch {
+ -ms-flex-item-align: stretch !important;
+ align-self: stretch !important;
+ }
+}
+
+@media (min-width: 992px) {
+ .flex-lg-row {
+ -ms-flex-direction: row !important;
+ flex-direction: row !important;
+ }
+
+ .flex-lg-column {
+ -ms-flex-direction: column !important;
+ flex-direction: column !important;
+ }
+
+ .flex-lg-row-reverse {
+ -ms-flex-direction: row-reverse !important;
+ flex-direction: row-reverse !important;
+ }
+
+ .flex-lg-column-reverse {
+ -ms-flex-direction: column-reverse !important;
+ flex-direction: column-reverse !important;
+ }
+
+ .flex-lg-wrap {
+ -ms-flex-wrap: wrap !important;
+ flex-wrap: wrap !important;
+ }
+
+ .flex-lg-nowrap {
+ -ms-flex-wrap: nowrap !important;
+ flex-wrap: nowrap !important;
+ }
+
+ .flex-lg-wrap-reverse {
+ -ms-flex-wrap: wrap-reverse !important;
+ flex-wrap: wrap-reverse !important;
+ }
+
+ .flex-lg-fill {
+ -ms-flex: 1 1 auto !important;
+ flex: 1 1 auto !important;
+ }
+
+ .flex-lg-grow-0 {
+ -ms-flex-positive: 0 !important;
+ flex-grow: 0 !important;
+ }
+
+ .flex-lg-grow-1 {
+ -ms-flex-positive: 1 !important;
+ flex-grow: 1 !important;
+ }
+
+ .flex-lg-shrink-0 {
+ -ms-flex-negative: 0 !important;
+ flex-shrink: 0 !important;
+ }
+
+ .flex-lg-shrink-1 {
+ -ms-flex-negative: 1 !important;
+ flex-shrink: 1 !important;
+ }
+
+ .justify-content-lg-start {
+ -ms-flex-pack: start !important;
+ justify-content: flex-start !important;
+ }
+
+ .justify-content-lg-end {
+ -ms-flex-pack: end !important;
+ justify-content: flex-end !important;
+ }
+
+ .justify-content-lg-center {
+ -ms-flex-pack: center !important;
+ justify-content: center !important;
+ }
+
+ .justify-content-lg-between {
+ -ms-flex-pack: justify !important;
+ justify-content: space-between !important;
+ }
+
+ .justify-content-lg-around {
+ -ms-flex-pack: distribute !important;
+ justify-content: space-around !important;
+ }
+
+ .align-items-lg-start {
+ -ms-flex-align: start !important;
+ align-items: flex-start !important;
+ }
+
+ .align-items-lg-end {
+ -ms-flex-align: end !important;
+ align-items: flex-end !important;
+ }
+
+ .align-items-lg-center {
+ -ms-flex-align: center !important;
+ align-items: center !important;
+ }
+
+ .align-items-lg-baseline {
+ -ms-flex-align: baseline !important;
+ align-items: baseline !important;
+ }
+
+ .align-items-lg-stretch {
+ -ms-flex-align: stretch !important;
+ align-items: stretch !important;
+ }
+
+ .align-content-lg-start {
+ -ms-flex-line-pack: start !important;
+ align-content: flex-start !important;
+ }
+
+ .align-content-lg-end {
+ -ms-flex-line-pack: end !important;
+ align-content: flex-end !important;
+ }
+
+ .align-content-lg-center {
+ -ms-flex-line-pack: center !important;
+ align-content: center !important;
+ }
+
+ .align-content-lg-between {
+ -ms-flex-line-pack: justify !important;
+ align-content: space-between !important;
+ }
+
+ .align-content-lg-around {
+ -ms-flex-line-pack: distribute !important;
+ align-content: space-around !important;
+ }
+
+ .align-content-lg-stretch {
+ -ms-flex-line-pack: stretch !important;
+ align-content: stretch !important;
+ }
+
+ .align-self-lg-auto {
+ -ms-flex-item-align: auto !important;
+ align-self: auto !important;
+ }
+
+ .align-self-lg-start {
+ -ms-flex-item-align: start !important;
+ align-self: flex-start !important;
+ }
+
+ .align-self-lg-end {
+ -ms-flex-item-align: end !important;
+ align-self: flex-end !important;
+ }
+
+ .align-self-lg-center {
+ -ms-flex-item-align: center !important;
+ align-self: center !important;
+ }
+
+ .align-self-lg-baseline {
+ -ms-flex-item-align: baseline !important;
+ align-self: baseline !important;
+ }
+
+ .align-self-lg-stretch {
+ -ms-flex-item-align: stretch !important;
+ align-self: stretch !important;
+ }
+}
+
+@media (min-width: 1200px) {
+ .flex-xl-row {
+ -ms-flex-direction: row !important;
+ flex-direction: row !important;
+ }
+
+ .flex-xl-column {
+ -ms-flex-direction: column !important;
+ flex-direction: column !important;
+ }
+
+ .flex-xl-row-reverse {
+ -ms-flex-direction: row-reverse !important;
+ flex-direction: row-reverse !important;
+ }
+
+ .flex-xl-column-reverse {
+ -ms-flex-direction: column-reverse !important;
+ flex-direction: column-reverse !important;
+ }
+
+ .flex-xl-wrap {
+ -ms-flex-wrap: wrap !important;
+ flex-wrap: wrap !important;
+ }
+
+ .flex-xl-nowrap {
+ -ms-flex-wrap: nowrap !important;
+ flex-wrap: nowrap !important;
+ }
+
+ .flex-xl-wrap-reverse {
+ -ms-flex-wrap: wrap-reverse !important;
+ flex-wrap: wrap-reverse !important;
+ }
+
+ .flex-xl-fill {
+ -ms-flex: 1 1 auto !important;
+ flex: 1 1 auto !important;
+ }
+
+ .flex-xl-grow-0 {
+ -ms-flex-positive: 0 !important;
+ flex-grow: 0 !important;
+ }
+
+ .flex-xl-grow-1 {
+ -ms-flex-positive: 1 !important;
+ flex-grow: 1 !important;
+ }
+
+ .flex-xl-shrink-0 {
+ -ms-flex-negative: 0 !important;
+ flex-shrink: 0 !important;
+ }
+
+ .flex-xl-shrink-1 {
+ -ms-flex-negative: 1 !important;
+ flex-shrink: 1 !important;
+ }
+
+ .justify-content-xl-start {
+ -ms-flex-pack: start !important;
+ justify-content: flex-start !important;
+ }
+
+ .justify-content-xl-end {
+ -ms-flex-pack: end !important;
+ justify-content: flex-end !important;
+ }
+
+ .justify-content-xl-center {
+ -ms-flex-pack: center !important;
+ justify-content: center !important;
+ }
+
+ .justify-content-xl-between {
+ -ms-flex-pack: justify !important;
+ justify-content: space-between !important;
+ }
+
+ .justify-content-xl-around {
+ -ms-flex-pack: distribute !important;
+ justify-content: space-around !important;
+ }
+
+ .align-items-xl-start {
+ -ms-flex-align: start !important;
+ align-items: flex-start !important;
+ }
+
+ .align-items-xl-end {
+ -ms-flex-align: end !important;
+ align-items: flex-end !important;
+ }
+
+ .align-items-xl-center {
+ -ms-flex-align: center !important;
+ align-items: center !important;
+ }
+
+ .align-items-xl-baseline {
+ -ms-flex-align: baseline !important;
+ align-items: baseline !important;
+ }
+
+ .align-items-xl-stretch {
+ -ms-flex-align: stretch !important;
+ align-items: stretch !important;
+ }
+
+ .align-content-xl-start {
+ -ms-flex-line-pack: start !important;
+ align-content: flex-start !important;
+ }
+
+ .align-content-xl-end {
+ -ms-flex-line-pack: end !important;
+ align-content: flex-end !important;
+ }
+
+ .align-content-xl-center {
+ -ms-flex-line-pack: center !important;
+ align-content: center !important;
+ }
+
+ .align-content-xl-between {
+ -ms-flex-line-pack: justify !important;
+ align-content: space-between !important;
+ }
+
+ .align-content-xl-around {
+ -ms-flex-line-pack: distribute !important;
+ align-content: space-around !important;
+ }
+
+ .align-content-xl-stretch {
+ -ms-flex-line-pack: stretch !important;
+ align-content: stretch !important;
+ }
+
+ .align-self-xl-auto {
+ -ms-flex-item-align: auto !important;
+ align-self: auto !important;
+ }
+
+ .align-self-xl-start {
+ -ms-flex-item-align: start !important;
+ align-self: flex-start !important;
+ }
+
+ .align-self-xl-end {
+ -ms-flex-item-align: end !important;
+ align-self: flex-end !important;
+ }
+
+ .align-self-xl-center {
+ -ms-flex-item-align: center !important;
+ align-self: center !important;
+ }
+
+ .align-self-xl-baseline {
+ -ms-flex-item-align: baseline !important;
+ align-self: baseline !important;
+ }
+
+ .align-self-xl-stretch {
+ -ms-flex-item-align: stretch !important;
+ align-self: stretch !important;
+ }
+}
+
+.float-left {
+ float: left !important;
+}
+
+.float-right {
+ float: right !important;
+}
+
+.float-none {
+ float: none !important;
+}
+
+@media (min-width: 576px) {
+ .float-sm-left {
+ float: left !important;
+ }
+
+ .float-sm-right {
+ float: right !important;
+ }
+
+ .float-sm-none {
+ float: none !important;
+ }
+}
+
+@media (min-width: 768px) {
+ .float-md-left {
+ float: left !important;
+ }
+
+ .float-md-right {
+ float: right !important;
+ }
+
+ .float-md-none {
+ float: none !important;
+ }
+}
+
+@media (min-width: 992px) {
+ .float-lg-left {
+ float: left !important;
+ }
+
+ .float-lg-right {
+ float: right !important;
+ }
+
+ .float-lg-none {
+ float: none !important;
+ }
+}
+
+@media (min-width: 1200px) {
+ .float-xl-left {
+ float: left !important;
+ }
+
+ .float-xl-right {
+ float: right !important;
+ }
+
+ .float-xl-none {
+ float: none !important;
+ }
+}
+
+.overflow-auto {
+ overflow: auto !important;
+}
+
+.overflow-hidden {
+ overflow: hidden !important;
+}
+
+.position-static {
+ position: static !important;
+}
+
+.position-relative {
+ position: relative !important;
+}
+
+.position-absolute {
+ position: absolute !important;
+}
+
+.position-fixed {
+ position: fixed !important;
+}
+
+.position-sticky {
+ position: -webkit-sticky !important;
+ position: sticky !important;
+}
+
+.fixed-top {
+ position: fixed;
+ top: 0;
+ right: 0;
+ left: 0;
+ z-index: 1030;
+}
+
+.fixed-bottom {
+ position: fixed;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 1030;
+}
+
+@supports ((position: -webkit-sticky) or (position: sticky)) {
+ .sticky-top {
+ position: -webkit-sticky;
+ position: sticky;
+ top: 0;
+ z-index: 1020;
+ }
+}
+
+.sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ white-space: nowrap;
+ border: 0;
+}
+
+.sr-only-focusable:active,
+.sr-only-focusable:focus {
+ position: static;
+ width: auto;
+ height: auto;
+ overflow: visible;
+ clip: auto;
+ white-space: normal;
+}
+
+.shadow-sm {
+ box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) !important;
+}
+
+.shadow {
+ box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important;
+}
+
+.shadow-lg {
+ box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important;
+}
+
+.shadow-none {
+ box-shadow: none !important;
+}
+
+.w-25 {
+ width: 25% !important;
+}
+
+.w-50 {
+ width: 50% !important;
+}
+
+.w-75 {
+ width: 75% !important;
+}
+
+.w-100 {
+ width: 100% !important;
+}
+
+.w-auto {
+ width: auto !important;
+}
+
+.h-25 {
+ height: 25% !important;
+}
+
+.h-50 {
+ height: 50% !important;
+}
+
+.h-75 {
+ height: 75% !important;
+}
+
+.h-100 {
+ height: 100% !important;
+}
+
+.h-auto {
+ height: auto !important;
+}
+
+.mw-100 {
+ max-width: 100% !important;
+}
+
+.mh-100 {
+ max-height: 100% !important;
+}
+
+.min-vw-100 {
+ min-width: 100vw !important;
+}
+
+.min-vh-100 {
+ min-height: 100vh !important;
+}
+
+.vw-100 {
+ width: 100vw !important;
+}
+
+.vh-100 {
+ height: 100vh !important;
+}
+
+.stretched-link::after {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 1;
+ pointer-events: auto;
+ content: "";
+ background-color: rgba(0, 0, 0, 0);
+}
+
+.m-0 {
+ margin: 0 !important;
+}
+
+.mt-0,
+.my-0 {
+ margin-top: 0 !important;
+}
+
+.mr-0,
+.mx-0 {
+ margin-right: 0 !important;
+}
+
+.mb-0,
+.my-0 {
+ margin-bottom: 0 !important;
+}
+
+.ml-0,
+.mx-0 {
+ margin-left: 0 !important;
+}
+
+.m-1 {
+ margin: 0.25rem !important;
+}
+
+.mt-1,
+.my-1 {
+ margin-top: 0.25rem !important;
+}
+
+.mr-1,
+.mx-1 {
+ margin-right: 0.25rem !important;
+}
+
+.mb-1,
+.my-1 {
+ margin-bottom: 0.25rem !important;
+}
+
+.ml-1,
+.mx-1 {
+ margin-left: 0.25rem !important;
+}
+
+.m-2 {
+ margin: 0.5rem !important;
+}
+
+.mt-2,
+.my-2 {
+ margin-top: 0.5rem !important;
+}
+
+.mr-2,
+.mx-2 {
+ margin-right: 0.5rem !important;
+}
+
+.mb-2,
+.my-2 {
+ margin-bottom: 0.5rem !important;
+}
+
+.ml-2,
+.mx-2 {
+ margin-left: 0.5rem !important;
+}
+
+.m-3 {
+ margin: 1rem !important;
+}
+
+.mt-3,
+.my-3 {
+ margin-top: 1rem !important;
+}
+
+.mr-3,
+.mx-3 {
+ margin-right: 1rem !important;
+}
+
+.mb-3,
+.my-3 {
+ margin-bottom: 1rem !important;
+}
+
+.ml-3,
+.mx-3 {
+ margin-left: 1rem !important;
+}
+
+.m-4 {
+ margin: 1.5rem !important;
+}
+
+.mt-4,
+.my-4 {
+ margin-top: 1.5rem !important;
+}
+
+.mr-4,
+.mx-4 {
+ margin-right: 1.5rem !important;
+}
+
+.mb-4,
+.my-4 {
+ margin-bottom: 1.5rem !important;
+}
+
+.ml-4,
+.mx-4 {
+ margin-left: 1.5rem !important;
+}
+
+.m-5 {
+ margin: 3rem !important;
+}
+
+.mt-5,
+.my-5 {
+ margin-top: 3rem !important;
+}
+
+.mr-5,
+.mx-5 {
+ margin-right: 3rem !important;
+}
+
+.mb-5,
+.my-5 {
+ margin-bottom: 3rem !important;
+}
+
+.ml-5,
+.mx-5 {
+ margin-left: 3rem !important;
+}
+
+.p-0 {
+ padding: 0 !important;
+}
+
+.pt-0,
+.py-0 {
+ padding-top: 0 !important;
+}
+
+.pr-0,
+.px-0 {
+ padding-right: 0 !important;
+}
+
+.pb-0,
+.py-0 {
+ padding-bottom: 0 !important;
+}
+
+.pl-0,
+.px-0 {
+ padding-left: 0 !important;
+}
+
+.p-1 {
+ padding: 0.25rem !important;
+}
+
+.pt-1,
+.py-1 {
+ padding-top: 0.25rem !important;
+}
+
+.pr-1,
+.px-1 {
+ padding-right: 0.25rem !important;
+}
+
+.pb-1,
+.py-1 {
+ padding-bottom: 0.25rem !important;
+}
+
+.pl-1,
+.px-1 {
+ padding-left: 0.25rem !important;
+}
+
+.p-2 {
+ padding: 0.5rem !important;
+}
+
+.pt-2,
+.py-2 {
+ padding-top: 0.5rem !important;
+}
+
+.pr-2,
+.px-2 {
+ padding-right: 0.5rem !important;
+}
+
+.pb-2,
+.py-2 {
+ padding-bottom: 0.5rem !important;
+}
+
+.pl-2,
+.px-2 {
+ padding-left: 0.5rem !important;
+}
+
+.p-3 {
+ padding: 1rem !important;
+}
+
+.pt-3,
+.py-3 {
+ padding-top: 1rem !important;
+}
+
+.pr-3,
+.px-3 {
+ padding-right: 1rem !important;
+}
+
+.pb-3,
+.py-3 {
+ padding-bottom: 1rem !important;
+}
+
+.pl-3,
+.px-3 {
+ padding-left: 1rem !important;
+}
+
+.p-4 {
+ padding: 1.5rem !important;
+}
+
+.pt-4,
+.py-4 {
+ padding-top: 1.5rem !important;
+}
+
+.pr-4,
+.px-4 {
+ padding-right: 1.5rem !important;
+}
+
+.pb-4,
+.py-4 {
+ padding-bottom: 1.5rem !important;
+}
+
+.pl-4,
+.px-4 {
+ padding-left: 1.5rem !important;
+}
+
+.p-5 {
+ padding: 3rem !important;
+}
+
+.pt-5,
+.py-5 {
+ padding-top: 3rem !important;
+}
+
+.pr-5,
+.px-5 {
+ padding-right: 3rem !important;
+}
+
+.pb-5,
+.py-5 {
+ padding-bottom: 3rem !important;
+}
+
+.pl-5,
+.px-5 {
+ padding-left: 3rem !important;
+}
+
+.m-n1 {
+ margin: -0.25rem !important;
+}
+
+.mt-n1,
+.my-n1 {
+ margin-top: -0.25rem !important;
+}
+
+.mr-n1,
+.mx-n1 {
+ margin-right: -0.25rem !important;
+}
+
+.mb-n1,
+.my-n1 {
+ margin-bottom: -0.25rem !important;
+}
+
+.ml-n1,
+.mx-n1 {
+ margin-left: -0.25rem !important;
+}
+
+.m-n2 {
+ margin: -0.5rem !important;
+}
+
+.mt-n2,
+.my-n2 {
+ margin-top: -0.5rem !important;
+}
+
+.mr-n2,
+.mx-n2 {
+ margin-right: -0.5rem !important;
+}
+
+.mb-n2,
+.my-n2 {
+ margin-bottom: -0.5rem !important;
+}
+
+.ml-n2,
+.mx-n2 {
+ margin-left: -0.5rem !important;
+}
+
+.m-n3 {
+ margin: -1rem !important;
+}
+
+.mt-n3,
+.my-n3 {
+ margin-top: -1rem !important;
+}
+
+.mr-n3,
+.mx-n3 {
+ margin-right: -1rem !important;
+}
+
+.mb-n3,
+.my-n3 {
+ margin-bottom: -1rem !important;
+}
+
+.ml-n3,
+.mx-n3 {
+ margin-left: -1rem !important;
+}
+
+.m-n4 {
+ margin: -1.5rem !important;
+}
+
+.mt-n4,
+.my-n4 {
+ margin-top: -1.5rem !important;
+}
+
+.mr-n4,
+.mx-n4 {
+ margin-right: -1.5rem !important;
+}
+
+.mb-n4,
+.my-n4 {
+ margin-bottom: -1.5rem !important;
+}
+
+.ml-n4,
+.mx-n4 {
+ margin-left: -1.5rem !important;
+}
+
+.m-n5 {
+ margin: -3rem !important;
+}
+
+.mt-n5,
+.my-n5 {
+ margin-top: -3rem !important;
+}
+
+.mr-n5,
+.mx-n5 {
+ margin-right: -3rem !important;
+}
+
+.mb-n5,
+.my-n5 {
+ margin-bottom: -3rem !important;
+}
+
+.ml-n5,
+.mx-n5 {
+ margin-left: -3rem !important;
+}
+
+.m-auto {
+ margin: auto !important;
+}
+
+.mt-auto,
+.my-auto {
+ margin-top: auto !important;
+}
+
+.mr-auto,
+.mx-auto {
+ margin-right: auto !important;
+}
+
+.mb-auto,
+.my-auto {
+ margin-bottom: auto !important;
+}
+
+.ml-auto,
+.mx-auto {
+ margin-left: auto !important;
+}
+
+@media (min-width: 576px) {
+ .m-sm-0 {
+ margin: 0 !important;
+ }
+
+ .mt-sm-0,
+ .my-sm-0 {
+ margin-top: 0 !important;
+ }
+
+ .mr-sm-0,
+ .mx-sm-0 {
+ margin-right: 0 !important;
+ }
+
+ .mb-sm-0,
+ .my-sm-0 {
+ margin-bottom: 0 !important;
+ }
+
+ .ml-sm-0,
+ .mx-sm-0 {
+ margin-left: 0 !important;
+ }
+
+ .m-sm-1 {
+ margin: 0.25rem !important;
+ }
+
+ .mt-sm-1,
+ .my-sm-1 {
+ margin-top: 0.25rem !important;
+ }
+
+ .mr-sm-1,
+ .mx-sm-1 {
+ margin-right: 0.25rem !important;
+ }
+
+ .mb-sm-1,
+ .my-sm-1 {
+ margin-bottom: 0.25rem !important;
+ }
+
+ .ml-sm-1,
+ .mx-sm-1 {
+ margin-left: 0.25rem !important;
+ }
+
+ .m-sm-2 {
+ margin: 0.5rem !important;
+ }
+
+ .mt-sm-2,
+ .my-sm-2 {
+ margin-top: 0.5rem !important;
+ }
+
+ .mr-sm-2,
+ .mx-sm-2 {
+ margin-right: 0.5rem !important;
+ }
+
+ .mb-sm-2,
+ .my-sm-2 {
+ margin-bottom: 0.5rem !important;
+ }
+
+ .ml-sm-2,
+ .mx-sm-2 {
+ margin-left: 0.5rem !important;
+ }
+
+ .m-sm-3 {
+ margin: 1rem !important;
+ }
+
+ .mt-sm-3,
+ .my-sm-3 {
+ margin-top: 1rem !important;
+ }
+
+ .mr-sm-3,
+ .mx-sm-3 {
+ margin-right: 1rem !important;
+ }
+
+ .mb-sm-3,
+ .my-sm-3 {
+ margin-bottom: 1rem !important;
+ }
+
+ .ml-sm-3,
+ .mx-sm-3 {
+ margin-left: 1rem !important;
+ }
+
+ .m-sm-4 {
+ margin: 1.5rem !important;
+ }
+
+ .mt-sm-4,
+ .my-sm-4 {
+ margin-top: 1.5rem !important;
+ }
+
+ .mr-sm-4,
+ .mx-sm-4 {
+ margin-right: 1.5rem !important;
+ }
+
+ .mb-sm-4,
+ .my-sm-4 {
+ margin-bottom: 1.5rem !important;
+ }
+
+ .ml-sm-4,
+ .mx-sm-4 {
+ margin-left: 1.5rem !important;
+ }
+
+ .m-sm-5 {
+ margin: 3rem !important;
+ }
+
+ .mt-sm-5,
+ .my-sm-5 {
+ margin-top: 3rem !important;
+ }
+
+ .mr-sm-5,
+ .mx-sm-5 {
+ margin-right: 3rem !important;
+ }
+
+ .mb-sm-5,
+ .my-sm-5 {
+ margin-bottom: 3rem !important;
+ }
+
+ .ml-sm-5,
+ .mx-sm-5 {
+ margin-left: 3rem !important;
+ }
+
+ .p-sm-0 {
+ padding: 0 !important;
+ }
+
+ .pt-sm-0,
+ .py-sm-0 {
+ padding-top: 0 !important;
+ }
+
+ .pr-sm-0,
+ .px-sm-0 {
+ padding-right: 0 !important;
+ }
+
+ .pb-sm-0,
+ .py-sm-0 {
+ padding-bottom: 0 !important;
+ }
+
+ .pl-sm-0,
+ .px-sm-0 {
+ padding-left: 0 !important;
+ }
+
+ .p-sm-1 {
+ padding: 0.25rem !important;
+ }
+
+ .pt-sm-1,
+ .py-sm-1 {
+ padding-top: 0.25rem !important;
+ }
+
+ .pr-sm-1,
+ .px-sm-1 {
+ padding-right: 0.25rem !important;
+ }
+
+ .pb-sm-1,
+ .py-sm-1 {
+ padding-bottom: 0.25rem !important;
+ }
+
+ .pl-sm-1,
+ .px-sm-1 {
+ padding-left: 0.25rem !important;
+ }
+
+ .p-sm-2 {
+ padding: 0.5rem !important;
+ }
+
+ .pt-sm-2,
+ .py-sm-2 {
+ padding-top: 0.5rem !important;
+ }
+
+ .pr-sm-2,
+ .px-sm-2 {
+ padding-right: 0.5rem !important;
+ }
+
+ .pb-sm-2,
+ .py-sm-2 {
+ padding-bottom: 0.5rem !important;
+ }
+
+ .pl-sm-2,
+ .px-sm-2 {
+ padding-left: 0.5rem !important;
+ }
+
+ .p-sm-3 {
+ padding: 1rem !important;
+ }
+
+ .pt-sm-3,
+ .py-sm-3 {
+ padding-top: 1rem !important;
+ }
+
+ .pr-sm-3,
+ .px-sm-3 {
+ padding-right: 1rem !important;
+ }
+
+ .pb-sm-3,
+ .py-sm-3 {
+ padding-bottom: 1rem !important;
+ }
+
+ .pl-sm-3,
+ .px-sm-3 {
+ padding-left: 1rem !important;
+ }
+
+ .p-sm-4 {
+ padding: 1.5rem !important;
+ }
+
+ .pt-sm-4,
+ .py-sm-4 {
+ padding-top: 1.5rem !important;
+ }
+
+ .pr-sm-4,
+ .px-sm-4 {
+ padding-right: 1.5rem !important;
+ }
+
+ .pb-sm-4,
+ .py-sm-4 {
+ padding-bottom: 1.5rem !important;
+ }
+
+ .pl-sm-4,
+ .px-sm-4 {
+ padding-left: 1.5rem !important;
+ }
+
+ .p-sm-5 {
+ padding: 3rem !important;
+ }
+
+ .pt-sm-5,
+ .py-sm-5 {
+ padding-top: 3rem !important;
+ }
+
+ .pr-sm-5,
+ .px-sm-5 {
+ padding-right: 3rem !important;
+ }
+
+ .pb-sm-5,
+ .py-sm-5 {
+ padding-bottom: 3rem !important;
+ }
+
+ .pl-sm-5,
+ .px-sm-5 {
+ padding-left: 3rem !important;
+ }
+
+ .m-sm-n1 {
+ margin: -0.25rem !important;
+ }
+
+ .mt-sm-n1,
+ .my-sm-n1 {
+ margin-top: -0.25rem !important;
+ }
+
+ .mr-sm-n1,
+ .mx-sm-n1 {
+ margin-right: -0.25rem !important;
+ }
+
+ .mb-sm-n1,
+ .my-sm-n1 {
+ margin-bottom: -0.25rem !important;
+ }
+
+ .ml-sm-n1,
+ .mx-sm-n1 {
+ margin-left: -0.25rem !important;
+ }
+
+ .m-sm-n2 {
+ margin: -0.5rem !important;
+ }
+
+ .mt-sm-n2,
+ .my-sm-n2 {
+ margin-top: -0.5rem !important;
+ }
+
+ .mr-sm-n2,
+ .mx-sm-n2 {
+ margin-right: -0.5rem !important;
+ }
+
+ .mb-sm-n2,
+ .my-sm-n2 {
+ margin-bottom: -0.5rem !important;
+ }
+
+ .ml-sm-n2,
+ .mx-sm-n2 {
+ margin-left: -0.5rem !important;
+ }
+
+ .m-sm-n3 {
+ margin: -1rem !important;
+ }
+
+ .mt-sm-n3,
+ .my-sm-n3 {
+ margin-top: -1rem !important;
+ }
+
+ .mr-sm-n3,
+ .mx-sm-n3 {
+ margin-right: -1rem !important;
+ }
+
+ .mb-sm-n3,
+ .my-sm-n3 {
+ margin-bottom: -1rem !important;
+ }
+
+ .ml-sm-n3,
+ .mx-sm-n3 {
+ margin-left: -1rem !important;
+ }
+
+ .m-sm-n4 {
+ margin: -1.5rem !important;
+ }
+
+ .mt-sm-n4,
+ .my-sm-n4 {
+ margin-top: -1.5rem !important;
+ }
+
+ .mr-sm-n4,
+ .mx-sm-n4 {
+ margin-right: -1.5rem !important;
+ }
+
+ .mb-sm-n4,
+ .my-sm-n4 {
+ margin-bottom: -1.5rem !important;
+ }
+
+ .ml-sm-n4,
+ .mx-sm-n4 {
+ margin-left: -1.5rem !important;
+ }
+
+ .m-sm-n5 {
+ margin: -3rem !important;
+ }
+
+ .mt-sm-n5,
+ .my-sm-n5 {
+ margin-top: -3rem !important;
+ }
+
+ .mr-sm-n5,
+ .mx-sm-n5 {
+ margin-right: -3rem !important;
+ }
+
+ .mb-sm-n5,
+ .my-sm-n5 {
+ margin-bottom: -3rem !important;
+ }
+
+ .ml-sm-n5,
+ .mx-sm-n5 {
+ margin-left: -3rem !important;
+ }
+
+ .m-sm-auto {
+ margin: auto !important;
+ }
+
+ .mt-sm-auto,
+ .my-sm-auto {
+ margin-top: auto !important;
+ }
+
+ .mr-sm-auto,
+ .mx-sm-auto {
+ margin-right: auto !important;
+ }
+
+ .mb-sm-auto,
+ .my-sm-auto {
+ margin-bottom: auto !important;
+ }
+
+ .ml-sm-auto,
+ .mx-sm-auto {
+ margin-left: auto !important;
+ }
+}
+
+@media (min-width: 768px) {
+ .m-md-0 {
+ margin: 0 !important;
+ }
+
+ .mt-md-0,
+ .my-md-0 {
+ margin-top: 0 !important;
+ }
+
+ .mr-md-0,
+ .mx-md-0 {
+ margin-right: 0 !important;
+ }
+
+ .mb-md-0,
+ .my-md-0 {
+ margin-bottom: 0 !important;
+ }
+
+ .ml-md-0,
+ .mx-md-0 {
+ margin-left: 0 !important;
+ }
+
+ .m-md-1 {
+ margin: 0.25rem !important;
+ }
+
+ .mt-md-1,
+ .my-md-1 {
+ margin-top: 0.25rem !important;
+ }
+
+ .mr-md-1,
+ .mx-md-1 {
+ margin-right: 0.25rem !important;
+ }
+
+ .mb-md-1,
+ .my-md-1 {
+ margin-bottom: 0.25rem !important;
+ }
+
+ .ml-md-1,
+ .mx-md-1 {
+ margin-left: 0.25rem !important;
+ }
+
+ .m-md-2 {
+ margin: 0.5rem !important;
+ }
+
+ .mt-md-2,
+ .my-md-2 {
+ margin-top: 0.5rem !important;
+ }
+
+ .mr-md-2,
+ .mx-md-2 {
+ margin-right: 0.5rem !important;
+ }
+
+ .mb-md-2,
+ .my-md-2 {
+ margin-bottom: 0.5rem !important;
+ }
+
+ .ml-md-2,
+ .mx-md-2 {
+ margin-left: 0.5rem !important;
+ }
+
+ .m-md-3 {
+ margin: 1rem !important;
+ }
+
+ .mt-md-3,
+ .my-md-3 {
+ margin-top: 1rem !important;
+ }
+
+ .mr-md-3,
+ .mx-md-3 {
+ margin-right: 1rem !important;
+ }
+
+ .mb-md-3,
+ .my-md-3 {
+ margin-bottom: 1rem !important;
+ }
+
+ .ml-md-3,
+ .mx-md-3 {
+ margin-left: 1rem !important;
+ }
+
+ .m-md-4 {
+ margin: 1.5rem !important;
+ }
+
+ .mt-md-4,
+ .my-md-4 {
+ margin-top: 1.5rem !important;
+ }
+
+ .mr-md-4,
+ .mx-md-4 {
+ margin-right: 1.5rem !important;
+ }
+
+ .mb-md-4,
+ .my-md-4 {
+ margin-bottom: 1.5rem !important;
+ }
+
+ .ml-md-4,
+ .mx-md-4 {
+ margin-left: 1.5rem !important;
+ }
+
+ .m-md-5 {
+ margin: 3rem !important;
+ }
+
+ .mt-md-5,
+ .my-md-5 {
+ margin-top: 3rem !important;
+ }
+
+ .mr-md-5,
+ .mx-md-5 {
+ margin-right: 3rem !important;
+ }
+
+ .mb-md-5,
+ .my-md-5 {
+ margin-bottom: 3rem !important;
+ }
+
+ .ml-md-5,
+ .mx-md-5 {
+ margin-left: 3rem !important;
+ }
+
+ .p-md-0 {
+ padding: 0 !important;
+ }
+
+ .pt-md-0,
+ .py-md-0 {
+ padding-top: 0 !important;
+ }
+
+ .pr-md-0,
+ .px-md-0 {
+ padding-right: 0 !important;
+ }
+
+ .pb-md-0,
+ .py-md-0 {
+ padding-bottom: 0 !important;
+ }
+
+ .pl-md-0,
+ .px-md-0 {
+ padding-left: 0 !important;
+ }
+
+ .p-md-1 {
+ padding: 0.25rem !important;
+ }
+
+ .pt-md-1,
+ .py-md-1 {
+ padding-top: 0.25rem !important;
+ }
+
+ .pr-md-1,
+ .px-md-1 {
+ padding-right: 0.25rem !important;
+ }
+
+ .pb-md-1,
+ .py-md-1 {
+ padding-bottom: 0.25rem !important;
+ }
+
+ .pl-md-1,
+ .px-md-1 {
+ padding-left: 0.25rem !important;
+ }
+
+ .p-md-2 {
+ padding: 0.5rem !important;
+ }
+
+ .pt-md-2,
+ .py-md-2 {
+ padding-top: 0.5rem !important;
+ }
+
+ .pr-md-2,
+ .px-md-2 {
+ padding-right: 0.5rem !important;
+ }
+
+ .pb-md-2,
+ .py-md-2 {
+ padding-bottom: 0.5rem !important;
+ }
+
+ .pl-md-2,
+ .px-md-2 {
+ padding-left: 0.5rem !important;
+ }
+
+ .p-md-3 {
+ padding: 1rem !important;
+ }
+
+ .pt-md-3,
+ .py-md-3 {
+ padding-top: 1rem !important;
+ }
+
+ .pr-md-3,
+ .px-md-3 {
+ padding-right: 1rem !important;
+ }
+
+ .pb-md-3,
+ .py-md-3 {
+ padding-bottom: 1rem !important;
+ }
+
+ .pl-md-3,
+ .px-md-3 {
+ padding-left: 1rem !important;
+ }
+
+ .p-md-4 {
+ padding: 1.5rem !important;
+ }
+
+ .pt-md-4,
+ .py-md-4 {
+ padding-top: 1.5rem !important;
+ }
+
+ .pr-md-4,
+ .px-md-4 {
+ padding-right: 1.5rem !important;
+ }
+
+ .pb-md-4,
+ .py-md-4 {
+ padding-bottom: 1.5rem !important;
+ }
+
+ .pl-md-4,
+ .px-md-4 {
+ padding-left: 1.5rem !important;
+ }
+
+ .p-md-5 {
+ padding: 3rem !important;
+ }
+
+ .pt-md-5,
+ .py-md-5 {
+ padding-top: 3rem !important;
+ }
+
+ .pr-md-5,
+ .px-md-5 {
+ padding-right: 3rem !important;
+ }
+
+ .pb-md-5,
+ .py-md-5 {
+ padding-bottom: 3rem !important;
+ }
+
+ .pl-md-5,
+ .px-md-5 {
+ padding-left: 3rem !important;
+ }
+
+ .m-md-n1 {
+ margin: -0.25rem !important;
+ }
+
+ .mt-md-n1,
+ .my-md-n1 {
+ margin-top: -0.25rem !important;
+ }
+
+ .mr-md-n1,
+ .mx-md-n1 {
+ margin-right: -0.25rem !important;
+ }
+
+ .mb-md-n1,
+ .my-md-n1 {
+ margin-bottom: -0.25rem !important;
+ }
+
+ .ml-md-n1,
+ .mx-md-n1 {
+ margin-left: -0.25rem !important;
+ }
+
+ .m-md-n2 {
+ margin: -0.5rem !important;
+ }
+
+ .mt-md-n2,
+ .my-md-n2 {
+ margin-top: -0.5rem !important;
+ }
+
+ .mr-md-n2,
+ .mx-md-n2 {
+ margin-right: -0.5rem !important;
+ }
+
+ .mb-md-n2,
+ .my-md-n2 {
+ margin-bottom: -0.5rem !important;
+ }
+
+ .ml-md-n2,
+ .mx-md-n2 {
+ margin-left: -0.5rem !important;
+ }
+
+ .m-md-n3 {
+ margin: -1rem !important;
+ }
+
+ .mt-md-n3,
+ .my-md-n3 {
+ margin-top: -1rem !important;
+ }
+
+ .mr-md-n3,
+ .mx-md-n3 {
+ margin-right: -1rem !important;
+ }
+
+ .mb-md-n3,
+ .my-md-n3 {
+ margin-bottom: -1rem !important;
+ }
+
+ .ml-md-n3,
+ .mx-md-n3 {
+ margin-left: -1rem !important;
+ }
+
+ .m-md-n4 {
+ margin: -1.5rem !important;
+ }
+
+ .mt-md-n4,
+ .my-md-n4 {
+ margin-top: -1.5rem !important;
+ }
+
+ .mr-md-n4,
+ .mx-md-n4 {
+ margin-right: -1.5rem !important;
+ }
+
+ .mb-md-n4,
+ .my-md-n4 {
+ margin-bottom: -1.5rem !important;
+ }
+
+ .ml-md-n4,
+ .mx-md-n4 {
+ margin-left: -1.5rem !important;
+ }
+
+ .m-md-n5 {
+ margin: -3rem !important;
+ }
+
+ .mt-md-n5,
+ .my-md-n5 {
+ margin-top: -3rem !important;
+ }
+
+ .mr-md-n5,
+ .mx-md-n5 {
+ margin-right: -3rem !important;
+ }
+
+ .mb-md-n5,
+ .my-md-n5 {
+ margin-bottom: -3rem !important;
+ }
+
+ .ml-md-n5,
+ .mx-md-n5 {
+ margin-left: -3rem !important;
+ }
+
+ .m-md-auto {
+ margin: auto !important;
+ }
+
+ .mt-md-auto,
+ .my-md-auto {
+ margin-top: auto !important;
+ }
+
+ .mr-md-auto,
+ .mx-md-auto {
+ margin-right: auto !important;
+ }
+
+ .mb-md-auto,
+ .my-md-auto {
+ margin-bottom: auto !important;
+ }
+
+ .ml-md-auto,
+ .mx-md-auto {
+ margin-left: auto !important;
+ }
+}
+
+@media (min-width: 992px) {
+ .m-lg-0 {
+ margin: 0 !important;
+ }
+
+ .mt-lg-0,
+ .my-lg-0 {
+ margin-top: 0 !important;
+ }
+
+ .mr-lg-0,
+ .mx-lg-0 {
+ margin-right: 0 !important;
+ }
+
+ .mb-lg-0,
+ .my-lg-0 {
+ margin-bottom: 0 !important;
+ }
+
+ .ml-lg-0,
+ .mx-lg-0 {
+ margin-left: 0 !important;
+ }
+
+ .m-lg-1 {
+ margin: 0.25rem !important;
+ }
+
+ .mt-lg-1,
+ .my-lg-1 {
+ margin-top: 0.25rem !important;
+ }
+
+ .mr-lg-1,
+ .mx-lg-1 {
+ margin-right: 0.25rem !important;
+ }
+
+ .mb-lg-1,
+ .my-lg-1 {
+ margin-bottom: 0.25rem !important;
+ }
+
+ .ml-lg-1,
+ .mx-lg-1 {
+ margin-left: 0.25rem !important;
+ }
+
+ .m-lg-2 {
+ margin: 0.5rem !important;
+ }
+
+ .mt-lg-2,
+ .my-lg-2 {
+ margin-top: 0.5rem !important;
+ }
+
+ .mr-lg-2,
+ .mx-lg-2 {
+ margin-right: 0.5rem !important;
+ }
+
+ .mb-lg-2,
+ .my-lg-2 {
+ margin-bottom: 0.5rem !important;
+ }
+
+ .ml-lg-2,
+ .mx-lg-2 {
+ margin-left: 0.5rem !important;
+ }
+
+ .m-lg-3 {
+ margin: 1rem !important;
+ }
+
+ .mt-lg-3,
+ .my-lg-3 {
+ margin-top: 1rem !important;
+ }
+
+ .mr-lg-3,
+ .mx-lg-3 {
+ margin-right: 1rem !important;
+ }
+
+ .mb-lg-3,
+ .my-lg-3 {
+ margin-bottom: 1rem !important;
+ }
+
+ .ml-lg-3,
+ .mx-lg-3 {
+ margin-left: 1rem !important;
+ }
+
+ .m-lg-4 {
+ margin: 1.5rem !important;
+ }
+
+ .mt-lg-4,
+ .my-lg-4 {
+ margin-top: 1.5rem !important;
+ }
+
+ .mr-lg-4,
+ .mx-lg-4 {
+ margin-right: 1.5rem !important;
+ }
+
+ .mb-lg-4,
+ .my-lg-4 {
+ margin-bottom: 1.5rem !important;
+ }
+
+ .ml-lg-4,
+ .mx-lg-4 {
+ margin-left: 1.5rem !important;
+ }
+
+ .m-lg-5 {
+ margin: 3rem !important;
+ }
+
+ .mt-lg-5,
+ .my-lg-5 {
+ margin-top: 3rem !important;
+ }
+
+ .mr-lg-5,
+ .mx-lg-5 {
+ margin-right: 3rem !important;
+ }
+
+ .mb-lg-5,
+ .my-lg-5 {
+ margin-bottom: 3rem !important;
+ }
+
+ .ml-lg-5,
+ .mx-lg-5 {
+ margin-left: 3rem !important;
+ }
+
+ .p-lg-0 {
+ padding: 0 !important;
+ }
+
+ .pt-lg-0,
+ .py-lg-0 {
+ padding-top: 0 !important;
+ }
+
+ .pr-lg-0,
+ .px-lg-0 {
+ padding-right: 0 !important;
+ }
+
+ .pb-lg-0,
+ .py-lg-0 {
+ padding-bottom: 0 !important;
+ }
+
+ .pl-lg-0,
+ .px-lg-0 {
+ padding-left: 0 !important;
+ }
+
+ .p-lg-1 {
+ padding: 0.25rem !important;
+ }
+
+ .pt-lg-1,
+ .py-lg-1 {
+ padding-top: 0.25rem !important;
+ }
+
+ .pr-lg-1,
+ .px-lg-1 {
+ padding-right: 0.25rem !important;
+ }
+
+ .pb-lg-1,
+ .py-lg-1 {
+ padding-bottom: 0.25rem !important;
+ }
+
+ .pl-lg-1,
+ .px-lg-1 {
+ padding-left: 0.25rem !important;
+ }
+
+ .p-lg-2 {
+ padding: 0.5rem !important;
+ }
+
+ .pt-lg-2,
+ .py-lg-2 {
+ padding-top: 0.5rem !important;
+ }
+
+ .pr-lg-2,
+ .px-lg-2 {
+ padding-right: 0.5rem !important;
+ }
+
+ .pb-lg-2,
+ .py-lg-2 {
+ padding-bottom: 0.5rem !important;
+ }
+
+ .pl-lg-2,
+ .px-lg-2 {
+ padding-left: 0.5rem !important;
+ }
+
+ .p-lg-3 {
+ padding: 1rem !important;
+ }
+
+ .pt-lg-3,
+ .py-lg-3 {
+ padding-top: 1rem !important;
+ }
+
+ .pr-lg-3,
+ .px-lg-3 {
+ padding-right: 1rem !important;
+ }
+
+ .pb-lg-3,
+ .py-lg-3 {
+ padding-bottom: 1rem !important;
+ }
+
+ .pl-lg-3,
+ .px-lg-3 {
+ padding-left: 1rem !important;
+ }
+
+ .p-lg-4 {
+ padding: 1.5rem !important;
+ }
+
+ .pt-lg-4,
+ .py-lg-4 {
+ padding-top: 1.5rem !important;
+ }
+
+ .pr-lg-4,
+ .px-lg-4 {
+ padding-right: 1.5rem !important;
+ }
+
+ .pb-lg-4,
+ .py-lg-4 {
+ padding-bottom: 1.5rem !important;
+ }
+
+ .pl-lg-4,
+ .px-lg-4 {
+ padding-left: 1.5rem !important;
+ }
+
+ .p-lg-5 {
+ padding: 3rem !important;
+ }
+
+ .pt-lg-5,
+ .py-lg-5 {
+ padding-top: 3rem !important;
+ }
+
+ .pr-lg-5,
+ .px-lg-5 {
+ padding-right: 3rem !important;
+ }
+
+ .pb-lg-5,
+ .py-lg-5 {
+ padding-bottom: 3rem !important;
+ }
+
+ .pl-lg-5,
+ .px-lg-5 {
+ padding-left: 3rem !important;
+ }
+
+ .m-lg-n1 {
+ margin: -0.25rem !important;
+ }
+
+ .mt-lg-n1,
+ .my-lg-n1 {
+ margin-top: -0.25rem !important;
+ }
+
+ .mr-lg-n1,
+ .mx-lg-n1 {
+ margin-right: -0.25rem !important;
+ }
+
+ .mb-lg-n1,
+ .my-lg-n1 {
+ margin-bottom: -0.25rem !important;
+ }
+
+ .ml-lg-n1,
+ .mx-lg-n1 {
+ margin-left: -0.25rem !important;
+ }
+
+ .m-lg-n2 {
+ margin: -0.5rem !important;
+ }
+
+ .mt-lg-n2,
+ .my-lg-n2 {
+ margin-top: -0.5rem !important;
+ }
+
+ .mr-lg-n2,
+ .mx-lg-n2 {
+ margin-right: -0.5rem !important;
+ }
+
+ .mb-lg-n2,
+ .my-lg-n2 {
+ margin-bottom: -0.5rem !important;
+ }
+
+ .ml-lg-n2,
+ .mx-lg-n2 {
+ margin-left: -0.5rem !important;
+ }
+
+ .m-lg-n3 {
+ margin: -1rem !important;
+ }
+
+ .mt-lg-n3,
+ .my-lg-n3 {
+ margin-top: -1rem !important;
+ }
+
+ .mr-lg-n3,
+ .mx-lg-n3 {
+ margin-right: -1rem !important;
+ }
+
+ .mb-lg-n3,
+ .my-lg-n3 {
+ margin-bottom: -1rem !important;
+ }
+
+ .ml-lg-n3,
+ .mx-lg-n3 {
+ margin-left: -1rem !important;
+ }
+
+ .m-lg-n4 {
+ margin: -1.5rem !important;
+ }
+
+ .mt-lg-n4,
+ .my-lg-n4 {
+ margin-top: -1.5rem !important;
+ }
+
+ .mr-lg-n4,
+ .mx-lg-n4 {
+ margin-right: -1.5rem !important;
+ }
+
+ .mb-lg-n4,
+ .my-lg-n4 {
+ margin-bottom: -1.5rem !important;
+ }
+
+ .ml-lg-n4,
+ .mx-lg-n4 {
+ margin-left: -1.5rem !important;
+ }
+
+ .m-lg-n5 {
+ margin: -3rem !important;
+ }
+
+ .mt-lg-n5,
+ .my-lg-n5 {
+ margin-top: -3rem !important;
+ }
+
+ .mr-lg-n5,
+ .mx-lg-n5 {
+ margin-right: -3rem !important;
+ }
+
+ .mb-lg-n5,
+ .my-lg-n5 {
+ margin-bottom: -3rem !important;
+ }
+
+ .ml-lg-n5,
+ .mx-lg-n5 {
+ margin-left: -3rem !important;
+ }
+
+ .m-lg-auto {
+ margin: auto !important;
+ }
+
+ .mt-lg-auto,
+ .my-lg-auto {
+ margin-top: auto !important;
+ }
+
+ .mr-lg-auto,
+ .mx-lg-auto {
+ margin-right: auto !important;
+ }
+
+ .mb-lg-auto,
+ .my-lg-auto {
+ margin-bottom: auto !important;
+ }
+
+ .ml-lg-auto,
+ .mx-lg-auto {
+ margin-left: auto !important;
+ }
+}
+
+@media (min-width: 1200px) {
+ .m-xl-0 {
+ margin: 0 !important;
+ }
+
+ .mt-xl-0,
+ .my-xl-0 {
+ margin-top: 0 !important;
+ }
+
+ .mr-xl-0,
+ .mx-xl-0 {
+ margin-right: 0 !important;
+ }
+
+ .mb-xl-0,
+ .my-xl-0 {
+ margin-bottom: 0 !important;
+ }
+
+ .ml-xl-0,
+ .mx-xl-0 {
+ margin-left: 0 !important;
+ }
+
+ .m-xl-1 {
+ margin: 0.25rem !important;
+ }
+
+ .mt-xl-1,
+ .my-xl-1 {
+ margin-top: 0.25rem !important;
+ }
+
+ .mr-xl-1,
+ .mx-xl-1 {
+ margin-right: 0.25rem !important;
+ }
+
+ .mb-xl-1,
+ .my-xl-1 {
+ margin-bottom: 0.25rem !important;
+ }
+
+ .ml-xl-1,
+ .mx-xl-1 {
+ margin-left: 0.25rem !important;
+ }
+
+ .m-xl-2 {
+ margin: 0.5rem !important;
+ }
+
+ .mt-xl-2,
+ .my-xl-2 {
+ margin-top: 0.5rem !important;
+ }
+
+ .mr-xl-2,
+ .mx-xl-2 {
+ margin-right: 0.5rem !important;
+ }
+
+ .mb-xl-2,
+ .my-xl-2 {
+ margin-bottom: 0.5rem !important;
+ }
+
+ .ml-xl-2,
+ .mx-xl-2 {
+ margin-left: 0.5rem !important;
+ }
+
+ .m-xl-3 {
+ margin: 1rem !important;
+ }
+
+ .mt-xl-3,
+ .my-xl-3 {
+ margin-top: 1rem !important;
+ }
+
+ .mr-xl-3,
+ .mx-xl-3 {
+ margin-right: 1rem !important;
+ }
+
+ .mb-xl-3,
+ .my-xl-3 {
+ margin-bottom: 1rem !important;
+ }
+
+ .ml-xl-3,
+ .mx-xl-3 {
+ margin-left: 1rem !important;
+ }
+
+ .m-xl-4 {
+ margin: 1.5rem !important;
+ }
+
+ .mt-xl-4,
+ .my-xl-4 {
+ margin-top: 1.5rem !important;
+ }
+
+ .mr-xl-4,
+ .mx-xl-4 {
+ margin-right: 1.5rem !important;
+ }
+
+ .mb-xl-4,
+ .my-xl-4 {
+ margin-bottom: 1.5rem !important;
+ }
+
+ .ml-xl-4,
+ .mx-xl-4 {
+ margin-left: 1.5rem !important;
+ }
+
+ .m-xl-5 {
+ margin: 3rem !important;
+ }
+
+ .mt-xl-5,
+ .my-xl-5 {
+ margin-top: 3rem !important;
+ }
+
+ .mr-xl-5,
+ .mx-xl-5 {
+ margin-right: 3rem !important;
+ }
+
+ .mb-xl-5,
+ .my-xl-5 {
+ margin-bottom: 3rem !important;
+ }
+
+ .ml-xl-5,
+ .mx-xl-5 {
+ margin-left: 3rem !important;
+ }
+
+ .p-xl-0 {
+ padding: 0 !important;
+ }
+
+ .pt-xl-0,
+ .py-xl-0 {
+ padding-top: 0 !important;
+ }
+
+ .pr-xl-0,
+ .px-xl-0 {
+ padding-right: 0 !important;
+ }
+
+ .pb-xl-0,
+ .py-xl-0 {
+ padding-bottom: 0 !important;
+ }
+
+ .pl-xl-0,
+ .px-xl-0 {
+ padding-left: 0 !important;
+ }
+
+ .p-xl-1 {
+ padding: 0.25rem !important;
+ }
+
+ .pt-xl-1,
+ .py-xl-1 {
+ padding-top: 0.25rem !important;
+ }
+
+ .pr-xl-1,
+ .px-xl-1 {
+ padding-right: 0.25rem !important;
+ }
+
+ .pb-xl-1,
+ .py-xl-1 {
+ padding-bottom: 0.25rem !important;
+ }
+
+ .pl-xl-1,
+ .px-xl-1 {
+ padding-left: 0.25rem !important;
+ }
+
+ .p-xl-2 {
+ padding: 0.5rem !important;
+ }
+
+ .pt-xl-2,
+ .py-xl-2 {
+ padding-top: 0.5rem !important;
+ }
+
+ .pr-xl-2,
+ .px-xl-2 {
+ padding-right: 0.5rem !important;
+ }
+
+ .pb-xl-2,
+ .py-xl-2 {
+ padding-bottom: 0.5rem !important;
+ }
+
+ .pl-xl-2,
+ .px-xl-2 {
+ padding-left: 0.5rem !important;
+ }
+
+ .p-xl-3 {
+ padding: 1rem !important;
+ }
+
+ .pt-xl-3,
+ .py-xl-3 {
+ padding-top: 1rem !important;
+ }
+
+ .pr-xl-3,
+ .px-xl-3 {
+ padding-right: 1rem !important;
+ }
+
+ .pb-xl-3,
+ .py-xl-3 {
+ padding-bottom: 1rem !important;
+ }
+
+ .pl-xl-3,
+ .px-xl-3 {
+ padding-left: 1rem !important;
+ }
+
+ .p-xl-4 {
+ padding: 1.5rem !important;
+ }
+
+ .pt-xl-4,
+ .py-xl-4 {
+ padding-top: 1.5rem !important;
+ }
+
+ .pr-xl-4,
+ .px-xl-4 {
+ padding-right: 1.5rem !important;
+ }
+
+ .pb-xl-4,
+ .py-xl-4 {
+ padding-bottom: 1.5rem !important;
+ }
+
+ .pl-xl-4,
+ .px-xl-4 {
+ padding-left: 1.5rem !important;
+ }
+
+ .p-xl-5 {
+ padding: 3rem !important;
+ }
+
+ .pt-xl-5,
+ .py-xl-5 {
+ padding-top: 3rem !important;
+ }
+
+ .pr-xl-5,
+ .px-xl-5 {
+ padding-right: 3rem !important;
+ }
+
+ .pb-xl-5,
+ .py-xl-5 {
+ padding-bottom: 3rem !important;
+ }
+
+ .pl-xl-5,
+ .px-xl-5 {
+ padding-left: 3rem !important;
+ }
+
+ .m-xl-n1 {
+ margin: -0.25rem !important;
+ }
+
+ .mt-xl-n1,
+ .my-xl-n1 {
+ margin-top: -0.25rem !important;
+ }
+
+ .mr-xl-n1,
+ .mx-xl-n1 {
+ margin-right: -0.25rem !important;
+ }
+
+ .mb-xl-n1,
+ .my-xl-n1 {
+ margin-bottom: -0.25rem !important;
+ }
+
+ .ml-xl-n1,
+ .mx-xl-n1 {
+ margin-left: -0.25rem !important;
+ }
+
+ .m-xl-n2 {
+ margin: -0.5rem !important;
+ }
+
+ .mt-xl-n2,
+ .my-xl-n2 {
+ margin-top: -0.5rem !important;
+ }
+
+ .mr-xl-n2,
+ .mx-xl-n2 {
+ margin-right: -0.5rem !important;
+ }
+
+ .mb-xl-n2,
+ .my-xl-n2 {
+ margin-bottom: -0.5rem !important;
+ }
+
+ .ml-xl-n2,
+ .mx-xl-n2 {
+ margin-left: -0.5rem !important;
+ }
+
+ .m-xl-n3 {
+ margin: -1rem !important;
+ }
+
+ .mt-xl-n3,
+ .my-xl-n3 {
+ margin-top: -1rem !important;
+ }
+
+ .mr-xl-n3,
+ .mx-xl-n3 {
+ margin-right: -1rem !important;
+ }
+
+ .mb-xl-n3,
+ .my-xl-n3 {
+ margin-bottom: -1rem !important;
+ }
+
+ .ml-xl-n3,
+ .mx-xl-n3 {
+ margin-left: -1rem !important;
+ }
+
+ .m-xl-n4 {
+ margin: -1.5rem !important;
+ }
+
+ .mt-xl-n4,
+ .my-xl-n4 {
+ margin-top: -1.5rem !important;
+ }
+
+ .mr-xl-n4,
+ .mx-xl-n4 {
+ margin-right: -1.5rem !important;
+ }
+
+ .mb-xl-n4,
+ .my-xl-n4 {
+ margin-bottom: -1.5rem !important;
+ }
+
+ .ml-xl-n4,
+ .mx-xl-n4 {
+ margin-left: -1.5rem !important;
+ }
+
+ .m-xl-n5 {
+ margin: -3rem !important;
+ }
+
+ .mt-xl-n5,
+ .my-xl-n5 {
+ margin-top: -3rem !important;
+ }
+
+ .mr-xl-n5,
+ .mx-xl-n5 {
+ margin-right: -3rem !important;
+ }
+
+ .mb-xl-n5,
+ .my-xl-n5 {
+ margin-bottom: -3rem !important;
+ }
+
+ .ml-xl-n5,
+ .mx-xl-n5 {
+ margin-left: -3rem !important;
+ }
+
+ .m-xl-auto {
+ margin: auto !important;
+ }
+
+ .mt-xl-auto,
+ .my-xl-auto {
+ margin-top: auto !important;
+ }
+
+ .mr-xl-auto,
+ .mx-xl-auto {
+ margin-right: auto !important;
+ }
+
+ .mb-xl-auto,
+ .my-xl-auto {
+ margin-bottom: auto !important;
+ }
+
+ .ml-xl-auto,
+ .mx-xl-auto {
+ margin-left: auto !important;
+ }
+}
+
+.text-monospace {
+ font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace !important;
+}
+
+.text-justify {
+ text-align: justify !important;
+}
+
+.text-wrap {
+ white-space: normal !important;
+}
+
+.text-nowrap {
+ white-space: nowrap !important;
+}
+
+.text-truncate {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.text-left {
+ text-align: left !important;
+}
+
+.text-right {
+ text-align: right !important;
+}
+
+.text-center {
+ text-align: center !important;
+}
+
+@media (min-width: 576px) {
+ .text-sm-left {
+ text-align: left !important;
+ }
+
+ .text-sm-right {
+ text-align: right !important;
+ }
+
+ .text-sm-center {
+ text-align: center !important;
+ }
+}
+
+@media (min-width: 768px) {
+ .text-md-left {
+ text-align: left !important;
+ }
+
+ .text-md-right {
+ text-align: right !important;
+ }
+
+ .text-md-center {
+ text-align: center !important;
+ }
+}
+
+@media (min-width: 992px) {
+ .text-lg-left {
+ text-align: left !important;
+ }
+
+ .text-lg-right {
+ text-align: right !important;
+ }
+
+ .text-lg-center {
+ text-align: center !important;
+ }
+}
+
+@media (min-width: 1200px) {
+ .text-xl-left {
+ text-align: left !important;
+ }
+
+ .text-xl-right {
+ text-align: right !important;
+ }
+
+ .text-xl-center {
+ text-align: center !important;
+ }
+}
+
+.text-lowercase {
+ text-transform: lowercase !important;
+}
+
+.text-uppercase {
+ text-transform: uppercase !important;
+}
+
+.text-capitalize {
+ text-transform: capitalize !important;
+}
+
+.font-weight-light {
+ font-weight: 300 !important;
+}
+
+.font-weight-lighter {
+ font-weight: lighter !important;
+}
+
+.font-weight-normal {
+ font-weight: 400 !important;
+}
+
+.font-weight-bold {
+ font-weight: 700 !important;
+}
+
+.font-weight-bolder {
+ font-weight: bolder !important;
+}
+
+.font-italic {
+ font-style: italic !important;
+}
+
+.text-white {
+ color: #fff !important;
+}
+
+.text-primary {
+ color: #007bff !important;
+}
+
+a.text-primary:hover,
+a.text-primary:focus {
+ color: #0056b3 !important;
+}
+
+.text-secondary {
+ color: #6c757d !important;
+}
+
+a.text-secondary:hover,
+a.text-secondary:focus {
+ color: #494f54 !important;
+}
+
+.text-success {
+ color: #28a745 !important;
+}
+
+a.text-success:hover,
+a.text-success:focus {
+ color: #19692c !important;
+}
+
+.text-info {
+ color: #17a2b8 !important;
+}
+
+a.text-info:hover,
+a.text-info:focus {
+ color: #0f6674 !important;
+}
+
+.text-warning {
+ color: #ffc107 !important;
+}
+
+a.text-warning:hover,
+a.text-warning:focus {
+ color: #ba8b00 !important;
+}
+
+.text-danger {
+ color: #dc3545 !important;
+}
+
+a.text-danger:hover,
+a.text-danger:focus {
+ color: #a71d2a !important;
+}
+
+.text-light {
+ color: #f8f9fa !important;
+}
+
+a.text-light:hover,
+a.text-light:focus {
+ color: #cbd3da !important;
+}
+
+.text-dark {
+ color: #343a40 !important;
+}
+
+a.text-dark:hover,
+a.text-dark:focus {
+ color: #121416 !important;
+}
+
+.text-body {
+ color: #212529 !important;
+}
+
+.text-muted {
+ color: #6c757d !important;
+}
+
+.text-black-50 {
+ color: rgba(0, 0, 0, 0.5) !important;
+}
+
+.text-white-50 {
+ color: rgba(255, 255, 255, 0.5) !important;
+}
+
+.text-hide {
+ font: 0/0 a;
+ color: transparent;
+ text-shadow: none;
+ background-color: transparent;
+ border: 0;
+}
+
+.text-decoration-none {
+ text-decoration: none !important;
+}
+
+.text-break {
+ word-break: break-word !important;
+ overflow-wrap: break-word !important;
+}
+
+.text-reset {
+ color: inherit !important;
+}
+
+.visible {
+ visibility: visible !important;
+}
+
+.invisible {
+ visibility: hidden !important;
+}
+
+@media print {
+
+ *,
+ *::before,
+ *::after {
+ text-shadow: none !important;
+ box-shadow: none !important;
+ }
+
+ a:not(.btn) {
+ text-decoration: underline;
+ }
+
+ abbr[title]::after {
+ content: " (" attr(title) ")";
+ }
+
+ pre {
+ white-space: pre-wrap !important;
+ }
+
+ pre,
+ blockquote {
+ border: 1px solid #adb5bd;
+ page-break-inside: avoid;
+ }
+
+ thead {
+ display: table-header-group;
+ }
+
+ tr,
+ img {
+ page-break-inside: avoid;
+ }
+
+ p,
+ h2,
+ h3 {
+ orphans: 3;
+ widows: 3;
+ }
+
+ h2,
+ h3 {
+ page-break-after: avoid;
+ }
+
+ @page {
+ size: a3;
+ }
+
+ body {
+ min-width: 992px !important;
+ }
+
+ .container {
+ min-width: 992px !important;
+ }
+
+ .navbar {
+ display: none;
+ }
+
+ .badge {
+ border: 1px solid #000;
+ }
+
+ .table {
+ border-collapse: collapse !important;
+ }
+
+ .table td,
+ .table th {
+ background-color: #fff !important;
+ }
+
+ .table-bordered th,
+ .table-bordered td {
+ border: 1px solid #dee2e6 !important;
+ }
+
+ .table-dark {
+ color: inherit;
+ }
+
+ .table-dark th,
+ .table-dark td,
+ .table-dark thead th,
+ .table-dark tbody+tbody {
+ border-color: #dee2e6;
+ }
+
+ .table .thead-dark th {
+ color: inherit;
+ border-color: #dee2e6;
+ }
+}
+
+/*# sourceMappingURL=bootstrap.css.map */
\ No newline at end of file
diff --git a/public/static/css/button.css b/public/static/css/button.css
new file mode 100644
index 0000000..b4067e5
--- /dev/null
+++ b/public/static/css/button.css
@@ -0,0 +1,38 @@
+.arrow{
+ border: 9px solid transparent;
+ border-bottom-color: rgb(0, 167, 134);
+ width: 0px;
+ height: 0px;
+ top:0px
+}
+.stick{
+ width: 8px;
+ height: 14px;
+ border-radius: 1px;
+ background-color: rgb(0, 167, 134);
+ top:15px;
+}
+#back_top div{
+ position: absolute;
+ margin: auto;
+ right: 0px;
+ left: 0px;
+}
+#back_top{
+ /* border: 1px solid rgb(0, 167, 134); */
+ background-color: rgb(220, 220, 220);
+ height: 38px;
+ width: 38px;
+ border-radius: 3px;
+ display: block;
+ cursor: pointer;
+ position: fixed;
+ right: 50px;
+ bottom: 100px;
+ display: none;
+}
+#back_top:hover{
+ border: 1px solid rgb(220, 220, 220);
+ transition: 0.5s;
+ background-color: white;
+}
\ No newline at end of file
diff --git a/public/static/css/companyIntro.css b/public/static/css/companyIntro.css
new file mode 100644
index 0000000..03afd3d
--- /dev/null
+++ b/public/static/css/companyIntro.css
@@ -0,0 +1,327 @@
+/* 公司简介 */
+body {
+ margin: 0;
+ padding: 0;
+}
+
+.topPic {
+ width: 100%;
+}
+
+.barGs:hover {
+ /* border-bottom: #fff 3px solid; */
+ padding-bottom: 15px;
+ /* border-right: none; */
+}
+
+.barTop {
+ display: flex;
+ width: 90%;
+ height: auto;
+ background-color: #00a587;
+ padding-left: 5%;
+ padding-top: 20px;
+ position: relative;
+ top: -75px;
+ margin-left: 10%;
+
+}
+
+.barTop2 {
+ position: absolute;
+ left: -10px;
+ width: 100%;
+
+}
+
+.barName {
+ width: 30%;
+ height: 100%;
+ line-height: 2;
+ margin-left: 30px;
+}
+
+.barHeadline {
+ font-size: 40px;
+ font-weight: bold;
+ color: #fff;
+ line-height: 1.6;
+ font-family: "Microsoft YaHei", 微软雅黑;
+}
+
+.barHeadline2 {
+ font-size: 30px;
+ color: #fff;
+ padding-bottom: 20px;
+}
+
+.barSize {
+ width: 200px;
+ height: 30px;
+ position: absolute;
+ display: flex;
+ right: 0;
+ margin-top: 20px;
+}
+
+.barSize img {
+ width: 20px;
+ height: 20px;
+}
+
+.barMap {
+ font-size: 14px;
+ color: #fff;
+}
+
+.bar {
+ margin-top: 50px;
+ display: flex;
+ /* width: 600px; */
+ height: 30px;
+ position: absolute;
+ right: 0;
+ top: 30px;
+}
+
+.barGs {
+ line-height: 40px;
+ /* display: flex;
+ justify-content: center;
+ text-align: center; */
+ margin: 0 20px;
+ height: 40px;
+ color: #fff;
+ font-size: 15px;
+ /* border-right: #e2e2e2 2px solid; */
+}
+
+.body {
+ margin: 20px auto 100px auto;
+ width: 90%;
+ max-width: 2000px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+.bodyTop {
+ font-size: 40px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ /* border-bottom: 1px solid #ccc; */
+ padding: 20px 0;
+
+}
+
+.you {
+ justify-content: center;
+ align-items: start;
+}
+
+.boxBody {
+ margin-left: 3.3%;
+ width: 45%;
+}
+
+.bodyName-1 {
+ color: #00a587;
+ font-size: 27px;
+ font-weight: bold;
+ width: 100%;
+ /*height: 50px;*/
+ margin-bottom: 20px;
+}
+
+.bodyName-2 {
+ color: #00a587;
+ font-weight: bold;
+ font-size: 22px;
+ width: 100%;
+ margin: 35px 0;
+}
+
+.bodyName-3 {
+ margin-top: 30px;
+ width: 100%;
+ line-height: 3;
+ font-size: 17px;
+ display: flex;
+ white-space: pre-wrap;
+ /*padding-bottom: 50px;*/
+ font-family: "Microsoft YaHei", 微软雅黑;
+}
+
+.boxPic {
+ width: 45%;
+ height: 500px;
+ margin: 10px 10px 10px 60px;
+}
+
+.boxPic img {
+ width: 100%;
+ height: 80%;
+ object-fit: fill;
+
+}
+
+.boxPic-1 {
+ width: 45%;
+ height: 300px;
+ margin-left: 3.3%;
+}
+
+.bodyPic-1 {
+ width: 45%;
+ height: 350px;
+ margin: 10px 0px 0px 90px;
+}
+
+.bodyName-4 {
+ margin-top: 20px;
+ color: #000;
+ font-size: 30px;
+ font-weight: bold;
+ height: 50px;
+ margin-bottom: 25px;
+}
+
+.xian {
+ background-color: #00a587;
+ width: 100px;
+ height: 2px;
+}
+
+.bodyPic-2 {
+ height: 350px;
+ margin: 20px 90px 0px 0px;
+}
+
+.barTop-xiao {
+ width: 100%;
+ background-color: #00a587;
+ padding: 20px 30px;
+}
+
+.barName-Mini {
+ color: #fff;
+ font-size: 700;
+ font-size: 30px;
+ border-bottom: #ccc 1px solid;
+ padding-left: 30px;
+}
+
+.bar-Mini {
+ display: flex;
+ margin: 20px 0px;
+}
+
+.barGs-Mini {
+ line-height: 20px;
+ display: flex;
+ justify-content: center;
+ text-align: center;
+ width: 70px;
+ height: 20px;
+ color: #fff;
+ border-right: #e2e2e2 2px solid;
+}
+
+.barGs-sm {
+ line-height: 20px;
+ display: flex;
+ justify-content: center;
+ text-align: center;
+ width: 90px;
+ height: 20px;
+ color: #fff;
+ border-right: #e2e2e2 2px solid;
+}
+
+.body-Mini {
+ width: 100%;
+}
+
+.bodyTop-Mini {
+ font-size: 30px;
+ height: 50px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 20px;
+ margin-top: 20px;
+}
+
+.bodyName-1-Mini {
+ color: #00a587;
+ font-size: 28px;
+ font-weight: bold;
+ width: 100%;
+ padding-bottom: 20px;
+}
+
+.bodyName-2-Mini {
+ color: #00a587;
+ font-weight: bold;
+ font-size: 18px;
+ width: 100%;
+ /* padding-bottom: 40px; */
+}
+
+.bodyName-3-Mini {
+ margin: 30px 0;
+ width: 100%;
+ font-size: 16px;
+ line-height: 28px;
+}
+
+.bodyPic-1-Mini {
+ width: 90%;
+ margin: 10px 0;
+}
+
+.bodyName-4-Mini {
+ padding: 50px 0 30px 0;
+ color: #000;
+ font-size: 22px;
+ font-weight: bold;
+ width: 90%;
+}
+
+.body-Mini img {
+ height: 100%;
+ max-width: 100%;
+ margin: 30px 0;
+}
+
+.comp a {
+ text-decoration: none !important;
+}
+
+.boxPic-Mini {
+ width: 100%;
+}
+
+.boxPic-Mini img {
+ width: 100%;
+ height: 100%;
+}
+
+.boxBody-Mini {
+ width: 100%;
+ margin-top: 30px;
+}
+
+.borderBottom {
+ width: 0;
+ transition: all 0.3s;
+ height: 3px;
+ background-color: #fff;
+ margin: auto;
+}
+
+.barGs:hover .borderBottom {
+ width: 60px;
+ margin: auto;
+}
\ No newline at end of file
diff --git a/public/static/css/developmentHistory.css b/public/static/css/developmentHistory.css
new file mode 100644
index 0000000..9e989f7
--- /dev/null
+++ b/public/static/css/developmentHistory.css
@@ -0,0 +1,338 @@
+/* 发展历程 */
+html,
+body {
+ width: 100%;
+ margin: 0;
+ padding: 0;
+}
+
+.topPic {
+ width: 100%;
+}
+
+.barGs:hover {
+ /* border-bottom: #fff 3px solid; */
+ padding-bottom: 15px;
+ /* border-right: none; */
+}
+
+.bodyBox,
+.bodyBox2 {
+ cursor: pointer;
+}
+
+.barTop {
+ display: flex;
+ width: 90%;
+ height: auto;
+ background-color: #00a587;
+ padding-left: 5%;
+ padding-top: 20px;
+ position: relative;
+ top: -75px;
+ margin-left: 10%;
+
+}
+
+.barTop2 {
+ position: absolute;
+ left: -10px;
+ width: 100%;
+
+}
+
+.barName {
+ width: 30%;
+ height: 100%;
+ line-height: 2;
+ margin-left: 30px;
+}
+
+.barHeadline {
+ font-size: 40px;
+ font-weight: bold;
+ color: #fff;
+ line-height: 1.6;
+}
+
+.barHeadline2 {
+ font-size: 30px;
+ color: #fff;
+ padding-bottom: 20px;
+}
+
+.barSize {
+ width: 200px;
+ height: 30px;
+ position: absolute;
+ display: flex;
+ right: 0;
+ margin-top: 20px;
+}
+
+.barSize img {
+ width: 20px;
+ height: 20px;
+}
+
+@media (max-width: 767px) {
+
+ .bodyBox,
+ .bodyBox2 {
+ width: 80% !important;
+ margin: 10px auto !important;
+ padding: 10px 0 !important;
+ display: flex !important;
+ }
+}
+
+
+.barMap {
+ font-size: 14px;
+ color: #fff;
+}
+
+.bar {
+ margin-top: 50px;
+ display: flex;
+ /* width: 600px; */
+ height: 30px;
+ position: absolute;
+ right: 0;
+ top: 30px;
+}
+
+.barGs {
+ line-height: 40px;
+ /* display: flex;
+ justify-content: center;
+ text-align: center; */
+ margin: 0 20px;
+ height: 40px;
+ color: #fff;
+ font-size: 15px;
+ /* border-right: #e2e2e2 2px solid; */
+}
+
+.body {
+ margin: 20px auto;
+ width: 100% !important;
+}
+
+.bodyTop {
+ font-size: 40px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 20px 0px;
+}
+
+.bodyTop-subtitle {
+ width: 100%;
+ line-height: 2;
+ font-size: 16px;
+ color: #333;
+ flex-wrap: wrap;
+ padding: 20px 50px;
+ margin: 0 auto;
+ text-align: center;
+}
+
+.bodyBox {
+ width: 70%;
+ margin: 10px auto;
+ padding: 10px 0;
+ display: flex;
+}
+
+.bodyBox2 {
+ width: 70%;
+ padding: 10px 0;
+ margin: 0 auto;
+ display: -webkit-box;
+ background-color: #edeaea;
+}
+
+.bodyBox:hover {
+ box-shadow: 0px 10px 5px #ccc;
+}
+
+.bodyBox2:hover {
+ box-shadow: 0px 10px 5px #ccc;
+}
+
+.bodyBox-year {
+ width: 50%;
+}
+
+.year {
+ font-size: 25px;
+ float: right;
+ margin-right: 20px;
+}
+
+.certer-line {
+ width: 20%;
+ padding-top: 20px;
+}
+
+.yuan {
+ width: 20px;
+ height: 20px;
+ border-radius: 50%;
+ border: #00a587 5px solid;
+ margin: 5px auto;
+}
+
+.xian {
+ width: 2px;
+ height: 100px;
+ background-color: #ccc;
+ margin-left: 9px;
+ margin-top: 10px;
+}
+
+.bodyThing {
+ width: 40%;
+ margin-left: 20px;
+}
+
+.thing1 {
+ color: #00a587;
+ font-size: 20px;
+ display: flex;
+ flex-wrap: wrap;
+}
+
+.thing2 {
+ color: #333;
+ font-size: 16px;
+ font-weight: 300;
+ display: flex;
+ flex-wrap: wrap;
+}
+
+.bodyThing-2 {
+ width: 50%;
+}
+
+.thing3 {
+ color: #00a587;
+ font-size: 20px;
+ width: 97%;
+ text-align: right;
+
+}
+
+.thing4 {
+ width: 97%;
+ color: #333;
+ font-size: 16px;
+ font-weight: 300;
+ text-align: right;
+
+}
+
+.bodyBox-year-2 {
+ width: 40%;
+}
+
+.year2 {
+ font-size: 24px;
+ float: left;
+ margin-left: 20px;
+}
+
+.barTop-xiao {
+ width: 100%;
+ background-color: #00a587;
+ box-sizing: border-box;
+ padding: 20px 30px;
+}
+
+.barName-Mini {
+ color: #fff;
+ font-size: 700;
+ font-size: 30px;
+ border-bottom: #ccc 1px solid;
+ padding-left: 30px;
+}
+
+.bar-Mini {
+ display: flex;
+ margin: 20px 0px;
+}
+
+.barGs-Mini {
+ line-height: 20px;
+ display: flex;
+ justify-content: center;
+ text-align: center;
+ width: 70px;
+ height: 20px;
+ color: #fff;
+ border-right: #e2e2e2 2px solid;
+}
+
+.barGs-sm {
+ line-height: 20px;
+ display: flex;
+ justify-content: center;
+ text-align: center;
+ width: 90px;
+ height: 20px;
+ color: #fff;
+ border-right: #e2e2e2 2px solid;
+}
+
+.body-Mini {
+ width: 100%;
+}
+
+.bodyTop-Mini {
+ font-size: 30px;
+ height: 50px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ box-sizing: border-box;
+ margin-top: 20px;
+}
+
+.bodyTop-subtitle-Mini {
+ width: 95%;
+ margin: 10px 2.5%;
+ line-height: 1.3;
+ font-size: 14px;
+ color: #333;
+ flex-wrap: wrap;
+ margin: 0 auto;
+ text-align: center;
+}
+
+.bodyBox-year-Mini {
+ width: 30%;
+}
+
+.bodyThing-Mini {
+ width: 60%;
+ margin-left: 20px;
+
+}
+
+#box {
+ width: 100%;
+}
+
+.borderBottom {
+ width: 0;
+ transition: all 0.3s;
+ height: 3px;
+ background-color: #fff;
+ margin: auto;
+}
+
+.barGs:hover .borderBottom {
+ width: 60px;
+ margin: auto;
+}
\ No newline at end of file
diff --git a/public/static/css/enterpriseCulture.css b/public/static/css/enterpriseCulture.css
new file mode 100644
index 0000000..a6e6466
--- /dev/null
+++ b/public/static/css/enterpriseCulture.css
@@ -0,0 +1,183 @@
+/* 企业文化 */
+body {
+ margin: 0;
+ padding: 0;
+}
+
+.topPic {
+ width: 100%;
+ /* margin: 0 50px; */
+ text-align: center;
+}
+
+.barGs:hover {
+ /* border-bottom: #fff 3px solid; */
+ padding-bottom: 15px;
+ /* border-right: none; */
+}
+
+.barTop {
+ display: flex;
+ width: 90%;
+ height: auto;
+ background-color: #00a587;
+ padding-left: 5%;
+ padding-top: 20px;
+ position: relative;
+ top: -75px;
+ margin-left: 10%;
+
+}
+
+.barTop2 {
+ position: absolute;
+ left: -10px;
+ width: 100%;
+
+}
+
+.barName {
+ width: 30%;
+ height: 100%;
+ line-height: 2;
+ margin-left: 30px;
+}
+
+.barHeadline {
+ font-size: 40px;
+ font-weight: bold;
+ color: #fff;
+ line-height: 1.6;
+}
+
+.barHeadline2 {
+ font-size: 30px;
+ color: #fff;
+ padding-bottom: 20px;
+}
+
+.barSize {
+ width: 200px;
+ height: 30px;
+ position: absolute;
+ display: flex;
+ right: 0;
+ margin-top: 20px;
+}
+
+.barSize img {
+ width: 20px;
+ height: 20px;
+}
+
+.barMap {
+ font-size: 14px;
+ color: #fff;
+}
+
+.bar {
+ margin-top: 50px;
+ display: flex;
+ /* width: 600px; */
+ height: 30px;
+ position: absolute;
+ right: 0;
+ top: 30px;
+}
+
+.barGs {
+ line-height: 40px;
+ /* display: flex;
+ justify-content: center;
+ text-align: center; */
+ margin: 0 20px;
+ height: 40px;
+ color: #fff;
+ font-size: 15px;
+ /* border-right: #e2e2e2 2px solid; */
+}
+
+.bodyHead {
+ display: flex;
+ text-align: center;
+ justify-content: center;
+ font-size: 40px;
+ /* border-bottom: 1px solid #ccc; */
+ padding: 20px;
+}
+
+.bodyPic {
+ align-items: center;
+ width: 80%;
+ max-width: 100%;
+ margin: 20px 10%;
+}
+
+.body {
+ width: 90%;
+ margin: 20px 5%;
+}
+
+.barTop-xiao {
+ width: 100%;
+ background-color: #00a587;
+ padding: 20px 30px;
+}
+
+.barName-Mini {
+ color: #fff;
+ font-size: 700;
+ font-size: 30px;
+ border-bottom: #ccc 1px solid;
+ padding-left: 30px;
+}
+
+.bar-Mini {
+ display: flex;
+ margin: 20px 0px;
+}
+
+.barGs-Mini {
+ line-height: 20px;
+ display: flex;
+ justify-content: center;
+ text-align: center;
+ width: 70px;
+ height: 20px;
+ color: #fff;
+ border-right: #e2e2e2 2px solid;
+}
+
+.barGs-sm {
+ line-height: 20px;
+ display: flex;
+ justify-content: center;
+ text-align: center;
+ width: 90px;
+ height: 20px;
+ color: #fff;
+ border-right: #e2e2e2 2px solid;
+}
+
+.bodyTop-Mini {
+ font-size: 30px;
+ height: 50px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 20px;
+ margin-top: 20px;
+}
+
+.borderBottom {
+ width: 0;
+ transition: all 0.3s;
+ height: 3px;
+ background-color: #fff;
+ margin: auto;
+}
+
+.barGs:hover .borderBottom {
+ width: 60px;
+ margin: auto;
+}
\ No newline at end of file
diff --git a/public/static/css/footer.css b/public/static/css/footer.css
new file mode 100644
index 0000000..c0fd20c
--- /dev/null
+++ b/public/static/css/footer.css
@@ -0,0 +1,10 @@
+.footer_box {
+ display: flex;
+ justify-content: center;
+ background-color: rgb(12, 50, 16);
+ color: blueviolet;
+}
+
+.ICPNumber {
+ color: #7a7a7a;
+}
\ No newline at end of file
diff --git a/public/static/css/headNavFoot.css b/public/static/css/headNavFoot.css
new file mode 100644
index 0000000..08628e5
--- /dev/null
+++ b/public/static/css/headNavFoot.css
@@ -0,0 +1,1058 @@
+/*public*/
+a {
+ text-decoration: none !important;
+}
+
+.f1 {
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ -webkit-line-clamp: 1;
+ font-size: 15px;
+}
+
+.f2 {
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ -webkit-line-clamp: 2;
+ font-size: 13px;
+}
+
+.f3 {
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ -webkit-line-clamp: 3;
+ font-size: 13px;
+}
+
+.headerBox a {
+ text-decoration: none !important;
+}
+
+.f7 {
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ -webkit-line-clamp: 7;
+ font-size: 13px;
+}
+
+.f5 {
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ -webkit-line-clamp: 5;
+ font-size: 13px;
+}
+
+.dNone {
+ display: none !important;
+}
+
+.df_jc_ac {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.df_jw_ac {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.df_jc_ac_fc {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+}
+
+.dBlock {
+ display: block !important;
+}
+
+.index {
+ background: #f3f3f3;
+}
+
+/*移动端*/
+@media (min-width: 576px) {
+ .container {
+ max-width: 540px;
+ }
+
+ /*.phoneReactive {*/
+ /* height: 315px !important;*/
+ /*}*/
+
+ .posterReactive {
+ height: 344px !important;
+ }
+
+ .showReactive {
+ /*height: 744px!important;*/
+ }
+
+ .showsBox {
+ width: 100% !important;
+ }
+
+ .productItem {
+ height: 520px !important;
+ }
+}
+
+@media (min-width: 768px) {
+ .container {
+ max-width: 720px;
+ }
+
+ /*.phoneReactive {*/
+ /* height: 420px !important;*/
+ /*}*/
+
+ .posterReactive {
+ height: 400px !important;
+ }
+
+ .phoneProductInfoReactive {
+ height: 420px !important;
+ }
+
+ /*.showReactive {*/
+ /* height: 440px !important;*/
+
+ /*}*/
+
+}
+
+@media (min-width: 991px) {
+ .container {
+ max-width: 960px;
+ }
+
+ /*.phoneReactive {*/
+ /* height: 471px !important;*/
+ /*}*/
+
+ .posterReactive {
+ height: 472px !important;
+ }
+
+ .phoneProductInfoReactive {
+ height: 520px !important;
+ }
+
+ .showReactive {
+ height: 650px !important;
+ }
+
+ .show-items {
+ height: 337px !important;
+ }
+
+ .productsBigBox {
+ max-width: 1028px !important;
+ }
+
+}
+
+@media (min-width: 1200px) {
+ .container {
+ max-width: 1140px;
+ }
+
+ /*.phoneReactive {*/
+ /* height: 700px !important;*/
+ /*}*/
+
+ .posterReactive {
+ height: 515px !important;
+ }
+
+ .phoneProductInfoReactive {
+ height: 620px !important;
+ }
+
+ .showReactive {
+ height: 715px !important;
+
+ }
+
+ .aboutCertificate {
+ display: -webkit-box;
+ overflow: hidden;
+ position: absolute !important;
+ bottom: 30px !important;
+ }
+
+ .show-items {
+ height: 370px !important;
+ }
+
+ .productsBigBox {
+ max-width: 1248px !important;
+ }
+
+}
+
+@media (min-width: 1540px) {
+ .container {
+ max-width: 1440px;
+ }
+
+ /*.phoneReactive {*/
+ /* height: 900px !important;*/
+ /*}*/
+
+ .posterReactive {
+ height: 605px !important;
+ }
+
+ .phoneProductInfoReactive {
+ height: 720px !important;
+ }
+
+ .showReactive {
+ height: 834px !important;
+ }
+
+ .show-items {
+ height: 429px !important;
+ }
+
+ .productsBigBox {
+ max-width: 1588px !important;
+ }
+}
+
+@media (max-width: 1273px) {
+ .phoneHeadNone {
+ display: none !important;
+ overflow: hidden;
+ }
+
+ .phoneBlock-head {
+ display: flex !important;
+ justify-content: right;
+ justify-content: flex-end;
+ padding-left: 55%;
+ margin-right: 4%;
+ width: 100%;
+ }
+
+ .phoneMoreBlockImportant {
+ display: block !important;
+ ;
+ }
+}
+
+@media (max-width: 991px) {
+ .phoneNone {
+ display: none !important;
+ }
+
+ .showLine {
+ border-right: 1px solid rgb(51, 186, 164) !important;
+ }
+}
+
+@media (max-width: 993px) {
+ .phoneBlock {
+ display: block;
+ }
+
+ .phoneBlockImportant {
+ display: block !important;
+ ;
+ }
+
+ .phoneShowContentBoxFlex {
+ display: flex !important;
+ }
+
+ .phone-f-c {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+ }
+
+ .phone-w-100 {
+ width: 100% !important;
+ }
+
+ .phoneFoot-df_jc_ac_fc {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column !important;
+ }
+
+ .phoneShowItem {
+ display: flex;
+ flex-direction: column;
+ !important;
+ justify-content: center;
+ !important;
+ align-items: center;
+ !important;
+ }
+}
+
+@media (max-width: 588px) {
+ .phoneSmallNone {
+ display: none !important;
+ }
+
+ .phoneSmallBlock {
+ display: block;
+ }
+
+ /*.phoneReactive {*/
+ /* height: 215px !important;*/
+ /*}*/
+
+ .swiper-button-prev {
+ display: none !important;
+ }
+
+ .swiper-button-next {
+ display: none !important;
+ }
+
+ .posterReactive {
+ height: 230px !important;
+ }
+
+ .phoneProductInfoReactive {
+ height: 400px !important;
+ }
+
+ .showsBox {
+ width: 100% !important;
+ }
+
+ .showButtonBox .showButton {
+ width: 85%;
+ }
+
+ .showLeftButton>img,
+ .showRightButton>img {
+ cursor: pointer;
+ padding: 0;
+ }
+
+ .showReactive {
+ /*height: 550px!important;*/
+ }
+
+ .showImg {
+ /*height: 430px!important;*/
+ }
+
+ .productItem {
+ height: 520px !important;
+ }
+}
+
+/* max-width:570px */
+@media (max-width:575px) {
+ .showLine {
+ border-right: 1px solid rgb(250, 250, 250) !important;
+ }
+}
+
+@media (max-width: 350px) {
+ .psHead {
+ width: 185px !important;
+ }
+
+ .posterReactive {
+ height: 162px !important;
+ }
+
+ /*.phoneReactive {*/
+ /* height: 142px !important;*/
+ /*}*/
+
+ .pscontainer {
+ padding: 0 !important;
+ }
+
+ .pscontainer>.productsBox {
+ padding: 20px 0 20px 0 !important;
+ }
+
+ .phoneProductInfoReactive {
+ height: 350px !important;
+ }
+
+ .productItem {
+ height: 520px !important;
+ }
+}
+
+/*修改*/
+.phoneBlock-head {
+ padding-left: 150px;
+}
+.phoneReactive{
+ width: 100%!important;
+ height: 49vw!important;
+}
+
+/*pc端*/
+
+/*menu*/
+/* .productMenusItem{
+ color: black;
+} */
+.phoneHideBox {
+ z-index: 9999;
+ backdrop-filter: blur(2px);
+ background: rgba(0, 10, 10, 0.5);
+ color: white;
+ height: 100vh;
+ width: 100%;
+ overflow-y: auto;
+ position: fixed;
+}
+
+.phoneHideBox::-webkit-scrollbar-track {
+ background: #999;
+ border-radius: 0;
+}
+
+.phoneHideBox::-webkit-scrollbar-thumb {
+ background: #f7f7f7;
+ border-radius: 0;
+}
+
+.phoneHideBox::selection {
+ background: #b3d4fc;
+ text-shadow: none;
+}
+
+/*nav*/
+.swiper-slide img {
+ position: relative;
+ left: auto;
+ top: auto;
+ /* transform: translate(0) scale(1.02); */
+ width: 100%;
+ max-width: none;
+ transition: all 1.8s cubic-bezier(0.250, 0.460, 0.450, 0.940);
+}
+
+.swiper-slide-active img,
+.swiper-slide-duplicate-active img {
+ transition: 1s linear;
+ transform: translate(0) scale(1) !important;
+ z-index: 99;
+}
+
+.navHeader {
+ display: flex;
+ flex-direction: column;
+}
+
+.navHeader>a {
+ padding: 30px;
+ cursor: pointer;
+ border-bottom: 1px solid #e2e3e5;
+ font-weight: 600;
+ transition: 0.6s;
+ color: white !important;
+}
+
+.navHeader>a:hover {
+ background: #e2e3e5;
+ color: #4e555b;
+}
+
+.fMenu {
+ padding: 30px;
+ cursor: pointer;
+ border-bottom: 1px solid #e2e3e5;
+ font-weight: 600;
+ transition: 0.6s;
+}
+
+.fMenu:hover {
+ background: #e2e3e5;
+ color: #4e555b;
+}
+
+.sMenu {
+ font-weight: 600;
+ padding: 30px 30px 30px 45px;
+ cursor: pointer;
+ transition: 0.6s;
+ color: white !important;
+}
+
+.sMenuBox {
+ display: flex;
+ flex-direction: column;
+}
+
+.sMenu:hover {
+ background: #e2e3e5;
+ color: #4e555b;
+}
+
+/*head*/
+.headerBox {
+ background: #11bea0;
+ position: fixed;
+ margin-left: 0;
+ margin-right: 0;
+ width: 100%;
+ margin-bottom: 0;
+ z-index: 1001;
+ padding: 3px;
+}
+
+.headList {
+ margin-left: 40%;
+ margin-right: 0;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding-left: 20px;
+ transition: 0.4s;
+}
+
+.headList>.f1 {
+ font-weight: 600;
+ margin: 0 20px 0 10px;
+ cursor: pointer;
+ color: white;
+ font-size: 16px;
+ transition: 0.4s;
+ height: 65px;
+ line-height: 65px;
+ width: 70px;
+ text-align: center;
+}
+
+.headList>.f1:after {
+ content: ' ';
+ position: relative;
+ z-index: 2;
+ display: block;
+ margin: -2px 0 0 0;
+ height: 2px;
+}
+
+.headList>.f1:hover:after {
+ height: 2px;
+ animation: ad_width .2s linear forwards;
+ background: #ffffff;
+}
+
+@keyframes ad_width {
+ from {
+ width: 0
+ }
+
+ to {
+ width: 100%
+ }
+}
+
+
+
+.active {
+ background: #4e555b;
+ color: white;
+ font-weight: 500;
+}
+
+.headerIcon {
+ height: 115px;
+ position: absolute;
+ margin: -11px 1.5%;
+ width: 315px;
+ padding: 0 !important;
+ z-index: -15;
+}
+
+.headerIcon>img {
+ height: 50%;
+ /* width: 80%; */
+ /* position: absolute; */
+ /* z-index: -50; */
+ margin: 15px 60px;
+}
+
+.headerIcon2 {
+ height: 84px;
+ position: absolute;
+ margin: -11px 1.5%;
+ width: 263px;
+ padding: 0 !important;
+ display: none;
+}
+
+.headerIcon2>img {
+ height: 50%;
+ margin: 20px 60px 20px 0;
+}
+
+.phoneBlock>div {
+ padding: 20px 10px 25px 16px;
+}
+
+.search {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+}
+
+.search>img {
+ cursor: pointer;
+ height: 20px;
+ width: 20px;
+}
+
+.moreButton {
+ display: none;
+}
+
+.moreButton>img {
+ cursor: pointer;
+ height: 20px;
+ width: 20px;
+}
+
+.index a {
+ text-decoration: none !important;
+}
+
+.messagePhone {
+ padding: 6px 10px 0 16px !important;
+ color: white;
+}
+
+.messagePhone>div:nth-of-type(1) {
+ font-size: 18px;
+ !important;
+}
+
+.messagePhone>div:nth-of-type(2) {
+ font-weight: 600;
+ font-size: 24px;
+ !important;
+ width: 180px;
+}
+
+/*foot*/
+footer {
+ padding: 0 !important;
+}
+
+.footBgBox {
+ display: flex;
+ flex-wrap: wrap;
+ padding: 0;
+ position: relative;
+}
+
+.footItem {
+ padding: 0;
+ position: relative;
+ height: 110px;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+}
+
+.footItem>img {
+ position: absolute;
+ height: 100%;
+ width: 100%;
+ object-fit: cover;
+}
+
+.footContent {
+ color: white;
+ position: relative;
+}
+
+.footTitle {
+ font-weight: 700;
+ font-size: 24px;
+}
+
+.footText {
+ font-size: 14px;
+}
+
+.footBigBox {
+ width: 100%;
+ color: white;
+ background: #222222;
+ padding: 30px 5% 30px 5%;
+}
+
+.footBox {
+ width: 100%;
+}
+
+.footLeftBoxLeft {
+ display: flex;
+ justify-content: left;
+ flex-direction: column;
+}
+
+.footLeftBox {
+ padding-left: 0;
+}
+
+.footLeftBox>div {
+ padding-left: 0;
+}
+
+.footLeftTitle {
+ font-weight: 550;
+ font-size: 18px;
+}
+
+.footLine {
+ margin: 20px auto;
+ width: 100%;
+ height: 1px;
+ background: #7a7a7a;
+}
+
+.footLeftContent {
+ display: flex;
+ justify-content: left;
+ flex-direction: column;
+ color: #7a7a7a;
+ font-weight: 550;
+}
+
+.footLeftContentItem {
+ margin: 5px 0;
+ cursor: pointer;
+ color: #7a7a7a !important;
+
+}
+
+.footRightTitle {
+ font-weight: 550;
+ font-size: 18px;
+}
+
+.footRightContent {
+ display: flex;
+ justify-content: left;
+ flex-direction: column;
+ color: #7a7a7a;
+ font-weight: 550;
+}
+
+.footRightContent>a {
+ margin: 5px 0;
+ cursor: pointer;
+}
+
+.footRightPhone {
+ display: flex;
+ justify-content: left;
+ align-items: center;
+}
+
+.footRightPhoneContent {
+ margin-left: 10px;
+}
+
+.footRightPhoneContentText {
+ color: #7a7a7a;
+ font-size: 8px;
+}
+
+.footRightPhoneContentTitle {
+ font-weight: 550;
+ color: white;
+ font-size: 22px;
+}
+
+.footRightContentItem {
+ margin: 5px 0;
+ cursor: pointer;
+}
+
+.footRightBoxRight {
+ display: flex;
+ justify-content: space-around;
+ /*align-items: center;*/
+}
+
+.footRightBoxImgItem {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+ margin: 5px;
+ width: 125px;
+ height: 100%;
+}
+
+.footRightBoxImgItem>img {
+ height: 100%;
+ width: 100%;
+}
+
+.footRightBoxImgItemText {
+ font-size: 8px;
+ color: #7a7a7a;
+ text-align: center;
+}
+
+.footFootBox {
+ margin: 20px auto;
+ width: 100%;
+}
+
+.footFootBox>div {
+ color: #7a7a7a;
+ font-size: 15px;
+ font-weight: 550;
+ font-family: "Microsoft YaHei", 微软雅黑;
+}
+
+/*nav*/
+.swiper {
+ width: 100%;
+ height: 900px;
+}
+
+.swiper-slide>img {
+ height: 100% !important;
+ width: 100% !important;
+ object-fit: fill;
+}
+
+.swiper-pagination>span {
+ cursor: pointer;
+}
+
+.swiper-button-prev:after {
+ color: #989899 !important;
+}
+
+.swiper-button-next:after {
+ color: #989899 !important;
+}
+
+.swiper-pagination-bullet-active {
+ background: #11bea0 !important;
+}
+
+.MenuActive {
+ border-bottom: 1px solid #4e555b;
+ background: #e2e3e5;
+ color: #4e555b;
+}
+
+.navBigBox {
+ position: relative;
+}
+
+.navBox {
+ display: -webkit-box;
+ overflow: hidden;
+ position: relative;
+ right: 0;
+ transition: 1s;
+}
+
+.navItem {
+ cursor: pointer;
+ height: 650px;
+ width: 100%;
+ position: relative;
+ right: 0;
+ transition: 0.5s;
+}
+
+.navItem>img {
+ height: 100%;
+ width: 100%;
+ object-fit: cover;
+ position: absolute;
+ right: 0;
+}
+
+.navSmallItem {
+ position: absolute;
+ display: flex;
+ justify-content: space-evenly;
+ align-items: center;
+ bottom: 0;
+ width: 100%;
+ margin: 30px auto;
+}
+
+.navIcon {
+ position: absolute;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ height: 500px;
+ top: 0;
+ left: 0;
+ width: 100%;
+}
+
+.navIcon>div {
+ height: 50px;
+ width: 50px;
+ cursor: pointer;
+ margin: 0 40px;
+}
+
+.navIcon img {
+ height: 100%;
+ width: 100%;
+}
+
+.navSmallItem>img {
+ height: 12%;
+ width: 12%;
+ object-fit: cover;
+ opacity: 0.3;
+}
+
+.navSmallItem>.navActive {
+ opacity: 1;
+}
+
+/*title*/
+.title {
+ text-align: center;
+ margin: 70px auto;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+}
+
+.titleTop {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.titleTop>div {
+ margin: 0 5px 0 5px;
+}
+
+.titleMiddle {
+ font-size: 28px;
+ font-weight: 800;
+ margin: 0 10px 0 10px;
+}
+
+.titleAside {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.titleAside>div {
+ margin: 0 5px 0 5px;
+ height: calc(var(--i)*10px);
+ opacity: calc(var(--i)*0.2);
+ width: 5px;
+ background: #24c3a9;
+}
+
+.titleBottom {
+ font-weight: 600;
+ font-family: "Microsoft YaHei", 微软雅黑;
+}
+
+.titleBottom>span:nth-of-type(1) {
+ color: #2faf94;
+}
+
+.titleBottom>span:nth-of-type(2) {
+ color: #e1e1e1;
+}
+
+/*搜索*/
+
+.searchBox:focus {
+ outline: none;
+}
+
+.searchBigBox {
+ height: 0;
+ width: 100%;
+ position: fixed;
+ z-index: 99999;
+ top: 75px;
+ left: 0;
+ text-align: center;
+ transition: 0.8s;
+ background: #11bea0;
+ padding-top: 0;
+}
+
+.searchBox {
+ height: 80%;
+ width: 80%;
+ margin: 0 auto;
+ border: none;
+ border-radius: 10px 10px 10px 10px;
+ display: none;
+ transition: 0.4s;
+ padding-left: 20px !important;
+
+}
+
+.searchDetail {
+ width: 80%;
+ display: none;
+ background: white;
+ margin: 0 auto;
+ text-align: left;
+ border-radius: 0 0 10px 10px;
+}
+
+.searchDetailItem {
+ padding: 20px;
+ text-decoration: none !important;
+ display: inline-block;
+ width: 100%;
+ color: #000000;
+ overflow: hidden;
+ overflow-y: visible;
+ overflow-x: visible;
+}
+
+/*score*/
+::-webkit-scrollbar {
+ width: 10px;
+ background-color: white;
+}
+
+::-webkit-scrollbar-thumb {
+ border-radius: 20px;
+ background: linear-gradient(#ffffff, #7cbe8b, #429b57, #1e7e34);
+}
\ No newline at end of file
diff --git a/public/static/css/header.css b/public/static/css/header.css
new file mode 100644
index 0000000..38016cc
--- /dev/null
+++ b/public/static/css/header.css
@@ -0,0 +1,6 @@
+.header_box {
+ display: flex;
+ justify-content: center;
+ background-color: aqua;
+ color: blueviolet;
+}
\ No newline at end of file
diff --git a/public/static/css/honour.css b/public/static/css/honour.css
new file mode 100644
index 0000000..1618769
--- /dev/null
+++ b/public/static/css/honour.css
@@ -0,0 +1,320 @@
+/* 荣誉资质 */
+body {
+ margin: 0;
+ padding: 0;
+ height: 100%;
+}
+
+
+
+.barGs:hover {
+ /* border-bottom: #fff 3px solid; */
+ padding-bottom: 15px;
+ /* border-right: none; */
+}
+
+.barTop {
+ display: flex;
+ width: 87%;
+ height: auto;
+ background-color: #00a587;
+ padding-left: 5%;
+ padding-top: 20px;
+ position: relative;
+ top: -75px;
+ margin-left: 13%;
+
+}
+
+.barTop2 {
+ position: absolute;
+ left: -10px;
+ width: 100%;
+
+}
+
+.barName {
+ width: 30%;
+ height: 100%;
+ line-height: 2;
+ margin-left: 30px;
+}
+
+.barHeadline {
+ font-size: 40px;
+ font-weight: bold;
+ color: #fff;
+ line-height: 1.6;
+}
+
+.barHeadline2 {
+ font-size: 30px;
+ color: #fff;
+ padding-bottom: 20px;
+}
+
+.barSize {
+ width: 200px;
+ height: 30px;
+ position: absolute;
+ display: flex;
+ right: 0;
+ margin-top: 20px;
+}
+
+.barSize img {
+ width: 20px;
+ height: 20px;
+}
+
+.barMap {
+ font-size: 14px;
+ color: #fff;
+}
+
+.bar {
+ margin-top: 50px;
+ display: flex;
+ /* width: 600px; */
+ height: 30px;
+ position: absolute;
+ right: 0;
+ top: 30px;
+}
+
+.barGs {
+ line-height: 40px;
+ /* display: flex;
+ justify-content: center;
+ text-align: center; */
+ margin: 0 20px;
+ height: 40px;
+ color: #fff;
+ font-size: 15px;
+ /* border-right: #e2e2e2 2px solid; */
+}
+
+.body {
+ margin: 20px auto;
+ width: 100%;
+ max-width: 1500px;
+}
+
+.bodyTop {
+ width: 100%;
+ font-size: 40px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border-bottom: 1px solid #ccc;
+ padding: 20px 0;
+}
+
+.bodyPicBox {
+ width: 100%;
+ display: flex;
+ float: left;
+ flex-wrap: wrap;
+ margin: 50px 0;
+}
+
+.bodyBox {
+ margin: 55px 2.5%;
+ width: 20%;
+ transition: all 1s;
+ cursor: pointer;
+ height: 200px;
+ background: #f2f2f2;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+ overflow: hidden;
+}
+
+.bodyBox:hover.bodyBox > img {
+ transform: scale(1.1);
+}
+.topPic {
+ width: 70%;
+ height: 150px;
+ object-fit: contain;
+ transition: all 1s;
+}
+.bodyPic {
+ width: 20%;
+ margin: 25px 25px 5%;
+ height: 70%;
+}
+
+.PicName {
+ color: #999;
+ font-size: 16px;
+ display: flex;
+ width: 100%;
+ padding: 10px;
+ align-items: center;
+ justify-content: center;
+ background-color: #f2f2f2;
+ z-index: 99;
+}
+
+.bodyBox:hover .PicName {
+ color: #fff;
+ background-color: #d94444;
+}
+
+.bodyBox:hover .bodyPic {
+ width: 270px;
+ height: 220px;
+ margin: 15px 15px 10px;
+}
+
+
+.barTop-xiao {
+ width: 100%;
+ background-color: #00a587;
+ padding: 20px 30px;
+}
+
+.barName-Mini {
+ color: #fff;
+ /*font-size: 700;*/
+ font-size: 30px;
+ border-bottom: #ccc 1px solid;
+ padding-left: 30px;
+}
+
+.bar-Mini {
+ display: flex;
+ margin: 20px 0px;
+}
+
+.barGs-Mini {
+ line-height: 20px;
+ display: flex;
+ justify-content: center;
+ text-align: center;
+ width: 70px;
+ height: 20px;
+ color: #fff;
+ border-right: #e2e2e2 2px solid;
+}
+
+.barGs-sm {
+ line-height: 20px;
+ display: flex;
+ justify-content: center;
+ text-align: center;
+ width: 90px;
+ height: 20px;
+ color: #fff;
+ border-right: #e2e2e2 2px solid;
+}
+
+.body-Mini {
+ width: 100%;
+ height: 350px;
+ cursor: pointer;
+}
+.bodyBox-Mini:hover .PicName-Mini {
+ color: #fff;
+ background-color: #d94444;
+}
+
+
+.bodyTop-Mini {
+ font-size: 30px;
+ height: 50px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 20px;
+ margin-top: 20px;
+}
+
+.bodyPicBox-Mini {
+ width: 90%;
+ margin: 10px 5%;
+ display: flex;
+ float: left;
+ flex-wrap: wrap;
+ margin-top: 50px;
+}
+
+.bodyPic-Mini {
+ width: 100%;
+ height: 150px;
+ object-fit: contain;
+ transition: 0.4s;
+}
+
+.bodyBox-Mini {
+ width: 40%;
+ margin: 10px 5%;
+ height: 300px;
+}
+
+
+.PicName-Mini {
+ color: #999;
+ font-size: 16px;
+ display: flex;
+ width: 100%;
+ height: 60px;
+ align-items: center;
+ justify-content: center;
+ background-color: #f2f2f2;
+ transition: 0.4s;
+}
+
+.borderBottom {
+ width: 0;
+ transition: all 0.3s;
+ height: 3px;
+ background-color: #fff;
+ margin: auto;
+}
+
+.barGs:hover .borderBottom {
+ width: 60px;
+ margin: auto;
+}
+
+#Picture_to_view {
+ display: none;
+ width: 100%;
+ height: 1000px;
+ background-color: #000;
+ color: #f2f2f2;
+ position: fixed;
+ z-index: 1000;
+ top: 0;
+
+}
+
+#hide {
+ position: absolute;
+ padding: 10px 20px;
+ align-items: center;
+ font-size: xx-large;
+ font-weight: 700;
+ top: 20px;
+ right: 20px;
+ cursor: pointer;
+}
+
+.HimgBox {
+ width: 60%;
+ margin: 5% 20%;
+ height: 600px;
+ position: relative;
+}
+
+.HimgBox>img {
+ position: absolute;
+ height: 100%;
+ width: 100%;
+ object-fit: contain;
+
+}
\ No newline at end of file
diff --git a/public/static/css/index.css b/public/static/css/index.css
new file mode 100644
index 0000000..3d99924
--- /dev/null
+++ b/public/static/css/index.css
@@ -0,0 +1,800 @@
+/*index.css*/
+/*nav*/
+.MenuActive {
+ border-bottom: 1px solid #4e555b;
+ background: #e2e3e5;
+ color: #4e555b;
+}
+
+.navBigBox {
+ position: relative;
+
+}
+
+.navBox {
+ display: -webkit-box;
+ overflow: hidden;
+ position: relative;
+ right: 0;
+ transition: 1s;
+
+}
+
+.navItem {
+ cursor: pointer;
+ height: 650px;
+ width: 100%;
+ position: relative;
+ right: 0;
+ transition: 0.5s;
+}
+
+.navItem>img {
+ height: 100%;
+ width: 100%;
+ object-fit: cover;
+ position: absolute;
+ right: 0;
+}
+
+.navSmallItem {
+ position: absolute;
+ display: flex;
+ justify-content: space-evenly;
+ align-items: center;
+ bottom: 0;
+ width: 100%;
+ margin: 30px auto;
+
+}
+
+.navSmallItem>img {
+ height: 12%;
+ width: 12%;
+ object-fit: cover;
+ opacity: 0.3;
+}
+
+.navSmallItem>.navActive {
+ opacity: 1;
+}
+
+/*title*/
+.title {
+ text-align: center;
+ margin: 70px auto;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+}
+
+.titleTop {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.titleTop>div {
+ margin: 0 5px 0 5px;
+}
+
+.titleMiddle {
+ font-size: 28px;
+ font-weight: 800;
+ margin: 0 10px 0 10px;
+}
+
+.titleAside {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.titleAside>div {
+ margin: 0 5px 0 5px;
+ height: calc(var(--i)*10px);
+ opacity: calc(var(--i)*0.2);
+ width: 5px;
+ background: #24c3a9;
+}
+
+.titleBottom {
+ font-weight: 600;
+}
+
+.titleBottom>span:nth-of-type(1) {
+ color: #2faf94;
+}
+
+.titleBottom>span:nth-of-type(2) {
+ color: #e1e1e1;
+}
+
+/*产品展示*/
+.showButtonBox {
+ margin: 40px 0 40px 0;
+ display: flex;
+ justify-content: left;
+ align-items: center;
+}
+
+.showButtonBox>* {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ background: #ffffff;
+ padding: 15px;
+ cursor: pointer;
+ /*font-weight: 550;*/
+ margin: 10px auto;
+}
+
+.showButton>div {
+ font-size: 15px !important;
+}
+
+.showActive {
+ background: #33baa4 !important;
+ color: white !important;
+}
+
+.showButton {
+ transition: 0.2s;
+ color: black;
+}
+
+.showButton:hover {
+ background: #33baa4 !important;
+ color: white !important;
+}
+
+.showButtonBox img {
+ margin-right: 10px;
+ object-fit: cover;
+}
+
+.shows {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: 100%;
+ margin: 50px auto;
+ padding: 0 0 20px 0;
+ background: #ededed;
+ overflow: hidden;
+}
+
+.show {
+ height: 100%;
+ background: #7abaff;
+}
+
+/*xiugai*/
+.showsBox {
+ height: 100%;
+}
+
+.show-item-poster {
+ margin-left: 0 !important;
+ height: 100%;
+ width: 60%;
+ margin-right: 30px;
+}
+
+.show-item-poster img {
+ height: 100%;
+ width: 100%;
+ /*object-fit: scale-down;*/
+ object-fit: fill;
+}
+
+.show-itemsBox {
+ height: 100%;
+ display: flex;
+ justify-content: space-evenly;
+ /*align-items: center;*/
+ width: 100%;
+ padding-left: 4px;
+}
+
+.show-itemsBox a {
+ display: inline;
+}
+
+.show-items {
+ padding: 0 12px 24px 12px;
+ height: 300px;
+ width: 100%;
+}
+
+.show-item {
+ height: 100%;
+ width: 100%;
+ position: relative;
+ /*background: #0b2e13;*/
+ cursor: pointer;
+ transition: 0.8s;
+ overflow: hidden;
+ border-left: 1px solid #e4e4e4;
+ border-right: 1px solid #e4e4e4;
+ border-top: 1px solid #e4e4e4;
+}
+
+.show-item:hover.show-item>img {
+ height: 90%;
+}
+
+.show-item>img {
+ position: absolute;
+ height: 85%;
+ width: 100%;
+ object-fit: contain;
+ top: 0;
+ right: 0;
+ transition: 0.8s;
+}
+
+.show-item-content {
+ position: absolute;
+ bottom: -43px;
+ right: 0;
+ z-index: 999;
+ background: rgba(48, 182, 158, 0.8);
+ width: 100%;
+ padding: 6px 0 12px 0;
+ backdrop-filter: blur(4px);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+ color: white;
+ transition: 0.8s;
+}
+
+.show-item:hover .show-item-content {
+ bottom: 0;
+}
+
+.show-item-detail {
+ margin-top: 10px;
+ border: 1px solid white;
+ border-radius: 5px;
+ padding: 2px 20px;
+ transition: 0.4s;
+}
+
+.show-item-detail:hover {
+ background: white;
+ color: rgb(48, 182, 158);
+ border: 1px solid rgb(48, 182, 158);
+
+}
+
+.showLine {
+ border-right: 1px solid rgb(250, 250, 250);
+}
+
+/*xiugai*/
+
+/*show2*/
+._showContentBox {
+ display: none;
+ height: 100%;
+ width: 80%;
+ align-items: center;
+ flex-direction: column;
+ position: relative;
+}
+
+._showContentIcon {
+ position: absolute;
+ border-left: 30px solid #11bea0;
+ border-right: 30px solid transparent;
+ border-top: 30px solid transparent;
+ transform: rotate(135deg);
+ margin: 0 auto;
+ top: 0;
+}
+
+._showContentText {
+ font-size: 15px;
+ margin-bottom: 30px;
+}
+
+._showContent {
+ width: 100%;
+ background: #11bea0;
+ color: white;
+ padding: 10px 10% 0 10%;
+ position: relative;
+ overflow: hidden;
+}
+
+._showContentType {
+ font-size: 25px;
+}
+
+/*about*/
+.aboutsBox {
+ position: relative;
+ background: white;
+
+}
+
+.aboutsBg {
+ height: 496px;
+ position: absolute;
+ width: 100%;
+}
+
+.aboutsBg>img {
+ height: 100%;
+ width: 100%;
+ object-fit: cover;
+}
+
+.abouts {
+ position: relative;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+}
+
+.about {
+ width: 100%;
+ height: 100%;
+ background: white;
+ margin: 270px 0 0 0;
+ border: 1px solid #d9d9d9;
+ padding: 30px;
+ display: flex;
+ justify-content: space-evenly;
+ position: relative;
+ align-items: stretch;
+}
+
+.aboutImg {
+ width: 100%;
+ position: relative;
+}
+
+.aboutImg>img {
+ height: 100%;
+ width: 100%;
+ object-fit: cover;
+}
+
+.aboutContent {
+ height: 100%;
+ width: 100%;
+ padding-left: 30px;
+}
+
+.aboutTitle {
+ color: #11bea0;
+ font-weight: 550;
+ font-size: 24px !important;
+}
+
+.aboutSmallTitle {
+ font-weight: 550;
+ font-size: 16px !important;
+}
+
+.aboutContentText {
+ margin: 20px auto;
+ color: #9b9b9b;
+ font-size: 14px;
+}
+
+.aboutCertificate {
+ display: -webkit-box;
+ overflow: hidden;
+ position: relative;
+}
+
+.aboutCertificate>div {
+ position: relative;
+ right: 0;
+ padding-left: 10px;
+}
+
+.aboutCertificate>div>img {
+ height: 150px;
+ width: 100%;
+ /*object-fit: cover;*/
+ object-fit: contain;
+}
+
+/*about2*/
+.aboutsBox2 {
+ display: none;
+ position: relative;
+ background: white;
+}
+
+._aboutContent {
+ height: 100%;
+ width: 100%;
+}
+
+._aboutsBg {
+ height: 285px;
+ position: absolute;
+ width: 100%;
+}
+
+._aboutsBg>img {
+ height: 100%;
+ width: 100%;
+ object-fit: cover;
+}
+
+.aboutHeadBox {
+ padding: 30px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+ background: white;
+}
+
+.aboutHeadTitle {
+ color: #11bea0;
+ font-size: 28px;
+ font-weight: 550;
+}
+
+.aboutHeadMiddle {
+ color: #11bea0;
+ font-size: 28px;
+}
+
+.aboutHeadBottom {
+ color: #d7d7d7;
+
+}
+
+._about {
+ width: 90%;
+ height: 100%;
+ background: white;
+ margin: 100px 0 0 0;
+ border: 1px solid #d9d9d9;
+ padding: 30px;
+ display: flex;
+ justify-content: space-evenly;
+ align-items: center;
+}
+
+/*new*/
+.newsBox {
+ background: white;
+ padding-bottom: 70px;
+}
+
+.newsBox>.title {
+ margin: 0 auto;
+ padding: 70px 0;
+}
+
+.news {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.news>div {
+ padding: 0;
+ height: 600px;
+}
+
+.newLeft {
+
+ display: flex;
+ justify-content: left;
+ position: relative;
+ background: #f8f8f8;
+}
+
+.newBg {
+ height: 200px;
+ background: #11bea0;
+ width: 100%;
+ position: absolute;
+}
+
+.newLeftContent {
+ position: relative;
+ padding: 0 30px;
+}
+
+.newLeftContent>div {
+ margin: 30px 0;
+}
+
+.newLeftContentHead {
+ color: white;
+}
+
+.newLeftContentHead>.newLeftContentHeadTitle:nth-of-type(1) {
+ font-size: 24px !important;
+ margin-bottom: 10px;
+}
+
+.newLeftContentHeadText {
+ font-size: 12px;
+}
+
+.newLeftContent>div {
+ margin-bottom: 30px;
+}
+
+.newLeftContentImg>img {
+ height: 100%;
+ width: 100%;
+ object-fit: cover;
+}
+
+.newLeftContentText>div {
+ color: #909090;
+ font-size: 18px;
+}
+
+.newLeftContentTime {
+ display: flex;
+ /*justify-content: space-between;*/
+ align-items: center;
+ width: 100%;
+ position: absolute;
+ bottom: 0;
+}
+
+.newLeftContentTime>div:nth-of-type(1) {
+ color: #a8a8a8;
+ margin-right: 15px;
+}
+
+.newLeftContentTime>div:nth-of-type(2) {
+ font-size: 14px;
+ color: #a8a8a8;
+}
+
+.newRight {
+ height: 100%;
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ margin-left: 40px;
+}
+
+.newRight>div {
+ height: 50%;
+ width: 100%;
+ overflow: hidden;
+ padding: 30px;
+ background: #f8f8f8;
+}
+
+.newRight>div:nth-of-type(1) {
+ margin-bottom: 5%;
+}
+
+.new2RightHeadRight {
+ margin-top: 20px;
+}
+
+.newRightHead {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.newRightHeadLeft>div:nth-of-type(1) {
+ font-size: 28px;
+}
+
+.newRightHeadLeft>div:nth-of-type(2) {
+ color: #999999;
+}
+
+.newRightHeadRight {
+ display: flex;
+}
+
+.newButton {
+ height: 15px;
+ width: 15px;
+ background: #dedede;
+ border-radius: 50%;
+ margin: 0 5px;
+ cursor: pointer;
+ transition: 0.6s cubic-bezier(0.58, -0.58, 0, 2.13);
+}
+
+.newButtonActive {
+ height: 15px;
+ width: 30px;
+ border-radius: 25px;
+ background: #11bea0;
+}
+
+.newRightTop {
+ position: relative;
+ overflow: hidden;
+}
+
+.newRightContentBox {
+ display: -webkit-box;
+ overflow: hidden;
+ position: relative;
+ transition: 0.4s;
+}
+
+/*.newRightContentBox:hover{*/
+/* background: white;*/
+/*}*/
+.newRightContent {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ position: relative;
+ right: 0;
+ user-select: none;
+ width: 50%;
+}
+
+.newRightContent>div {
+ padding-right: 30px;
+ width: 100%;
+ height: 100%;
+}
+
+.newRightContentLeft {
+ cursor: pointer;
+ position: relative;
+}
+
+.newRightContentLeftTitle {
+ margin: 15px auto;
+ color: #2d2d2d;
+ font-weight: 550;
+ font-size: 14px;
+}
+
+.newRightContentLeftText {
+ color: #a8a8a8;
+}
+
+.newRightContentRightTitle {
+ margin: 15px auto;
+ color: #2d2d2d;
+ font-weight: 550;
+ font-size: 14px;
+}
+
+.newRightContentRightText {
+ color: #a8a8a8;
+}
+
+.newRightContentLeftTime {
+ display: flex;
+ justify-content: space-between;
+ margin-top: 35px;
+ bottom: 0;
+}
+
+/*new2*/
+.news2 {
+ display: none;
+}
+
+.new2Item {
+ background: #f8f8f8;
+ padding: 20px;
+}
+
+.new2Content {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.new2Content>div:nth-of-type(1) {
+ padding-right: 30px;
+}
+
+.new2ContentLeftTitle {
+ margin: 15px auto;
+ color: #2d2d2d;
+ font-weight: 550;
+ font-size: 14px;
+}
+
+.new2ContentLeftText {
+ color: #a8a8a8;
+}
+
+.new2ContentRightTitle {
+ margin: 15px auto;
+ color: #2d2d2d;
+ font-weight: 550;
+ font-size: 14px;
+}
+
+.new2ContentRightText {
+ color: #a8a8a8;
+}
+
+.new2ContentLeftTime {
+ display: flex;
+ justify-content: space-between;
+ margin-top: 20px;
+}
+
+.new2ContentLeftTime {
+ display: flex;
+}
+.showsDetailButton{
+ width: 150px;
+ display:flex;
+ justify-content: center;
+ align-items: center;
+ background: #58c4b0;
+ color: white;
+ cursor: pointer;
+ margin: 0 auto;
+ padding: 5px;
+ text-decoration: none;
+ transition: 0.4s;
+
+}
+.showItemHandle{
+ display: flex;
+ width: 100%;
+ align-items: start;
+ justify-content: center;
+ height: 100%;
+ margin-bottom: 10px;
+}
+.showsDetailButton:hover{
+ background: #ffffff!important;
+ color: #58c4b0!important;
+
+}
+/*re shit*/
+.showItemBox {
+ display: none;
+}
+
+.showItemBoxActive {
+ width: 100%;
+ height: 100%;
+ display: block;
+}
+
+.showDetailBox2 {
+ display: none;
+}
+
+.showDetailBox2Active {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.showItem {
+ display: none !important;
+}
+
+.showItemActive {
+ display: flex !important;
+}
+
+.showsButtonActive{
+ display: flex;
+ width: 100%;
+ margin: 0 auto;
+}
\ No newline at end of file
diff --git a/public/static/css/joinAgent.css b/public/static/css/joinAgent.css
new file mode 100644
index 0000000..7bbb12f
--- /dev/null
+++ b/public/static/css/joinAgent.css
@@ -0,0 +1,241 @@
+body {
+ margin: 0;
+ padding: 0;
+}
+
+.topPic {
+ width: 100%;
+}
+
+.barGs:hover {
+ /* border-bottom: #fff 3px solid; */
+ padding-bottom: 15px;
+ /* border-right: none; */
+}
+
+.barTop {
+ display: flex;
+ width: 90%;
+ height: auto;
+ background-color: #00a587;
+ padding-left: 5%;
+ padding-top: 20px;
+ position: relative;
+ top: -75px;
+ margin-left: 10%;
+
+}
+
+.barTop2 {
+ position: absolute;
+ left: -10px;
+ width: 100%;
+
+}
+
+.barName {
+ width: 30%;
+ height: 100%;
+ line-height: 2;
+ margin-left: 30px;
+}
+
+.barHeadline {
+ font-size: 40px;
+ font-weight: bold;
+ color: #fff;
+ line-height: 1.6;
+}
+
+.barHeadline2 {
+ font-size: 30px;
+ color: #fff;
+ padding-bottom: 20px;
+}
+
+.barSize {
+ width: 220px;
+ height: 30px;
+ position: absolute;
+ display: flex;
+ right: 0;
+ padding-top: 30px;
+}
+
+.barSize img {
+ width: 20px;
+ height: 20px;
+}
+
+.barMap {
+ font-size: 14px;
+ color: #fff;
+}
+
+.bar {
+ margin-top: 50px;
+ display: flex;
+ /* width: 600px; */
+ height: 30px;
+ position: absolute;
+ right: 0;
+ top: 30px;
+}
+
+.barGs {
+ line-height: 40px;
+ /* display: flex;
+ justify-content: center;
+ text-align: center; */
+ margin: 0 20px;
+ height: 40px;
+ color: #fff;
+ font-size: 15px;
+ /* border-right: #e2e2e2 2px solid; */
+}
+
+.body {
+ width: 100%;
+}
+
+.bodyTop {
+ padding: 30px 0;
+ font-size: 40px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ /* border-bottom: 1px solid #ccc; */
+}
+
+.bodyBox {
+ width: 100%;
+ height: auto;
+ margin-bottom: 50px;
+}
+
+.BoxName {
+ width: 100%;
+ color: #00a587;
+ font-size: 30px;
+ font-weight: 600;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ margin: 50px 0;
+}
+
+.bodyPic {
+ width: 25%;
+ height: 300px;
+ object-fit: cover;
+ margin: 0 10px;
+}
+
+.bodyPic1 {
+ width: 80%;
+ margin-left: 10%;
+ height: auto;
+ margin-bottom: 30px;
+}
+
+@media (max-width: 767px) {
+ .PicBox {
+ align-items: center;
+ justify-content: center;
+ display: grid;
+ flex-wrap: wrap;
+ width: 100%;
+ height: auto;
+ }
+}
+
+@media (max-width: 767px) {
+ .bodyPic {
+ width: 80%;
+ height: 300px;
+ object-fit: cover;
+ margin: 0 auto;
+ padding: 10px 0;
+ }
+}
+
+.PicBox {
+ align-items: center;
+ justify-content: center;
+ display: flex;
+ flex-wrap: wrap;
+ width: 100%;
+ height: auto;
+}
+
+.PicBox-Mini {
+ width: 70%;
+ margin: 20px 15%;
+}
+
+.bodyPic-Mini {
+ margin: 50px 123.3px;
+ width: 80%;
+ height: auto;
+
+}
+
+.barTop-xiao {
+ width: 100%;
+ background-color: #00a587;
+ padding: 10px 10%;
+}
+
+.barName-Mini {
+ width: 90%;
+ color: #fff;
+ font-weight: 700;
+ font-size: 30px;
+ border-bottom: #ccc 1px solid;
+ padding-left: 3%;
+}
+
+.bar-Mini {
+ display: flex;
+ margin: 20px 0px;
+}
+
+.barGs-Mini {
+ line-height: 20px;
+ display: flex;
+ justify-content: center;
+ text-align: center;
+ width: 90px;
+ height: 20px;
+ color: #fff;
+ border-right: #e2e2e2 2px solid;
+}
+
+.bodyPic-Mini {
+ width: 80%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.bodyTop-Mini {
+ margin-top: 20px;
+ font-size: 30px;
+ height: 50px;
+ display: flex;
+ /* align-items: center; */
+ justify-content: center;
+}
+
+.borderBottom {
+ width: 0;
+ transition: all 0.3s;
+ height: 3px;
+ background-color: #fff;
+ margin: auto;
+}
+
+.barGs:hover .borderBottom {
+ width: 60px;
+ margin: auto;
+}
\ No newline at end of file
diff --git a/public/static/css/joinPolicy.css b/public/static/css/joinPolicy.css
new file mode 100644
index 0000000..e512c86
--- /dev/null
+++ b/public/static/css/joinPolicy.css
@@ -0,0 +1,256 @@
+招商政策
+
+/* join 通用css样式 */
+body {
+ margin: 0;
+ padding: 0;
+}
+
+.topPic {
+ width: 100%;
+}
+
+.barTop {
+ display: flex;
+ width: 90%;
+ height: auto;
+ background-color: #00a587;
+ padding-left: 5%;
+ padding-top: 20px;
+ position: relative;
+ top: -75px;
+ margin-left: 10%;
+
+}
+
+.barGs:hover {
+ /* border-bottom: #fff 3px solid; */
+ padding-bottom: 15px;
+ /* border-right: none; */
+}
+
+a {
+ text-decoration: none
+}
+
+.barTop2 {
+ position: absolute;
+ left: -10px;
+ width: 100%;
+
+}
+
+.barName {
+ width: 30%;
+ height: 100%;
+ line-height: 2;
+}
+
+.barHeadline {
+ font-size: 40px;
+ font-weight: bold;
+ color: #fff;
+ line-height: 1.6;
+}
+
+.barHeadline2 {
+ font-size: 30px;
+ color: #fff;
+ padding-bottom: 20px;
+}
+
+.barSize {
+ width: 220px;
+ height: 30px;
+ position: absolute;
+ display: flex;
+ right: 0;
+ padding-top: 30px;
+}
+
+.barSize img {
+ width: 20px;
+ height: 20px;
+}
+
+.barMap {
+ font-size: 14px;
+ color: #fff;
+}
+
+.bar {
+ margin-top: 50px;
+ display: flex;
+ /* width: 600px; */
+ height: 30px;
+ position: absolute;
+ right: 0;
+ top: 30px;
+}
+
+.barGs {
+ line-height: 40px;
+ /* display: flex;
+ justify-content: center;
+ text-align: center; */
+ margin: 0 20px;
+ height: 40px;
+ color: #fff;
+ font-size: 15px;
+ /* border-right: #e2e2e2 2px solid; */
+}
+
+.body {
+ margin: 0px auto;
+ width: 100%;
+ margin: 0 auto;
+ max-width: 1500px;
+}
+
+.bodyTop {
+ width: 94%;
+ margin-left: 3%;
+ padding: 30px 0;
+ font-size: 40px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ /* border-bottom: 1px solid #ccc; */
+}
+
+.bodyText {
+
+ width: 94%;
+ height: auto;
+ margin-top: 10px;
+}
+
+.bodyTextTop {
+ color: #00a587;
+ font-weight: bold;
+ font-size: 24px;
+ /*padding-left: 10px;*/
+}
+
+.thread {
+ margin-top: 30px;
+ width: 50px;
+ height: 2px;
+ background-color: #00a587;
+ /*padding-left: 10px;*/
+}
+
+.bodyTextTop,
+.thread {
+ margin-left: 30px;
+}
+
+.text {
+ width: 100%;
+ margin: 20px 0;
+ display: flex;
+ flex-direction: row;
+}
+
+.text-Mini {
+ width: 100%;
+ margin: 20px 0;
+
+}
+
+.text-1 {
+ line-height: 2;
+ font-size: 14px;
+ color: #333;
+ flex-wrap: wrap;
+ font-weight: 500;
+ list-style-type: decimal;
+ padding-bottom: 20px;
+}
+
+.bodyPic {
+ width: 100%;
+ height: 100%;
+ margin-left: 10%;
+ padding-top: 10px;
+}
+
+.boxPic {
+ width: 600px;
+ height: 400px;
+ position: relative;
+ top: -100px;
+}
+
+.barTop-xiao {
+ width: 100%;
+ background-color: #00a587;
+ padding: 20px 30px;
+}
+
+.barName-Mini {
+ color: #fff;
+ font-size: 700;
+ font-size: 30px;
+ border-bottom: #ccc 1px solid;
+ padding-left: 30px;
+}
+
+.bar-Mini {
+ display: flex;
+ margin: 20px 0px;
+}
+
+.barGs-Mini {
+ line-height: 20px;
+ display: flex;
+ justify-content: center;
+ text-align: center;
+ width: 90px;
+ height: 20px;
+ color: #fff;
+ border-right: #e2e2e2 2px solid;
+}
+
+.bodyPic-Mini {
+ width: 90%;
+ height: 400px;
+ margin-left: 9%;
+ margin-bottom: 30px;
+ background-size: contain;
+ object-fit: contain;
+
+}
+
+.bodyTop-Mini {
+ margin-top: 20px;
+ padding: 30px 0px;
+ font-size: 30px;
+ height: 50px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.text-1-max {
+ font-size: 16px;
+ font-weight: 500;
+ margin-bottom: 20px;
+ list-style-type: decimal;
+ line-height: 40px;
+
+
+}
+
+.borderBottom {
+ width: 0;
+ transition: all 0.3s;
+ height: 3px;
+ background-color: #fff;
+ margin: auto;
+}
+
+.barGs:hover .borderBottom {
+ width: 60px;
+ margin: auto;
+}
\ No newline at end of file
diff --git a/public/static/css/news.css b/public/static/css/news.css
new file mode 100644
index 0000000..c781b85
--- /dev/null
+++ b/public/static/css/news.css
@@ -0,0 +1,632 @@
+body {
+ margin: 0;
+ padding: 0;
+}
+
+/* 临时修改 */
+.news-header-nav {
+ z-index: 99999;
+}
+
+.news-header-nav-item {
+ background-color: rgb(255, 255, 255, 0.32);
+ display: flex;
+ justify-content: left;
+ align-items: center;
+ padding-left: 10%;
+ font-size: 15px;
+}
+
+.news_border {
+ padding: 20px;
+}
+
+.news-content-item-title {
+ background: white;
+ padding: 20px;
+}
+
+.news-content-item-title>div {
+ padding-bottom: 10px;
+}
+
+.news-content-item-title>div:nth-of-type(3) {
+ /*padding: 10px 10px 0px 10px!important;*/
+ padding-bottom: 0 !important;
+}
+
+/* */
+
+@media (max-width: 767px) {
+ #_phone {
+ display: block !important;
+ }
+}
+
+@media (max-width: 767px) {
+ .news-header-nav-item {
+ display: none !important;
+ }
+}
+
+#_phone {
+ display: none;
+ background: rgb(244, 245, 247);
+}
+
+a {
+ text-decoration: none !important;
+ color: white;
+}
+
+.news_img {
+ width: 100%;
+ height: auto;
+}
+
+.news_img img {
+ width: 100%;
+ height: 100%;
+}
+
+#news {
+ width: 90%;
+ height: 60px;
+ align-items: center;
+ justify-content: center;
+}
+
+.news_box {
+ width: 90%;
+ height: 20%;
+ margin-top: 10px;
+}
+
+.news_box_left {
+ width: 40%;
+ height: 100%;
+ margin-top: 5px;
+ float: left;
+}
+
+.size1 {
+ font-size: 40px;
+ font-weight: 600;
+}
+
+.size2 {
+ font-size: 30px;
+}
+
+#news span {
+ color: rgb(187, 186, 186);
+ font-size: 15px;
+ line-height: 20px;
+}
+
+.news_box_right {
+ /* width: 300px;
+ height: 100%; */
+ float: left;
+ font-size: 18px;
+}
+
+.news_box_right img {
+ width: 20px;
+ height: 20px;
+}
+
+
+
+.news_border1 {
+ display: flex;
+ align-items: center;
+ margin-top: 20px;
+ flex-direction: row-reverse;
+}
+
+.news_border1 span {
+ font-size: 14px;
+}
+
+.news_border span {
+ padding-bottom: 4px;
+}
+
+.news_border span:hover {
+ border-bottom: 3px solid white;
+}
+
+#news_visible {
+ width: 100%;
+ height: 120px;
+ background-color: rgb(0, 167, 134);
+}
+
+.news_visible_box {
+ width: 80%;
+ height: 80%;
+ position: relative;
+ top: 10%;
+ margin: auto;
+}
+
+.news_visible_top {
+ width: 100%;
+ height: 50%;
+ display: flex;
+ align-items: center;
+}
+
+.news_visible_top span {
+ color: white;
+ font-size: 24px;
+ font-weight: 600;
+}
+
+.news_visible_bottom {
+ width: 100%;
+ height: 50%;
+ display: flex;
+ align-items: center;
+}
+
+.news_visible_border a {
+ width: 65px;
+ font-size: 14px;
+ float: left;
+ border-right: 1px solid white;
+ margin-right: 10px;
+}
+
+#news_list {
+ width: 100%;
+ height: auto;
+ /* display: flex; */
+ justify-content: center;
+}
+
+.news_list_content {
+ width: 80%;
+ height: 200px;
+ margin: 20px auto;
+ /* border-bottom: 1px solid rgb(190, 190, 190); */
+ /* background: #7b14db; */
+}
+
+.news_list_content_img {
+ width: 30%;
+ height: 200px;
+ /* margin: 0 50px; */
+ float: left;
+ /* display: flex; */
+ justify-content: center;
+ align-items: center;
+ cursor: pointer;
+ text-align: center;
+ line-height: 200px;
+ object-fit: cover;
+}
+
+.news_list_content_img img {
+ width: 90%;
+ height: 90%;
+ object-fit: cover;
+ margin-right: 30px;
+}
+
+.news_list_content_title {
+ width: 100%;
+ height: 25%;
+}
+
+.news_list_content_name {
+ width: 100%;
+ height: 25%;
+}
+
+.news_list_content_time {
+ width: 100%;
+ height: 25%;
+ word-break: break-all;
+ /* line-height: 80px; */
+ font-size: 15px;
+ color: #898989;
+ /* padding: 40px 0; */
+ font-family: "Microsoft YaHei", 微软雅黑;
+}
+
+.news_list_content_outline {
+ width: 70%;
+ height: 45px;
+ word-break: break-all;
+ font-size: 15px;
+ color: #898989;
+ margin: 20px 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ display: -webkit-box;
+ -webkit-line-clamp: 2;
+ -webkit-box-orient: vertical;
+ font-family: "Microsoft YaHei", 微软雅黑;
+}
+
+#news_list_visible {
+ width: 100%;
+ height: auto;
+ margin-top: 10px;
+ border-bottom: 10px solid #f5f5f5;
+}
+
+.news_list_content_visible {
+ width: 90%;
+ height: 100%;
+ margin: 0 auto;
+}
+
+.content_visible_box {
+ width: 100%;
+ height: 100%;
+}
+
+.news_list_content_visible_img {
+ width: 20%;
+ height: 50px;
+ float: left;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.news_list_content_visible_img img {
+ /* width: 50px;
+ height: 50px; */
+ width: 80%;
+ height: 200px;
+ margin: 0 10%;
+ float: left;
+ justify-content: center;
+ align-items: center;
+ cursor: pointer;
+ text-align: center;
+ line-height: 200px;
+ object-fit: cover;
+ "
+
+}
+
+.news_list_content_visible_title {
+ width: 100%;
+ height: 33%;
+ float: left;
+ font-weight: 600;
+ font-size: 15px;
+
+}
+
+.news_list_content_visible_name {
+ width: 80%;
+ height: 33%;
+ float: left;
+}
+
+.news_list_content_visible_time {
+ width: 100%;
+ height: 33%;
+ float: left;
+ color: #ccc;
+}
+
+#toggle_bar {
+ width: 100%;
+ height: 80px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.toggle_bar_page {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.toggle_bar_page_size {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.toggle_bar_page_size div {
+ cursor: pointer;
+ padding: 8px 15px 8px 14px;
+ border: 1px solid #eeeeee;
+ height: 42px;
+ width: 46px;
+ text-align: center;
+}
+
+.toggle_bar_page button {
+ border-style: none;
+ height: 42px;
+ width: 46px;
+ border: 1px solid #eeeeee;
+ margin: 10px;
+}
+
+#toggle_bar_page_button1 {
+ background-color: rgb(255, 255, 255);
+}
+
+#toggle_bar_page_button2 {
+ background-color: #fff;
+}
+
+#toggle_bar_page_button1 img {
+ width: 15px;
+ height: 18px;
+}
+
+#toggle_bar_page_button2 img {
+ width: 15px;
+ height: 18px;
+}
+
+#toggle_bar_page_button1:hover {
+ color: rgb(240, 18, 18);
+}
+
+#toggle_bar_page_button2:hover {
+ color: white;
+}
+
+#toggle_bar_visible {
+ width: 100%;
+ height: 50px;
+ margin-bottom: 10px;
+}
+
+.toggle_bar_page_visible {
+ width: 90%;
+ height: 100%;
+ margin: 0 auto;
+ display: flex;
+ justify-content: space-around;
+}
+
+.toggle_bar_page_visible button {
+ border-style: none;
+ width: 38%;
+ height: 80%;
+ float: left;
+ margin-top: 10px;
+ margin-bottom: 10px;
+}
+
+#visible_bottom1 {
+ /* margin-left: 3%; */
+ background: #11bea0;
+ font-size: 18px;
+ display: flex;
+ justify-content: space-around;
+ align-items: center;
+ color: #fff;
+}
+
+#visible_bottom1 img {
+ width: 20px;
+ height: 20px;
+}
+
+#visible_bottom2 {
+ /* margin-left: 5%; */
+ background: #11bea0;
+ font-size: 18px;
+ display: flex;
+ justify-content: space-around;
+ align-items: center;
+ color: #fff;
+}
+
+#visible_bottom2 img {
+ width: 20px;
+ height: 20px;
+}
+
+.borderBottom {
+ width: 0;
+ transition: all 0.3s;
+ height: 3px;
+ background-color: rgb(0, 182, 142);
+ margin: auto;
+}
+
+.barGs:hover .borderBottom {
+ width: 50px;
+ margin: auto;
+
+}
+
+.barGs123 {
+ border-bottom: 2px solid #00a587;
+}
+
+.news_list_content_visible_outline {
+ font-size: 14px;
+ width: 80%;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ display: -webkit-box;
+ -webkit-line-clamp: 2;
+ -webkit-box-orient: vertical;
+}
+
+.productPage {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ margin-bottom: 50px;
+ margin-top: 20px;
+}
+
+.productPage>.page {
+ margin: 0 5px 0 5px;
+ cursor: pointer;
+ padding: 8px 15px 8px 14px;
+ border: 1px solid #eeeeee;
+ height: 42px;
+ width: 46px;
+ text-align: center;
+ font-size: 15px;
+}
+
+.productPage img {
+ height: 62%;
+ width: 100%;
+ object-fit: cover;
+}
+
+.toggle_bar_page_visible a {
+ width: 20px;
+ height: 80%;
+}
+
+
+
+/*page*/
+.page {
+ transition: 0.4s;
+}
+
+.page:hover {
+ background: #11bea0;
+ color: white;
+}
+
+.pageBigBox {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ margin-bottom: 50px;
+ flex-wrap: wrap;
+}
+
+.pageBigBox>div {
+ margin: 10px 0;
+}
+
+.productPage {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.pageActive {
+ background: #11bea0;
+ color: white;
+}
+
+.productPage>.page {
+ border: 1px solid #eeeeee;
+ height: 42px;
+ width: 46px;
+ text-align: center;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.productPage>div {
+ margin: 0 5px 0 5px;
+ cursor: pointer;
+ padding: 8px 15px 8px 14px;
+}
+
+.pageMoreInput {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.pageMoreInput label {
+ margin-bottom: 0 !important;
+}
+
+.pageMoreInput input {
+ width: 40px;
+ border: 1px solid #a8a8a8;
+}
+
+.pageMore {
+ margin: 0 !important;
+ padding: 8px 7px 8px 7px;
+
+}
+
+.productPage img {
+ height: 62%;
+ width: 100%;
+ object-fit: cover;
+}
+
+.pageMoreInput {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.pageMoreInput>* {
+ padding-left: 4px;
+ padding-right: 4px;
+ color: #898989;
+ font-size: 16px;
+}
+
+.productJump {
+ cursor: pointer;
+ transition: 0.4s;
+ background: #00a587;
+ color: white;
+ padding: 5px 10px;
+}
+
+.productJump:hover {
+ color: #00a587;
+ background: white;
+}
+
+@media (min-width: 1540px) {
+ .container {
+ padding-top: 10px !important;
+ max-width: 100% !important;
+ margin: 0 !important;
+ background: rgb(248, 246, 246);
+ }
+}
+
+.phone {
+ width: 100%;
+ height: 100%;
+}
+
+.news_box {
+ margin: 10px 0;
+ width: 80%;
+ height: 500px;
+ margin: auto;
+}
+
+.f2 {}
+
+.news_img_box {
+ width: 100%;
+ height: 60%;
+ background: white;
+}
+
+.news_img_box img {
+ width: 100%;
+ height: 100%;
+ padding: 10px;
+ object-fit: cover;
+}
\ No newline at end of file
diff --git a/public/static/css/news_detail.css b/public/static/css/news_detail.css
new file mode 100644
index 0000000..7adb001
--- /dev/null
+++ b/public/static/css/news_detail.css
@@ -0,0 +1,350 @@
+body {
+ margin: 0;
+ padding: 0;
+}
+
+a {
+ text-decoration: none;
+ color: white;
+}
+
+a:hover {
+ text-decoration: none;
+ color: white;
+}
+
+.news_img {
+ width: 100%;
+ height: auto;
+}
+
+.news_img img {
+ width: 100%;
+ height: 100%;
+}
+
+#news {
+ width: 87%;
+ height: 160px;
+ background-color: rgb(0, 167, 134);
+ position: relative;
+ top: -90px;
+ left: 13%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.news_box {
+ width: 90%;
+ height: 90%;
+}
+
+.news_box_left {
+ width: 25%;
+ height: 100%;
+ float: left;
+}
+
+.size1 {
+ font-size: 40px;
+ font-weight: 600;
+}
+
+.size2 {
+ font-size: 30px;
+}
+
+.news span {
+ color: white;
+}
+
+.news_box_right {
+ width: 75%;
+ height: 100%;
+ float: left;
+ font-size: 18px;
+}
+
+.news_box_right img {
+ width: 20px;
+ height: 20px;
+}
+
+.news_border {
+ text-align: right;
+ width: 90px;
+ margin-left: 30px;
+ font-size: 14px;
+ margin-top: 20px;
+ float: right;
+}
+
+.news_border1 {
+ display: flex;
+ align-items: center;
+ margin-top: 30px;
+ flex-direction: row-reverse;
+}
+
+.news_border1 span {
+ font-size: 14px;
+}
+
+.news_border :hover {
+ border-bottom: 3px solid white;
+}
+
+#news_visible {
+ width: 100%;
+ height: 120px;
+ background-color: rgb(0, 167, 134);
+
+}
+
+.news_visible_box {
+ width: 80%;
+ height: 80%;
+ position: relative;
+ top: 10%;
+ margin: auto;
+}
+
+.news_visible_top {
+ width: 100%;
+ height: 50%;
+ display: flex;
+ align-items: center;
+}
+
+.news_visible_top span {
+ color: white;
+ font-size: 24px;
+ font-weight: 600;
+}
+
+.news_visible_bottom {
+ width: 100%;
+ height: 50%;
+ display: flex;
+ align-items: center;
+}
+
+.news_visible_border a {
+ width: 65px;
+ font-size: 14px;
+ float: left;
+ margin-right: 10px;
+}
+
+#content {
+ width: 90%;
+ height: auto;
+ margin: 0 auto;
+ border-bottom: 1px solid rgb(192, 192, 192);
+}
+
+.content_title {
+ display: flex;
+ justify-content: center;
+ /* border-bottom: 1px solid rgb(192, 192, 192); */
+ font-weight: 800;
+ font-size: 20px;
+}
+
+.content_information {
+ display: flex;
+ justify-content: center;
+}
+
+.content_link {
+ display: flex;
+ justify-content: space-between;
+ margin-top: 10px
+}
+
+.content_link a {
+ color: black;
+}
+
+#content_information_visible span {
+ width: 100%;
+ margin: 0 auto;
+}
+
+#content_visible {
+ width: 90%;
+ height: auto;
+ margin: 0 auto;
+}
+
+.content_title_visible {
+ display: flex;
+ justify-content: center;
+ font-weight: 800;
+ font-size: 20px;
+}
+
+.content_information_visible {
+ display: flex;
+ justify-content: center;
+ border-bottom: 1px solid rgb(192, 192, 192);
+}
+
+.content_link_visible p {
+ margin-top: 20px;
+}
+
+.content_link_visible a {
+ color: black;
+ cursor: pointer;
+}
+
+.news_content {
+ /* width: 100%;
+ height: auto;
+ border-bottom: 1px solid rgb(192, 192, 192);
+ overflow: hidden; */
+}
+
+.news_content img {
+ width: 60%;
+ object-fit: contain;
+}
+
+.information {
+ width: 100%;
+ height: 30px;
+ background-color: rgb(0, 167, 134);
+ display: flex;
+ align-items: center;
+ margin-top: 15px;
+}
+
+.information span {
+ margin-left: 10px;
+ color: white;
+}
+
+.content {
+ width: 100%;
+ display: flex;
+ justify-content: space-around;
+}
+
+.keywords {
+ width: 100%;
+ height: 30px;
+ display: flex;
+ align-items: center;
+ margin-top: 10px;
+}
+
+a {
+ cursor: pointer;
+}
+
+.pc {
+ display: flex;
+ justify-content: space-around;
+}
+
+#news_list {
+ width: 28%;
+ height: 250px;
+ display: flex;
+ justify-content: center;
+}
+
+#news_list a {
+ background-color: transparent !important;
+ color: #007bff;
+}
+
+.news_list_content {
+ width: 100%;
+ height: 80%;
+ margin: 10px auto;
+ box-shadow: darkgrey 5px 5px 8px -6px;
+}
+
+.mobile:hover {
+ margin-top: 20px;
+ box-shadow: rgb(233 165 165) 9px 9px 18px -6px;
+}
+
+.news_list_content_img {
+ width: 100%;
+ height: 50%;
+ text-align: center;
+ overflow: hidden;
+}
+
+.news_list_content_img img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+}
+
+#news_list_visible {
+ width: 25%;
+ height: 200px;
+}
+
+.news_list_content_title {
+
+ overflow: hidden;
+ text-overflow: ellipsis;
+ margin-top: 15px !important;
+ font-weight: 600;
+ color: #181818;
+ padding-left: 15px;
+
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ -webkit-line-clamp: 1;
+}
+
+@media (max-width: 748px) {
+ .content {
+ width: 100%;
+ display: flex;
+ justify-content: space-around;
+ flex-direction: column !important;
+ }
+
+ #news_list_visible {
+ width: 100%;
+ height: 300px;
+ }
+
+ .news_list_content_title {
+
+ overflow: hidden;
+ text-overflow: ellipsis;
+ margin-top: 15px !important;
+ font-weight: 600;
+ color: #181818;
+ padding-left: 10px;
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ -webkit-line-clamp: 1;
+ }
+
+ .de {
+ padding-left: 10px;
+ }
+
+}
+
+.text_box {
+ width: 100%;
+ height: 100%;
+}
+
+
+.news_title {
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
\ No newline at end of file
diff --git a/public/static/css/poster.css b/public/static/css/poster.css
new file mode 100644
index 0000000..b19534d
--- /dev/null
+++ b/public/static/css/poster.css
@@ -0,0 +1,32 @@
+.posterBigBox{
+ overflow: hidden;
+}
+.posterBox{
+ height: 700px;
+ width: 100%;
+ position: relative;
+ transform: translateY(0);
+}
+.poster{
+ position: absolute;
+ height: 100%;
+ width: 100%;
+ background-image: url('/glh_web/api/public/uploads/bannerImg/20220718/0a9df91715a8c1a152b4ce52f40c30d1.jpg');
+ /*background-repeat: no-repeat;*/
+ /*background-attachment: fixed;*/
+ /*transition: 0.4s;*/
+ /*background-size: 100% 100%;*/
+ /*-webkit-background-size: cover;*/
+ /*background-size: 100% auto;*/
+ /*-moz-background-size: 100% 100%;*/
+ /*-webkit-background-size: 100% 100%;*/
+ /*-o-background-size: 100% 100%;*/
+ /*filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src="/glh_web/api/public/uploads/bannerImg/20220718/0a9df91715a8c1a152b4ce52f40c30d1.jpg",sizingMethod='scale');*/
+ /*background-position:center center;*/
+ background: no-repeat center/cover;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ filter: blur(0);
+}
diff --git a/public/static/css/product.css b/public/static/css/product.css
new file mode 100644
index 0000000..7743d5a
--- /dev/null
+++ b/public/static/css/product.css
@@ -0,0 +1,351 @@
+/*product.css*/
+/*product*/
+.product a {
+ text-decoration: none !important;
+}
+
+.productsBigBox {
+ margin: 60px auto;
+ display: flex;
+ justify-content: center;
+}
+
+/* .productsBigBox>div {
+ padding: 20px;
+} */
+
+.productMenusBox {
+ display: flex;
+ flex-direction: column;
+}
+
+/* .productMenusBox>div{
+ border: 1px solid #5555;
+} */
+.productMenusHead {
+ background: #11bea0;
+ font-size: 20px;
+ font-weight: 550;
+ color: white;
+ text-align: left;
+ position: relative;
+ overflow: hidden;
+
+}
+
+.productMenusHeadContent {
+ position: relative;
+ height: 80px;
+ text-align: center;
+ font-size: 25px;
+ line-height: 80px;
+}
+
+.upLeft {
+ width: 0;
+ height: 0;
+ position: absolute;
+ border-top: solid 15px transparent;
+ border-right: solid 15px #ffffff;
+ border-bottom: solid 15px transparent;
+ left: -3px;
+ transform: rotate(45deg);
+ top: -10px;
+}
+
+.upLeft2 {
+ width: 0;
+ height: 0;
+ position: absolute;
+ border-top: solid 10px transparent;
+ border-right: solid 10px #11bea0;
+ border-bottom: solid 10px transparent;
+ left: -1px;
+ transform: rotate(45deg);
+ top: -6px;
+}
+
+.upLeft3 {
+ width: 0;
+ height: 0;
+ position: absolute;
+ border-top: solid 18px transparent;
+ border-right: solid 18px #ffffff;
+ border-bottom: solid 18px transparent;
+ right: -3px;
+ transform: rotate(-135deg);
+ bottom: -13px;
+}
+
+.upLeft4 {
+ width: 0;
+ height: 0;
+ position: absolute;
+ border-top: solid 8px transparent;
+ border-right: solid 8px #11bea0;
+ border-bottom: solid 8px transparent;
+ right: -1px;
+ transform: rotate(-135deg);
+ bottom: -6px;
+}
+
+.upLeft5 {
+ width: 40px;
+ height: 3px;
+ position: absolute;
+ background: #11bea0;
+ right: -12px;
+ transform: rotate(-225deg);
+ bottom: 10px;
+}
+
+.productMenusBody>a {
+ padding: 10px 10px;
+ border-bottom: 1px solid rgb(234, 234, 234);
+ font-family: "Microsoft YaHei", 微软雅黑;
+}
+
+.productMenusBody {
+ width: 100%;
+ /* border-bottom: 1px solid #5555; */
+ border: 1px solid rgb(234, 234, 234);
+ /* background: #f2fbfa; */
+}
+
+.productMenusBody:last-child {
+ border-top: none;
+}
+
+.productMenusBody>a:last-child {
+ border-bottom: none;
+}
+
+.productMenusBodyTitle {
+ font-size: 20px;
+ color: rgb(121, 121, 121);
+ font-weight: 550;
+ height: 40px;
+ line-height: 20px;
+ border-bottom: 1px solid rgb(234, 234, 234);
+ padding: 10px;
+}
+
+.productMenusItem {
+ width: 100%;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ transition: 0.6s;
+ /* border-bottom: 1px solid #aaaaaa; */
+ color: #898989;
+}
+
+.productMenusItem:hover {
+ background: #11bea0;
+ color: white;
+ margin-left: 0;
+ padding: 10px 15px 10px 20px;
+}
+
+.productMenusItemActive {
+ background: #11bea0 !important;
+ color: rgb(250, 246, 246) !important;
+ margin-left: -0px !important;
+ padding: 10px 20px 10px 20px !important;
+}
+
+.productMenuLine {
+ border-bottom: 1px solid #ecf2f2;
+}
+
+.productsTitle {
+ font-size: 28px;
+}
+
+.productsTitleDetail {
+ color: #bbbbbb;
+ font-size: 18px !important;
+}
+
+.productsBody {
+ display: flex;
+ justify-content: left;
+ align-items: center;
+ flex-wrap: wrap;
+ width: 100%;
+}
+
+.productItem {
+ height: 350px;
+ padding: 10px;
+}
+
+.products {
+ height: 100%;
+ position: relative;
+ overflow: hidden;
+ cursor: pointer;
+ border: 1px solid #f5f5f5;
+ transition: 0.6s;
+}
+
+.products>.productImg {
+ /*position: absolute;*/
+ height: 80%;
+ width: 100%;
+ /*padding: 10px;*/
+ object-fit: contain;
+}
+
+.productsBox {
+ width: 100%;
+ padding-top: 10px;
+}
+
+.productMenusBox {
+ width: 30%;
+ /*padding: 20px 50px 0 1px;*/
+ padding: 20px 20px 0 20px;
+ margin: 0 auto;
+}
+
+.productContent {
+ position: absolute;
+ bottom: 0;
+ /*height: 23%;*/
+ z-index: 9;
+ width: 100%;
+ background: white;
+ padding-bottom: 20px;
+}
+
+.productContent>div,
+.productContent>span {
+ margin: 5px 0 11px 20px;
+}
+
+.productButton {
+ border: 1px solid #eeeeee;
+ color: #4fcaaf;
+ background: white;
+ transition: 0.6s;
+ padding: 5px;
+}
+
+.productType {
+ color: #cccccc;
+}
+
+.products:hover {
+ transform: translate(0, -10px);
+ box-shadow: 4px 4px 4px #f5f5f5;
+}
+
+.productButton:hover {
+ background: #4fcaaf;
+ color: white;
+}
+
+.productSMenu {
+ cursor: pointer;
+ /* color: #898989; */
+}
+
+/*page*/
+.page {
+ transition: 0.4s;
+}
+
+.page:hover {
+ background: #11bea0;
+ color: white;
+}
+
+.pageBigBox {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ margin-bottom: 50px;
+ flex-wrap: wrap;
+}
+
+.pageBigBox>div {
+ margin: 10px 0;
+}
+
+.productPage {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.pageActive {
+ background: #11bea0;
+ color: white;
+}
+
+.productPage>.page {
+ border: 1px solid #eeeeee;
+ height: 42px;
+ width: 46px;
+ text-align: center;
+}
+
+.productPage>div {
+ margin: 0 5px 0 5px;
+ cursor: pointer;
+ padding: 8px 15px 8px 14px;
+}
+
+.pageMoreInput {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.pageMoreInput label {
+ margin-bottom: 0 !important;
+}
+
+.pageMoreInput input {
+ width: 40px;
+ border: 1px solid #a8a8a8;
+}
+
+.pageMore {
+ margin: 0 !important;
+ padding: 8px 7px 8px 7px;
+
+}
+
+.productPage img {
+ height: 62%;
+ width: 100%;
+ object-fit: cover;
+}
+
+.pageMoreInput {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.pageMoreInput>* {
+ padding-left: 4px;
+ padding-right: 4px;
+ color: #898989;
+ font-size: 16px;
+}
+
+.productJump {
+ cursor: pointer;
+ transition: 0.4s;
+ background: #00a587;
+ color: white;
+ padding: 5px 10px;
+}
+
+.productJump:hover {
+ color: #00a587;
+ background: white;
+}
\ No newline at end of file
diff --git a/public/static/css/productInfo.css b/public/static/css/productInfo.css
new file mode 100644
index 0000000..54ba8a0
--- /dev/null
+++ b/public/static/css/productInfo.css
@@ -0,0 +1,180 @@
+/*productInfo.css*/
+/*productInfoTitle*/
+/*title*/
+.ProductInfosTitle {
+ position: relative;
+ padding: 10px 15px;
+}
+
+
+.ProductInfosTitle>* {
+ display: inline-block;
+ font-size: 24px;
+}
+
+.ProductInfosTitle>span {
+ width: 6px;
+ height: 12px;
+ position: absolute;
+ left: 0;
+ top: calc(50% - 4px);
+ z-index: -1;
+ transition: all .8s;
+ background: #0cbf97;
+}
+
+section:hover .ProductInfosTitle>span {
+ width: 100%;
+}
+
+/*title-end*/
+.f12 {
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ -webkit-line-clamp: 12;
+ font-size: 13px;
+}
+
+.aboutContentText {
+ text-indent: 2em;
+}
+
+.productInfo {
+ padding-bottom: 20px;
+ background: white;
+}
+
+.productInfoT {
+ padding-left: 50px !important;
+ position: relative;
+}
+
+.productInfosBox>div {
+ margin-top: 50px;
+}
+
+.productInfosHeader {
+ display: flex;
+ width: 100%;
+ justify-content: space-between;
+ align-items: center;
+ padding: 0 !important;
+}
+
+.productsInfos {
+ display: flex;
+ justify-content: space-between;
+}
+
+.productInfoImgBox {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+ height: 500px;
+ border: 1px solid #e6e6e6;
+ width: 100%;
+}
+
+.productInfoImgBox>img {
+ height: 100%;
+ width: 100%;
+ object-fit: contain;
+ cursor: crosshair;
+}
+
+.productInfoMd {
+ font-size: 15px;
+ color: #717171;
+}
+
+.productsInfos {
+ margin-bottom: 50px;
+ margin-left: 0 !important;
+ margin-right: 0 !important;
+ position: relative;
+}
+
+.productDetail {
+ position: absolute;
+ left: 0;
+ top: 0;
+ height: 100%;
+ width: 100%;
+ background: white;
+ z-index: 999;
+ display: -webkit-box;
+ overflow: hidden;
+ display: none;
+}
+
+.productDetail>img {
+ position: relative;
+ left: 0;
+ top: 0;
+ height: 100%;
+ width: 100%;
+ object-fit: contain;
+ transform: scale(2);
+}
+
+.productInfoTitle {
+ font-size: 28px;
+ font-weight: 600;
+ margin-bottom: 40px;
+ margin-top: 20px;
+}
+
+.productInfoLine {
+ height: 2px;
+ width: 100%;
+ background: #eeeeee;
+}
+
+.productInfoContent {
+ margin-top: 20px;
+ font-size: 15px !important;
+ margin-bottom: 100px;
+ line-height: 2;
+}
+
+.productInfoBuy>a {
+ display: inline-block;
+ background: #00a587;
+ color: #ffffff;
+ padding: 10px 50px 10px 50px;
+ cursor: pointer;
+ margin-top: 15px;
+ transition: 0.4s;
+}
+
+.productInfoBuy>a:hover {
+ background: #ffffff;
+ color: #00a587;
+}
+
+.productInfoDetailRtx {
+ margin: 40px 0;
+}
+
+.productInfoDetailRtx img {
+ width: 100%;
+}
+
+.productInfoPage {
+ width: 100%;
+ padding: 20px 0;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.productInfoPage>div {
+ cursor: pointer;
+}
+
+.productInfoPage>div:hover {
+ color: #00a587;
+}
\ No newline at end of file
diff --git a/public/static/css/swiper-bundle.css b/public/static/css/swiper-bundle.css
new file mode 100644
index 0000000..a995259
--- /dev/null
+++ b/public/static/css/swiper-bundle.css
@@ -0,0 +1,647 @@
+/**
+ * Swiper 8.3.0
+ * Most modern mobile touch slider and framework with hardware accelerated transitions
+ * https://swiperjs.com
+ *
+ * Copyright 2014-2022 Vladimir Kharlampidi
+ *
+ * Released under the MIT License
+ *
+ * Released on: July 6, 2022
+ */
+
+@font-face {
+ font-family: 'swiper-icons';
+ src: url('data:application/font-woff;charset=utf-8;base64, d09GRgABAAAAAAZgABAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABGRlRNAAAGRAAAABoAAAAci6qHkUdERUYAAAWgAAAAIwAAACQAYABXR1BPUwAABhQAAAAuAAAANuAY7+xHU1VCAAAFxAAAAFAAAABm2fPczU9TLzIAAAHcAAAASgAAAGBP9V5RY21hcAAAAkQAAACIAAABYt6F0cBjdnQgAAACzAAAAAQAAAAEABEBRGdhc3AAAAWYAAAACAAAAAj//wADZ2x5ZgAAAywAAADMAAAD2MHtryVoZWFkAAABbAAAADAAAAA2E2+eoWhoZWEAAAGcAAAAHwAAACQC9gDzaG10eAAAAigAAAAZAAAArgJkABFsb2NhAAAC0AAAAFoAAABaFQAUGG1heHAAAAG8AAAAHwAAACAAcABAbmFtZQAAA/gAAAE5AAACXvFdBwlwb3N0AAAFNAAAAGIAAACE5s74hXjaY2BkYGAAYpf5Hu/j+W2+MnAzMYDAzaX6QjD6/4//Bxj5GA8AuRwMYGkAPywL13jaY2BkYGA88P8Agx4j+/8fQDYfA1AEBWgDAIB2BOoAeNpjYGRgYNBh4GdgYgABEMnIABJzYNADCQAACWgAsQB42mNgYfzCOIGBlYGB0YcxjYGBwR1Kf2WQZGhhYGBiYGVmgAFGBiQQkOaawtDAoMBQxXjg/wEGPcYDDA4wNUA2CCgwsAAAO4EL6gAAeNpj2M0gyAACqxgGNWBkZ2D4/wMA+xkDdgAAAHjaY2BgYGaAYBkGRgYQiAHyGMF8FgYHIM3DwMHABGQrMOgyWDLEM1T9/w8UBfEMgLzE////P/5//f/V/xv+r4eaAAeMbAxwIUYmIMHEgKYAYjUcsDAwsLKxc3BycfPw8jEQA/gZBASFhEVExcQlJKWkZWTl5BUUlZRVVNXUNTQZBgMAAMR+E+gAEQFEAAAAKgAqACoANAA+AEgAUgBcAGYAcAB6AIQAjgCYAKIArAC2AMAAygDUAN4A6ADyAPwBBgEQARoBJAEuATgBQgFMAVYBYAFqAXQBfgGIAZIBnAGmAbIBzgHsAAB42u2NMQ6CUAyGW568x9AneYYgm4MJbhKFaExIOAVX8ApewSt4Bic4AfeAid3VOBixDxfPYEza5O+Xfi04YADggiUIULCuEJK8VhO4bSvpdnktHI5QCYtdi2sl8ZnXaHlqUrNKzdKcT8cjlq+rwZSvIVczNiezsfnP/uznmfPFBNODM2K7MTQ45YEAZqGP81AmGGcF3iPqOop0r1SPTaTbVkfUe4HXj97wYE+yNwWYxwWu4v1ugWHgo3S1XdZEVqWM7ET0cfnLGxWfkgR42o2PvWrDMBSFj/IHLaF0zKjRgdiVMwScNRAoWUoH78Y2icB/yIY09An6AH2Bdu/UB+yxopYshQiEvnvu0dURgDt8QeC8PDw7Fpji3fEA4z/PEJ6YOB5hKh4dj3EvXhxPqH/SKUY3rJ7srZ4FZnh1PMAtPhwP6fl2PMJMPDgeQ4rY8YT6Gzao0eAEA409DuggmTnFnOcSCiEiLMgxCiTI6Cq5DZUd3Qmp10vO0LaLTd2cjN4fOumlc7lUYbSQcZFkutRG7g6JKZKy0RmdLY680CDnEJ+UMkpFFe1RN7nxdVpXrC4aTtnaurOnYercZg2YVmLN/d/gczfEimrE/fs/bOuq29Zmn8tloORaXgZgGa78yO9/cnXm2BpaGvq25Dv9S4E9+5SIc9PqupJKhYFSSl47+Qcr1mYNAAAAeNptw0cKwkAAAMDZJA8Q7OUJvkLsPfZ6zFVERPy8qHh2YER+3i/BP83vIBLLySsoKimrqKqpa2hp6+jq6RsYGhmbmJqZSy0sraxtbO3sHRydnEMU4uR6yx7JJXveP7WrDycAAAAAAAH//wACeNpjYGRgYOABYhkgZgJCZgZNBkYGLQZtIJsFLMYAAAw3ALgAeNolizEKgDAQBCchRbC2sFER0YD6qVQiBCv/H9ezGI6Z5XBAw8CBK/m5iQQVauVbXLnOrMZv2oLdKFa8Pjuru2hJzGabmOSLzNMzvutpB3N42mNgZGBg4GKQYzBhYMxJLMlj4GBgAYow/P/PAJJhLM6sSoWKfWCAAwDAjgbRAAB42mNgYGBkAIIbCZo5IPrmUn0hGA0AO8EFTQAA');
+ font-weight: 400;
+ font-style: normal;
+}
+:root {
+ --swiper-theme-color: #007aff;
+}
+.swiper {
+ margin-left: auto;
+ margin-right: auto;
+ position: relative;
+ overflow: hidden;
+ list-style: none;
+ padding: 0;
+ /* Fix of Webkit flickering */
+ z-index: 1;
+}
+.swiper-vertical > .swiper-wrapper {
+ flex-direction: column;
+}
+.swiper-wrapper {
+ position: relative;
+ width: 100%;
+ height: 100%;
+ z-index: 1;
+ display: flex;
+ transition-property: transform;
+ box-sizing: content-box;
+}
+.swiper-android .swiper-slide,
+.swiper-wrapper {
+ transform: translate3d(0px, 0, 0);
+}
+.swiper-pointer-events {
+ touch-action: pan-y;
+}
+.swiper-pointer-events.swiper-vertical {
+ touch-action: pan-x;
+}
+.swiper-slide {
+ flex-shrink: 0;
+ width: 100%;
+ height: 100%;
+ position: relative;
+ transition-property: transform;
+}
+.swiper-slide-invisible-blank {
+ visibility: hidden;
+}
+/* Auto Height */
+.swiper-autoheight,
+.swiper-autoheight .swiper-slide {
+ height: auto;
+}
+.swiper-autoheight .swiper-wrapper {
+ align-items: flex-start;
+ transition-property: transform, height;
+}
+.swiper-backface-hidden .swiper-slide {
+ transform: translateZ(0);
+ -webkit-backface-visibility: hidden;
+ backface-visibility: hidden;
+}
+/* 3D Effects */
+.swiper-3d,
+.swiper-3d.swiper-css-mode .swiper-wrapper {
+ perspective: 1200px;
+}
+.swiper-3d .swiper-wrapper,
+.swiper-3d .swiper-slide,
+.swiper-3d .swiper-slide-shadow,
+.swiper-3d .swiper-slide-shadow-left,
+.swiper-3d .swiper-slide-shadow-right,
+.swiper-3d .swiper-slide-shadow-top,
+.swiper-3d .swiper-slide-shadow-bottom,
+.swiper-3d .swiper-cube-shadow {
+ transform-style: preserve-3d;
+}
+.swiper-3d .swiper-slide-shadow,
+.swiper-3d .swiper-slide-shadow-left,
+.swiper-3d .swiper-slide-shadow-right,
+.swiper-3d .swiper-slide-shadow-top,
+.swiper-3d .swiper-slide-shadow-bottom {
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ pointer-events: none;
+ z-index: 10;
+}
+.swiper-3d .swiper-slide-shadow {
+ background: rgba(0, 0, 0, 0.15);
+}
+.swiper-3d .swiper-slide-shadow-left {
+ background-image: linear-gradient(to left, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
+}
+.swiper-3d .swiper-slide-shadow-right {
+ background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
+}
+.swiper-3d .swiper-slide-shadow-top {
+ background-image: linear-gradient(to top, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
+}
+.swiper-3d .swiper-slide-shadow-bottom {
+ background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
+}
+/* CSS Mode */
+.swiper-css-mode > .swiper-wrapper {
+ overflow: auto;
+ scrollbar-width: none;
+ /* For Firefox */
+ -ms-overflow-style: none;
+ /* For Internet Explorer and Edge */
+}
+.swiper-css-mode > .swiper-wrapper::-webkit-scrollbar {
+ display: none;
+}
+.swiper-css-mode > .swiper-wrapper > .swiper-slide {
+ scroll-snap-align: start start;
+}
+.swiper-horizontal.swiper-css-mode > .swiper-wrapper {
+ scroll-snap-type: x mandatory;
+}
+.swiper-vertical.swiper-css-mode > .swiper-wrapper {
+ scroll-snap-type: y mandatory;
+}
+.swiper-centered > .swiper-wrapper::before {
+ content: '';
+ flex-shrink: 0;
+ order: 9999;
+}
+.swiper-centered.swiper-horizontal > .swiper-wrapper > .swiper-slide:first-child {
+ margin-inline-start: var(--swiper-centered-offset-before);
+}
+.swiper-centered.swiper-horizontal > .swiper-wrapper::before {
+ height: 100%;
+ min-height: 1px;
+ width: var(--swiper-centered-offset-after);
+}
+.swiper-centered.swiper-vertical > .swiper-wrapper > .swiper-slide:first-child {
+ margin-block-start: var(--swiper-centered-offset-before);
+}
+.swiper-centered.swiper-vertical > .swiper-wrapper::before {
+ width: 100%;
+ min-width: 1px;
+ height: var(--swiper-centered-offset-after);
+}
+.swiper-centered > .swiper-wrapper > .swiper-slide {
+ scroll-snap-align: center center;
+}
+.swiper-virtual .swiper-slide {
+ -webkit-backface-visibility: hidden;
+ transform: translateZ(0);
+}
+.swiper-virtual.swiper-css-mode .swiper-wrapper::after {
+ content: '';
+ position: absolute;
+ left: 0;
+ top: 0;
+ pointer-events: none;
+}
+.swiper-virtual.swiper-css-mode.swiper-horizontal .swiper-wrapper::after {
+ height: 1px;
+ width: var(--swiper-virtual-size);
+}
+.swiper-virtual.swiper-css-mode.swiper-vertical .swiper-wrapper::after {
+ width: 1px;
+ height: var(--swiper-virtual-size);
+}
+:root {
+ --swiper-navigation-size: 44px;
+ /*
+ --swiper-navigation-color: var(--swiper-theme-color);
+ */
+}
+.swiper-button-prev,
+.swiper-button-next {
+ position: absolute;
+ top: 50%;
+ width: calc(var(--swiper-navigation-size) / 44 * 27);
+ height: var(--swiper-navigation-size);
+ margin-top: calc(0px - (var(--swiper-navigation-size) / 2));
+ z-index: 10;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: var(--swiper-navigation-color, var(--swiper-theme-color));
+}
+.swiper-button-prev.swiper-button-disabled,
+.swiper-button-next.swiper-button-disabled {
+ opacity: 0.35;
+ cursor: auto;
+ pointer-events: none;
+}
+.swiper-button-prev.swiper-button-hidden,
+.swiper-button-next.swiper-button-hidden {
+ opacity: 0;
+ cursor: auto;
+ pointer-events: none;
+}
+.swiper-navigation-disabled .swiper-button-prev,
+.swiper-navigation-disabled .swiper-button-next {
+ display: none !important;
+}
+.swiper-button-prev:after,
+.swiper-button-next:after {
+ font-family: swiper-icons;
+ font-size: var(--swiper-navigation-size);
+ text-transform: none !important;
+ letter-spacing: 0;
+ font-variant: initial;
+ line-height: 1;
+}
+.swiper-button-prev,
+.swiper-rtl .swiper-button-next {
+ left: 10px;
+ right: auto;
+}
+.swiper-button-prev:after,
+.swiper-rtl .swiper-button-next:after {
+ content: 'prev';
+}
+.swiper-button-next,
+.swiper-rtl .swiper-button-prev {
+ right: 10px;
+ left: auto;
+}
+.swiper-button-next:after,
+.swiper-rtl .swiper-button-prev:after {
+ content: 'next';
+}
+.swiper-button-lock {
+ display: none;
+}
+:root {
+ /*
+ --swiper-pagination-color: var(--swiper-theme-color);
+ --swiper-pagination-bullet-size: 8px;
+ --swiper-pagination-bullet-width: 8px;
+ --swiper-pagination-bullet-height: 8px;
+ --swiper-pagination-bullet-inactive-color: #000;
+ --swiper-pagination-bullet-inactive-opacity: 0.2;
+ --swiper-pagination-bullet-opacity: 1;
+ --swiper-pagination-bullet-horizontal-gap: 4px;
+ --swiper-pagination-bullet-vertical-gap: 6px;
+ */
+}
+.swiper-pagination {
+ position: absolute;
+ text-align: center;
+ transition: 300ms opacity;
+ transform: translate3d(0, 0, 0);
+ z-index: 10;
+}
+.swiper-pagination.swiper-pagination-hidden {
+ opacity: 0;
+}
+.swiper-pagination-disabled > .swiper-pagination,
+.swiper-pagination.swiper-pagination-disabled {
+ display: none !important;
+}
+/* Common Styles */
+.swiper-pagination-fraction,
+.swiper-pagination-custom,
+.swiper-horizontal > .swiper-pagination-bullets,
+.swiper-pagination-bullets.swiper-pagination-horizontal {
+ bottom: 10px;
+ left: 0;
+ width: 100%;
+}
+/* Bullets */
+.swiper-pagination-bullets-dynamic {
+ overflow: hidden;
+ font-size: 0;
+}
+.swiper-pagination-bullets-dynamic .swiper-pagination-bullet {
+ transform: scale(0.33);
+ position: relative;
+}
+.swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active {
+ transform: scale(1);
+}
+.swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active-main {
+ transform: scale(1);
+}
+.swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active-prev {
+ transform: scale(0.66);
+}
+.swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active-prev-prev {
+ transform: scale(0.33);
+}
+.swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active-next {
+ transform: scale(0.66);
+}
+.swiper-pagination-bullets-dynamic .swiper-pagination-bullet-active-next-next {
+ transform: scale(0.33);
+}
+.swiper-pagination-bullet {
+ width: var(--swiper-pagination-bullet-width, var(--swiper-pagination-bullet-size, 8px));
+ height: var(--swiper-pagination-bullet-height, var(--swiper-pagination-bullet-size, 8px));
+ display: inline-block;
+ border-radius: 50%;
+ background: var(--swiper-pagination-bullet-inactive-color, #000);
+ opacity: var(--swiper-pagination-bullet-inactive-opacity, 0.2);
+}
+button.swiper-pagination-bullet {
+ border: none;
+ margin: 0;
+ padding: 0;
+ box-shadow: none;
+ -webkit-appearance: none;
+ appearance: none;
+}
+.swiper-pagination-clickable .swiper-pagination-bullet {
+ cursor: pointer;
+}
+.swiper-pagination-bullet:only-child {
+ display: none !important;
+}
+.swiper-pagination-bullet-active {
+ opacity: var(--swiper-pagination-bullet-opacity, 1);
+ background: var(--swiper-pagination-color, var(--swiper-theme-color));
+}
+.swiper-vertical > .swiper-pagination-bullets,
+.swiper-pagination-vertical.swiper-pagination-bullets {
+ right: 10px;
+ top: 50%;
+ transform: translate3d(0px, -50%, 0);
+}
+.swiper-vertical > .swiper-pagination-bullets .swiper-pagination-bullet,
+.swiper-pagination-vertical.swiper-pagination-bullets .swiper-pagination-bullet {
+ margin: var(--swiper-pagination-bullet-vertical-gap, 6px) 0;
+ display: block;
+}
+.swiper-vertical > .swiper-pagination-bullets.swiper-pagination-bullets-dynamic,
+.swiper-pagination-vertical.swiper-pagination-bullets.swiper-pagination-bullets-dynamic {
+ top: 50%;
+ transform: translateY(-50%);
+ width: 8px;
+}
+.swiper-vertical > .swiper-pagination-bullets.swiper-pagination-bullets-dynamic .swiper-pagination-bullet,
+.swiper-pagination-vertical.swiper-pagination-bullets.swiper-pagination-bullets-dynamic .swiper-pagination-bullet {
+ display: inline-block;
+ transition: 200ms transform, 200ms top;
+}
+.swiper-horizontal > .swiper-pagination-bullets .swiper-pagination-bullet,
+.swiper-pagination-horizontal.swiper-pagination-bullets .swiper-pagination-bullet {
+ margin: 0 var(--swiper-pagination-bullet-horizontal-gap, 4px);
+}
+.swiper-horizontal > .swiper-pagination-bullets.swiper-pagination-bullets-dynamic,
+.swiper-pagination-horizontal.swiper-pagination-bullets.swiper-pagination-bullets-dynamic {
+ left: 50%;
+ transform: translateX(-50%);
+ white-space: nowrap;
+}
+.swiper-horizontal > .swiper-pagination-bullets.swiper-pagination-bullets-dynamic .swiper-pagination-bullet,
+.swiper-pagination-horizontal.swiper-pagination-bullets.swiper-pagination-bullets-dynamic .swiper-pagination-bullet {
+ transition: 200ms transform, 200ms left;
+}
+.swiper-horizontal.swiper-rtl > .swiper-pagination-bullets-dynamic .swiper-pagination-bullet {
+ transition: 200ms transform, 200ms right;
+}
+/* Progress */
+.swiper-pagination-progressbar {
+ background: rgba(0, 0, 0, 0.25);
+ position: absolute;
+}
+.swiper-pagination-progressbar .swiper-pagination-progressbar-fill {
+ background: var(--swiper-pagination-color, var(--swiper-theme-color));
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ transform: scale(0);
+ transform-origin: left top;
+}
+.swiper-rtl .swiper-pagination-progressbar .swiper-pagination-progressbar-fill {
+ transform-origin: right top;
+}
+.swiper-horizontal > .swiper-pagination-progressbar,
+.swiper-pagination-progressbar.swiper-pagination-horizontal,
+.swiper-vertical > .swiper-pagination-progressbar.swiper-pagination-progressbar-opposite,
+.swiper-pagination-progressbar.swiper-pagination-vertical.swiper-pagination-progressbar-opposite {
+ width: 100%;
+ height: 4px;
+ left: 0;
+ top: 0;
+}
+.swiper-vertical > .swiper-pagination-progressbar,
+.swiper-pagination-progressbar.swiper-pagination-vertical,
+.swiper-horizontal > .swiper-pagination-progressbar.swiper-pagination-progressbar-opposite,
+.swiper-pagination-progressbar.swiper-pagination-horizontal.swiper-pagination-progressbar-opposite {
+ width: 4px;
+ height: 100%;
+ left: 0;
+ top: 0;
+}
+.swiper-pagination-lock {
+ display: none;
+}
+/* Scrollbar */
+.swiper-scrollbar {
+ border-radius: 10px;
+ position: relative;
+ -ms-touch-action: none;
+ background: rgba(0, 0, 0, 0.1);
+}
+.swiper-scrollbar-disabled > .swiper-scrollbar,
+.swiper-scrollbar.swiper-scrollbar-disabled {
+ display: none !important;
+}
+.swiper-horizontal > .swiper-scrollbar,
+.swiper-scrollbar.swiper-scrollbar-horizontal {
+ position: absolute;
+ left: 1%;
+ bottom: 3px;
+ z-index: 50;
+ height: 5px;
+ width: 98%;
+}
+.swiper-vertical > .swiper-scrollbar,
+.swiper-scrollbar.swiper-scrollbar-vertical {
+ position: absolute;
+ right: 3px;
+ top: 1%;
+ z-index: 50;
+ width: 5px;
+ height: 98%;
+}
+.swiper-scrollbar-drag {
+ height: 100%;
+ width: 100%;
+ position: relative;
+ background: rgba(0, 0, 0, 0.5);
+ border-radius: 10px;
+ left: 0;
+ top: 0;
+}
+.swiper-scrollbar-cursor-drag {
+ cursor: move;
+}
+.swiper-scrollbar-lock {
+ display: none;
+}
+.swiper-zoom-container {
+ width: 100%;
+ height: 100%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ text-align: center;
+}
+.swiper-zoom-container > img,
+.swiper-zoom-container > svg,
+.swiper-zoom-container > canvas {
+ max-width: 100%;
+ max-height: 100%;
+ object-fit: contain;
+}
+.swiper-slide-zoomed {
+ cursor: move;
+}
+/* Preloader */
+:root {
+ /*
+ --swiper-preloader-color: var(--swiper-theme-color);
+ */
+}
+.swiper-lazy-preloader {
+ width: 42px;
+ height: 42px;
+ position: absolute;
+ left: 50%;
+ top: 50%;
+ margin-left: -21px;
+ margin-top: -21px;
+ z-index: 10;
+ transform-origin: 50%;
+ box-sizing: border-box;
+ border: 4px solid var(--swiper-preloader-color, var(--swiper-theme-color));
+ border-radius: 50%;
+ border-top-color: transparent;
+}
+.swiper:not(.swiper-watch-progress) .swiper-lazy-preloader,
+.swiper-watch-progress .swiper-slide-visible .swiper-lazy-preloader {
+ animation: swiper-preloader-spin 1s infinite linear;
+}
+.swiper-lazy-preloader-white {
+ --swiper-preloader-color: #fff;
+}
+.swiper-lazy-preloader-black {
+ --swiper-preloader-color: #000;
+}
+@keyframes swiper-preloader-spin {
+ 0% {
+ transform: rotate(0deg);
+ }
+ 100% {
+ transform: rotate(360deg);
+ }
+}
+/* a11y */
+.swiper .swiper-notification {
+ position: absolute;
+ left: 0;
+ top: 0;
+ pointer-events: none;
+ opacity: 0;
+ z-index: -1000;
+}
+.swiper-free-mode > .swiper-wrapper {
+ transition-timing-function: ease-out;
+ margin: 0 auto;
+}
+.swiper-grid > .swiper-wrapper {
+ flex-wrap: wrap;
+}
+.swiper-grid-column > .swiper-wrapper {
+ flex-wrap: wrap;
+ flex-direction: column;
+}
+.swiper-fade.swiper-free-mode .swiper-slide {
+ transition-timing-function: ease-out;
+}
+.swiper-fade .swiper-slide {
+ pointer-events: none;
+ transition-property: opacity;
+}
+.swiper-fade .swiper-slide .swiper-slide {
+ pointer-events: none;
+}
+.swiper-fade .swiper-slide-active,
+.swiper-fade .swiper-slide-active .swiper-slide-active {
+ pointer-events: auto;
+}
+.swiper-cube {
+ overflow: visible;
+}
+.swiper-cube .swiper-slide {
+ pointer-events: none;
+ -webkit-backface-visibility: hidden;
+ backface-visibility: hidden;
+ z-index: 1;
+ visibility: hidden;
+ transform-origin: 0 0;
+ width: 100%;
+ height: 100%;
+}
+.swiper-cube .swiper-slide .swiper-slide {
+ pointer-events: none;
+}
+.swiper-cube.swiper-rtl .swiper-slide {
+ transform-origin: 100% 0;
+}
+.swiper-cube .swiper-slide-active,
+.swiper-cube .swiper-slide-active .swiper-slide-active {
+ pointer-events: auto;
+}
+.swiper-cube .swiper-slide-active,
+.swiper-cube .swiper-slide-next,
+.swiper-cube .swiper-slide-prev,
+.swiper-cube .swiper-slide-next + .swiper-slide {
+ pointer-events: auto;
+ visibility: visible;
+}
+.swiper-cube .swiper-slide-shadow-top,
+.swiper-cube .swiper-slide-shadow-bottom,
+.swiper-cube .swiper-slide-shadow-left,
+.swiper-cube .swiper-slide-shadow-right {
+ z-index: 0;
+ -webkit-backface-visibility: hidden;
+ backface-visibility: hidden;
+}
+.swiper-cube .swiper-cube-shadow {
+ position: absolute;
+ left: 0;
+ bottom: 0px;
+ width: 100%;
+ height: 100%;
+ opacity: 0.6;
+ z-index: 0;
+}
+.swiper-cube .swiper-cube-shadow:before {
+ content: '';
+ background: #000;
+ position: absolute;
+ left: 0;
+ top: 0;
+ bottom: 0;
+ right: 0;
+ filter: blur(50px);
+}
+.swiper-flip {
+ overflow: visible;
+}
+.swiper-flip .swiper-slide {
+ pointer-events: none;
+ -webkit-backface-visibility: hidden;
+ backface-visibility: hidden;
+ z-index: 1;
+}
+.swiper-flip .swiper-slide .swiper-slide {
+ pointer-events: none;
+}
+.swiper-flip .swiper-slide-active,
+.swiper-flip .swiper-slide-active .swiper-slide-active {
+ pointer-events: auto;
+}
+.swiper-flip .swiper-slide-shadow-top,
+.swiper-flip .swiper-slide-shadow-bottom,
+.swiper-flip .swiper-slide-shadow-left,
+.swiper-flip .swiper-slide-shadow-right {
+ z-index: 0;
+ -webkit-backface-visibility: hidden;
+ backface-visibility: hidden;
+}
+.swiper-creative .swiper-slide {
+ -webkit-backface-visibility: hidden;
+ backface-visibility: hidden;
+ overflow: hidden;
+ transition-property: transform, opacity, height;
+}
+.swiper-cards {
+ overflow: visible;
+}
+.swiper-cards .swiper-slide {
+ transform-origin: center bottom;
+ -webkit-backface-visibility: hidden;
+ backface-visibility: hidden;
+ overflow: hidden;
+}
diff --git a/public/static/js/button.js b/public/static/js/button.js
new file mode 100644
index 0000000..c57296c
--- /dev/null
+++ b/public/static/js/button.js
@@ -0,0 +1,25 @@
+$(function(){
+
+ $(window).scroll(function(){ //只要窗口滚动,就触发下面代码
+
+ var scrollt = document.documentElement.scrollTop + document.body.scrollTop; //获取滚动后的高度
+
+ if( scrollt >200 ){ //判断滚动后高度超过200px,就显示
+
+ $("#back_top").fadeIn(400); //淡入
+
+ }else{
+
+ $("#back_top").stop().fadeOut(400); //如果返回或者没有超过,就淡出.必须加上stop()停止之前动画,否则会出现闪动
+
+ }
+
+ });
+
+ $("#back_top").click(function(){ //当点击标签的时候,使用animate在200毫秒的时间内,滚到顶部
+
+ $("html,body").animate({scrollTop:"0px"},200);
+
+ });
+
+});
\ No newline at end of file
diff --git a/public/static/js/contact.js b/public/static/js/contact.js
new file mode 100644
index 0000000..53598bc
--- /dev/null
+++ b/public/static/js/contact.js
@@ -0,0 +1,69 @@
+//高德地图
+var map = new AMap.Map("container", {
+ zoom: 30,
+ center: [113.241678, 22.69780]
+ // center:[113.241538,22.698132]
+});
+var marker = new AMap.Marker({
+ position: new AMap.LngLat(113.241678, 22.69780),
+ title: "高路华"
+});
+map.add(marker)
+
+
+
+
+function ajaxPost(type, addMsgApi, captchaApi) {
+ let message_content;
+ let message_name;
+ let message_phone;
+ let message_mail;
+ let message_address;
+ let captcha;
+
+ if (type == 'pc') {
+ message_content = $('textarea[name="message_content"]').val();
+ message_name = $('input[name="message_name"]').val();
+ message_phone = $('input[name="message_phone"]').val();
+ message_mail = $('input[name="message_mail"]').val();
+ message_address = $('input[name="message_address"]').val();
+ captcha = $('input[name="captcha"]').val();
+ } else {
+ message_content = $('textarea[name="message_content_app"]').val();
+ message_name = $('input[name="message_name_app"]').val();
+ message_phone = $('input[name="message_phone_app"]').val();
+ message_mail = $('input[name="message_mail_app"]').val();
+ message_address = $('input[name="message_address_app"]').val();
+ captcha = $('input[name="captcha_app"]').val();
+ }
+
+
+ console.log('表单数据');
+ $.ajax({
+ url: addMsgApi,
+ type: "POST",
+ data: {
+ message_content: message_content,
+ message_name: message_name,
+ message_phone: message_phone,
+ message_mail: message_mail,
+ message_address: message_address,
+ captcha: captcha,
+ },
+ success: function (data) {
+ if (data.code == 0) {
+ alert(data.msg);
+ } else {
+ alert(data.msg);
+ }
+ //留言后刷新验证码
+ if (type == 'pc') {
+ $('#msg_code').attr("src", captchaApi + '?' + Math.random())
+ } else {
+ $('#msg_code_app').attr("src", captchaApi + '?' + Math.random())
+ }
+ }
+
+ })
+}
+
diff --git a/public/static/js/hyw/bootstrap/css/bootstrap.css b/public/static/js/hyw/bootstrap/css/bootstrap.css
new file mode 100644
index 0000000..8eac957
--- /dev/null
+++ b/public/static/js/hyw/bootstrap/css/bootstrap.css
@@ -0,0 +1,10224 @@
+/*!
+ * Bootstrap v4.4.1 (https://getbootstrap.com/)
+ * Copyright 2011-2019 The Bootstrap Authors
+ * Copyright 2011-2019 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */
+:root {
+ --blue: #007bff;
+ --indigo: #6610f2;
+ --purple: #6f42c1;
+ --pink: #e83e8c;
+ --red: #dc3545;
+ --orange: #fd7e14;
+ --yellow: #ffc107;
+ --green: #28a745;
+ --teal: #20c997;
+ --cyan: #17a2b8;
+ --white: #fff;
+ --gray: #6c757d;
+ --gray-dark: #343a40;
+ --primary: #007bff;
+ --secondary: #6c757d;
+ --success: #28a745;
+ --info: #17a2b8;
+ --warning: #ffc107;
+ --danger: #dc3545;
+ --light: #f8f9fa;
+ --dark: #343a40;
+ --breakpoint-xs: 0;
+ --breakpoint-sm: 576px;
+ --breakpoint-md: 768px;
+ --breakpoint-lg: 992px;
+ --breakpoint-xl: 1200px;
+ --font-family-sans-serif: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
+ --font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
+}
+
+*,
+*::before,
+*::after {
+ box-sizing: border-box;
+}
+
+html {
+ font-family: sans-serif;
+ line-height: 1.15;
+ -webkit-text-size-adjust: 100%;
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+}
+
+article, aside, figcaption, figure, footer, header, hgroup, main, nav, section {
+ display: block;
+}
+
+body {
+ margin: 0;
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
+ font-size: 1rem;
+ font-weight: 400;
+ line-height: 1.5;
+ color: #212529;
+ text-align: left;
+ background-color: #fff;
+}
+
+[tabindex="-1"]:focus:not(:focus-visible) {
+ outline: 0 !important;
+}
+
+hr {
+ box-sizing: content-box;
+ height: 0;
+ overflow: visible;
+}
+
+h1, h2, h3, h4, h5, h6 {
+ margin-top: 0;
+ margin-bottom: 0.5rem;
+}
+
+p {
+ margin-top: 0;
+ margin-bottom: 1rem;
+}
+
+abbr[title],
+abbr[data-original-title] {
+ text-decoration: underline;
+ -webkit-text-decoration: underline dotted;
+ text-decoration: underline dotted;
+ cursor: help;
+ border-bottom: 0;
+ -webkit-text-decoration-skip-ink: none;
+ text-decoration-skip-ink: none;
+}
+
+address {
+ margin-bottom: 1rem;
+ font-style: normal;
+ line-height: inherit;
+}
+
+ol,
+ul,
+dl {
+ margin-top: 0;
+ margin-bottom: 1rem;
+}
+
+ol ol,
+ul ul,
+ol ul,
+ul ol {
+ margin-bottom: 0;
+}
+
+dt {
+ font-weight: 700;
+}
+
+dd {
+ margin-bottom: .5rem;
+ margin-left: 0;
+}
+
+blockquote {
+ margin: 0 0 1rem;
+}
+
+b,
+strong {
+ font-weight: bolder;
+}
+
+small {
+ font-size: 80%;
+}
+
+sub,
+sup {
+ position: relative;
+ font-size: 75%;
+ line-height: 0;
+ vertical-align: baseline;
+}
+
+sub {
+ bottom: -.25em;
+}
+
+sup {
+ top: -.5em;
+}
+
+a {
+ color: #007bff;
+ text-decoration: none;
+ background-color: transparent;
+}
+
+a:hover {
+ color: #0056b3;
+ text-decoration: underline;
+}
+
+a:not([href]) {
+ color: inherit;
+ text-decoration: none;
+}
+
+a:not([href]):hover {
+ color: inherit;
+ text-decoration: none;
+}
+
+pre,
+code,
+kbd,
+samp {
+ font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
+ font-size: 1em;
+}
+
+pre {
+ margin-top: 0;
+ margin-bottom: 1rem;
+ overflow: auto;
+}
+
+figure {
+ margin: 0 0 1rem;
+}
+
+img {
+ vertical-align: middle;
+ border-style: none;
+}
+
+svg {
+ overflow: hidden;
+ vertical-align: middle;
+}
+
+table {
+ border-collapse: collapse;
+}
+
+caption {
+ padding-top: 0.75rem;
+ padding-bottom: 0.75rem;
+ color: #6c757d;
+ text-align: left;
+ caption-side: bottom;
+}
+
+th {
+ text-align: inherit;
+}
+
+label {
+ display: inline-block;
+ margin-bottom: 0.5rem;
+}
+
+button {
+ border-radius: 0;
+}
+
+button:focus {
+ outline: 1px dotted;
+ outline: 5px auto -webkit-focus-ring-color;
+}
+
+input,
+button,
+select,
+optgroup,
+textarea {
+ margin: 0;
+ font-family: inherit;
+ font-size: inherit;
+ line-height: inherit;
+}
+
+button,
+input {
+ overflow: visible;
+}
+
+button,
+select {
+ text-transform: none;
+}
+
+select {
+ word-wrap: normal;
+}
+
+button,
+[type="button"],
+[type="reset"],
+[type="submit"] {
+ -webkit-appearance: button;
+}
+
+button:not(:disabled),
+[type="button"]:not(:disabled),
+[type="reset"]:not(:disabled),
+[type="submit"]:not(:disabled) {
+ cursor: pointer;
+}
+
+button::-moz-focus-inner,
+[type="button"]::-moz-focus-inner,
+[type="reset"]::-moz-focus-inner,
+[type="submit"]::-moz-focus-inner {
+ padding: 0;
+ border-style: none;
+}
+
+input[type="radio"],
+input[type="checkbox"] {
+ box-sizing: border-box;
+ padding: 0;
+}
+
+input[type="date"],
+input[type="time"],
+input[type="datetime-local"],
+input[type="month"] {
+ -webkit-appearance: listbox;
+}
+
+textarea {
+ overflow: auto;
+ resize: vertical;
+}
+
+fieldset {
+ min-width: 0;
+ padding: 0;
+ margin: 0;
+ border: 0;
+}
+
+legend {
+ display: block;
+ width: 100%;
+ max-width: 100%;
+ padding: 0;
+ margin-bottom: .5rem;
+ font-size: 1.5rem;
+ line-height: inherit;
+ color: inherit;
+ white-space: normal;
+}
+
+progress {
+ vertical-align: baseline;
+}
+
+[type="number"]::-webkit-inner-spin-button,
+[type="number"]::-webkit-outer-spin-button {
+ height: auto;
+}
+
+[type="search"] {
+ outline-offset: -2px;
+ -webkit-appearance: none;
+}
+
+[type="search"]::-webkit-search-decoration {
+ -webkit-appearance: none;
+}
+
+::-webkit-file-upload-button {
+ font: inherit;
+ -webkit-appearance: button;
+}
+
+output {
+ display: inline-block;
+}
+
+summary {
+ display: list-item;
+ cursor: pointer;
+}
+
+template {
+ display: none;
+}
+
+[hidden] {
+ display: none !important;
+}
+
+h1, h2, h3, h4, h5, h6,
+.h1, .h2, .h3, .h4, .h5, .h6 {
+ margin-bottom: 0.5rem;
+ font-weight: 500;
+ line-height: 1.2;
+}
+
+h1, .h1 {
+ font-size: 2.5rem;
+}
+
+h2, .h2 {
+ font-size: 2rem;
+}
+
+h3, .h3 {
+ font-size: 1.75rem;
+}
+
+h4, .h4 {
+ font-size: 1.5rem;
+}
+
+h5, .h5 {
+ font-size: 1.25rem;
+}
+
+h6, .h6 {
+ font-size: 1rem;
+}
+
+.lead {
+ font-size: 1.25rem;
+ font-weight: 300;
+}
+
+.display-1 {
+ font-size: 6rem;
+ font-weight: 300;
+ line-height: 1.2;
+}
+
+.display-2 {
+ font-size: 5.5rem;
+ font-weight: 300;
+ line-height: 1.2;
+}
+
+.display-3 {
+ font-size: 4.5rem;
+ font-weight: 300;
+ line-height: 1.2;
+}
+
+.display-4 {
+ font-size: 3.5rem;
+ font-weight: 300;
+ line-height: 1.2;
+}
+
+hr {
+ margin-top: 1rem;
+ margin-bottom: 1rem;
+ border: 0;
+ border-top: 1px solid rgba(0, 0, 0, 0.1);
+}
+
+small,
+.small {
+ font-size: 80%;
+ font-weight: 400;
+}
+
+mark,
+.mark {
+ padding: 0.2em;
+ background-color: #fcf8e3;
+}
+
+.list-unstyled {
+ padding-left: 0;
+ list-style: none;
+}
+
+.list-inline {
+ padding-left: 0;
+ list-style: none;
+}
+
+.list-inline-item {
+ display: inline-block;
+}
+
+.list-inline-item:not(:last-child) {
+ margin-right: 0.5rem;
+}
+
+.initialism {
+ font-size: 90%;
+ text-transform: uppercase;
+}
+
+.blockquote {
+ margin-bottom: 1rem;
+ font-size: 1.25rem;
+}
+
+.blockquote-footer {
+ display: block;
+ font-size: 80%;
+ color: #6c757d;
+}
+
+.blockquote-footer::before {
+ content: "\2014\00A0";
+}
+
+.img-fluid {
+ max-width: 100%;
+ height: auto;
+}
+
+.img-thumbnail {
+ padding: 0.25rem;
+ background-color: #fff;
+ border: 1px solid #dee2e6;
+ border-radius: 0.25rem;
+ max-width: 100%;
+ height: auto;
+}
+
+.figure {
+ display: inline-block;
+}
+
+.figure-img {
+ margin-bottom: 0.5rem;
+ line-height: 1;
+}
+
+.figure-caption {
+ font-size: 90%;
+ color: #6c757d;
+}
+
+code {
+ font-size: 87.5%;
+ color: #e83e8c;
+ word-wrap: break-word;
+}
+
+a > code {
+ color: inherit;
+}
+
+kbd {
+ padding: 0.2rem 0.4rem;
+ font-size: 87.5%;
+ color: #fff;
+ background-color: #212529;
+ border-radius: 0.2rem;
+}
+
+kbd kbd {
+ padding: 0;
+ font-size: 100%;
+ font-weight: 700;
+}
+
+pre {
+ display: block;
+ font-size: 87.5%;
+ color: #212529;
+}
+
+pre code {
+ font-size: inherit;
+ color: inherit;
+ word-break: normal;
+}
+
+.pre-scrollable {
+ max-height: 340px;
+ overflow-y: scroll;
+}
+
+.container {
+ width: 100%;
+ padding-right: 15px;
+ padding-left: 15px;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+@media (min-width: 576px) {
+ .container {
+ max-width: 540px;
+ }
+}
+
+@media (min-width: 768px) {
+ .container {
+ max-width: 720px;
+ }
+}
+
+@media (min-width: 992px) {
+ .container {
+ max-width: 960px;
+ }
+}
+
+@media (min-width: 1200px) {
+ .container {
+ max-width: 1140px;
+ }
+}
+
+.container-fluid, .container-sm, .container-md, .container-lg, .container-xl {
+ width: 100%;
+ padding-right: 15px;
+ padding-left: 15px;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+@media (min-width: 576px) {
+ .container, .container-sm {
+ max-width: 540px;
+ }
+}
+
+@media (min-width: 768px) {
+ .container, .container-sm, .container-md {
+ max-width: 720px;
+ }
+}
+
+@media (min-width: 992px) {
+ .container, .container-sm, .container-md, .container-lg {
+ max-width: 960px;
+ }
+}
+
+@media (min-width: 1200px) {
+ .container, .container-sm, .container-md, .container-lg, .container-xl {
+ max-width: 1140px;
+ }
+}
+
+.row {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-wrap: wrap;
+ flex-wrap: wrap;
+ margin-right: -15px;
+ margin-left: -15px;
+}
+
+.no-gutters {
+ margin-right: 0;
+ margin-left: 0;
+}
+
+.no-gutters > .col,
+.no-gutters > [class*="col-"] {
+ padding-right: 0;
+ padding-left: 0;
+}
+
+.col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12, .col,
+.col-auto, .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12, .col-sm,
+.col-sm-auto, .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12, .col-md,
+.col-md-auto, .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12, .col-lg,
+.col-lg-auto, .col-xl-1, .col-xl-2, .col-xl-3, .col-xl-4, .col-xl-5, .col-xl-6, .col-xl-7, .col-xl-8, .col-xl-9, .col-xl-10, .col-xl-11, .col-xl-12, .col-xl,
+.col-xl-auto {
+ position: relative;
+ width: 100%;
+ padding-right: 15px;
+ padding-left: 15px;
+}
+
+.col {
+ -ms-flex-preferred-size: 0;
+ flex-basis: 0;
+ -ms-flex-positive: 1;
+ flex-grow: 1;
+ max-width: 100%;
+}
+
+.row-cols-1 > * {
+ -ms-flex: 0 0 100%;
+ flex: 0 0 100%;
+ max-width: 100%;
+}
+
+.row-cols-2 > * {
+ -ms-flex: 0 0 50%;
+ flex: 0 0 50%;
+ max-width: 50%;
+}
+
+.row-cols-3 > * {
+ -ms-flex: 0 0 33.333333%;
+ flex: 0 0 33.333333%;
+ max-width: 33.333333%;
+}
+
+.row-cols-4 > * {
+ -ms-flex: 0 0 25%;
+ flex: 0 0 25%;
+ max-width: 25%;
+}
+
+.row-cols-5 > * {
+ -ms-flex: 0 0 20%;
+ flex: 0 0 20%;
+ max-width: 20%;
+}
+
+.row-cols-6 > * {
+ -ms-flex: 0 0 16.666667%;
+ flex: 0 0 16.666667%;
+ max-width: 16.666667%;
+}
+
+.col-auto {
+ -ms-flex: 0 0 auto;
+ flex: 0 0 auto;
+ width: auto;
+ max-width: 100%;
+}
+
+.col-1 {
+ -ms-flex: 0 0 8.333333%;
+ flex: 0 0 8.333333%;
+ max-width: 8.333333%;
+}
+
+.col-2 {
+ -ms-flex: 0 0 16.666667%;
+ flex: 0 0 16.666667%;
+ max-width: 16.666667%;
+}
+
+.col-3 {
+ -ms-flex: 0 0 25%;
+ flex: 0 0 25%;
+ max-width: 25%;
+}
+
+.col-4 {
+ -ms-flex: 0 0 33.333333%;
+ flex: 0 0 33.333333%;
+ max-width: 33.333333%;
+}
+
+.col-5 {
+ -ms-flex: 0 0 41.666667%;
+ flex: 0 0 41.666667%;
+ max-width: 41.666667%;
+}
+
+.col-6 {
+ -ms-flex: 0 0 50%;
+ flex: 0 0 50%;
+ max-width: 50%;
+}
+
+.col-7 {
+ -ms-flex: 0 0 58.333333%;
+ flex: 0 0 58.333333%;
+ max-width: 58.333333%;
+}
+
+.col-8 {
+ -ms-flex: 0 0 66.666667%;
+ flex: 0 0 66.666667%;
+ max-width: 66.666667%;
+}
+
+.col-9 {
+ -ms-flex: 0 0 75%;
+ flex: 0 0 75%;
+ max-width: 75%;
+}
+
+.col-10 {
+ -ms-flex: 0 0 83.333333%;
+ flex: 0 0 83.333333%;
+ max-width: 83.333333%;
+}
+
+.col-11 {
+ -ms-flex: 0 0 91.666667%;
+ flex: 0 0 91.666667%;
+ max-width: 91.666667%;
+}
+
+.col-12 {
+ -ms-flex: 0 0 100%;
+ flex: 0 0 100%;
+ max-width: 100%;
+}
+
+.order-first {
+ -ms-flex-order: -1;
+ order: -1;
+}
+
+.order-last {
+ -ms-flex-order: 13;
+ order: 13;
+}
+
+.order-0 {
+ -ms-flex-order: 0;
+ order: 0;
+}
+
+.order-1 {
+ -ms-flex-order: 1;
+ order: 1;
+}
+
+.order-2 {
+ -ms-flex-order: 2;
+ order: 2;
+}
+
+.order-3 {
+ -ms-flex-order: 3;
+ order: 3;
+}
+
+.order-4 {
+ -ms-flex-order: 4;
+ order: 4;
+}
+
+.order-5 {
+ -ms-flex-order: 5;
+ order: 5;
+}
+
+.order-6 {
+ -ms-flex-order: 6;
+ order: 6;
+}
+
+.order-7 {
+ -ms-flex-order: 7;
+ order: 7;
+}
+
+.order-8 {
+ -ms-flex-order: 8;
+ order: 8;
+}
+
+.order-9 {
+ -ms-flex-order: 9;
+ order: 9;
+}
+
+.order-10 {
+ -ms-flex-order: 10;
+ order: 10;
+}
+
+.order-11 {
+ -ms-flex-order: 11;
+ order: 11;
+}
+
+.order-12 {
+ -ms-flex-order: 12;
+ order: 12;
+}
+
+.offset-1 {
+ margin-left: 8.333333%;
+}
+
+.offset-2 {
+ margin-left: 16.666667%;
+}
+
+.offset-3 {
+ margin-left: 25%;
+}
+
+.offset-4 {
+ margin-left: 33.333333%;
+}
+
+.offset-5 {
+ margin-left: 41.666667%;
+}
+
+.offset-6 {
+ margin-left: 50%;
+}
+
+.offset-7 {
+ margin-left: 58.333333%;
+}
+
+.offset-8 {
+ margin-left: 66.666667%;
+}
+
+.offset-9 {
+ margin-left: 75%;
+}
+
+.offset-10 {
+ margin-left: 83.333333%;
+}
+
+.offset-11 {
+ margin-left: 91.666667%;
+}
+
+@media (min-width: 576px) {
+ .col-sm {
+ -ms-flex-preferred-size: 0;
+ flex-basis: 0;
+ -ms-flex-positive: 1;
+ flex-grow: 1;
+ max-width: 100%;
+ }
+ .row-cols-sm-1 > * {
+ -ms-flex: 0 0 100%;
+ flex: 0 0 100%;
+ max-width: 100%;
+ }
+ .row-cols-sm-2 > * {
+ -ms-flex: 0 0 50%;
+ flex: 0 0 50%;
+ max-width: 50%;
+ }
+ .row-cols-sm-3 > * {
+ -ms-flex: 0 0 33.333333%;
+ flex: 0 0 33.333333%;
+ max-width: 33.333333%;
+ }
+ .row-cols-sm-4 > * {
+ -ms-flex: 0 0 25%;
+ flex: 0 0 25%;
+ max-width: 25%;
+ }
+ .row-cols-sm-5 > * {
+ -ms-flex: 0 0 20%;
+ flex: 0 0 20%;
+ max-width: 20%;
+ }
+ .row-cols-sm-6 > * {
+ -ms-flex: 0 0 16.666667%;
+ flex: 0 0 16.666667%;
+ max-width: 16.666667%;
+ }
+ .col-sm-auto {
+ -ms-flex: 0 0 auto;
+ flex: 0 0 auto;
+ width: auto;
+ max-width: 100%;
+ }
+ .col-sm-1 {
+ -ms-flex: 0 0 8.333333%;
+ flex: 0 0 8.333333%;
+ max-width: 8.333333%;
+ }
+ .col-sm-2 {
+ -ms-flex: 0 0 16.666667%;
+ flex: 0 0 16.666667%;
+ max-width: 16.666667%;
+ }
+ .col-sm-3 {
+ -ms-flex: 0 0 25%;
+ flex: 0 0 25%;
+ max-width: 25%;
+ }
+ .col-sm-4 {
+ -ms-flex: 0 0 33.333333%;
+ flex: 0 0 33.333333%;
+ max-width: 33.333333%;
+ }
+ .col-sm-5 {
+ -ms-flex: 0 0 41.666667%;
+ flex: 0 0 41.666667%;
+ max-width: 41.666667%;
+ }
+ .col-sm-6 {
+ -ms-flex: 0 0 50%;
+ flex: 0 0 50%;
+ max-width: 50%;
+ }
+ .col-sm-7 {
+ -ms-flex: 0 0 58.333333%;
+ flex: 0 0 58.333333%;
+ max-width: 58.333333%;
+ }
+ .col-sm-8 {
+ -ms-flex: 0 0 66.666667%;
+ flex: 0 0 66.666667%;
+ max-width: 66.666667%;
+ }
+ .col-sm-9 {
+ -ms-flex: 0 0 75%;
+ flex: 0 0 75%;
+ max-width: 75%;
+ }
+ .col-sm-10 {
+ -ms-flex: 0 0 83.333333%;
+ flex: 0 0 83.333333%;
+ max-width: 83.333333%;
+ }
+ .col-sm-11 {
+ -ms-flex: 0 0 91.666667%;
+ flex: 0 0 91.666667%;
+ max-width: 91.666667%;
+ }
+ .col-sm-12 {
+ -ms-flex: 0 0 100%;
+ flex: 0 0 100%;
+ max-width: 100%;
+ }
+ .order-sm-first {
+ -ms-flex-order: -1;
+ order: -1;
+ }
+ .order-sm-last {
+ -ms-flex-order: 13;
+ order: 13;
+ }
+ .order-sm-0 {
+ -ms-flex-order: 0;
+ order: 0;
+ }
+ .order-sm-1 {
+ -ms-flex-order: 1;
+ order: 1;
+ }
+ .order-sm-2 {
+ -ms-flex-order: 2;
+ order: 2;
+ }
+ .order-sm-3 {
+ -ms-flex-order: 3;
+ order: 3;
+ }
+ .order-sm-4 {
+ -ms-flex-order: 4;
+ order: 4;
+ }
+ .order-sm-5 {
+ -ms-flex-order: 5;
+ order: 5;
+ }
+ .order-sm-6 {
+ -ms-flex-order: 6;
+ order: 6;
+ }
+ .order-sm-7 {
+ -ms-flex-order: 7;
+ order: 7;
+ }
+ .order-sm-8 {
+ -ms-flex-order: 8;
+ order: 8;
+ }
+ .order-sm-9 {
+ -ms-flex-order: 9;
+ order: 9;
+ }
+ .order-sm-10 {
+ -ms-flex-order: 10;
+ order: 10;
+ }
+ .order-sm-11 {
+ -ms-flex-order: 11;
+ order: 11;
+ }
+ .order-sm-12 {
+ -ms-flex-order: 12;
+ order: 12;
+ }
+ .offset-sm-0 {
+ margin-left: 0;
+ }
+ .offset-sm-1 {
+ margin-left: 8.333333%;
+ }
+ .offset-sm-2 {
+ margin-left: 16.666667%;
+ }
+ .offset-sm-3 {
+ margin-left: 25%;
+ }
+ .offset-sm-4 {
+ margin-left: 33.333333%;
+ }
+ .offset-sm-5 {
+ margin-left: 41.666667%;
+ }
+ .offset-sm-6 {
+ margin-left: 50%;
+ }
+ .offset-sm-7 {
+ margin-left: 58.333333%;
+ }
+ .offset-sm-8 {
+ margin-left: 66.666667%;
+ }
+ .offset-sm-9 {
+ margin-left: 75%;
+ }
+ .offset-sm-10 {
+ margin-left: 83.333333%;
+ }
+ .offset-sm-11 {
+ margin-left: 91.666667%;
+ }
+}
+
+@media (min-width: 768px) {
+ .col-md {
+ -ms-flex-preferred-size: 0;
+ flex-basis: 0;
+ -ms-flex-positive: 1;
+ flex-grow: 1;
+ max-width: 100%;
+ }
+ .row-cols-md-1 > * {
+ -ms-flex: 0 0 100%;
+ flex: 0 0 100%;
+ max-width: 100%;
+ }
+ .row-cols-md-2 > * {
+ -ms-flex: 0 0 50%;
+ flex: 0 0 50%;
+ max-width: 50%;
+ }
+ .row-cols-md-3 > * {
+ -ms-flex: 0 0 33.333333%;
+ flex: 0 0 33.333333%;
+ max-width: 33.333333%;
+ }
+ .row-cols-md-4 > * {
+ -ms-flex: 0 0 25%;
+ flex: 0 0 25%;
+ max-width: 25%;
+ }
+ .row-cols-md-5 > * {
+ -ms-flex: 0 0 20%;
+ flex: 0 0 20%;
+ max-width: 20%;
+ }
+ .row-cols-md-6 > * {
+ -ms-flex: 0 0 16.666667%;
+ flex: 0 0 16.666667%;
+ max-width: 16.666667%;
+ }
+ .col-md-auto {
+ -ms-flex: 0 0 auto;
+ flex: 0 0 auto;
+ width: auto;
+ max-width: 100%;
+ }
+ .col-md-1 {
+ -ms-flex: 0 0 8.333333%;
+ flex: 0 0 8.333333%;
+ max-width: 8.333333%;
+ }
+ .col-md-2 {
+ -ms-flex: 0 0 16.666667%;
+ flex: 0 0 16.666667%;
+ max-width: 16.666667%;
+ }
+ .col-md-3 {
+ -ms-flex: 0 0 25%;
+ flex: 0 0 25%;
+ max-width: 25%;
+ }
+ .col-md-4 {
+ -ms-flex: 0 0 33.333333%;
+ flex: 0 0 33.333333%;
+ max-width: 33.333333%;
+ }
+ .col-md-5 {
+ -ms-flex: 0 0 41.666667%;
+ flex: 0 0 41.666667%;
+ max-width: 41.666667%;
+ }
+ .col-md-6 {
+ -ms-flex: 0 0 50%;
+ flex: 0 0 50%;
+ max-width: 50%;
+ }
+ .col-md-7 {
+ -ms-flex: 0 0 58.333333%;
+ flex: 0 0 58.333333%;
+ max-width: 58.333333%;
+ }
+ .col-md-8 {
+ -ms-flex: 0 0 66.666667%;
+ flex: 0 0 66.666667%;
+ max-width: 66.666667%;
+ }
+ .col-md-9 {
+ -ms-flex: 0 0 75%;
+ flex: 0 0 75%;
+ max-width: 75%;
+ }
+ .col-md-10 {
+ -ms-flex: 0 0 83.333333%;
+ flex: 0 0 83.333333%;
+ max-width: 83.333333%;
+ }
+ .col-md-11 {
+ -ms-flex: 0 0 91.666667%;
+ flex: 0 0 91.666667%;
+ max-width: 91.666667%;
+ }
+ .col-md-12 {
+ -ms-flex: 0 0 100%;
+ flex: 0 0 100%;
+ max-width: 100%;
+ }
+ .order-md-first {
+ -ms-flex-order: -1;
+ order: -1;
+ }
+ .order-md-last {
+ -ms-flex-order: 13;
+ order: 13;
+ }
+ .order-md-0 {
+ -ms-flex-order: 0;
+ order: 0;
+ }
+ .order-md-1 {
+ -ms-flex-order: 1;
+ order: 1;
+ }
+ .order-md-2 {
+ -ms-flex-order: 2;
+ order: 2;
+ }
+ .order-md-3 {
+ -ms-flex-order: 3;
+ order: 3;
+ }
+ .order-md-4 {
+ -ms-flex-order: 4;
+ order: 4;
+ }
+ .order-md-5 {
+ -ms-flex-order: 5;
+ order: 5;
+ }
+ .order-md-6 {
+ -ms-flex-order: 6;
+ order: 6;
+ }
+ .order-md-7 {
+ -ms-flex-order: 7;
+ order: 7;
+ }
+ .order-md-8 {
+ -ms-flex-order: 8;
+ order: 8;
+ }
+ .order-md-9 {
+ -ms-flex-order: 9;
+ order: 9;
+ }
+ .order-md-10 {
+ -ms-flex-order: 10;
+ order: 10;
+ }
+ .order-md-11 {
+ -ms-flex-order: 11;
+ order: 11;
+ }
+ .order-md-12 {
+ -ms-flex-order: 12;
+ order: 12;
+ }
+ .offset-md-0 {
+ margin-left: 0;
+ }
+ .offset-md-1 {
+ margin-left: 8.333333%;
+ }
+ .offset-md-2 {
+ margin-left: 16.666667%;
+ }
+ .offset-md-3 {
+ margin-left: 25%;
+ }
+ .offset-md-4 {
+ margin-left: 33.333333%;
+ }
+ .offset-md-5 {
+ margin-left: 41.666667%;
+ }
+ .offset-md-6 {
+ margin-left: 50%;
+ }
+ .offset-md-7 {
+ margin-left: 58.333333%;
+ }
+ .offset-md-8 {
+ margin-left: 66.666667%;
+ }
+ .offset-md-9 {
+ margin-left: 75%;
+ }
+ .offset-md-10 {
+ margin-left: 83.333333%;
+ }
+ .offset-md-11 {
+ margin-left: 91.666667%;
+ }
+}
+
+@media (min-width: 992px) {
+ .col-lg {
+ -ms-flex-preferred-size: 0;
+ flex-basis: 0;
+ -ms-flex-positive: 1;
+ flex-grow: 1;
+ max-width: 100%;
+ }
+ .row-cols-lg-1 > * {
+ -ms-flex: 0 0 100%;
+ flex: 0 0 100%;
+ max-width: 100%;
+ }
+ .row-cols-lg-2 > * {
+ -ms-flex: 0 0 50%;
+ flex: 0 0 50%;
+ max-width: 50%;
+ }
+ .row-cols-lg-3 > * {
+ -ms-flex: 0 0 33.333333%;
+ flex: 0 0 33.333333%;
+ max-width: 33.333333%;
+ }
+ .row-cols-lg-4 > * {
+ -ms-flex: 0 0 25%;
+ flex: 0 0 25%;
+ max-width: 25%;
+ }
+ .row-cols-lg-5 > * {
+ -ms-flex: 0 0 20%;
+ flex: 0 0 20%;
+ max-width: 20%;
+ }
+ .row-cols-lg-6 > * {
+ -ms-flex: 0 0 16.666667%;
+ flex: 0 0 16.666667%;
+ max-width: 16.666667%;
+ }
+ .col-lg-auto {
+ -ms-flex: 0 0 auto;
+ flex: 0 0 auto;
+ width: auto;
+ max-width: 100%;
+ }
+ .col-lg-1 {
+ -ms-flex: 0 0 8.333333%;
+ flex: 0 0 8.333333%;
+ max-width: 8.333333%;
+ }
+ .col-lg-2 {
+ -ms-flex: 0 0 16.666667%;
+ flex: 0 0 16.666667%;
+ max-width: 16.666667%;
+ }
+ .col-lg-3 {
+ -ms-flex: 0 0 25%;
+ flex: 0 0 25%;
+ max-width: 25%;
+ }
+ .col-lg-4 {
+ -ms-flex: 0 0 33.333333%;
+ flex: 0 0 33.333333%;
+ max-width: 33.333333%;
+ }
+ .col-lg-5 {
+ -ms-flex: 0 0 41.666667%;
+ flex: 0 0 41.666667%;
+ max-width: 41.666667%;
+ }
+ .col-lg-6 {
+ -ms-flex: 0 0 50%;
+ flex: 0 0 50%;
+ max-width: 50%;
+ }
+ .col-lg-7 {
+ -ms-flex: 0 0 58.333333%;
+ flex: 0 0 58.333333%;
+ max-width: 58.333333%;
+ }
+ .col-lg-8 {
+ -ms-flex: 0 0 66.666667%;
+ flex: 0 0 66.666667%;
+ max-width: 66.666667%;
+ }
+ .col-lg-9 {
+ -ms-flex: 0 0 75%;
+ flex: 0 0 75%;
+ max-width: 75%;
+ }
+ .col-lg-10 {
+ -ms-flex: 0 0 83.333333%;
+ flex: 0 0 83.333333%;
+ max-width: 83.333333%;
+ }
+ .col-lg-11 {
+ -ms-flex: 0 0 91.666667%;
+ flex: 0 0 91.666667%;
+ max-width: 91.666667%;
+ }
+ .col-lg-12 {
+ -ms-flex: 0 0 100%;
+ flex: 0 0 100%;
+ max-width: 100%;
+ }
+ .order-lg-first {
+ -ms-flex-order: -1;
+ order: -1;
+ }
+ .order-lg-last {
+ -ms-flex-order: 13;
+ order: 13;
+ }
+ .order-lg-0 {
+ -ms-flex-order: 0;
+ order: 0;
+ }
+ .order-lg-1 {
+ -ms-flex-order: 1;
+ order: 1;
+ }
+ .order-lg-2 {
+ -ms-flex-order: 2;
+ order: 2;
+ }
+ .order-lg-3 {
+ -ms-flex-order: 3;
+ order: 3;
+ }
+ .order-lg-4 {
+ -ms-flex-order: 4;
+ order: 4;
+ }
+ .order-lg-5 {
+ -ms-flex-order: 5;
+ order: 5;
+ }
+ .order-lg-6 {
+ -ms-flex-order: 6;
+ order: 6;
+ }
+ .order-lg-7 {
+ -ms-flex-order: 7;
+ order: 7;
+ }
+ .order-lg-8 {
+ -ms-flex-order: 8;
+ order: 8;
+ }
+ .order-lg-9 {
+ -ms-flex-order: 9;
+ order: 9;
+ }
+ .order-lg-10 {
+ -ms-flex-order: 10;
+ order: 10;
+ }
+ .order-lg-11 {
+ -ms-flex-order: 11;
+ order: 11;
+ }
+ .order-lg-12 {
+ -ms-flex-order: 12;
+ order: 12;
+ }
+ .offset-lg-0 {
+ margin-left: 0;
+ }
+ .offset-lg-1 {
+ margin-left: 8.333333%;
+ }
+ .offset-lg-2 {
+ margin-left: 16.666667%;
+ }
+ .offset-lg-3 {
+ margin-left: 25%;
+ }
+ .offset-lg-4 {
+ margin-left: 33.333333%;
+ }
+ .offset-lg-5 {
+ margin-left: 41.666667%;
+ }
+ .offset-lg-6 {
+ margin-left: 50%;
+ }
+ .offset-lg-7 {
+ margin-left: 58.333333%;
+ }
+ .offset-lg-8 {
+ margin-left: 66.666667%;
+ }
+ .offset-lg-9 {
+ margin-left: 75%;
+ }
+ .offset-lg-10 {
+ margin-left: 83.333333%;
+ }
+ .offset-lg-11 {
+ margin-left: 91.666667%;
+ }
+}
+
+@media (min-width: 1200px) {
+ .col-xl {
+ -ms-flex-preferred-size: 0;
+ flex-basis: 0;
+ -ms-flex-positive: 1;
+ flex-grow: 1;
+ max-width: 100%;
+ }
+ .row-cols-xl-1 > * {
+ -ms-flex: 0 0 100%;
+ flex: 0 0 100%;
+ max-width: 100%;
+ }
+ .row-cols-xl-2 > * {
+ -ms-flex: 0 0 50%;
+ flex: 0 0 50%;
+ max-width: 50%;
+ }
+ .row-cols-xl-3 > * {
+ -ms-flex: 0 0 33.333333%;
+ flex: 0 0 33.333333%;
+ max-width: 33.333333%;
+ }
+ .row-cols-xl-4 > * {
+ -ms-flex: 0 0 25%;
+ flex: 0 0 25%;
+ max-width: 25%;
+ }
+ .row-cols-xl-5 > * {
+ -ms-flex: 0 0 20%;
+ flex: 0 0 20%;
+ max-width: 20%;
+ }
+ .row-cols-xl-6 > * {
+ -ms-flex: 0 0 16.666667%;
+ flex: 0 0 16.666667%;
+ max-width: 16.666667%;
+ }
+ .col-xl-auto {
+ -ms-flex: 0 0 auto;
+ flex: 0 0 auto;
+ width: auto;
+ max-width: 100%;
+ }
+ .col-xl-1 {
+ -ms-flex: 0 0 8.333333%;
+ flex: 0 0 8.333333%;
+ max-width: 8.333333%;
+ }
+ .col-xl-2 {
+ -ms-flex: 0 0 16.666667%;
+ flex: 0 0 16.666667%;
+ max-width: 16.666667%;
+ }
+ .col-xl-3 {
+ -ms-flex: 0 0 25%;
+ flex: 0 0 25%;
+ max-width: 25%;
+ }
+ .col-xl-4 {
+ -ms-flex: 0 0 33.333333%;
+ flex: 0 0 33.333333%;
+ max-width: 33.333333%;
+ }
+ .col-xl-5 {
+ -ms-flex: 0 0 41.666667%;
+ flex: 0 0 41.666667%;
+ max-width: 41.666667%;
+ }
+ .col-xl-6 {
+ -ms-flex: 0 0 50%;
+ flex: 0 0 50%;
+ max-width: 50%;
+ }
+ .col-xl-7 {
+ -ms-flex: 0 0 58.333333%;
+ flex: 0 0 58.333333%;
+ max-width: 58.333333%;
+ }
+ .col-xl-8 {
+ -ms-flex: 0 0 66.666667%;
+ flex: 0 0 66.666667%;
+ max-width: 66.666667%;
+ }
+ .col-xl-9 {
+ -ms-flex: 0 0 75%;
+ flex: 0 0 75%;
+ max-width: 75%;
+ }
+ .col-xl-10 {
+ -ms-flex: 0 0 83.333333%;
+ flex: 0 0 83.333333%;
+ max-width: 83.333333%;
+ }
+ .col-xl-11 {
+ -ms-flex: 0 0 91.666667%;
+ flex: 0 0 91.666667%;
+ max-width: 91.666667%;
+ }
+ .col-xl-12 {
+ -ms-flex: 0 0 100%;
+ flex: 0 0 100%;
+ max-width: 100%;
+ }
+ .order-xl-first {
+ -ms-flex-order: -1;
+ order: -1;
+ }
+ .order-xl-last {
+ -ms-flex-order: 13;
+ order: 13;
+ }
+ .order-xl-0 {
+ -ms-flex-order: 0;
+ order: 0;
+ }
+ .order-xl-1 {
+ -ms-flex-order: 1;
+ order: 1;
+ }
+ .order-xl-2 {
+ -ms-flex-order: 2;
+ order: 2;
+ }
+ .order-xl-3 {
+ -ms-flex-order: 3;
+ order: 3;
+ }
+ .order-xl-4 {
+ -ms-flex-order: 4;
+ order: 4;
+ }
+ .order-xl-5 {
+ -ms-flex-order: 5;
+ order: 5;
+ }
+ .order-xl-6 {
+ -ms-flex-order: 6;
+ order: 6;
+ }
+ .order-xl-7 {
+ -ms-flex-order: 7;
+ order: 7;
+ }
+ .order-xl-8 {
+ -ms-flex-order: 8;
+ order: 8;
+ }
+ .order-xl-9 {
+ -ms-flex-order: 9;
+ order: 9;
+ }
+ .order-xl-10 {
+ -ms-flex-order: 10;
+ order: 10;
+ }
+ .order-xl-11 {
+ -ms-flex-order: 11;
+ order: 11;
+ }
+ .order-xl-12 {
+ -ms-flex-order: 12;
+ order: 12;
+ }
+ .offset-xl-0 {
+ margin-left: 0;
+ }
+ .offset-xl-1 {
+ margin-left: 8.333333%;
+ }
+ .offset-xl-2 {
+ margin-left: 16.666667%;
+ }
+ .offset-xl-3 {
+ margin-left: 25%;
+ }
+ .offset-xl-4 {
+ margin-left: 33.333333%;
+ }
+ .offset-xl-5 {
+ margin-left: 41.666667%;
+ }
+ .offset-xl-6 {
+ margin-left: 50%;
+ }
+ .offset-xl-7 {
+ margin-left: 58.333333%;
+ }
+ .offset-xl-8 {
+ margin-left: 66.666667%;
+ }
+ .offset-xl-9 {
+ margin-left: 75%;
+ }
+ .offset-xl-10 {
+ margin-left: 83.333333%;
+ }
+ .offset-xl-11 {
+ margin-left: 91.666667%;
+ }
+}
+
+.table {
+ width: 100%;
+ margin-bottom: 1rem;
+ color: #212529;
+}
+
+.table th,
+.table td {
+ padding: 0.75rem;
+ vertical-align: top;
+ border-top: 1px solid #dee2e6;
+}
+
+.table thead th {
+ vertical-align: bottom;
+ border-bottom: 2px solid #dee2e6;
+}
+
+.table tbody + tbody {
+ border-top: 2px solid #dee2e6;
+}
+
+.table-sm th,
+.table-sm td {
+ padding: 0.3rem;
+}
+
+.table-bordered {
+ border: 1px solid #dee2e6;
+}
+
+.table-bordered th,
+.table-bordered td {
+ border: 1px solid #dee2e6;
+}
+
+.table-bordered thead th,
+.table-bordered thead td {
+ border-bottom-width: 2px;
+}
+
+.table-borderless th,
+.table-borderless td,
+.table-borderless thead th,
+.table-borderless tbody + tbody {
+ border: 0;
+}
+
+.table-striped tbody tr:nth-of-type(odd) {
+ background-color: rgba(0, 0, 0, 0.05);
+}
+
+.table-hover tbody tr:hover {
+ color: #212529;
+ background-color: rgba(0, 0, 0, 0.075);
+}
+
+.table-primary,
+.table-primary > th,
+.table-primary > td {
+ background-color: #b8daff;
+}
+
+.table-primary th,
+.table-primary td,
+.table-primary thead th,
+.table-primary tbody + tbody {
+ border-color: #7abaff;
+}
+
+.table-hover .table-primary:hover {
+ background-color: #9fcdff;
+}
+
+.table-hover .table-primary:hover > td,
+.table-hover .table-primary:hover > th {
+ background-color: #9fcdff;
+}
+
+.table-secondary,
+.table-secondary > th,
+.table-secondary > td {
+ background-color: #d6d8db;
+}
+
+.table-secondary th,
+.table-secondary td,
+.table-secondary thead th,
+.table-secondary tbody + tbody {
+ border-color: #b3b7bb;
+}
+
+.table-hover .table-secondary:hover {
+ background-color: #c8cbcf;
+}
+
+.table-hover .table-secondary:hover > td,
+.table-hover .table-secondary:hover > th {
+ background-color: #c8cbcf;
+}
+
+.table-success,
+.table-success > th,
+.table-success > td {
+ background-color: #c3e6cb;
+}
+
+.table-success th,
+.table-success td,
+.table-success thead th,
+.table-success tbody + tbody {
+ border-color: #8fd19e;
+}
+
+.table-hover .table-success:hover {
+ background-color: #b1dfbb;
+}
+
+.table-hover .table-success:hover > td,
+.table-hover .table-success:hover > th {
+ background-color: #b1dfbb;
+}
+
+.table-info,
+.table-info > th,
+.table-info > td {
+ background-color: #bee5eb;
+}
+
+.table-info th,
+.table-info td,
+.table-info thead th,
+.table-info tbody + tbody {
+ border-color: #86cfda;
+}
+
+.table-hover .table-info:hover {
+ background-color: #abdde5;
+}
+
+.table-hover .table-info:hover > td,
+.table-hover .table-info:hover > th {
+ background-color: #abdde5;
+}
+
+.table-warning,
+.table-warning > th,
+.table-warning > td {
+ background-color: #ffeeba;
+}
+
+.table-warning th,
+.table-warning td,
+.table-warning thead th,
+.table-warning tbody + tbody {
+ border-color: #ffdf7e;
+}
+
+.table-hover .table-warning:hover {
+ background-color: #ffe8a1;
+}
+
+.table-hover .table-warning:hover > td,
+.table-hover .table-warning:hover > th {
+ background-color: #ffe8a1;
+}
+
+.table-danger,
+.table-danger > th,
+.table-danger > td {
+ background-color: #f5c6cb;
+}
+
+.table-danger th,
+.table-danger td,
+.table-danger thead th,
+.table-danger tbody + tbody {
+ border-color: #ed969e;
+}
+
+.table-hover .table-danger:hover {
+ background-color: #f1b0b7;
+}
+
+.table-hover .table-danger:hover > td,
+.table-hover .table-danger:hover > th {
+ background-color: #f1b0b7;
+}
+
+.table-light,
+.table-light > th,
+.table-light > td {
+ background-color: #fdfdfe;
+}
+
+.table-light th,
+.table-light td,
+.table-light thead th,
+.table-light tbody + tbody {
+ border-color: #fbfcfc;
+}
+
+.table-hover .table-light:hover {
+ background-color: #ececf6;
+}
+
+.table-hover .table-light:hover > td,
+.table-hover .table-light:hover > th {
+ background-color: #ececf6;
+}
+
+.table-dark,
+.table-dark > th,
+.table-dark > td {
+ background-color: #c6c8ca;
+}
+
+.table-dark th,
+.table-dark td,
+.table-dark thead th,
+.table-dark tbody + tbody {
+ border-color: #95999c;
+}
+
+.table-hover .table-dark:hover {
+ background-color: #b9bbbe;
+}
+
+.table-hover .table-dark:hover > td,
+.table-hover .table-dark:hover > th {
+ background-color: #b9bbbe;
+}
+
+.table-active,
+.table-active > th,
+.table-active > td {
+ background-color: rgba(0, 0, 0, 0.075);
+}
+
+.table-hover .table-active:hover {
+ background-color: rgba(0, 0, 0, 0.075);
+}
+
+.table-hover .table-active:hover > td,
+.table-hover .table-active:hover > th {
+ background-color: rgba(0, 0, 0, 0.075);
+}
+
+.table .thead-dark th {
+ color: #fff;
+ background-color: #343a40;
+ border-color: #454d55;
+}
+
+.table .thead-light th {
+ color: #495057;
+ background-color: #e9ecef;
+ border-color: #dee2e6;
+}
+
+.table-dark {
+ color: #fff;
+ background-color: #343a40;
+}
+
+.table-dark th,
+.table-dark td,
+.table-dark thead th {
+ border-color: #454d55;
+}
+
+.table-dark.table-bordered {
+ border: 0;
+}
+
+.table-dark.table-striped tbody tr:nth-of-type(odd) {
+ background-color: rgba(255, 255, 255, 0.05);
+}
+
+.table-dark.table-hover tbody tr:hover {
+ color: #fff;
+ background-color: rgba(255, 255, 255, 0.075);
+}
+
+@media (max-width: 575.98px) {
+ .table-responsive-sm {
+ display: block;
+ width: 100%;
+ overflow-x: auto;
+ -webkit-overflow-scrolling: touch;
+ }
+ .table-responsive-sm > .table-bordered {
+ border: 0;
+ }
+}
+
+@media (max-width: 767.98px) {
+ .table-responsive-md {
+ display: block;
+ width: 100%;
+ overflow-x: auto;
+ -webkit-overflow-scrolling: touch;
+ }
+ .table-responsive-md > .table-bordered {
+ border: 0;
+ }
+}
+
+@media (max-width: 991.98px) {
+ .table-responsive-lg {
+ display: block;
+ width: 100%;
+ overflow-x: auto;
+ -webkit-overflow-scrolling: touch;
+ }
+ .table-responsive-lg > .table-bordered {
+ border: 0;
+ }
+}
+
+@media (max-width: 1199.98px) {
+ .table-responsive-xl {
+ display: block;
+ width: 100%;
+ overflow-x: auto;
+ -webkit-overflow-scrolling: touch;
+ }
+ .table-responsive-xl > .table-bordered {
+ border: 0;
+ }
+}
+
+.table-responsive {
+ display: block;
+ width: 100%;
+ overflow-x: auto;
+ -webkit-overflow-scrolling: touch;
+}
+
+.table-responsive > .table-bordered {
+ border: 0;
+}
+
+.form-control {
+ display: block;
+ width: 100%;
+ height: calc(1.5em + 0.75rem + 2px);
+ padding: 0.375rem 0.75rem;
+ font-size: 1rem;
+ font-weight: 400;
+ line-height: 1.5;
+ color: #495057;
+ background-color: #fff;
+ background-clip: padding-box;
+ border: 1px solid #ced4da;
+ border-radius: 0.25rem;
+ transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .form-control {
+ transition: none;
+ }
+}
+
+.form-control::-ms-expand {
+ background-color: transparent;
+ border: 0;
+}
+
+.form-control:-moz-focusring {
+ color: transparent;
+ text-shadow: 0 0 0 #495057;
+}
+
+.form-control:focus {
+ color: #495057;
+ background-color: #fff;
+ border-color: #80bdff;
+ outline: 0;
+ box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
+}
+
+.form-control::-webkit-input-placeholder {
+ color: #6c757d;
+ opacity: 1;
+}
+
+.form-control::-moz-placeholder {
+ color: #6c757d;
+ opacity: 1;
+}
+
+.form-control:-ms-input-placeholder {
+ color: #6c757d;
+ opacity: 1;
+}
+
+.form-control::-ms-input-placeholder {
+ color: #6c757d;
+ opacity: 1;
+}
+
+.form-control::placeholder {
+ color: #6c757d;
+ opacity: 1;
+}
+
+.form-control:disabled, .form-control[readonly] {
+ background-color: #e9ecef;
+ opacity: 1;
+}
+
+select.form-control:focus::-ms-value {
+ color: #495057;
+ background-color: #fff;
+}
+
+.form-control-file,
+.form-control-range {
+ display: block;
+ width: 100%;
+}
+
+.col-form-label {
+ padding-top: calc(0.375rem + 1px);
+ padding-bottom: calc(0.375rem + 1px);
+ margin-bottom: 0;
+ font-size: inherit;
+ line-height: 1.5;
+}
+
+.col-form-label-lg {
+ padding-top: calc(0.5rem + 1px);
+ padding-bottom: calc(0.5rem + 1px);
+ font-size: 1.25rem;
+ line-height: 1.5;
+}
+
+.col-form-label-sm {
+ padding-top: calc(0.25rem + 1px);
+ padding-bottom: calc(0.25rem + 1px);
+ font-size: 0.875rem;
+ line-height: 1.5;
+}
+
+.form-control-plaintext {
+ display: block;
+ width: 100%;
+ padding: 0.375rem 0;
+ margin-bottom: 0;
+ font-size: 1rem;
+ line-height: 1.5;
+ color: #212529;
+ background-color: transparent;
+ border: solid transparent;
+ border-width: 1px 0;
+}
+
+.form-control-plaintext.form-control-sm, .form-control-plaintext.form-control-lg {
+ padding-right: 0;
+ padding-left: 0;
+}
+
+.form-control-sm {
+ height: calc(1.5em + 0.5rem + 2px);
+ padding: 0.25rem 0.5rem;
+ font-size: 0.875rem;
+ line-height: 1.5;
+ border-radius: 0.2rem;
+}
+
+.form-control-lg {
+ height: calc(1.5em + 1rem + 2px);
+ padding: 0.5rem 1rem;
+ font-size: 1.25rem;
+ line-height: 1.5;
+ border-radius: 0.3rem;
+}
+
+select.form-control[size], select.form-control[multiple] {
+ height: auto;
+}
+
+textarea.form-control {
+ height: auto;
+}
+
+.form-group {
+ margin-bottom: 1rem;
+}
+
+.form-text {
+ display: block;
+ margin-top: 0.25rem;
+}
+
+.form-row {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-wrap: wrap;
+ flex-wrap: wrap;
+ margin-right: -5px;
+ margin-left: -5px;
+}
+
+.form-row > .col,
+.form-row > [class*="col-"] {
+ padding-right: 5px;
+ padding-left: 5px;
+}
+
+.form-check {
+ position: relative;
+ display: block;
+ padding-left: 1.25rem;
+}
+
+.form-check-input {
+ position: absolute;
+ margin-top: 0.3rem;
+ margin-left: -1.25rem;
+}
+
+.form-check-input[disabled] ~ .form-check-label,
+.form-check-input:disabled ~ .form-check-label {
+ color: #6c757d;
+}
+
+.form-check-label {
+ margin-bottom: 0;
+}
+
+.form-check-inline {
+ display: -ms-inline-flexbox;
+ display: inline-flex;
+ -ms-flex-align: center;
+ align-items: center;
+ padding-left: 0;
+ margin-right: 0.75rem;
+}
+
+.form-check-inline .form-check-input {
+ position: static;
+ margin-top: 0;
+ margin-right: 0.3125rem;
+ margin-left: 0;
+}
+
+.valid-feedback {
+ display: none;
+ width: 100%;
+ margin-top: 0.25rem;
+ font-size: 80%;
+ color: #28a745;
+}
+
+.valid-tooltip {
+ position: absolute;
+ top: 100%;
+ z-index: 5;
+ display: none;
+ max-width: 100%;
+ padding: 0.25rem 0.5rem;
+ margin-top: .1rem;
+ font-size: 0.875rem;
+ line-height: 1.5;
+ color: #fff;
+ background-color: rgba(40, 167, 69, 0.9);
+ border-radius: 0.25rem;
+}
+
+.was-validated :valid ~ .valid-feedback,
+.was-validated :valid ~ .valid-tooltip,
+.is-valid ~ .valid-feedback,
+.is-valid ~ .valid-tooltip {
+ display: block;
+}
+
+.was-validated .form-control:valid, .form-control.is-valid {
+ border-color: #28a745;
+ padding-right: calc(1.5em + 0.75rem);
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");
+ background-repeat: no-repeat;
+ background-position: right calc(0.375em + 0.1875rem) center;
+ background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
+}
+
+.was-validated .form-control:valid:focus, .form-control.is-valid:focus {
+ border-color: #28a745;
+ box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25);
+}
+
+.was-validated textarea.form-control:valid, textarea.form-control.is-valid {
+ padding-right: calc(1.5em + 0.75rem);
+ background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem);
+}
+
+.was-validated .custom-select:valid, .custom-select.is-valid {
+ border-color: #28a745;
+ padding-right: calc(0.75em + 2.3125rem);
+ background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px, url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%2328a745' d='M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
+}
+
+.was-validated .custom-select:valid:focus, .custom-select.is-valid:focus {
+ border-color: #28a745;
+ box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25);
+}
+
+.was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label {
+ color: #28a745;
+}
+
+.was-validated .form-check-input:valid ~ .valid-feedback,
+.was-validated .form-check-input:valid ~ .valid-tooltip, .form-check-input.is-valid ~ .valid-feedback,
+.form-check-input.is-valid ~ .valid-tooltip {
+ display: block;
+}
+
+.was-validated .custom-control-input:valid ~ .custom-control-label, .custom-control-input.is-valid ~ .custom-control-label {
+ color: #28a745;
+}
+
+.was-validated .custom-control-input:valid ~ .custom-control-label::before, .custom-control-input.is-valid ~ .custom-control-label::before {
+ border-color: #28a745;
+}
+
+.was-validated .custom-control-input:valid:checked ~ .custom-control-label::before, .custom-control-input.is-valid:checked ~ .custom-control-label::before {
+ border-color: #34ce57;
+ background-color: #34ce57;
+}
+
+.was-validated .custom-control-input:valid:focus ~ .custom-control-label::before, .custom-control-input.is-valid:focus ~ .custom-control-label::before {
+ box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25);
+}
+
+.was-validated .custom-control-input:valid:focus:not(:checked) ~ .custom-control-label::before, .custom-control-input.is-valid:focus:not(:checked) ~ .custom-control-label::before {
+ border-color: #28a745;
+}
+
+.was-validated .custom-file-input:valid ~ .custom-file-label, .custom-file-input.is-valid ~ .custom-file-label {
+ border-color: #28a745;
+}
+
+.was-validated .custom-file-input:valid:focus ~ .custom-file-label, .custom-file-input.is-valid:focus ~ .custom-file-label {
+ border-color: #28a745;
+ box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25);
+}
+
+.invalid-feedback {
+ display: none;
+ width: 100%;
+ margin-top: 0.25rem;
+ font-size: 80%;
+ color: #dc3545;
+}
+
+.invalid-tooltip {
+ position: absolute;
+ top: 100%;
+ z-index: 5;
+ display: none;
+ max-width: 100%;
+ padding: 0.25rem 0.5rem;
+ margin-top: .1rem;
+ font-size: 0.875rem;
+ line-height: 1.5;
+ color: #fff;
+ background-color: rgba(220, 53, 69, 0.9);
+ border-radius: 0.25rem;
+}
+
+.was-validated :invalid ~ .invalid-feedback,
+.was-validated :invalid ~ .invalid-tooltip,
+.is-invalid ~ .invalid-feedback,
+.is-invalid ~ .invalid-tooltip {
+ display: block;
+}
+
+.was-validated .form-control:invalid, .form-control.is-invalid {
+ border-color: #dc3545;
+ padding-right: calc(1.5em + 0.75rem);
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");
+ background-repeat: no-repeat;
+ background-position: right calc(0.375em + 0.1875rem) center;
+ background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
+}
+
+.was-validated .form-control:invalid:focus, .form-control.is-invalid:focus {
+ border-color: #dc3545;
+ box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);
+}
+
+.was-validated textarea.form-control:invalid, textarea.form-control.is-invalid {
+ padding-right: calc(1.5em + 0.75rem);
+ background-position: top calc(0.375em + 0.1875rem) right calc(0.375em + 0.1875rem);
+}
+
+.was-validated .custom-select:invalid, .custom-select.is-invalid {
+ border-color: #dc3545;
+ padding-right: calc(0.75em + 2.3125rem);
+ background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px, url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e") #fff no-repeat center right 1.75rem/calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
+}
+
+.was-validated .custom-select:invalid:focus, .custom-select.is-invalid:focus {
+ border-color: #dc3545;
+ box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);
+}
+
+.was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label {
+ color: #dc3545;
+}
+
+.was-validated .form-check-input:invalid ~ .invalid-feedback,
+.was-validated .form-check-input:invalid ~ .invalid-tooltip, .form-check-input.is-invalid ~ .invalid-feedback,
+.form-check-input.is-invalid ~ .invalid-tooltip {
+ display: block;
+}
+
+.was-validated .custom-control-input:invalid ~ .custom-control-label, .custom-control-input.is-invalid ~ .custom-control-label {
+ color: #dc3545;
+}
+
+.was-validated .custom-control-input:invalid ~ .custom-control-label::before, .custom-control-input.is-invalid ~ .custom-control-label::before {
+ border-color: #dc3545;
+}
+
+.was-validated .custom-control-input:invalid:checked ~ .custom-control-label::before, .custom-control-input.is-invalid:checked ~ .custom-control-label::before {
+ border-color: #e4606d;
+ background-color: #e4606d;
+}
+
+.was-validated .custom-control-input:invalid:focus ~ .custom-control-label::before, .custom-control-input.is-invalid:focus ~ .custom-control-label::before {
+ box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);
+}
+
+.was-validated .custom-control-input:invalid:focus:not(:checked) ~ .custom-control-label::before, .custom-control-input.is-invalid:focus:not(:checked) ~ .custom-control-label::before {
+ border-color: #dc3545;
+}
+
+.was-validated .custom-file-input:invalid ~ .custom-file-label, .custom-file-input.is-invalid ~ .custom-file-label {
+ border-color: #dc3545;
+}
+
+.was-validated .custom-file-input:invalid:focus ~ .custom-file-label, .custom-file-input.is-invalid:focus ~ .custom-file-label {
+ border-color: #dc3545;
+ box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);
+}
+
+.form-inline {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-flow: row wrap;
+ flex-flow: row wrap;
+ -ms-flex-align: center;
+ align-items: center;
+}
+
+.form-inline .form-check {
+ width: 100%;
+}
+
+@media (min-width: 576px) {
+ .form-inline label {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-align: center;
+ align-items: center;
+ -ms-flex-pack: center;
+ justify-content: center;
+ margin-bottom: 0;
+ }
+ .form-inline .form-group {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex: 0 0 auto;
+ flex: 0 0 auto;
+ -ms-flex-flow: row wrap;
+ flex-flow: row wrap;
+ -ms-flex-align: center;
+ align-items: center;
+ margin-bottom: 0;
+ }
+ .form-inline .form-control {
+ display: inline-block;
+ width: auto;
+ vertical-align: middle;
+ }
+ .form-inline .form-control-plaintext {
+ display: inline-block;
+ }
+ .form-inline .input-group,
+ .form-inline .custom-select {
+ width: auto;
+ }
+ .form-inline .form-check {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-align: center;
+ align-items: center;
+ -ms-flex-pack: center;
+ justify-content: center;
+ width: auto;
+ padding-left: 0;
+ }
+ .form-inline .form-check-input {
+ position: relative;
+ -ms-flex-negative: 0;
+ flex-shrink: 0;
+ margin-top: 0;
+ margin-right: 0.25rem;
+ margin-left: 0;
+ }
+ .form-inline .custom-control {
+ -ms-flex-align: center;
+ align-items: center;
+ -ms-flex-pack: center;
+ justify-content: center;
+ }
+ .form-inline .custom-control-label {
+ margin-bottom: 0;
+ }
+}
+
+.btn {
+ display: inline-block;
+ font-weight: 400;
+ color: #212529;
+ text-align: center;
+ vertical-align: middle;
+ cursor: pointer;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ background-color: transparent;
+ border: 1px solid transparent;
+ padding: 0.375rem 0.75rem;
+ font-size: 1rem;
+ line-height: 1.5;
+ border-radius: 0.25rem;
+ transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .btn {
+ transition: none;
+ }
+}
+
+.btn:hover {
+ color: #212529;
+ text-decoration: none;
+}
+
+.btn:focus, .btn.focus {
+ outline: 0;
+ box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
+}
+
+.btn.disabled, .btn:disabled {
+ opacity: 0.65;
+}
+
+a.btn.disabled,
+fieldset:disabled a.btn {
+ pointer-events: none;
+}
+
+.btn-primary {
+ color: #fff;
+ background-color: #007bff;
+ border-color: #007bff;
+}
+
+.btn-primary:hover {
+ color: #fff;
+ background-color: #0069d9;
+ border-color: #0062cc;
+}
+
+.btn-primary:focus, .btn-primary.focus {
+ color: #fff;
+ background-color: #0069d9;
+ border-color: #0062cc;
+ box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5);
+}
+
+.btn-primary.disabled, .btn-primary:disabled {
+ color: #fff;
+ background-color: #007bff;
+ border-color: #007bff;
+}
+
+.btn-primary:not(:disabled):not(.disabled):active, .btn-primary:not(:disabled):not(.disabled).active,
+.show > .btn-primary.dropdown-toggle {
+ color: #fff;
+ background-color: #0062cc;
+ border-color: #005cbf;
+}
+
+.btn-primary:not(:disabled):not(.disabled):active:focus, .btn-primary:not(:disabled):not(.disabled).active:focus,
+.show > .btn-primary.dropdown-toggle:focus {
+ box-shadow: 0 0 0 0.2rem rgba(38, 143, 255, 0.5);
+}
+
+.btn-secondary {
+ color: #fff;
+ background-color: #6c757d;
+ border-color: #6c757d;
+}
+
+.btn-secondary:hover {
+ color: #fff;
+ background-color: #5a6268;
+ border-color: #545b62;
+}
+
+.btn-secondary:focus, .btn-secondary.focus {
+ color: #fff;
+ background-color: #5a6268;
+ border-color: #545b62;
+ box-shadow: 0 0 0 0.2rem rgba(130, 138, 145, 0.5);
+}
+
+.btn-secondary.disabled, .btn-secondary:disabled {
+ color: #fff;
+ background-color: #6c757d;
+ border-color: #6c757d;
+}
+
+.btn-secondary:not(:disabled):not(.disabled):active, .btn-secondary:not(:disabled):not(.disabled).active,
+.show > .btn-secondary.dropdown-toggle {
+ color: #fff;
+ background-color: #545b62;
+ border-color: #4e555b;
+}
+
+.btn-secondary:not(:disabled):not(.disabled):active:focus, .btn-secondary:not(:disabled):not(.disabled).active:focus,
+.show > .btn-secondary.dropdown-toggle:focus {
+ box-shadow: 0 0 0 0.2rem rgba(130, 138, 145, 0.5);
+}
+
+.btn-success {
+ color: #fff;
+ background-color: #28a745;
+ border-color: #28a745;
+}
+
+.btn-success:hover {
+ color: #fff;
+ background-color: #218838;
+ border-color: #1e7e34;
+}
+
+.btn-success:focus, .btn-success.focus {
+ color: #fff;
+ background-color: #218838;
+ border-color: #1e7e34;
+ box-shadow: 0 0 0 0.2rem rgba(72, 180, 97, 0.5);
+}
+
+.btn-success.disabled, .btn-success:disabled {
+ color: #fff;
+ background-color: #28a745;
+ border-color: #28a745;
+}
+
+.btn-success:not(:disabled):not(.disabled):active, .btn-success:not(:disabled):not(.disabled).active,
+.show > .btn-success.dropdown-toggle {
+ color: #fff;
+ background-color: #1e7e34;
+ border-color: #1c7430;
+}
+
+.btn-success:not(:disabled):not(.disabled):active:focus, .btn-success:not(:disabled):not(.disabled).active:focus,
+.show > .btn-success.dropdown-toggle:focus {
+ box-shadow: 0 0 0 0.2rem rgba(72, 180, 97, 0.5);
+}
+
+.btn-info {
+ color: #fff;
+ background-color: #17a2b8;
+ border-color: #17a2b8;
+}
+
+.btn-info:hover {
+ color: #fff;
+ background-color: #138496;
+ border-color: #117a8b;
+}
+
+.btn-info:focus, .btn-info.focus {
+ color: #fff;
+ background-color: #138496;
+ border-color: #117a8b;
+ box-shadow: 0 0 0 0.2rem rgba(58, 176, 195, 0.5);
+}
+
+.btn-info.disabled, .btn-info:disabled {
+ color: #fff;
+ background-color: #17a2b8;
+ border-color: #17a2b8;
+}
+
+.btn-info:not(:disabled):not(.disabled):active, .btn-info:not(:disabled):not(.disabled).active,
+.show > .btn-info.dropdown-toggle {
+ color: #fff;
+ background-color: #117a8b;
+ border-color: #10707f;
+}
+
+.btn-info:not(:disabled):not(.disabled):active:focus, .btn-info:not(:disabled):not(.disabled).active:focus,
+.show > .btn-info.dropdown-toggle:focus {
+ box-shadow: 0 0 0 0.2rem rgba(58, 176, 195, 0.5);
+}
+
+.btn-warning {
+ color: #212529;
+ background-color: #ffc107;
+ border-color: #ffc107;
+}
+
+.btn-warning:hover {
+ color: #212529;
+ background-color: #e0a800;
+ border-color: #d39e00;
+}
+
+.btn-warning:focus, .btn-warning.focus {
+ color: #212529;
+ background-color: #e0a800;
+ border-color: #d39e00;
+ box-shadow: 0 0 0 0.2rem rgba(222, 170, 12, 0.5);
+}
+
+.btn-warning.disabled, .btn-warning:disabled {
+ color: #212529;
+ background-color: #ffc107;
+ border-color: #ffc107;
+}
+
+.btn-warning:not(:disabled):not(.disabled):active, .btn-warning:not(:disabled):not(.disabled).active,
+.show > .btn-warning.dropdown-toggle {
+ color: #212529;
+ background-color: #d39e00;
+ border-color: #c69500;
+}
+
+.btn-warning:not(:disabled):not(.disabled):active:focus, .btn-warning:not(:disabled):not(.disabled).active:focus,
+.show > .btn-warning.dropdown-toggle:focus {
+ box-shadow: 0 0 0 0.2rem rgba(222, 170, 12, 0.5);
+}
+
+.btn-danger {
+ color: #fff;
+ background-color: #dc3545;
+ border-color: #dc3545;
+}
+
+.btn-danger:hover {
+ color: #fff;
+ background-color: #c82333;
+ border-color: #bd2130;
+}
+
+.btn-danger:focus, .btn-danger.focus {
+ color: #fff;
+ background-color: #c82333;
+ border-color: #bd2130;
+ box-shadow: 0 0 0 0.2rem rgba(225, 83, 97, 0.5);
+}
+
+.btn-danger.disabled, .btn-danger:disabled {
+ color: #fff;
+ background-color: #dc3545;
+ border-color: #dc3545;
+}
+
+.btn-danger:not(:disabled):not(.disabled):active, .btn-danger:not(:disabled):not(.disabled).active,
+.show > .btn-danger.dropdown-toggle {
+ color: #fff;
+ background-color: #bd2130;
+ border-color: #b21f2d;
+}
+
+.btn-danger:not(:disabled):not(.disabled):active:focus, .btn-danger:not(:disabled):not(.disabled).active:focus,
+.show > .btn-danger.dropdown-toggle:focus {
+ box-shadow: 0 0 0 0.2rem rgba(225, 83, 97, 0.5);
+}
+
+.btn-light {
+ color: #212529;
+ background-color: #f8f9fa;
+ border-color: #f8f9fa;
+}
+
+.btn-light:hover {
+ color: #212529;
+ background-color: #e2e6ea;
+ border-color: #dae0e5;
+}
+
+.btn-light:focus, .btn-light.focus {
+ color: #212529;
+ background-color: #e2e6ea;
+ border-color: #dae0e5;
+ box-shadow: 0 0 0 0.2rem rgba(216, 217, 219, 0.5);
+}
+
+.btn-light.disabled, .btn-light:disabled {
+ color: #212529;
+ background-color: #f8f9fa;
+ border-color: #f8f9fa;
+}
+
+.btn-light:not(:disabled):not(.disabled):active, .btn-light:not(:disabled):not(.disabled).active,
+.show > .btn-light.dropdown-toggle {
+ color: #212529;
+ background-color: #dae0e5;
+ border-color: #d3d9df;
+}
+
+.btn-light:not(:disabled):not(.disabled):active:focus, .btn-light:not(:disabled):not(.disabled).active:focus,
+.show > .btn-light.dropdown-toggle:focus {
+ box-shadow: 0 0 0 0.2rem rgba(216, 217, 219, 0.5);
+}
+
+.btn-dark {
+ color: #fff;
+ background-color: #343a40;
+ border-color: #343a40;
+}
+
+.btn-dark:hover {
+ color: #fff;
+ background-color: #23272b;
+ border-color: #1d2124;
+}
+
+.btn-dark:focus, .btn-dark.focus {
+ color: #fff;
+ background-color: #23272b;
+ border-color: #1d2124;
+ box-shadow: 0 0 0 0.2rem rgba(82, 88, 93, 0.5);
+}
+
+.btn-dark.disabled, .btn-dark:disabled {
+ color: #fff;
+ background-color: #343a40;
+ border-color: #343a40;
+}
+
+.btn-dark:not(:disabled):not(.disabled):active, .btn-dark:not(:disabled):not(.disabled).active,
+.show > .btn-dark.dropdown-toggle {
+ color: #fff;
+ background-color: #1d2124;
+ border-color: #171a1d;
+}
+
+.btn-dark:not(:disabled):not(.disabled):active:focus, .btn-dark:not(:disabled):not(.disabled).active:focus,
+.show > .btn-dark.dropdown-toggle:focus {
+ box-shadow: 0 0 0 0.2rem rgba(82, 88, 93, 0.5);
+}
+
+.btn-outline-primary {
+ color: #007bff;
+ border-color: #007bff;
+}
+
+.btn-outline-primary:hover {
+ color: #fff;
+ background-color: #007bff;
+ border-color: #007bff;
+}
+
+.btn-outline-primary:focus, .btn-outline-primary.focus {
+ box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);
+}
+
+.btn-outline-primary.disabled, .btn-outline-primary:disabled {
+ color: #007bff;
+ background-color: transparent;
+}
+
+.btn-outline-primary:not(:disabled):not(.disabled):active, .btn-outline-primary:not(:disabled):not(.disabled).active,
+.show > .btn-outline-primary.dropdown-toggle {
+ color: #fff;
+ background-color: #007bff;
+ border-color: #007bff;
+}
+
+.btn-outline-primary:not(:disabled):not(.disabled):active:focus, .btn-outline-primary:not(:disabled):not(.disabled).active:focus,
+.show > .btn-outline-primary.dropdown-toggle:focus {
+ box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);
+}
+
+.btn-outline-secondary {
+ color: #6c757d;
+ border-color: #6c757d;
+}
+
+.btn-outline-secondary:hover {
+ color: #fff;
+ background-color: #6c757d;
+ border-color: #6c757d;
+}
+
+.btn-outline-secondary:focus, .btn-outline-secondary.focus {
+ box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);
+}
+
+.btn-outline-secondary.disabled, .btn-outline-secondary:disabled {
+ color: #6c757d;
+ background-color: transparent;
+}
+
+.btn-outline-secondary:not(:disabled):not(.disabled):active, .btn-outline-secondary:not(:disabled):not(.disabled).active,
+.show > .btn-outline-secondary.dropdown-toggle {
+ color: #fff;
+ background-color: #6c757d;
+ border-color: #6c757d;
+}
+
+.btn-outline-secondary:not(:disabled):not(.disabled):active:focus, .btn-outline-secondary:not(:disabled):not(.disabled).active:focus,
+.show > .btn-outline-secondary.dropdown-toggle:focus {
+ box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);
+}
+
+.btn-outline-success {
+ color: #28a745;
+ border-color: #28a745;
+}
+
+.btn-outline-success:hover {
+ color: #fff;
+ background-color: #28a745;
+ border-color: #28a745;
+}
+
+.btn-outline-success:focus, .btn-outline-success.focus {
+ box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);
+}
+
+.btn-outline-success.disabled, .btn-outline-success:disabled {
+ color: #28a745;
+ background-color: transparent;
+}
+
+.btn-outline-success:not(:disabled):not(.disabled):active, .btn-outline-success:not(:disabled):not(.disabled).active,
+.show > .btn-outline-success.dropdown-toggle {
+ color: #fff;
+ background-color: #28a745;
+ border-color: #28a745;
+}
+
+.btn-outline-success:not(:disabled):not(.disabled):active:focus, .btn-outline-success:not(:disabled):not(.disabled).active:focus,
+.show > .btn-outline-success.dropdown-toggle:focus {
+ box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);
+}
+
+.btn-outline-info {
+ color: #17a2b8;
+ border-color: #17a2b8;
+}
+
+.btn-outline-info:hover {
+ color: #fff;
+ background-color: #17a2b8;
+ border-color: #17a2b8;
+}
+
+.btn-outline-info:focus, .btn-outline-info.focus {
+ box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);
+}
+
+.btn-outline-info.disabled, .btn-outline-info:disabled {
+ color: #17a2b8;
+ background-color: transparent;
+}
+
+.btn-outline-info:not(:disabled):not(.disabled):active, .btn-outline-info:not(:disabled):not(.disabled).active,
+.show > .btn-outline-info.dropdown-toggle {
+ color: #fff;
+ background-color: #17a2b8;
+ border-color: #17a2b8;
+}
+
+.btn-outline-info:not(:disabled):not(.disabled):active:focus, .btn-outline-info:not(:disabled):not(.disabled).active:focus,
+.show > .btn-outline-info.dropdown-toggle:focus {
+ box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);
+}
+
+.btn-outline-warning {
+ color: #ffc107;
+ border-color: #ffc107;
+}
+
+.btn-outline-warning:hover {
+ color: #212529;
+ background-color: #ffc107;
+ border-color: #ffc107;
+}
+
+.btn-outline-warning:focus, .btn-outline-warning.focus {
+ box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);
+}
+
+.btn-outline-warning.disabled, .btn-outline-warning:disabled {
+ color: #ffc107;
+ background-color: transparent;
+}
+
+.btn-outline-warning:not(:disabled):not(.disabled):active, .btn-outline-warning:not(:disabled):not(.disabled).active,
+.show > .btn-outline-warning.dropdown-toggle {
+ color: #212529;
+ background-color: #ffc107;
+ border-color: #ffc107;
+}
+
+.btn-outline-warning:not(:disabled):not(.disabled):active:focus, .btn-outline-warning:not(:disabled):not(.disabled).active:focus,
+.show > .btn-outline-warning.dropdown-toggle:focus {
+ box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);
+}
+
+.btn-outline-danger {
+ color: #dc3545;
+ border-color: #dc3545;
+}
+
+.btn-outline-danger:hover {
+ color: #fff;
+ background-color: #dc3545;
+ border-color: #dc3545;
+}
+
+.btn-outline-danger:focus, .btn-outline-danger.focus {
+ box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);
+}
+
+.btn-outline-danger.disabled, .btn-outline-danger:disabled {
+ color: #dc3545;
+ background-color: transparent;
+}
+
+.btn-outline-danger:not(:disabled):not(.disabled):active, .btn-outline-danger:not(:disabled):not(.disabled).active,
+.show > .btn-outline-danger.dropdown-toggle {
+ color: #fff;
+ background-color: #dc3545;
+ border-color: #dc3545;
+}
+
+.btn-outline-danger:not(:disabled):not(.disabled):active:focus, .btn-outline-danger:not(:disabled):not(.disabled).active:focus,
+.show > .btn-outline-danger.dropdown-toggle:focus {
+ box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);
+}
+
+.btn-outline-light {
+ color: #f8f9fa;
+ border-color: #f8f9fa;
+}
+
+.btn-outline-light:hover {
+ color: #212529;
+ background-color: #f8f9fa;
+ border-color: #f8f9fa;
+}
+
+.btn-outline-light:focus, .btn-outline-light.focus {
+ box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);
+}
+
+.btn-outline-light.disabled, .btn-outline-light:disabled {
+ color: #f8f9fa;
+ background-color: transparent;
+}
+
+.btn-outline-light:not(:disabled):not(.disabled):active, .btn-outline-light:not(:disabled):not(.disabled).active,
+.show > .btn-outline-light.dropdown-toggle {
+ color: #212529;
+ background-color: #f8f9fa;
+ border-color: #f8f9fa;
+}
+
+.btn-outline-light:not(:disabled):not(.disabled):active:focus, .btn-outline-light:not(:disabled):not(.disabled).active:focus,
+.show > .btn-outline-light.dropdown-toggle:focus {
+ box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);
+}
+
+.btn-outline-dark {
+ color: #343a40;
+ border-color: #343a40;
+}
+
+.btn-outline-dark:hover {
+ color: #fff;
+ background-color: #343a40;
+ border-color: #343a40;
+}
+
+.btn-outline-dark:focus, .btn-outline-dark.focus {
+ box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);
+}
+
+.btn-outline-dark.disabled, .btn-outline-dark:disabled {
+ color: #343a40;
+ background-color: transparent;
+}
+
+.btn-outline-dark:not(:disabled):not(.disabled):active, .btn-outline-dark:not(:disabled):not(.disabled).active,
+.show > .btn-outline-dark.dropdown-toggle {
+ color: #fff;
+ background-color: #343a40;
+ border-color: #343a40;
+}
+
+.btn-outline-dark:not(:disabled):not(.disabled):active:focus, .btn-outline-dark:not(:disabled):not(.disabled).active:focus,
+.show > .btn-outline-dark.dropdown-toggle:focus {
+ box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);
+}
+
+.btn-link {
+ font-weight: 400;
+ color: #007bff;
+ text-decoration: none;
+}
+
+.btn-link:hover {
+ color: #0056b3;
+ text-decoration: underline;
+}
+
+.btn-link:focus, .btn-link.focus {
+ text-decoration: underline;
+ box-shadow: none;
+}
+
+.btn-link:disabled, .btn-link.disabled {
+ color: #6c757d;
+ pointer-events: none;
+}
+
+.btn-lg, .btn-group-lg > .btn {
+ padding: 0.5rem 1rem;
+ font-size: 1.25rem;
+ line-height: 1.5;
+ border-radius: 0.3rem;
+}
+
+.btn-sm, .btn-group-sm > .btn {
+ padding: 0.25rem 0.5rem;
+ font-size: 0.875rem;
+ line-height: 1.5;
+ border-radius: 0.2rem;
+}
+
+.btn-block {
+ display: block;
+ width: 100%;
+}
+
+.btn-block + .btn-block {
+ margin-top: 0.5rem;
+}
+
+input[type="submit"].btn-block,
+input[type="reset"].btn-block,
+input[type="button"].btn-block {
+ width: 100%;
+}
+
+.fade {
+ transition: opacity 0.15s linear;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .fade {
+ transition: none;
+ }
+}
+
+.fade:not(.show) {
+ opacity: 0;
+}
+
+.collapse:not(.show) {
+ display: none;
+}
+
+.collapsing {
+ position: relative;
+ height: 0;
+ overflow: hidden;
+ transition: height 0.35s ease;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .collapsing {
+ transition: none;
+ }
+}
+
+.dropup,
+.dropright,
+.dropdown,
+.dropleft {
+ position: relative;
+}
+
+.dropdown-toggle {
+ white-space: nowrap;
+}
+
+.dropdown-toggle::after {
+ display: inline-block;
+ margin-left: 0.255em;
+ vertical-align: 0.255em;
+ content: "";
+ border-top: 0.3em solid;
+ border-right: 0.3em solid transparent;
+ border-bottom: 0;
+ border-left: 0.3em solid transparent;
+}
+
+.dropdown-toggle:empty::after {
+ margin-left: 0;
+}
+
+.dropdown-menu {
+ position: absolute;
+ top: 100%;
+ left: 0;
+ z-index: 1000;
+ display: none;
+ float: left;
+ min-width: 10rem;
+ padding: 0.5rem 0;
+ margin: 0.125rem 0 0;
+ font-size: 1rem;
+ color: #212529;
+ text-align: left;
+ list-style: none;
+ background-color: #fff;
+ background-clip: padding-box;
+ border: 1px solid rgba(0, 0, 0, 0.15);
+ border-radius: 0.25rem;
+}
+
+.dropdown-menu-left {
+ right: auto;
+ left: 0;
+}
+
+.dropdown-menu-right {
+ right: 0;
+ left: auto;
+}
+
+@media (min-width: 576px) {
+ .dropdown-menu-sm-left {
+ right: auto;
+ left: 0;
+ }
+ .dropdown-menu-sm-right {
+ right: 0;
+ left: auto;
+ }
+}
+
+@media (min-width: 768px) {
+ .dropdown-menu-md-left {
+ right: auto;
+ left: 0;
+ }
+ .dropdown-menu-md-right {
+ right: 0;
+ left: auto;
+ }
+}
+
+@media (min-width: 992px) {
+ .dropdown-menu-lg-left {
+ right: auto;
+ left: 0;
+ }
+ .dropdown-menu-lg-right {
+ right: 0;
+ left: auto;
+ }
+}
+
+@media (min-width: 1200px) {
+ .dropdown-menu-xl-left {
+ right: auto;
+ left: 0;
+ }
+ .dropdown-menu-xl-right {
+ right: 0;
+ left: auto;
+ }
+}
+
+.dropup .dropdown-menu {
+ top: auto;
+ bottom: 100%;
+ margin-top: 0;
+ margin-bottom: 0.125rem;
+}
+
+.dropup .dropdown-toggle::after {
+ display: inline-block;
+ margin-left: 0.255em;
+ vertical-align: 0.255em;
+ content: "";
+ border-top: 0;
+ border-right: 0.3em solid transparent;
+ border-bottom: 0.3em solid;
+ border-left: 0.3em solid transparent;
+}
+
+.dropup .dropdown-toggle:empty::after {
+ margin-left: 0;
+}
+
+.dropright .dropdown-menu {
+ top: 0;
+ right: auto;
+ left: 100%;
+ margin-top: 0;
+ margin-left: 0.125rem;
+}
+
+.dropright .dropdown-toggle::after {
+ display: inline-block;
+ margin-left: 0.255em;
+ vertical-align: 0.255em;
+ content: "";
+ border-top: 0.3em solid transparent;
+ border-right: 0;
+ border-bottom: 0.3em solid transparent;
+ border-left: 0.3em solid;
+}
+
+.dropright .dropdown-toggle:empty::after {
+ margin-left: 0;
+}
+
+.dropright .dropdown-toggle::after {
+ vertical-align: 0;
+}
+
+.dropleft .dropdown-menu {
+ top: 0;
+ right: 100%;
+ left: auto;
+ margin-top: 0;
+ margin-right: 0.125rem;
+}
+
+.dropleft .dropdown-toggle::after {
+ display: inline-block;
+ margin-left: 0.255em;
+ vertical-align: 0.255em;
+ content: "";
+}
+
+.dropleft .dropdown-toggle::after {
+ display: none;
+}
+
+.dropleft .dropdown-toggle::before {
+ display: inline-block;
+ margin-right: 0.255em;
+ vertical-align: 0.255em;
+ content: "";
+ border-top: 0.3em solid transparent;
+ border-right: 0.3em solid;
+ border-bottom: 0.3em solid transparent;
+}
+
+.dropleft .dropdown-toggle:empty::after {
+ margin-left: 0;
+}
+
+.dropleft .dropdown-toggle::before {
+ vertical-align: 0;
+}
+
+.dropdown-menu[x-placement^="top"], .dropdown-menu[x-placement^="right"], .dropdown-menu[x-placement^="bottom"], .dropdown-menu[x-placement^="left"] {
+ right: auto;
+ bottom: auto;
+}
+
+.dropdown-divider {
+ height: 0;
+ margin: 0.5rem 0;
+ overflow: hidden;
+ border-top: 1px solid #e9ecef;
+}
+
+.dropdown-item {
+ display: block;
+ width: 100%;
+ padding: 0.25rem 1.5rem;
+ clear: both;
+ font-weight: 400;
+ color: #212529;
+ text-align: inherit;
+ white-space: nowrap;
+ background-color: transparent;
+ border: 0;
+}
+
+.dropdown-item:hover, .dropdown-item:focus {
+ color: #16181b;
+ text-decoration: none;
+ background-color: #f8f9fa;
+}
+
+.dropdown-item.active, .dropdown-item:active {
+ color: #fff;
+ text-decoration: none;
+ background-color: #007bff;
+}
+
+.dropdown-item.disabled, .dropdown-item:disabled {
+ color: #6c757d;
+ pointer-events: none;
+ background-color: transparent;
+}
+
+.dropdown-menu.show {
+ display: block;
+}
+
+.dropdown-header {
+ display: block;
+ padding: 0.5rem 1.5rem;
+ margin-bottom: 0;
+ font-size: 0.875rem;
+ color: #6c757d;
+ white-space: nowrap;
+}
+
+.dropdown-item-text {
+ display: block;
+ padding: 0.25rem 1.5rem;
+ color: #212529;
+}
+
+.btn-group,
+.btn-group-vertical {
+ position: relative;
+ display: -ms-inline-flexbox;
+ display: inline-flex;
+ vertical-align: middle;
+}
+
+.btn-group > .btn,
+.btn-group-vertical > .btn {
+ position: relative;
+ -ms-flex: 1 1 auto;
+ flex: 1 1 auto;
+}
+
+.btn-group > .btn:hover,
+.btn-group-vertical > .btn:hover {
+ z-index: 1;
+}
+
+.btn-group > .btn:focus, .btn-group > .btn:active, .btn-group > .btn.active,
+.btn-group-vertical > .btn:focus,
+.btn-group-vertical > .btn:active,
+.btn-group-vertical > .btn.active {
+ z-index: 1;
+}
+
+.btn-toolbar {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-wrap: wrap;
+ flex-wrap: wrap;
+ -ms-flex-pack: start;
+ justify-content: flex-start;
+}
+
+.btn-toolbar .input-group {
+ width: auto;
+}
+
+.btn-group > .btn:not(:first-child),
+.btn-group > .btn-group:not(:first-child) {
+ margin-left: -1px;
+}
+
+.btn-group > .btn:not(:last-child):not(.dropdown-toggle),
+.btn-group > .btn-group:not(:last-child) > .btn {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+}
+
+.btn-group > .btn:not(:first-child),
+.btn-group > .btn-group:not(:first-child) > .btn {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+}
+
+.dropdown-toggle-split {
+ padding-right: 0.5625rem;
+ padding-left: 0.5625rem;
+}
+
+.dropdown-toggle-split::after,
+.dropup .dropdown-toggle-split::after,
+.dropright .dropdown-toggle-split::after {
+ margin-left: 0;
+}
+
+.dropleft .dropdown-toggle-split::before {
+ margin-right: 0;
+}
+
+.btn-sm + .dropdown-toggle-split, .btn-group-sm > .btn + .dropdown-toggle-split {
+ padding-right: 0.375rem;
+ padding-left: 0.375rem;
+}
+
+.btn-lg + .dropdown-toggle-split, .btn-group-lg > .btn + .dropdown-toggle-split {
+ padding-right: 0.75rem;
+ padding-left: 0.75rem;
+}
+
+.btn-group-vertical {
+ -ms-flex-direction: column;
+ flex-direction: column;
+ -ms-flex-align: start;
+ align-items: flex-start;
+ -ms-flex-pack: center;
+ justify-content: center;
+}
+
+.btn-group-vertical > .btn,
+.btn-group-vertical > .btn-group {
+ width: 100%;
+}
+
+.btn-group-vertical > .btn:not(:first-child),
+.btn-group-vertical > .btn-group:not(:first-child) {
+ margin-top: -1px;
+}
+
+.btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle),
+.btn-group-vertical > .btn-group:not(:last-child) > .btn {
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0;
+}
+
+.btn-group-vertical > .btn:not(:first-child),
+.btn-group-vertical > .btn-group:not(:first-child) > .btn {
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+}
+
+.btn-group-toggle > .btn,
+.btn-group-toggle > .btn-group > .btn {
+ margin-bottom: 0;
+}
+
+.btn-group-toggle > .btn input[type="radio"],
+.btn-group-toggle > .btn input[type="checkbox"],
+.btn-group-toggle > .btn-group > .btn input[type="radio"],
+.btn-group-toggle > .btn-group > .btn input[type="checkbox"] {
+ position: absolute;
+ clip: rect(0, 0, 0, 0);
+ pointer-events: none;
+}
+
+.input-group {
+ position: relative;
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-wrap: wrap;
+ flex-wrap: wrap;
+ -ms-flex-align: stretch;
+ align-items: stretch;
+ width: 100%;
+}
+
+.input-group > .form-control,
+.input-group > .form-control-plaintext,
+.input-group > .custom-select,
+.input-group > .custom-file {
+ position: relative;
+ -ms-flex: 1 1 0%;
+ flex: 1 1 0%;
+ min-width: 0;
+ margin-bottom: 0;
+}
+
+.input-group > .form-control + .form-control,
+.input-group > .form-control + .custom-select,
+.input-group > .form-control + .custom-file,
+.input-group > .form-control-plaintext + .form-control,
+.input-group > .form-control-plaintext + .custom-select,
+.input-group > .form-control-plaintext + .custom-file,
+.input-group > .custom-select + .form-control,
+.input-group > .custom-select + .custom-select,
+.input-group > .custom-select + .custom-file,
+.input-group > .custom-file + .form-control,
+.input-group > .custom-file + .custom-select,
+.input-group > .custom-file + .custom-file {
+ margin-left: -1px;
+}
+
+.input-group > .form-control:focus,
+.input-group > .custom-select:focus,
+.input-group > .custom-file .custom-file-input:focus ~ .custom-file-label {
+ z-index: 3;
+}
+
+.input-group > .custom-file .custom-file-input:focus {
+ z-index: 4;
+}
+
+.input-group > .form-control:not(:last-child),
+.input-group > .custom-select:not(:last-child) {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+}
+
+.input-group > .form-control:not(:first-child),
+.input-group > .custom-select:not(:first-child) {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+}
+
+.input-group > .custom-file {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-align: center;
+ align-items: center;
+}
+
+.input-group > .custom-file:not(:last-child) .custom-file-label,
+.input-group > .custom-file:not(:last-child) .custom-file-label::after {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+}
+
+.input-group > .custom-file:not(:first-child) .custom-file-label {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+}
+
+.input-group-prepend,
+.input-group-append {
+ display: -ms-flexbox;
+ display: flex;
+}
+
+.input-group-prepend .btn,
+.input-group-append .btn {
+ position: relative;
+ z-index: 2;
+}
+
+.input-group-prepend .btn:focus,
+.input-group-append .btn:focus {
+ z-index: 3;
+}
+
+.input-group-prepend .btn + .btn,
+.input-group-prepend .btn + .input-group-text,
+.input-group-prepend .input-group-text + .input-group-text,
+.input-group-prepend .input-group-text + .btn,
+.input-group-append .btn + .btn,
+.input-group-append .btn + .input-group-text,
+.input-group-append .input-group-text + .input-group-text,
+.input-group-append .input-group-text + .btn {
+ margin-left: -1px;
+}
+
+.input-group-prepend {
+ margin-right: -1px;
+}
+
+.input-group-append {
+ margin-left: -1px;
+}
+
+.input-group-text {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-align: center;
+ align-items: center;
+ padding: 0.375rem 0.75rem;
+ margin-bottom: 0;
+ font-size: 1rem;
+ font-weight: 400;
+ line-height: 1.5;
+ color: #495057;
+ text-align: center;
+ white-space: nowrap;
+ background-color: #e9ecef;
+ border: 1px solid #ced4da;
+ border-radius: 0.25rem;
+}
+
+.input-group-text input[type="radio"],
+.input-group-text input[type="checkbox"] {
+ margin-top: 0;
+}
+
+.input-group-lg > .form-control:not(textarea),
+.input-group-lg > .custom-select {
+ height: calc(1.5em + 1rem + 2px);
+}
+
+.input-group-lg > .form-control,
+.input-group-lg > .custom-select,
+.input-group-lg > .input-group-prepend > .input-group-text,
+.input-group-lg > .input-group-append > .input-group-text,
+.input-group-lg > .input-group-prepend > .btn,
+.input-group-lg > .input-group-append > .btn {
+ padding: 0.5rem 1rem;
+ font-size: 1.25rem;
+ line-height: 1.5;
+ border-radius: 0.3rem;
+}
+
+.input-group-sm > .form-control:not(textarea),
+.input-group-sm > .custom-select {
+ height: calc(1.5em + 0.5rem + 2px);
+}
+
+.input-group-sm > .form-control,
+.input-group-sm > .custom-select,
+.input-group-sm > .input-group-prepend > .input-group-text,
+.input-group-sm > .input-group-append > .input-group-text,
+.input-group-sm > .input-group-prepend > .btn,
+.input-group-sm > .input-group-append > .btn {
+ padding: 0.25rem 0.5rem;
+ font-size: 0.875rem;
+ line-height: 1.5;
+ border-radius: 0.2rem;
+}
+
+.input-group-lg > .custom-select,
+.input-group-sm > .custom-select {
+ padding-right: 1.75rem;
+}
+
+.input-group > .input-group-prepend > .btn,
+.input-group > .input-group-prepend > .input-group-text,
+.input-group > .input-group-append:not(:last-child) > .btn,
+.input-group > .input-group-append:not(:last-child) > .input-group-text,
+.input-group > .input-group-append:last-child > .btn:not(:last-child):not(.dropdown-toggle),
+.input-group > .input-group-append:last-child > .input-group-text:not(:last-child) {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+}
+
+.input-group > .input-group-append > .btn,
+.input-group > .input-group-append > .input-group-text,
+.input-group > .input-group-prepend:not(:first-child) > .btn,
+.input-group > .input-group-prepend:not(:first-child) > .input-group-text,
+.input-group > .input-group-prepend:first-child > .btn:not(:first-child),
+.input-group > .input-group-prepend:first-child > .input-group-text:not(:first-child) {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+}
+
+.custom-control {
+ position: relative;
+ display: block;
+ min-height: 1.5rem;
+ padding-left: 1.5rem;
+}
+
+.custom-control-inline {
+ display: -ms-inline-flexbox;
+ display: inline-flex;
+ margin-right: 1rem;
+}
+
+.custom-control-input {
+ position: absolute;
+ left: 0;
+ z-index: -1;
+ width: 1rem;
+ height: 1.25rem;
+ opacity: 0;
+}
+
+.custom-control-input:checked ~ .custom-control-label::before {
+ color: #fff;
+ border-color: #007bff;
+ background-color: #007bff;
+}
+
+.custom-control-input:focus ~ .custom-control-label::before {
+ box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
+}
+
+.custom-control-input:focus:not(:checked) ~ .custom-control-label::before {
+ border-color: #80bdff;
+}
+
+.custom-control-input:not(:disabled):active ~ .custom-control-label::before {
+ color: #fff;
+ background-color: #b3d7ff;
+ border-color: #b3d7ff;
+}
+
+.custom-control-input[disabled] ~ .custom-control-label, .custom-control-input:disabled ~ .custom-control-label {
+ color: #6c757d;
+}
+
+.custom-control-input[disabled] ~ .custom-control-label::before, .custom-control-input:disabled ~ .custom-control-label::before {
+ background-color: #e9ecef;
+}
+
+.custom-control-label {
+ position: relative;
+ margin-bottom: 0;
+ vertical-align: top;
+}
+
+.custom-control-label::before {
+ position: absolute;
+ top: 0.25rem;
+ left: -1.5rem;
+ display: block;
+ width: 1rem;
+ height: 1rem;
+ pointer-events: none;
+ content: "";
+ background-color: #fff;
+ border: #adb5bd solid 1px;
+}
+
+.custom-control-label::after {
+ position: absolute;
+ top: 0.25rem;
+ left: -1.5rem;
+ display: block;
+ width: 1rem;
+ height: 1rem;
+ content: "";
+ background: no-repeat 50% / 50% 50%;
+}
+
+.custom-checkbox .custom-control-label::before {
+ border-radius: 0.25rem;
+}
+
+.custom-checkbox .custom-control-input:checked ~ .custom-control-label::after {
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26l2.974 2.99L8 2.193z'/%3e%3c/svg%3e");
+}
+
+.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::before {
+ border-color: #007bff;
+ background-color: #007bff;
+}
+
+.custom-checkbox .custom-control-input:indeterminate ~ .custom-control-label::after {
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 4'%3e%3cpath stroke='%23fff' d='M0 2h4'/%3e%3c/svg%3e");
+}
+
+.custom-checkbox .custom-control-input:disabled:checked ~ .custom-control-label::before {
+ background-color: rgba(0, 123, 255, 0.5);
+}
+
+.custom-checkbox .custom-control-input:disabled:indeterminate ~ .custom-control-label::before {
+ background-color: rgba(0, 123, 255, 0.5);
+}
+
+.custom-radio .custom-control-label::before {
+ border-radius: 50%;
+}
+
+.custom-radio .custom-control-input:checked ~ .custom-control-label::after {
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e");
+}
+
+.custom-radio .custom-control-input:disabled:checked ~ .custom-control-label::before {
+ background-color: rgba(0, 123, 255, 0.5);
+}
+
+.custom-switch {
+ padding-left: 2.25rem;
+}
+
+.custom-switch .custom-control-label::before {
+ left: -2.25rem;
+ width: 1.75rem;
+ pointer-events: all;
+ border-radius: 0.5rem;
+}
+
+.custom-switch .custom-control-label::after {
+ top: calc(0.25rem + 2px);
+ left: calc(-2.25rem + 2px);
+ width: calc(1rem - 4px);
+ height: calc(1rem - 4px);
+ background-color: #adb5bd;
+ border-radius: 0.5rem;
+ transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-transform 0.15s ease-in-out;
+ transition: transform 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
+ transition: transform 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, -webkit-transform 0.15s ease-in-out;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .custom-switch .custom-control-label::after {
+ transition: none;
+ }
+}
+
+.custom-switch .custom-control-input:checked ~ .custom-control-label::after {
+ background-color: #fff;
+ -webkit-transform: translateX(0.75rem);
+ transform: translateX(0.75rem);
+}
+
+.custom-switch .custom-control-input:disabled:checked ~ .custom-control-label::before {
+ background-color: rgba(0, 123, 255, 0.5);
+}
+
+.custom-select {
+ display: inline-block;
+ width: 100%;
+ height: calc(1.5em + 0.75rem + 2px);
+ padding: 0.375rem 1.75rem 0.375rem 0.75rem;
+ font-size: 1rem;
+ font-weight: 400;
+ line-height: 1.5;
+ color: #495057;
+ vertical-align: middle;
+ background: #fff url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 5'%3e%3cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3e%3c/svg%3e") no-repeat right 0.75rem center/8px 10px;
+ border: 1px solid #ced4da;
+ border-radius: 0.25rem;
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ appearance: none;
+}
+
+.custom-select:focus {
+ border-color: #80bdff;
+ outline: 0;
+ box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
+}
+
+.custom-select:focus::-ms-value {
+ color: #495057;
+ background-color: #fff;
+}
+
+.custom-select[multiple], .custom-select[size]:not([size="1"]) {
+ height: auto;
+ padding-right: 0.75rem;
+ background-image: none;
+}
+
+.custom-select:disabled {
+ color: #6c757d;
+ background-color: #e9ecef;
+}
+
+.custom-select::-ms-expand {
+ display: none;
+}
+
+.custom-select:-moz-focusring {
+ color: transparent;
+ text-shadow: 0 0 0 #495057;
+}
+
+.custom-select-sm {
+ height: calc(1.5em + 0.5rem + 2px);
+ padding-top: 0.25rem;
+ padding-bottom: 0.25rem;
+ padding-left: 0.5rem;
+ font-size: 0.875rem;
+}
+
+.custom-select-lg {
+ height: calc(1.5em + 1rem + 2px);
+ padding-top: 0.5rem;
+ padding-bottom: 0.5rem;
+ padding-left: 1rem;
+ font-size: 1.25rem;
+}
+
+.custom-file {
+ position: relative;
+ display: inline-block;
+ width: 100%;
+ height: calc(1.5em + 0.75rem + 2px);
+ margin-bottom: 0;
+}
+
+.custom-file-input {
+ position: relative;
+ z-index: 2;
+ width: 100%;
+ height: calc(1.5em + 0.75rem + 2px);
+ margin: 0;
+ opacity: 0;
+}
+
+.custom-file-input:focus ~ .custom-file-label {
+ border-color: #80bdff;
+ box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
+}
+
+.custom-file-input[disabled] ~ .custom-file-label,
+.custom-file-input:disabled ~ .custom-file-label {
+ background-color: #e9ecef;
+}
+
+.custom-file-input:lang(en) ~ .custom-file-label::after {
+ content: "Browse";
+}
+
+.custom-file-input ~ .custom-file-label[data-browse]::after {
+ content: attr(data-browse);
+}
+
+.custom-file-label {
+ position: absolute;
+ top: 0;
+ right: 0;
+ left: 0;
+ z-index: 1;
+ height: calc(1.5em + 0.75rem + 2px);
+ padding: 0.375rem 0.75rem;
+ font-weight: 400;
+ line-height: 1.5;
+ color: #495057;
+ background-color: #fff;
+ border: 1px solid #ced4da;
+ border-radius: 0.25rem;
+}
+
+.custom-file-label::after {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ z-index: 3;
+ display: block;
+ height: calc(1.5em + 0.75rem);
+ padding: 0.375rem 0.75rem;
+ line-height: 1.5;
+ color: #495057;
+ content: "Browse";
+ background-color: #e9ecef;
+ border-left: inherit;
+ border-radius: 0 0.25rem 0.25rem 0;
+}
+
+.custom-range {
+ width: 100%;
+ height: 1.4rem;
+ padding: 0;
+ background-color: transparent;
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ appearance: none;
+}
+
+.custom-range:focus {
+ outline: none;
+}
+
+.custom-range:focus::-webkit-slider-thumb {
+ box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
+}
+
+.custom-range:focus::-moz-range-thumb {
+ box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
+}
+
+.custom-range:focus::-ms-thumb {
+ box-shadow: 0 0 0 1px #fff, 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
+}
+
+.custom-range::-moz-focus-outer {
+ border: 0;
+}
+
+.custom-range::-webkit-slider-thumb {
+ width: 1rem;
+ height: 1rem;
+ margin-top: -0.25rem;
+ background-color: #007bff;
+ border: 0;
+ border-radius: 1rem;
+ -webkit-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
+ transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
+ -webkit-appearance: none;
+ appearance: none;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .custom-range::-webkit-slider-thumb {
+ -webkit-transition: none;
+ transition: none;
+ }
+}
+
+.custom-range::-webkit-slider-thumb:active {
+ background-color: #b3d7ff;
+}
+
+.custom-range::-webkit-slider-runnable-track {
+ width: 100%;
+ height: 0.5rem;
+ color: transparent;
+ cursor: pointer;
+ background-color: #dee2e6;
+ border-color: transparent;
+ border-radius: 1rem;
+}
+
+.custom-range::-moz-range-thumb {
+ width: 1rem;
+ height: 1rem;
+ background-color: #007bff;
+ border: 0;
+ border-radius: 1rem;
+ -moz-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
+ transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
+ -moz-appearance: none;
+ appearance: none;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .custom-range::-moz-range-thumb {
+ -moz-transition: none;
+ transition: none;
+ }
+}
+
+.custom-range::-moz-range-thumb:active {
+ background-color: #b3d7ff;
+}
+
+.custom-range::-moz-range-track {
+ width: 100%;
+ height: 0.5rem;
+ color: transparent;
+ cursor: pointer;
+ background-color: #dee2e6;
+ border-color: transparent;
+ border-radius: 1rem;
+}
+
+.custom-range::-ms-thumb {
+ width: 1rem;
+ height: 1rem;
+ margin-top: 0;
+ margin-right: 0.2rem;
+ margin-left: 0.2rem;
+ background-color: #007bff;
+ border: 0;
+ border-radius: 1rem;
+ -ms-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
+ transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
+ appearance: none;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .custom-range::-ms-thumb {
+ -ms-transition: none;
+ transition: none;
+ }
+}
+
+.custom-range::-ms-thumb:active {
+ background-color: #b3d7ff;
+}
+
+.custom-range::-ms-track {
+ width: 100%;
+ height: 0.5rem;
+ color: transparent;
+ cursor: pointer;
+ background-color: transparent;
+ border-color: transparent;
+ border-width: 0.5rem;
+}
+
+.custom-range::-ms-fill-lower {
+ background-color: #dee2e6;
+ border-radius: 1rem;
+}
+
+.custom-range::-ms-fill-upper {
+ margin-right: 15px;
+ background-color: #dee2e6;
+ border-radius: 1rem;
+}
+
+.custom-range:disabled::-webkit-slider-thumb {
+ background-color: #adb5bd;
+}
+
+.custom-range:disabled::-webkit-slider-runnable-track {
+ cursor: default;
+}
+
+.custom-range:disabled::-moz-range-thumb {
+ background-color: #adb5bd;
+}
+
+.custom-range:disabled::-moz-range-track {
+ cursor: default;
+}
+
+.custom-range:disabled::-ms-thumb {
+ background-color: #adb5bd;
+}
+
+.custom-control-label::before,
+.custom-file-label,
+.custom-select {
+ transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .custom-control-label::before,
+ .custom-file-label,
+ .custom-select {
+ transition: none;
+ }
+}
+
+.nav {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-wrap: wrap;
+ flex-wrap: wrap;
+ padding-left: 0;
+ margin-bottom: 0;
+ list-style: none;
+}
+
+.nav-link {
+ display: block;
+ padding: 0.5rem 1rem;
+}
+
+.nav-link:hover, .nav-link:focus {
+ text-decoration: none;
+}
+
+.nav-link.disabled {
+ color: #6c757d;
+ pointer-events: none;
+ cursor: default;
+}
+
+.nav-tabs {
+ border-bottom: 1px solid #dee2e6;
+}
+
+.nav-tabs .nav-item {
+ margin-bottom: -1px;
+}
+
+.nav-tabs .nav-link {
+ border: 1px solid transparent;
+ border-top-left-radius: 0.25rem;
+ border-top-right-radius: 0.25rem;
+}
+
+.nav-tabs .nav-link:hover, .nav-tabs .nav-link:focus {
+ border-color: #e9ecef #e9ecef #dee2e6;
+}
+
+.nav-tabs .nav-link.disabled {
+ color: #6c757d;
+ background-color: transparent;
+ border-color: transparent;
+}
+
+.nav-tabs .nav-link.active,
+.nav-tabs .nav-item.show .nav-link {
+ color: #495057;
+ background-color: #fff;
+ border-color: #dee2e6 #dee2e6 #fff;
+}
+
+.nav-tabs .dropdown-menu {
+ margin-top: -1px;
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+}
+
+.nav-pills .nav-link {
+ border-radius: 0.25rem;
+}
+
+.nav-pills .nav-link.active,
+.nav-pills .show > .nav-link {
+ color: #fff;
+ background-color: #007bff;
+}
+
+.nav-fill .nav-item {
+ -ms-flex: 1 1 auto;
+ flex: 1 1 auto;
+ text-align: center;
+}
+
+.nav-justified .nav-item {
+ -ms-flex-preferred-size: 0;
+ flex-basis: 0;
+ -ms-flex-positive: 1;
+ flex-grow: 1;
+ text-align: center;
+}
+
+.tab-content > .tab-pane {
+ display: none;
+}
+
+.tab-content > .active {
+ display: block;
+}
+
+.navbar {
+ position: relative;
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-wrap: wrap;
+ flex-wrap: wrap;
+ -ms-flex-align: center;
+ align-items: center;
+ -ms-flex-pack: justify;
+ justify-content: space-between;
+ padding: 0.5rem 1rem;
+}
+
+.navbar .container,
+.navbar .container-fluid, .navbar .container-sm, .navbar .container-md, .navbar .container-lg, .navbar .container-xl {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-wrap: wrap;
+ flex-wrap: wrap;
+ -ms-flex-align: center;
+ align-items: center;
+ -ms-flex-pack: justify;
+ justify-content: space-between;
+}
+
+.navbar-brand {
+ display: inline-block;
+ padding-top: 0.3125rem;
+ padding-bottom: 0.3125rem;
+ margin-right: 1rem;
+ font-size: 1.25rem;
+ line-height: inherit;
+ white-space: nowrap;
+}
+
+.navbar-brand:hover, .navbar-brand:focus {
+ text-decoration: none;
+}
+
+.navbar-nav {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-direction: column;
+ flex-direction: column;
+ padding-left: 0;
+ margin-bottom: 0;
+ list-style: none;
+}
+
+.navbar-nav .nav-link {
+ padding-right: 0;
+ padding-left: 0;
+}
+
+.navbar-nav .dropdown-menu {
+ position: static;
+ float: none;
+}
+
+.navbar-text {
+ display: inline-block;
+ padding-top: 0.5rem;
+ padding-bottom: 0.5rem;
+}
+
+.navbar-collapse {
+ -ms-flex-preferred-size: 100%;
+ flex-basis: 100%;
+ -ms-flex-positive: 1;
+ flex-grow: 1;
+ -ms-flex-align: center;
+ align-items: center;
+}
+
+.navbar-toggler {
+ padding: 0.25rem 0.75rem;
+ font-size: 1.25rem;
+ line-height: 1;
+ background-color: transparent;
+ border: 1px solid transparent;
+ border-radius: 0.25rem;
+}
+
+.navbar-toggler:hover, .navbar-toggler:focus {
+ text-decoration: none;
+}
+
+.navbar-toggler-icon {
+ display: inline-block;
+ width: 1.5em;
+ height: 1.5em;
+ vertical-align: middle;
+ content: "";
+ background: no-repeat center center;
+ background-size: 100% 100%;
+}
+
+@media (max-width: 575.98px) {
+ .navbar-expand-sm > .container,
+ .navbar-expand-sm > .container-fluid, .navbar-expand-sm > .container-sm, .navbar-expand-sm > .container-md, .navbar-expand-sm > .container-lg, .navbar-expand-sm > .container-xl {
+ padding-right: 0;
+ padding-left: 0;
+ }
+}
+
+@media (min-width: 576px) {
+ .navbar-expand-sm {
+ -ms-flex-flow: row nowrap;
+ flex-flow: row nowrap;
+ -ms-flex-pack: start;
+ justify-content: flex-start;
+ }
+ .navbar-expand-sm .navbar-nav {
+ -ms-flex-direction: row;
+ flex-direction: row;
+ }
+ .navbar-expand-sm .navbar-nav .dropdown-menu {
+ position: absolute;
+ }
+ .navbar-expand-sm .navbar-nav .nav-link {
+ padding-right: 0.5rem;
+ padding-left: 0.5rem;
+ }
+ .navbar-expand-sm > .container,
+ .navbar-expand-sm > .container-fluid, .navbar-expand-sm > .container-sm, .navbar-expand-sm > .container-md, .navbar-expand-sm > .container-lg, .navbar-expand-sm > .container-xl {
+ -ms-flex-wrap: nowrap;
+ flex-wrap: nowrap;
+ }
+ .navbar-expand-sm .navbar-collapse {
+ display: -ms-flexbox !important;
+ display: flex !important;
+ -ms-flex-preferred-size: auto;
+ flex-basis: auto;
+ }
+ .navbar-expand-sm .navbar-toggler {
+ display: none;
+ }
+}
+
+@media (max-width: 767.98px) {
+ .navbar-expand-md > .container,
+ .navbar-expand-md > .container-fluid, .navbar-expand-md > .container-sm, .navbar-expand-md > .container-md, .navbar-expand-md > .container-lg, .navbar-expand-md > .container-xl {
+ padding-right: 0;
+ padding-left: 0;
+ }
+}
+
+@media (min-width: 768px) {
+ .navbar-expand-md {
+ -ms-flex-flow: row nowrap;
+ flex-flow: row nowrap;
+ -ms-flex-pack: start;
+ justify-content: flex-start;
+ }
+ .navbar-expand-md .navbar-nav {
+ -ms-flex-direction: row;
+ flex-direction: row;
+ }
+ .navbar-expand-md .navbar-nav .dropdown-menu {
+ position: absolute;
+ }
+ .navbar-expand-md .navbar-nav .nav-link {
+ padding-right: 0.5rem;
+ padding-left: 0.5rem;
+ }
+ .navbar-expand-md > .container,
+ .navbar-expand-md > .container-fluid, .navbar-expand-md > .container-sm, .navbar-expand-md > .container-md, .navbar-expand-md > .container-lg, .navbar-expand-md > .container-xl {
+ -ms-flex-wrap: nowrap;
+ flex-wrap: nowrap;
+ }
+ .navbar-expand-md .navbar-collapse {
+ display: -ms-flexbox !important;
+ display: flex !important;
+ -ms-flex-preferred-size: auto;
+ flex-basis: auto;
+ }
+ .navbar-expand-md .navbar-toggler {
+ display: none;
+ }
+}
+
+@media (max-width: 991.98px) {
+ .navbar-expand-lg > .container,
+ .navbar-expand-lg > .container-fluid, .navbar-expand-lg > .container-sm, .navbar-expand-lg > .container-md, .navbar-expand-lg > .container-lg, .navbar-expand-lg > .container-xl {
+ padding-right: 0;
+ padding-left: 0;
+ }
+}
+
+@media (min-width: 992px) {
+ .navbar-expand-lg {
+ -ms-flex-flow: row nowrap;
+ flex-flow: row nowrap;
+ -ms-flex-pack: start;
+ justify-content: flex-start;
+ }
+ .navbar-expand-lg .navbar-nav {
+ -ms-flex-direction: row;
+ flex-direction: row;
+ }
+ .navbar-expand-lg .navbar-nav .dropdown-menu {
+ position: absolute;
+ }
+ .navbar-expand-lg .navbar-nav .nav-link {
+ padding-right: 0.5rem;
+ padding-left: 0.5rem;
+ }
+ .navbar-expand-lg > .container,
+ .navbar-expand-lg > .container-fluid, .navbar-expand-lg > .container-sm, .navbar-expand-lg > .container-md, .navbar-expand-lg > .container-lg, .navbar-expand-lg > .container-xl {
+ -ms-flex-wrap: nowrap;
+ flex-wrap: nowrap;
+ }
+ .navbar-expand-lg .navbar-collapse {
+ display: -ms-flexbox !important;
+ display: flex !important;
+ -ms-flex-preferred-size: auto;
+ flex-basis: auto;
+ }
+ .navbar-expand-lg .navbar-toggler {
+ display: none;
+ }
+}
+
+@media (max-width: 1199.98px) {
+ .navbar-expand-xl > .container,
+ .navbar-expand-xl > .container-fluid, .navbar-expand-xl > .container-sm, .navbar-expand-xl > .container-md, .navbar-expand-xl > .container-lg, .navbar-expand-xl > .container-xl {
+ padding-right: 0;
+ padding-left: 0;
+ }
+}
+
+@media (min-width: 1200px) {
+ .navbar-expand-xl {
+ -ms-flex-flow: row nowrap;
+ flex-flow: row nowrap;
+ -ms-flex-pack: start;
+ justify-content: flex-start;
+ }
+ .navbar-expand-xl .navbar-nav {
+ -ms-flex-direction: row;
+ flex-direction: row;
+ }
+ .navbar-expand-xl .navbar-nav .dropdown-menu {
+ position: absolute;
+ }
+ .navbar-expand-xl .navbar-nav .nav-link {
+ padding-right: 0.5rem;
+ padding-left: 0.5rem;
+ }
+ .navbar-expand-xl > .container,
+ .navbar-expand-xl > .container-fluid, .navbar-expand-xl > .container-sm, .navbar-expand-xl > .container-md, .navbar-expand-xl > .container-lg, .navbar-expand-xl > .container-xl {
+ -ms-flex-wrap: nowrap;
+ flex-wrap: nowrap;
+ }
+ .navbar-expand-xl .navbar-collapse {
+ display: -ms-flexbox !important;
+ display: flex !important;
+ -ms-flex-preferred-size: auto;
+ flex-basis: auto;
+ }
+ .navbar-expand-xl .navbar-toggler {
+ display: none;
+ }
+}
+
+.navbar-expand {
+ -ms-flex-flow: row nowrap;
+ flex-flow: row nowrap;
+ -ms-flex-pack: start;
+ justify-content: flex-start;
+}
+
+.navbar-expand > .container,
+.navbar-expand > .container-fluid, .navbar-expand > .container-sm, .navbar-expand > .container-md, .navbar-expand > .container-lg, .navbar-expand > .container-xl {
+ padding-right: 0;
+ padding-left: 0;
+}
+
+.navbar-expand .navbar-nav {
+ -ms-flex-direction: row;
+ flex-direction: row;
+}
+
+.navbar-expand .navbar-nav .dropdown-menu {
+ position: absolute;
+}
+
+.navbar-expand .navbar-nav .nav-link {
+ padding-right: 0.5rem;
+ padding-left: 0.5rem;
+}
+
+.navbar-expand > .container,
+.navbar-expand > .container-fluid, .navbar-expand > .container-sm, .navbar-expand > .container-md, .navbar-expand > .container-lg, .navbar-expand > .container-xl {
+ -ms-flex-wrap: nowrap;
+ flex-wrap: nowrap;
+}
+
+.navbar-expand .navbar-collapse {
+ display: -ms-flexbox !important;
+ display: flex !important;
+ -ms-flex-preferred-size: auto;
+ flex-basis: auto;
+}
+
+.navbar-expand .navbar-toggler {
+ display: none;
+}
+
+.navbar-light .navbar-brand {
+ color: rgba(0, 0, 0, 0.9);
+}
+
+.navbar-light .navbar-brand:hover, .navbar-light .navbar-brand:focus {
+ color: rgba(0, 0, 0, 0.9);
+}
+
+.navbar-light .navbar-nav .nav-link {
+ color: rgba(0, 0, 0, 0.5);
+}
+
+.navbar-light .navbar-nav .nav-link:hover, .navbar-light .navbar-nav .nav-link:focus {
+ color: rgba(0, 0, 0, 0.7);
+}
+
+.navbar-light .navbar-nav .nav-link.disabled {
+ color: rgba(0, 0, 0, 0.3);
+}
+
+.navbar-light .navbar-nav .show > .nav-link,
+.navbar-light .navbar-nav .active > .nav-link,
+.navbar-light .navbar-nav .nav-link.show,
+.navbar-light .navbar-nav .nav-link.active {
+ color: rgba(0, 0, 0, 0.9);
+}
+
+.navbar-light .navbar-toggler {
+ color: rgba(0, 0, 0, 0.5);
+ border-color: rgba(0, 0, 0, 0.1);
+}
+
+.navbar-light .navbar-toggler-icon {
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba(0, 0, 0, 0.5)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
+}
+
+.navbar-light .navbar-text {
+ color: rgba(0, 0, 0, 0.5);
+}
+
+.navbar-light .navbar-text a {
+ color: rgba(0, 0, 0, 0.9);
+}
+
+.navbar-light .navbar-text a:hover, .navbar-light .navbar-text a:focus {
+ color: rgba(0, 0, 0, 0.9);
+}
+
+.navbar-dark .navbar-brand {
+ color: #fff;
+}
+
+.navbar-dark .navbar-brand:hover, .navbar-dark .navbar-brand:focus {
+ color: #fff;
+}
+
+.navbar-dark .navbar-nav .nav-link {
+ color: rgba(255, 255, 255, 0.5);
+}
+
+.navbar-dark .navbar-nav .nav-link:hover, .navbar-dark .navbar-nav .nav-link:focus {
+ color: rgba(255, 255, 255, 0.75);
+}
+
+.navbar-dark .navbar-nav .nav-link.disabled {
+ color: rgba(255, 255, 255, 0.25);
+}
+
+.navbar-dark .navbar-nav .show > .nav-link,
+.navbar-dark .navbar-nav .active > .nav-link,
+.navbar-dark .navbar-nav .nav-link.show,
+.navbar-dark .navbar-nav .nav-link.active {
+ color: #fff;
+}
+
+.navbar-dark .navbar-toggler {
+ color: rgba(255, 255, 255, 0.5);
+ border-color: rgba(255, 255, 255, 0.1);
+}
+
+.navbar-dark .navbar-toggler-icon {
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba(255, 255, 255, 0.5)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
+}
+
+.navbar-dark .navbar-text {
+ color: rgba(255, 255, 255, 0.5);
+}
+
+.navbar-dark .navbar-text a {
+ color: #fff;
+}
+
+.navbar-dark .navbar-text a:hover, .navbar-dark .navbar-text a:focus {
+ color: #fff;
+}
+
+.card {
+ position: relative;
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-direction: column;
+ flex-direction: column;
+ min-width: 0;
+ word-wrap: break-word;
+ background-color: #fff;
+ background-clip: border-box;
+ border: 1px solid rgba(0, 0, 0, 0.125);
+ border-radius: 0.25rem;
+}
+
+.card > hr {
+ margin-right: 0;
+ margin-left: 0;
+}
+
+.card > .list-group:first-child .list-group-item:first-child {
+ border-top-left-radius: 0.25rem;
+ border-top-right-radius: 0.25rem;
+}
+
+.card > .list-group:last-child .list-group-item:last-child {
+ border-bottom-right-radius: 0.25rem;
+ border-bottom-left-radius: 0.25rem;
+}
+
+.card-body {
+ -ms-flex: 1 1 auto;
+ flex: 1 1 auto;
+ min-height: 1px;
+ padding: 1.25rem;
+}
+
+.card-title {
+ margin-bottom: 0.75rem;
+}
+
+.card-subtitle {
+ margin-top: -0.375rem;
+ margin-bottom: 0;
+}
+
+.card-text:last-child {
+ margin-bottom: 0;
+}
+
+.card-link:hover {
+ text-decoration: none;
+}
+
+.card-link + .card-link {
+ margin-left: 1.25rem;
+}
+
+.card-header {
+ padding: 0.75rem 1.25rem;
+ margin-bottom: 0;
+ background-color: rgba(0, 0, 0, 0.03);
+ border-bottom: 1px solid rgba(0, 0, 0, 0.125);
+}
+
+.card-header:first-child {
+ border-radius: calc(0.25rem - 1px) calc(0.25rem - 1px) 0 0;
+}
+
+.card-header + .list-group .list-group-item:first-child {
+ border-top: 0;
+}
+
+.card-footer {
+ padding: 0.75rem 1.25rem;
+ background-color: rgba(0, 0, 0, 0.03);
+ border-top: 1px solid rgba(0, 0, 0, 0.125);
+}
+
+.card-footer:last-child {
+ border-radius: 0 0 calc(0.25rem - 1px) calc(0.25rem - 1px);
+}
+
+.card-header-tabs {
+ margin-right: -0.625rem;
+ margin-bottom: -0.75rem;
+ margin-left: -0.625rem;
+ border-bottom: 0;
+}
+
+.card-header-pills {
+ margin-right: -0.625rem;
+ margin-left: -0.625rem;
+}
+
+.card-img-overlay {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ padding: 1.25rem;
+}
+
+.card-img,
+.card-img-top,
+.card-img-bottom {
+ -ms-flex-negative: 0;
+ flex-shrink: 0;
+ width: 100%;
+}
+
+.card-img,
+.card-img-top {
+ border-top-left-radius: calc(0.25rem - 1px);
+ border-top-right-radius: calc(0.25rem - 1px);
+}
+
+.card-img,
+.card-img-bottom {
+ border-bottom-right-radius: calc(0.25rem - 1px);
+ border-bottom-left-radius: calc(0.25rem - 1px);
+}
+
+.card-deck .card {
+ margin-bottom: 15px;
+}
+
+@media (min-width: 576px) {
+ .card-deck {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-flow: row wrap;
+ flex-flow: row wrap;
+ margin-right: -15px;
+ margin-left: -15px;
+ }
+ .card-deck .card {
+ -ms-flex: 1 0 0%;
+ flex: 1 0 0%;
+ margin-right: 15px;
+ margin-bottom: 0;
+ margin-left: 15px;
+ }
+}
+
+.card-group > .card {
+ margin-bottom: 15px;
+}
+
+@media (min-width: 576px) {
+ .card-group {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-flow: row wrap;
+ flex-flow: row wrap;
+ }
+ .card-group > .card {
+ -ms-flex: 1 0 0%;
+ flex: 1 0 0%;
+ margin-bottom: 0;
+ }
+ .card-group > .card + .card {
+ margin-left: 0;
+ border-left: 0;
+ }
+ .card-group > .card:not(:last-child) {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ }
+ .card-group > .card:not(:last-child) .card-img-top,
+ .card-group > .card:not(:last-child) .card-header {
+ border-top-right-radius: 0;
+ }
+ .card-group > .card:not(:last-child) .card-img-bottom,
+ .card-group > .card:not(:last-child) .card-footer {
+ border-bottom-right-radius: 0;
+ }
+ .card-group > .card:not(:first-child) {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+ }
+ .card-group > .card:not(:first-child) .card-img-top,
+ .card-group > .card:not(:first-child) .card-header {
+ border-top-left-radius: 0;
+ }
+ .card-group > .card:not(:first-child) .card-img-bottom,
+ .card-group > .card:not(:first-child) .card-footer {
+ border-bottom-left-radius: 0;
+ }
+}
+
+.card-columns .card {
+ margin-bottom: 0.75rem;
+}
+
+@media (min-width: 576px) {
+ .card-columns {
+ -webkit-column-count: 3;
+ -moz-column-count: 3;
+ column-count: 3;
+ -webkit-column-gap: 1.25rem;
+ -moz-column-gap: 1.25rem;
+ column-gap: 1.25rem;
+ orphans: 1;
+ widows: 1;
+ }
+ .card-columns .card {
+ display: inline-block;
+ width: 100%;
+ }
+}
+
+.accordion > .card {
+ overflow: hidden;
+}
+
+.accordion > .card:not(:last-of-type) {
+ border-bottom: 0;
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0;
+}
+
+.accordion > .card:not(:first-of-type) {
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+}
+
+.accordion > .card > .card-header {
+ border-radius: 0;
+ margin-bottom: -1px;
+}
+
+.breadcrumb {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-wrap: wrap;
+ flex-wrap: wrap;
+ padding: 0.75rem 1rem;
+ margin-bottom: 1rem;
+ list-style: none;
+ background-color: #e9ecef;
+ border-radius: 0.25rem;
+}
+
+.breadcrumb-item + .breadcrumb-item {
+ padding-left: 0.5rem;
+}
+
+.breadcrumb-item + .breadcrumb-item::before {
+ display: inline-block;
+ padding-right: 0.5rem;
+ color: #6c757d;
+ content: "/";
+}
+
+.breadcrumb-item + .breadcrumb-item:hover::before {
+ text-decoration: underline;
+}
+
+.breadcrumb-item + .breadcrumb-item:hover::before {
+ text-decoration: none;
+}
+
+.breadcrumb-item.active {
+ color: #6c757d;
+}
+
+.pagination {
+ display: -ms-flexbox;
+ display: flex;
+ padding-left: 0;
+ list-style: none;
+ border-radius: 0.25rem;
+}
+
+.page-link {
+ position: relative;
+ display: block;
+ padding: 0.5rem 0.75rem;
+ margin-left: -1px;
+ line-height: 1.25;
+ color: #007bff;
+ background-color: #fff;
+ border: 1px solid #dee2e6;
+}
+
+.page-link:hover {
+ z-index: 2;
+ color: #0056b3;
+ text-decoration: none;
+ background-color: #e9ecef;
+ border-color: #dee2e6;
+}
+
+.page-link:focus {
+ z-index: 3;
+ outline: 0;
+ box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
+}
+
+.page-item:first-child .page-link {
+ margin-left: 0;
+ border-top-left-radius: 0.25rem;
+ border-bottom-left-radius: 0.25rem;
+}
+
+.page-item:last-child .page-link {
+ border-top-right-radius: 0.25rem;
+ border-bottom-right-radius: 0.25rem;
+}
+
+.page-item.active .page-link {
+ z-index: 3;
+ color: #fff;
+ background-color: #007bff;
+ border-color: #007bff;
+}
+
+.page-item.disabled .page-link {
+ color: #6c757d;
+ pointer-events: none;
+ cursor: auto;
+ background-color: #fff;
+ border-color: #dee2e6;
+}
+
+.pagination-lg .page-link {
+ padding: 0.75rem 1.5rem;
+ font-size: 1.25rem;
+ line-height: 1.5;
+}
+
+.pagination-lg .page-item:first-child .page-link {
+ border-top-left-radius: 0.3rem;
+ border-bottom-left-radius: 0.3rem;
+}
+
+.pagination-lg .page-item:last-child .page-link {
+ border-top-right-radius: 0.3rem;
+ border-bottom-right-radius: 0.3rem;
+}
+
+.pagination-sm .page-link {
+ padding: 0.25rem 0.5rem;
+ font-size: 0.875rem;
+ line-height: 1.5;
+}
+
+.pagination-sm .page-item:first-child .page-link {
+ border-top-left-radius: 0.2rem;
+ border-bottom-left-radius: 0.2rem;
+}
+
+.pagination-sm .page-item:last-child .page-link {
+ border-top-right-radius: 0.2rem;
+ border-bottom-right-radius: 0.2rem;
+}
+
+.badge {
+ display: inline-block;
+ padding: 0.25em 0.4em;
+ font-size: 75%;
+ font-weight: 700;
+ line-height: 1;
+ text-align: center;
+ white-space: nowrap;
+ vertical-align: baseline;
+ border-radius: 0.25rem;
+ transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .badge {
+ transition: none;
+ }
+}
+
+a.badge:hover, a.badge:focus {
+ text-decoration: none;
+}
+
+.badge:empty {
+ display: none;
+}
+
+.btn .badge {
+ position: relative;
+ top: -1px;
+}
+
+.badge-pill {
+ padding-right: 0.6em;
+ padding-left: 0.6em;
+ border-radius: 10rem;
+}
+
+.badge-primary {
+ color: #fff;
+ background-color: #007bff;
+}
+
+a.badge-primary:hover, a.badge-primary:focus {
+ color: #fff;
+ background-color: #0062cc;
+}
+
+a.badge-primary:focus, a.badge-primary.focus {
+ outline: 0;
+ box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.5);
+}
+
+.badge-secondary {
+ color: #fff;
+ background-color: #6c757d;
+}
+
+a.badge-secondary:hover, a.badge-secondary:focus {
+ color: #fff;
+ background-color: #545b62;
+}
+
+a.badge-secondary:focus, a.badge-secondary.focus {
+ outline: 0;
+ box-shadow: 0 0 0 0.2rem rgba(108, 117, 125, 0.5);
+}
+
+.badge-success {
+ color: #fff;
+ background-color: #28a745;
+}
+
+a.badge-success:hover, a.badge-success:focus {
+ color: #fff;
+ background-color: #1e7e34;
+}
+
+a.badge-success:focus, a.badge-success.focus {
+ outline: 0;
+ box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.5);
+}
+
+.badge-info {
+ color: #fff;
+ background-color: #17a2b8;
+}
+
+a.badge-info:hover, a.badge-info:focus {
+ color: #fff;
+ background-color: #117a8b;
+}
+
+a.badge-info:focus, a.badge-info.focus {
+ outline: 0;
+ box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5);
+}
+
+.badge-warning {
+ color: #212529;
+ background-color: #ffc107;
+}
+
+a.badge-warning:hover, a.badge-warning:focus {
+ color: #212529;
+ background-color: #d39e00;
+}
+
+a.badge-warning:focus, a.badge-warning.focus {
+ outline: 0;
+ box-shadow: 0 0 0 0.2rem rgba(255, 193, 7, 0.5);
+}
+
+.badge-danger {
+ color: #fff;
+ background-color: #dc3545;
+}
+
+a.badge-danger:hover, a.badge-danger:focus {
+ color: #fff;
+ background-color: #bd2130;
+}
+
+a.badge-danger:focus, a.badge-danger.focus {
+ outline: 0;
+ box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.5);
+}
+
+.badge-light {
+ color: #212529;
+ background-color: #f8f9fa;
+}
+
+a.badge-light:hover, a.badge-light:focus {
+ color: #212529;
+ background-color: #dae0e5;
+}
+
+a.badge-light:focus, a.badge-light.focus {
+ outline: 0;
+ box-shadow: 0 0 0 0.2rem rgba(248, 249, 250, 0.5);
+}
+
+.badge-dark {
+ color: #fff;
+ background-color: #343a40;
+}
+
+a.badge-dark:hover, a.badge-dark:focus {
+ color: #fff;
+ background-color: #1d2124;
+}
+
+a.badge-dark:focus, a.badge-dark.focus {
+ outline: 0;
+ box-shadow: 0 0 0 0.2rem rgba(52, 58, 64, 0.5);
+}
+
+.jumbotron {
+ padding: 2rem 1rem;
+ margin-bottom: 2rem;
+ background-color: #e9ecef;
+ border-radius: 0.3rem;
+}
+
+@media (min-width: 576px) {
+ .jumbotron {
+ padding: 4rem 2rem;
+ }
+}
+
+.jumbotron-fluid {
+ padding-right: 0;
+ padding-left: 0;
+ border-radius: 0;
+}
+
+.alert {
+ position: relative;
+ padding: 0.75rem 1.25rem;
+ margin-bottom: 1rem;
+ border: 1px solid transparent;
+ border-radius: 0.25rem;
+}
+
+.alert-heading {
+ color: inherit;
+}
+
+.alert-link {
+ font-weight: 700;
+}
+
+.alert-dismissible {
+ padding-right: 4rem;
+}
+
+.alert-dismissible .close {
+ position: absolute;
+ top: 0;
+ right: 0;
+ padding: 0.75rem 1.25rem;
+ color: inherit;
+}
+
+.alert-primary {
+ color: #004085;
+ background-color: #cce5ff;
+ border-color: #b8daff;
+}
+
+.alert-primary hr {
+ border-top-color: #9fcdff;
+}
+
+.alert-primary .alert-link {
+ color: #002752;
+}
+
+.alert-secondary {
+ color: #383d41;
+ background-color: #e2e3e5;
+ border-color: #d6d8db;
+}
+
+.alert-secondary hr {
+ border-top-color: #c8cbcf;
+}
+
+.alert-secondary .alert-link {
+ color: #202326;
+}
+
+.alert-success {
+ color: #155724;
+ background-color: #d4edda;
+ border-color: #c3e6cb;
+}
+
+.alert-success hr {
+ border-top-color: #b1dfbb;
+}
+
+.alert-success .alert-link {
+ color: #0b2e13;
+}
+
+.alert-info {
+ color: #0c5460;
+ background-color: #d1ecf1;
+ border-color: #bee5eb;
+}
+
+.alert-info hr {
+ border-top-color: #abdde5;
+}
+
+.alert-info .alert-link {
+ color: #062c33;
+}
+
+.alert-warning {
+ color: #856404;
+ background-color: #fff3cd;
+ border-color: #ffeeba;
+}
+
+.alert-warning hr {
+ border-top-color: #ffe8a1;
+}
+
+.alert-warning .alert-link {
+ color: #533f03;
+}
+
+.alert-danger {
+ color: #721c24;
+ background-color: #f8d7da;
+ border-color: #f5c6cb;
+}
+
+.alert-danger hr {
+ border-top-color: #f1b0b7;
+}
+
+.alert-danger .alert-link {
+ color: #491217;
+}
+
+.alert-light {
+ color: #818182;
+ background-color: #fefefe;
+ border-color: #fdfdfe;
+}
+
+.alert-light hr {
+ border-top-color: #ececf6;
+}
+
+.alert-light .alert-link {
+ color: #686868;
+}
+
+.alert-dark {
+ color: #1b1e21;
+ background-color: #d6d8d9;
+ border-color: #c6c8ca;
+}
+
+.alert-dark hr {
+ border-top-color: #b9bbbe;
+}
+
+.alert-dark .alert-link {
+ color: #040505;
+}
+
+@-webkit-keyframes progress-bar-stripes {
+ from {
+ background-position: 1rem 0;
+ }
+ to {
+ background-position: 0 0;
+ }
+}
+
+@keyframes progress-bar-stripes {
+ from {
+ background-position: 1rem 0;
+ }
+ to {
+ background-position: 0 0;
+ }
+}
+
+.progress {
+ display: -ms-flexbox;
+ display: flex;
+ height: 1rem;
+ overflow: hidden;
+ font-size: 0.75rem;
+ background-color: #e9ecef;
+ border-radius: 0.25rem;
+}
+
+.progress-bar {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-direction: column;
+ flex-direction: column;
+ -ms-flex-pack: center;
+ justify-content: center;
+ overflow: hidden;
+ color: #fff;
+ text-align: center;
+ white-space: nowrap;
+ background-color: #007bff;
+ transition: width 0.6s ease;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .progress-bar {
+ transition: none;
+ }
+}
+
+.progress-bar-striped {
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-size: 1rem 1rem;
+}
+
+.progress-bar-animated {
+ -webkit-animation: progress-bar-stripes 1s linear infinite;
+ animation: progress-bar-stripes 1s linear infinite;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .progress-bar-animated {
+ -webkit-animation: none;
+ animation: none;
+ }
+}
+
+.media {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-align: start;
+ align-items: flex-start;
+}
+
+.media-body {
+ -ms-flex: 1;
+ flex: 1;
+}
+
+.list-group {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-direction: column;
+ flex-direction: column;
+ padding-left: 0;
+ margin-bottom: 0;
+}
+
+.list-group-item-action {
+ width: 100%;
+ color: #495057;
+ text-align: inherit;
+}
+
+.list-group-item-action:hover, .list-group-item-action:focus {
+ z-index: 1;
+ color: #495057;
+ text-decoration: none;
+ background-color: #f8f9fa;
+}
+
+.list-group-item-action:active {
+ color: #212529;
+ background-color: #e9ecef;
+}
+
+.list-group-item {
+ position: relative;
+ display: block;
+ padding: 0.75rem 1.25rem;
+ background-color: #fff;
+ border: 1px solid rgba(0, 0, 0, 0.125);
+}
+
+.list-group-item:first-child {
+ border-top-left-radius: 0.25rem;
+ border-top-right-radius: 0.25rem;
+}
+
+.list-group-item:last-child {
+ border-bottom-right-radius: 0.25rem;
+ border-bottom-left-radius: 0.25rem;
+}
+
+.list-group-item.disabled, .list-group-item:disabled {
+ color: #6c757d;
+ pointer-events: none;
+ background-color: #fff;
+}
+
+.list-group-item.active {
+ z-index: 2;
+ color: #fff;
+ background-color: #007bff;
+ border-color: #007bff;
+}
+
+.list-group-item + .list-group-item {
+ border-top-width: 0;
+}
+
+.list-group-item + .list-group-item.active {
+ margin-top: -1px;
+ border-top-width: 1px;
+}
+
+.list-group-horizontal {
+ -ms-flex-direction: row;
+ flex-direction: row;
+}
+
+.list-group-horizontal .list-group-item:first-child {
+ border-bottom-left-radius: 0.25rem;
+ border-top-right-radius: 0;
+}
+
+.list-group-horizontal .list-group-item:last-child {
+ border-top-right-radius: 0.25rem;
+ border-bottom-left-radius: 0;
+}
+
+.list-group-horizontal .list-group-item.active {
+ margin-top: 0;
+}
+
+.list-group-horizontal .list-group-item + .list-group-item {
+ border-top-width: 1px;
+ border-left-width: 0;
+}
+
+.list-group-horizontal .list-group-item + .list-group-item.active {
+ margin-left: -1px;
+ border-left-width: 1px;
+}
+
+@media (min-width: 576px) {
+ .list-group-horizontal-sm {
+ -ms-flex-direction: row;
+ flex-direction: row;
+ }
+ .list-group-horizontal-sm .list-group-item:first-child {
+ border-bottom-left-radius: 0.25rem;
+ border-top-right-radius: 0;
+ }
+ .list-group-horizontal-sm .list-group-item:last-child {
+ border-top-right-radius: 0.25rem;
+ border-bottom-left-radius: 0;
+ }
+ .list-group-horizontal-sm .list-group-item.active {
+ margin-top: 0;
+ }
+ .list-group-horizontal-sm .list-group-item + .list-group-item {
+ border-top-width: 1px;
+ border-left-width: 0;
+ }
+ .list-group-horizontal-sm .list-group-item + .list-group-item.active {
+ margin-left: -1px;
+ border-left-width: 1px;
+ }
+}
+
+@media (min-width: 768px) {
+ .list-group-horizontal-md {
+ -ms-flex-direction: row;
+ flex-direction: row;
+ }
+ .list-group-horizontal-md .list-group-item:first-child {
+ border-bottom-left-radius: 0.25rem;
+ border-top-right-radius: 0;
+ }
+ .list-group-horizontal-md .list-group-item:last-child {
+ border-top-right-radius: 0.25rem;
+ border-bottom-left-radius: 0;
+ }
+ .list-group-horizontal-md .list-group-item.active {
+ margin-top: 0;
+ }
+ .list-group-horizontal-md .list-group-item + .list-group-item {
+ border-top-width: 1px;
+ border-left-width: 0;
+ }
+ .list-group-horizontal-md .list-group-item + .list-group-item.active {
+ margin-left: -1px;
+ border-left-width: 1px;
+ }
+}
+
+@media (min-width: 992px) {
+ .list-group-horizontal-lg {
+ -ms-flex-direction: row;
+ flex-direction: row;
+ }
+ .list-group-horizontal-lg .list-group-item:first-child {
+ border-bottom-left-radius: 0.25rem;
+ border-top-right-radius: 0;
+ }
+ .list-group-horizontal-lg .list-group-item:last-child {
+ border-top-right-radius: 0.25rem;
+ border-bottom-left-radius: 0;
+ }
+ .list-group-horizontal-lg .list-group-item.active {
+ margin-top: 0;
+ }
+ .list-group-horizontal-lg .list-group-item + .list-group-item {
+ border-top-width: 1px;
+ border-left-width: 0;
+ }
+ .list-group-horizontal-lg .list-group-item + .list-group-item.active {
+ margin-left: -1px;
+ border-left-width: 1px;
+ }
+}
+
+@media (min-width: 1200px) {
+ .list-group-horizontal-xl {
+ -ms-flex-direction: row;
+ flex-direction: row;
+ }
+ .list-group-horizontal-xl .list-group-item:first-child {
+ border-bottom-left-radius: 0.25rem;
+ border-top-right-radius: 0;
+ }
+ .list-group-horizontal-xl .list-group-item:last-child {
+ border-top-right-radius: 0.25rem;
+ border-bottom-left-radius: 0;
+ }
+ .list-group-horizontal-xl .list-group-item.active {
+ margin-top: 0;
+ }
+ .list-group-horizontal-xl .list-group-item + .list-group-item {
+ border-top-width: 1px;
+ border-left-width: 0;
+ }
+ .list-group-horizontal-xl .list-group-item + .list-group-item.active {
+ margin-left: -1px;
+ border-left-width: 1px;
+ }
+}
+
+.list-group-flush .list-group-item {
+ border-right-width: 0;
+ border-left-width: 0;
+ border-radius: 0;
+}
+
+.list-group-flush .list-group-item:first-child {
+ border-top-width: 0;
+}
+
+.list-group-flush:last-child .list-group-item:last-child {
+ border-bottom-width: 0;
+}
+
+.list-group-item-primary {
+ color: #004085;
+ background-color: #b8daff;
+}
+
+.list-group-item-primary.list-group-item-action:hover, .list-group-item-primary.list-group-item-action:focus {
+ color: #004085;
+ background-color: #9fcdff;
+}
+
+.list-group-item-primary.list-group-item-action.active {
+ color: #fff;
+ background-color: #004085;
+ border-color: #004085;
+}
+
+.list-group-item-secondary {
+ color: #383d41;
+ background-color: #d6d8db;
+}
+
+.list-group-item-secondary.list-group-item-action:hover, .list-group-item-secondary.list-group-item-action:focus {
+ color: #383d41;
+ background-color: #c8cbcf;
+}
+
+.list-group-item-secondary.list-group-item-action.active {
+ color: #fff;
+ background-color: #383d41;
+ border-color: #383d41;
+}
+
+.list-group-item-success {
+ color: #155724;
+ background-color: #c3e6cb;
+}
+
+.list-group-item-success.list-group-item-action:hover, .list-group-item-success.list-group-item-action:focus {
+ color: #155724;
+ background-color: #b1dfbb;
+}
+
+.list-group-item-success.list-group-item-action.active {
+ color: #fff;
+ background-color: #155724;
+ border-color: #155724;
+}
+
+.list-group-item-info {
+ color: #0c5460;
+ background-color: #bee5eb;
+}
+
+.list-group-item-info.list-group-item-action:hover, .list-group-item-info.list-group-item-action:focus {
+ color: #0c5460;
+ background-color: #abdde5;
+}
+
+.list-group-item-info.list-group-item-action.active {
+ color: #fff;
+ background-color: #0c5460;
+ border-color: #0c5460;
+}
+
+.list-group-item-warning {
+ color: #856404;
+ background-color: #ffeeba;
+}
+
+.list-group-item-warning.list-group-item-action:hover, .list-group-item-warning.list-group-item-action:focus {
+ color: #856404;
+ background-color: #ffe8a1;
+}
+
+.list-group-item-warning.list-group-item-action.active {
+ color: #fff;
+ background-color: #856404;
+ border-color: #856404;
+}
+
+.list-group-item-danger {
+ color: #721c24;
+ background-color: #f5c6cb;
+}
+
+.list-group-item-danger.list-group-item-action:hover, .list-group-item-danger.list-group-item-action:focus {
+ color: #721c24;
+ background-color: #f1b0b7;
+}
+
+.list-group-item-danger.list-group-item-action.active {
+ color: #fff;
+ background-color: #721c24;
+ border-color: #721c24;
+}
+
+.list-group-item-light {
+ color: #818182;
+ background-color: #fdfdfe;
+}
+
+.list-group-item-light.list-group-item-action:hover, .list-group-item-light.list-group-item-action:focus {
+ color: #818182;
+ background-color: #ececf6;
+}
+
+.list-group-item-light.list-group-item-action.active {
+ color: #fff;
+ background-color: #818182;
+ border-color: #818182;
+}
+
+.list-group-item-dark {
+ color: #1b1e21;
+ background-color: #c6c8ca;
+}
+
+.list-group-item-dark.list-group-item-action:hover, .list-group-item-dark.list-group-item-action:focus {
+ color: #1b1e21;
+ background-color: #b9bbbe;
+}
+
+.list-group-item-dark.list-group-item-action.active {
+ color: #fff;
+ background-color: #1b1e21;
+ border-color: #1b1e21;
+}
+
+.close {
+ float: right;
+ font-size: 1.5rem;
+ font-weight: 700;
+ line-height: 1;
+ color: #000;
+ text-shadow: 0 1px 0 #fff;
+ opacity: .5;
+}
+
+.close:hover {
+ color: #000;
+ text-decoration: none;
+}
+
+.close:not(:disabled):not(.disabled):hover, .close:not(:disabled):not(.disabled):focus {
+ opacity: .75;
+}
+
+button.close {
+ padding: 0;
+ background-color: transparent;
+ border: 0;
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ appearance: none;
+}
+
+a.close.disabled {
+ pointer-events: none;
+}
+
+.toast {
+ max-width: 350px;
+ overflow: hidden;
+ font-size: 0.875rem;
+ background-color: rgba(255, 255, 255, 0.85);
+ background-clip: padding-box;
+ border: 1px solid rgba(0, 0, 0, 0.1);
+ box-shadow: 0 0.25rem 0.75rem rgba(0, 0, 0, 0.1);
+ -webkit-backdrop-filter: blur(10px);
+ backdrop-filter: blur(10px);
+ opacity: 0;
+ border-radius: 0.25rem;
+}
+
+.toast:not(:last-child) {
+ margin-bottom: 0.75rem;
+}
+
+.toast.showing {
+ opacity: 1;
+}
+
+.toast.show {
+ display: block;
+ opacity: 1;
+}
+
+.toast.hide {
+ display: none;
+}
+
+.toast-header {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-align: center;
+ align-items: center;
+ padding: 0.25rem 0.75rem;
+ color: #6c757d;
+ background-color: rgba(255, 255, 255, 0.85);
+ background-clip: padding-box;
+ border-bottom: 1px solid rgba(0, 0, 0, 0.05);
+}
+
+.toast-body {
+ padding: 0.75rem;
+}
+
+.modal-open {
+ overflow: hidden;
+}
+
+.modal-open .modal {
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.modal {
+ position: fixed;
+ top: 0;
+ left: 0;
+ z-index: 1050;
+ display: none;
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+ outline: 0;
+}
+
+.modal-dialog {
+ position: relative;
+ width: auto;
+ margin: 0.5rem;
+ pointer-events: none;
+}
+
+.modal.fade .modal-dialog {
+ transition: -webkit-transform 0.3s ease-out;
+ transition: transform 0.3s ease-out;
+ transition: transform 0.3s ease-out, -webkit-transform 0.3s ease-out;
+ -webkit-transform: translate(0, -50px);
+ transform: translate(0, -50px);
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .modal.fade .modal-dialog {
+ transition: none;
+ }
+}
+
+.modal.show .modal-dialog {
+ -webkit-transform: none;
+ transform: none;
+}
+
+.modal.modal-static .modal-dialog {
+ -webkit-transform: scale(1.02);
+ transform: scale(1.02);
+}
+
+.modal-dialog-scrollable {
+ display: -ms-flexbox;
+ display: flex;
+ max-height: calc(100% - 1rem);
+}
+
+.modal-dialog-scrollable .modal-content {
+ max-height: calc(100vh - 1rem);
+ overflow: hidden;
+}
+
+.modal-dialog-scrollable .modal-header,
+.modal-dialog-scrollable .modal-footer {
+ -ms-flex-negative: 0;
+ flex-shrink: 0;
+}
+
+.modal-dialog-scrollable .modal-body {
+ overflow-y: auto;
+}
+
+.modal-dialog-centered {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-align: center;
+ align-items: center;
+ min-height: calc(100% - 1rem);
+}
+
+.modal-dialog-centered::before {
+ display: block;
+ height: calc(100vh - 1rem);
+ content: "";
+}
+
+.modal-dialog-centered.modal-dialog-scrollable {
+ -ms-flex-direction: column;
+ flex-direction: column;
+ -ms-flex-pack: center;
+ justify-content: center;
+ height: 100%;
+}
+
+.modal-dialog-centered.modal-dialog-scrollable .modal-content {
+ max-height: none;
+}
+
+.modal-dialog-centered.modal-dialog-scrollable::before {
+ content: none;
+}
+
+.modal-content {
+ position: relative;
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-direction: column;
+ flex-direction: column;
+ width: 100%;
+ pointer-events: auto;
+ background-color: #fff;
+ background-clip: padding-box;
+ border: 1px solid rgba(0, 0, 0, 0.2);
+ border-radius: 0.3rem;
+ outline: 0;
+}
+
+.modal-backdrop {
+ position: fixed;
+ top: 0;
+ left: 0;
+ z-index: 1040;
+ width: 100vw;
+ height: 100vh;
+ background-color: #000;
+}
+
+.modal-backdrop.fade {
+ opacity: 0;
+}
+
+.modal-backdrop.show {
+ opacity: 0.5;
+}
+
+.modal-header {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-align: start;
+ align-items: flex-start;
+ -ms-flex-pack: justify;
+ justify-content: space-between;
+ padding: 1rem 1rem;
+ border-bottom: 1px solid #dee2e6;
+ border-top-left-radius: calc(0.3rem - 1px);
+ border-top-right-radius: calc(0.3rem - 1px);
+}
+
+.modal-header .close {
+ padding: 1rem 1rem;
+ margin: -1rem -1rem -1rem auto;
+}
+
+.modal-title {
+ margin-bottom: 0;
+ line-height: 1.5;
+}
+
+.modal-body {
+ position: relative;
+ -ms-flex: 1 1 auto;
+ flex: 1 1 auto;
+ padding: 1rem;
+}
+
+.modal-footer {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-wrap: wrap;
+ flex-wrap: wrap;
+ -ms-flex-align: center;
+ align-items: center;
+ -ms-flex-pack: end;
+ justify-content: flex-end;
+ padding: 0.75rem;
+ border-top: 1px solid #dee2e6;
+ border-bottom-right-radius: calc(0.3rem - 1px);
+ border-bottom-left-radius: calc(0.3rem - 1px);
+}
+
+.modal-footer > * {
+ margin: 0.25rem;
+}
+
+.modal-scrollbar-measure {
+ position: absolute;
+ top: -9999px;
+ width: 50px;
+ height: 50px;
+ overflow: scroll;
+}
+
+@media (min-width: 576px) {
+ .modal-dialog {
+ max-width: 500px;
+ margin: 1.75rem auto;
+ }
+ .modal-dialog-scrollable {
+ max-height: calc(100% - 3.5rem);
+ }
+ .modal-dialog-scrollable .modal-content {
+ max-height: calc(100vh - 3.5rem);
+ }
+ .modal-dialog-centered {
+ min-height: calc(100% - 3.5rem);
+ }
+ .modal-dialog-centered::before {
+ height: calc(100vh - 3.5rem);
+ }
+ .modal-sm {
+ max-width: 300px;
+ }
+}
+
+@media (min-width: 992px) {
+ .modal-lg,
+ .modal-xl {
+ max-width: 800px;
+ }
+}
+
+@media (min-width: 1200px) {
+ .modal-xl {
+ max-width: 1140px;
+ }
+}
+
+.tooltip {
+ position: absolute;
+ z-index: 1070;
+ display: block;
+ margin: 0;
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
+ font-style: normal;
+ font-weight: 400;
+ line-height: 1.5;
+ text-align: left;
+ text-align: start;
+ text-decoration: none;
+ text-shadow: none;
+ text-transform: none;
+ letter-spacing: normal;
+ word-break: normal;
+ word-spacing: normal;
+ white-space: normal;
+ line-break: auto;
+ font-size: 0.875rem;
+ word-wrap: break-word;
+ opacity: 0;
+}
+
+.tooltip.show {
+ opacity: 0.9;
+}
+
+.tooltip .arrow {
+ position: absolute;
+ display: block;
+ width: 0.8rem;
+ height: 0.4rem;
+}
+
+.tooltip .arrow::before {
+ position: absolute;
+ content: "";
+ border-color: transparent;
+ border-style: solid;
+}
+
+.bs-tooltip-top, .bs-tooltip-auto[x-placement^="top"] {
+ padding: 0.4rem 0;
+}
+
+.bs-tooltip-top .arrow, .bs-tooltip-auto[x-placement^="top"] .arrow {
+ bottom: 0;
+}
+
+.bs-tooltip-top .arrow::before, .bs-tooltip-auto[x-placement^="top"] .arrow::before {
+ top: 0;
+ border-width: 0.4rem 0.4rem 0;
+ border-top-color: #000;
+}
+
+.bs-tooltip-right, .bs-tooltip-auto[x-placement^="right"] {
+ padding: 0 0.4rem;
+}
+
+.bs-tooltip-right .arrow, .bs-tooltip-auto[x-placement^="right"] .arrow {
+ left: 0;
+ width: 0.4rem;
+ height: 0.8rem;
+}
+
+.bs-tooltip-right .arrow::before, .bs-tooltip-auto[x-placement^="right"] .arrow::before {
+ right: 0;
+ border-width: 0.4rem 0.4rem 0.4rem 0;
+ border-right-color: #000;
+}
+
+.bs-tooltip-bottom, .bs-tooltip-auto[x-placement^="bottom"] {
+ padding: 0.4rem 0;
+}
+
+.bs-tooltip-bottom .arrow, .bs-tooltip-auto[x-placement^="bottom"] .arrow {
+ top: 0;
+}
+
+.bs-tooltip-bottom .arrow::before, .bs-tooltip-auto[x-placement^="bottom"] .arrow::before {
+ bottom: 0;
+ border-width: 0 0.4rem 0.4rem;
+ border-bottom-color: #000;
+}
+
+.bs-tooltip-left, .bs-tooltip-auto[x-placement^="left"] {
+ padding: 0 0.4rem;
+}
+
+.bs-tooltip-left .arrow, .bs-tooltip-auto[x-placement^="left"] .arrow {
+ right: 0;
+ width: 0.4rem;
+ height: 0.8rem;
+}
+
+.bs-tooltip-left .arrow::before, .bs-tooltip-auto[x-placement^="left"] .arrow::before {
+ left: 0;
+ border-width: 0.4rem 0 0.4rem 0.4rem;
+ border-left-color: #000;
+}
+
+.tooltip-inner {
+ max-width: 200px;
+ padding: 0.25rem 0.5rem;
+ color: #fff;
+ text-align: center;
+ background-color: #000;
+ border-radius: 0.25rem;
+}
+
+.popover {
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 1060;
+ display: block;
+ max-width: 276px;
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
+ font-style: normal;
+ font-weight: 400;
+ line-height: 1.5;
+ text-align: left;
+ text-align: start;
+ text-decoration: none;
+ text-shadow: none;
+ text-transform: none;
+ letter-spacing: normal;
+ word-break: normal;
+ word-spacing: normal;
+ white-space: normal;
+ line-break: auto;
+ font-size: 0.875rem;
+ word-wrap: break-word;
+ background-color: #fff;
+ background-clip: padding-box;
+ border: 1px solid rgba(0, 0, 0, 0.2);
+ border-radius: 0.3rem;
+}
+
+.popover .arrow {
+ position: absolute;
+ display: block;
+ width: 1rem;
+ height: 0.5rem;
+ margin: 0 0.3rem;
+}
+
+.popover .arrow::before, .popover .arrow::after {
+ position: absolute;
+ display: block;
+ content: "";
+ border-color: transparent;
+ border-style: solid;
+}
+
+.bs-popover-top, .bs-popover-auto[x-placement^="top"] {
+ margin-bottom: 0.5rem;
+}
+
+.bs-popover-top > .arrow, .bs-popover-auto[x-placement^="top"] > .arrow {
+ bottom: calc(-0.5rem - 1px);
+}
+
+.bs-popover-top > .arrow::before, .bs-popover-auto[x-placement^="top"] > .arrow::before {
+ bottom: 0;
+ border-width: 0.5rem 0.5rem 0;
+ border-top-color: rgba(0, 0, 0, 0.25);
+}
+
+.bs-popover-top > .arrow::after, .bs-popover-auto[x-placement^="top"] > .arrow::after {
+ bottom: 1px;
+ border-width: 0.5rem 0.5rem 0;
+ border-top-color: #fff;
+}
+
+.bs-popover-right, .bs-popover-auto[x-placement^="right"] {
+ margin-left: 0.5rem;
+}
+
+.bs-popover-right > .arrow, .bs-popover-auto[x-placement^="right"] > .arrow {
+ left: calc(-0.5rem - 1px);
+ width: 0.5rem;
+ height: 1rem;
+ margin: 0.3rem 0;
+}
+
+.bs-popover-right > .arrow::before, .bs-popover-auto[x-placement^="right"] > .arrow::before {
+ left: 0;
+ border-width: 0.5rem 0.5rem 0.5rem 0;
+ border-right-color: rgba(0, 0, 0, 0.25);
+}
+
+.bs-popover-right > .arrow::after, .bs-popover-auto[x-placement^="right"] > .arrow::after {
+ left: 1px;
+ border-width: 0.5rem 0.5rem 0.5rem 0;
+ border-right-color: #fff;
+}
+
+.bs-popover-bottom, .bs-popover-auto[x-placement^="bottom"] {
+ margin-top: 0.5rem;
+}
+
+.bs-popover-bottom > .arrow, .bs-popover-auto[x-placement^="bottom"] > .arrow {
+ top: calc(-0.5rem - 1px);
+}
+
+.bs-popover-bottom > .arrow::before, .bs-popover-auto[x-placement^="bottom"] > .arrow::before {
+ top: 0;
+ border-width: 0 0.5rem 0.5rem 0.5rem;
+ border-bottom-color: rgba(0, 0, 0, 0.25);
+}
+
+.bs-popover-bottom > .arrow::after, .bs-popover-auto[x-placement^="bottom"] > .arrow::after {
+ top: 1px;
+ border-width: 0 0.5rem 0.5rem 0.5rem;
+ border-bottom-color: #fff;
+}
+
+.bs-popover-bottom .popover-header::before, .bs-popover-auto[x-placement^="bottom"] .popover-header::before {
+ position: absolute;
+ top: 0;
+ left: 50%;
+ display: block;
+ width: 1rem;
+ margin-left: -0.5rem;
+ content: "";
+ border-bottom: 1px solid #f7f7f7;
+}
+
+.bs-popover-left, .bs-popover-auto[x-placement^="left"] {
+ margin-right: 0.5rem;
+}
+
+.bs-popover-left > .arrow, .bs-popover-auto[x-placement^="left"] > .arrow {
+ right: calc(-0.5rem - 1px);
+ width: 0.5rem;
+ height: 1rem;
+ margin: 0.3rem 0;
+}
+
+.bs-popover-left > .arrow::before, .bs-popover-auto[x-placement^="left"] > .arrow::before {
+ right: 0;
+ border-width: 0.5rem 0 0.5rem 0.5rem;
+ border-left-color: rgba(0, 0, 0, 0.25);
+}
+
+.bs-popover-left > .arrow::after, .bs-popover-auto[x-placement^="left"] > .arrow::after {
+ right: 1px;
+ border-width: 0.5rem 0 0.5rem 0.5rem;
+ border-left-color: #fff;
+}
+
+.popover-header {
+ padding: 0.5rem 0.75rem;
+ margin-bottom: 0;
+ font-size: 1rem;
+ background-color: #f7f7f7;
+ border-bottom: 1px solid #ebebeb;
+ border-top-left-radius: calc(0.3rem - 1px);
+ border-top-right-radius: calc(0.3rem - 1px);
+}
+
+.popover-header:empty {
+ display: none;
+}
+
+.popover-body {
+ padding: 0.5rem 0.75rem;
+ color: #212529;
+}
+
+.carousel {
+ position: relative;
+}
+
+.carousel.pointer-event {
+ -ms-touch-action: pan-y;
+ touch-action: pan-y;
+}
+
+.carousel-inner {
+ position: relative;
+ width: 100%;
+ overflow: hidden;
+}
+
+.carousel-inner::after {
+ display: block;
+ clear: both;
+ content: "";
+}
+
+.carousel-item {
+ position: relative;
+ display: none;
+ float: left;
+ width: 100%;
+ margin-right: -100%;
+ -webkit-backface-visibility: hidden;
+ backface-visibility: hidden;
+ transition: -webkit-transform 0.6s ease-in-out;
+ transition: transform 0.6s ease-in-out;
+ transition: transform 0.6s ease-in-out, -webkit-transform 0.6s ease-in-out;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .carousel-item {
+ transition: none;
+ }
+}
+
+.carousel-item.active,
+.carousel-item-next,
+.carousel-item-prev {
+ display: block;
+}
+
+.carousel-item-next:not(.carousel-item-left),
+.active.carousel-item-right {
+ -webkit-transform: translateX(100%);
+ transform: translateX(100%);
+}
+
+.carousel-item-prev:not(.carousel-item-right),
+.active.carousel-item-left {
+ -webkit-transform: translateX(-100%);
+ transform: translateX(-100%);
+}
+
+.carousel-fade .carousel-item {
+ opacity: 0;
+ transition-property: opacity;
+ -webkit-transform: none;
+ transform: none;
+}
+
+.carousel-fade .carousel-item.active,
+.carousel-fade .carousel-item-next.carousel-item-left,
+.carousel-fade .carousel-item-prev.carousel-item-right {
+ z-index: 1;
+ opacity: 1;
+}
+
+.carousel-fade .active.carousel-item-left,
+.carousel-fade .active.carousel-item-right {
+ z-index: 0;
+ opacity: 0;
+ transition: opacity 0s 0.6s;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .carousel-fade .active.carousel-item-left,
+ .carousel-fade .active.carousel-item-right {
+ transition: none;
+ }
+}
+
+.carousel-control-prev,
+.carousel-control-next {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ z-index: 1;
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-align: center;
+ align-items: center;
+ -ms-flex-pack: center;
+ justify-content: center;
+ width: 15%;
+ color: #fff;
+ text-align: center;
+ opacity: 0.5;
+ transition: opacity 0.15s ease;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .carousel-control-prev,
+ .carousel-control-next {
+ transition: none;
+ }
+}
+
+.carousel-control-prev:hover, .carousel-control-prev:focus,
+.carousel-control-next:hover,
+.carousel-control-next:focus {
+ color: #fff;
+ text-decoration: none;
+ outline: 0;
+ opacity: 0.9;
+}
+
+.carousel-control-prev {
+ left: 0;
+}
+
+.carousel-control-next {
+ right: 0;
+}
+
+.carousel-control-prev-icon,
+.carousel-control-next-icon {
+ display: inline-block;
+ width: 20px;
+ height: 20px;
+ background: no-repeat 50% / 100% 100%;
+}
+
+.carousel-control-prev-icon {
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M5.25 0l-4 4 4 4 1.5-1.5L4.25 4l2.5-2.5L5.25 0z'/%3e%3c/svg%3e");
+}
+
+.carousel-control-next-icon {
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 8'%3e%3cpath d='M2.75 0l-1.5 1.5L3.75 4l-2.5 2.5L2.75 8l4-4-4-4z'/%3e%3c/svg%3e");
+}
+
+.carousel-indicators {
+ position: absolute;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 15;
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-pack: center;
+ justify-content: center;
+ padding-left: 0;
+ margin-right: 15%;
+ margin-left: 15%;
+ list-style: none;
+}
+
+.carousel-indicators li {
+ box-sizing: content-box;
+ -ms-flex: 0 1 auto;
+ flex: 0 1 auto;
+ width: 30px;
+ height: 3px;
+ margin-right: 3px;
+ margin-left: 3px;
+ text-indent: -999px;
+ cursor: pointer;
+ background-color: #fff;
+ background-clip: padding-box;
+ border-top: 10px solid transparent;
+ border-bottom: 10px solid transparent;
+ opacity: .5;
+ transition: opacity 0.6s ease;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .carousel-indicators li {
+ transition: none;
+ }
+}
+
+.carousel-indicators .active {
+ opacity: 1;
+}
+
+.carousel-caption {
+ position: absolute;
+ right: 15%;
+ bottom: 20px;
+ left: 15%;
+ z-index: 10;
+ padding-top: 20px;
+ padding-bottom: 20px;
+ color: #fff;
+ text-align: center;
+}
+
+@-webkit-keyframes spinner-border {
+ to {
+ -webkit-transform: rotate(360deg);
+ transform: rotate(360deg);
+ }
+}
+
+@keyframes spinner-border {
+ to {
+ -webkit-transform: rotate(360deg);
+ transform: rotate(360deg);
+ }
+}
+
+.spinner-border {
+ display: inline-block;
+ width: 2rem;
+ height: 2rem;
+ vertical-align: text-bottom;
+ border: 0.25em solid currentColor;
+ border-right-color: transparent;
+ border-radius: 50%;
+ -webkit-animation: spinner-border .75s linear infinite;
+ animation: spinner-border .75s linear infinite;
+}
+
+.spinner-border-sm {
+ width: 1rem;
+ height: 1rem;
+ border-width: 0.2em;
+}
+
+@-webkit-keyframes spinner-grow {
+ 0% {
+ -webkit-transform: scale(0);
+ transform: scale(0);
+ }
+ 50% {
+ opacity: 1;
+ }
+}
+
+@keyframes spinner-grow {
+ 0% {
+ -webkit-transform: scale(0);
+ transform: scale(0);
+ }
+ 50% {
+ opacity: 1;
+ }
+}
+
+.spinner-grow {
+ display: inline-block;
+ width: 2rem;
+ height: 2rem;
+ vertical-align: text-bottom;
+ background-color: currentColor;
+ border-radius: 50%;
+ opacity: 0;
+ -webkit-animation: spinner-grow .75s linear infinite;
+ animation: spinner-grow .75s linear infinite;
+}
+
+.spinner-grow-sm {
+ width: 1rem;
+ height: 1rem;
+}
+
+.align-baseline {
+ vertical-align: baseline !important;
+}
+
+.align-top {
+ vertical-align: top !important;
+}
+
+.align-middle {
+ vertical-align: middle !important;
+}
+
+.align-bottom {
+ vertical-align: bottom !important;
+}
+
+.align-text-bottom {
+ vertical-align: text-bottom !important;
+}
+
+.align-text-top {
+ vertical-align: text-top !important;
+}
+
+.bg-primary {
+ background-color: #007bff !important;
+}
+
+a.bg-primary:hover, a.bg-primary:focus,
+button.bg-primary:hover,
+button.bg-primary:focus {
+ background-color: #0062cc !important;
+}
+
+.bg-secondary {
+ background-color: #6c757d !important;
+}
+
+a.bg-secondary:hover, a.bg-secondary:focus,
+button.bg-secondary:hover,
+button.bg-secondary:focus {
+ background-color: #545b62 !important;
+}
+
+.bg-success {
+ background-color: #28a745 !important;
+}
+
+a.bg-success:hover, a.bg-success:focus,
+button.bg-success:hover,
+button.bg-success:focus {
+ background-color: #1e7e34 !important;
+}
+
+.bg-info {
+ background-color: #17a2b8 !important;
+}
+
+a.bg-info:hover, a.bg-info:focus,
+button.bg-info:hover,
+button.bg-info:focus {
+ background-color: #117a8b !important;
+}
+
+.bg-warning {
+ background-color: #ffc107 !important;
+}
+
+a.bg-warning:hover, a.bg-warning:focus,
+button.bg-warning:hover,
+button.bg-warning:focus {
+ background-color: #d39e00 !important;
+}
+
+.bg-danger {
+ background-color: #dc3545 !important;
+}
+
+a.bg-danger:hover, a.bg-danger:focus,
+button.bg-danger:hover,
+button.bg-danger:focus {
+ background-color: #bd2130 !important;
+}
+
+.bg-light {
+ background-color: #f8f9fa !important;
+}
+
+a.bg-light:hover, a.bg-light:focus,
+button.bg-light:hover,
+button.bg-light:focus {
+ background-color: #dae0e5 !important;
+}
+
+.bg-dark {
+ background-color: #343a40 !important;
+}
+
+a.bg-dark:hover, a.bg-dark:focus,
+button.bg-dark:hover,
+button.bg-dark:focus {
+ background-color: #1d2124 !important;
+}
+
+.bg-white {
+ background-color: #fff !important;
+}
+
+.bg-transparent {
+ background-color: transparent !important;
+}
+
+.border {
+ border: 1px solid #dee2e6 !important;
+}
+
+.border-top {
+ border-top: 1px solid #dee2e6 !important;
+}
+
+.border-right {
+ border-right: 1px solid #dee2e6 !important;
+}
+
+.border-bottom {
+ border-bottom: 1px solid #dee2e6 !important;
+}
+
+.border-left {
+ border-left: 1px solid #dee2e6 !important;
+}
+
+.border-0 {
+ border: 0 !important;
+}
+
+.border-top-0 {
+ border-top: 0 !important;
+}
+
+.border-right-0 {
+ border-right: 0 !important;
+}
+
+.border-bottom-0 {
+ border-bottom: 0 !important;
+}
+
+.border-left-0 {
+ border-left: 0 !important;
+}
+
+.border-primary {
+ border-color: #007bff !important;
+}
+
+.border-secondary {
+ border-color: #6c757d !important;
+}
+
+.border-success {
+ border-color: #28a745 !important;
+}
+
+.border-info {
+ border-color: #17a2b8 !important;
+}
+
+.border-warning {
+ border-color: #ffc107 !important;
+}
+
+.border-danger {
+ border-color: #dc3545 !important;
+}
+
+.border-light {
+ border-color: #f8f9fa !important;
+}
+
+.border-dark {
+ border-color: #343a40 !important;
+}
+
+.border-white {
+ border-color: #fff !important;
+}
+
+.rounded-sm {
+ border-radius: 0.2rem !important;
+}
+
+.rounded {
+ border-radius: 0.25rem !important;
+}
+
+.rounded-top {
+ border-top-left-radius: 0.25rem !important;
+ border-top-right-radius: 0.25rem !important;
+}
+
+.rounded-right {
+ border-top-right-radius: 0.25rem !important;
+ border-bottom-right-radius: 0.25rem !important;
+}
+
+.rounded-bottom {
+ border-bottom-right-radius: 0.25rem !important;
+ border-bottom-left-radius: 0.25rem !important;
+}
+
+.rounded-left {
+ border-top-left-radius: 0.25rem !important;
+ border-bottom-left-radius: 0.25rem !important;
+}
+
+.rounded-lg {
+ border-radius: 0.3rem !important;
+}
+
+.rounded-circle {
+ border-radius: 50% !important;
+}
+
+.rounded-pill {
+ border-radius: 50rem !important;
+}
+
+.rounded-0 {
+ border-radius: 0 !important;
+}
+
+.clearfix::after {
+ display: block;
+ clear: both;
+ content: "";
+}
+
+.d-none {
+ display: none !important;
+}
+
+.d-inline {
+ display: inline !important;
+}
+
+.d-inline-block {
+ display: inline-block !important;
+}
+
+.d-block {
+ display: block !important;
+}
+
+.d-table {
+ display: table !important;
+}
+
+.d-table-row {
+ display: table-row !important;
+}
+
+.d-table-cell {
+ display: table-cell !important;
+}
+
+.d-flex {
+ display: -ms-flexbox !important;
+ display: flex !important;
+}
+
+.d-inline-flex {
+ display: -ms-inline-flexbox !important;
+ display: inline-flex !important;
+}
+
+@media (min-width: 576px) {
+ .d-sm-none {
+ display: none !important;
+ }
+ .d-sm-inline {
+ display: inline !important;
+ }
+ .d-sm-inline-block {
+ display: inline-block !important;
+ }
+ .d-sm-block {
+ display: block !important;
+ }
+ .d-sm-table {
+ display: table !important;
+ }
+ .d-sm-table-row {
+ display: table-row !important;
+ }
+ .d-sm-table-cell {
+ display: table-cell !important;
+ }
+ .d-sm-flex {
+ display: -ms-flexbox !important;
+ display: flex !important;
+ }
+ .d-sm-inline-flex {
+ display: -ms-inline-flexbox !important;
+ display: inline-flex !important;
+ }
+}
+
+@media (min-width: 768px) {
+ .d-md-none {
+ display: none !important;
+ }
+ .d-md-inline {
+ display: inline !important;
+ }
+ .d-md-inline-block {
+ display: inline-block !important;
+ }
+ .d-md-block {
+ display: block !important;
+ }
+ .d-md-table {
+ display: table !important;
+ }
+ .d-md-table-row {
+ display: table-row !important;
+ }
+ .d-md-table-cell {
+ display: table-cell !important;
+ }
+ .d-md-flex {
+ display: -ms-flexbox !important;
+ display: flex !important;
+ }
+ .d-md-inline-flex {
+ display: -ms-inline-flexbox !important;
+ display: inline-flex !important;
+ }
+}
+
+@media (min-width: 992px) {
+ .d-lg-none {
+ display: none !important;
+ }
+ .d-lg-inline {
+ display: inline !important;
+ }
+ .d-lg-inline-block {
+ display: inline-block !important;
+ }
+ .d-lg-block {
+ display: block !important;
+ }
+ .d-lg-table {
+ display: table !important;
+ }
+ .d-lg-table-row {
+ display: table-row !important;
+ }
+ .d-lg-table-cell {
+ display: table-cell !important;
+ }
+ .d-lg-flex {
+ display: -ms-flexbox !important;
+ display: flex !important;
+ }
+ .d-lg-inline-flex {
+ display: -ms-inline-flexbox !important;
+ display: inline-flex !important;
+ }
+}
+
+@media (min-width: 1200px) {
+ .d-xl-none {
+ display: none !important;
+ }
+ .d-xl-inline {
+ display: inline !important;
+ }
+ .d-xl-inline-block {
+ display: inline-block !important;
+ }
+ .d-xl-block {
+ display: block !important;
+ }
+ .d-xl-table {
+ display: table !important;
+ }
+ .d-xl-table-row {
+ display: table-row !important;
+ }
+ .d-xl-table-cell {
+ display: table-cell !important;
+ }
+ .d-xl-flex {
+ display: -ms-flexbox !important;
+ display: flex !important;
+ }
+ .d-xl-inline-flex {
+ display: -ms-inline-flexbox !important;
+ display: inline-flex !important;
+ }
+}
+
+@media print {
+ .d-print-none {
+ display: none !important;
+ }
+ .d-print-inline {
+ display: inline !important;
+ }
+ .d-print-inline-block {
+ display: inline-block !important;
+ }
+ .d-print-block {
+ display: block !important;
+ }
+ .d-print-table {
+ display: table !important;
+ }
+ .d-print-table-row {
+ display: table-row !important;
+ }
+ .d-print-table-cell {
+ display: table-cell !important;
+ }
+ .d-print-flex {
+ display: -ms-flexbox !important;
+ display: flex !important;
+ }
+ .d-print-inline-flex {
+ display: -ms-inline-flexbox !important;
+ display: inline-flex !important;
+ }
+}
+
+.embed-responsive {
+ position: relative;
+ display: block;
+ width: 100%;
+ padding: 0;
+ overflow: hidden;
+}
+
+.embed-responsive::before {
+ display: block;
+ content: "";
+}
+
+.embed-responsive .embed-responsive-item,
+.embed-responsive iframe,
+.embed-responsive embed,
+.embed-responsive object,
+.embed-responsive video {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ border: 0;
+}
+
+.embed-responsive-21by9::before {
+ padding-top: 42.857143%;
+}
+
+.embed-responsive-16by9::before {
+ padding-top: 56.25%;
+}
+
+.embed-responsive-4by3::before {
+ padding-top: 75%;
+}
+
+.embed-responsive-1by1::before {
+ padding-top: 100%;
+}
+
+.flex-row {
+ -ms-flex-direction: row !important;
+ flex-direction: row !important;
+}
+
+.flex-column {
+ -ms-flex-direction: column !important;
+ flex-direction: column !important;
+}
+
+.flex-row-reverse {
+ -ms-flex-direction: row-reverse !important;
+ flex-direction: row-reverse !important;
+}
+
+.flex-column-reverse {
+ -ms-flex-direction: column-reverse !important;
+ flex-direction: column-reverse !important;
+}
+
+.flex-wrap {
+ -ms-flex-wrap: wrap !important;
+ flex-wrap: wrap !important;
+}
+
+.flex-nowrap {
+ -ms-flex-wrap: nowrap !important;
+ flex-wrap: nowrap !important;
+}
+
+.flex-wrap-reverse {
+ -ms-flex-wrap: wrap-reverse !important;
+ flex-wrap: wrap-reverse !important;
+}
+
+.flex-fill {
+ -ms-flex: 1 1 auto !important;
+ flex: 1 1 auto !important;
+}
+
+.flex-grow-0 {
+ -ms-flex-positive: 0 !important;
+ flex-grow: 0 !important;
+}
+
+.flex-grow-1 {
+ -ms-flex-positive: 1 !important;
+ flex-grow: 1 !important;
+}
+
+.flex-shrink-0 {
+ -ms-flex-negative: 0 !important;
+ flex-shrink: 0 !important;
+}
+
+.flex-shrink-1 {
+ -ms-flex-negative: 1 !important;
+ flex-shrink: 1 !important;
+}
+
+.justify-content-start {
+ -ms-flex-pack: start !important;
+ justify-content: flex-start !important;
+}
+
+.justify-content-end {
+ -ms-flex-pack: end !important;
+ justify-content: flex-end !important;
+}
+
+.justify-content-center {
+ -ms-flex-pack: center !important;
+ justify-content: center !important;
+}
+
+.justify-content-between {
+ -ms-flex-pack: justify !important;
+ justify-content: space-between !important;
+}
+
+.justify-content-around {
+ -ms-flex-pack: distribute !important;
+ justify-content: space-around !important;
+}
+
+.align-items-start {
+ -ms-flex-align: start !important;
+ align-items: flex-start !important;
+}
+
+.align-items-end {
+ -ms-flex-align: end !important;
+ align-items: flex-end !important;
+}
+
+.align-items-center {
+ -ms-flex-align: center !important;
+ align-items: center !important;
+}
+
+.align-items-baseline {
+ -ms-flex-align: baseline !important;
+ align-items: baseline !important;
+}
+
+.align-items-stretch {
+ -ms-flex-align: stretch !important;
+ align-items: stretch !important;
+}
+
+.align-content-start {
+ -ms-flex-line-pack: start !important;
+ align-content: flex-start !important;
+}
+
+.align-content-end {
+ -ms-flex-line-pack: end !important;
+ align-content: flex-end !important;
+}
+
+.align-content-center {
+ -ms-flex-line-pack: center !important;
+ align-content: center !important;
+}
+
+.align-content-between {
+ -ms-flex-line-pack: justify !important;
+ align-content: space-between !important;
+}
+
+.align-content-around {
+ -ms-flex-line-pack: distribute !important;
+ align-content: space-around !important;
+}
+
+.align-content-stretch {
+ -ms-flex-line-pack: stretch !important;
+ align-content: stretch !important;
+}
+
+.align-self-auto {
+ -ms-flex-item-align: auto !important;
+ align-self: auto !important;
+}
+
+.align-self-start {
+ -ms-flex-item-align: start !important;
+ align-self: flex-start !important;
+}
+
+.align-self-end {
+ -ms-flex-item-align: end !important;
+ align-self: flex-end !important;
+}
+
+.align-self-center {
+ -ms-flex-item-align: center !important;
+ align-self: center !important;
+}
+
+.align-self-baseline {
+ -ms-flex-item-align: baseline !important;
+ align-self: baseline !important;
+}
+
+.align-self-stretch {
+ -ms-flex-item-align: stretch !important;
+ align-self: stretch !important;
+}
+
+@media (min-width: 576px) {
+ .flex-sm-row {
+ -ms-flex-direction: row !important;
+ flex-direction: row !important;
+ }
+ .flex-sm-column {
+ -ms-flex-direction: column !important;
+ flex-direction: column !important;
+ }
+ .flex-sm-row-reverse {
+ -ms-flex-direction: row-reverse !important;
+ flex-direction: row-reverse !important;
+ }
+ .flex-sm-column-reverse {
+ -ms-flex-direction: column-reverse !important;
+ flex-direction: column-reverse !important;
+ }
+ .flex-sm-wrap {
+ -ms-flex-wrap: wrap !important;
+ flex-wrap: wrap !important;
+ }
+ .flex-sm-nowrap {
+ -ms-flex-wrap: nowrap !important;
+ flex-wrap: nowrap !important;
+ }
+ .flex-sm-wrap-reverse {
+ -ms-flex-wrap: wrap-reverse !important;
+ flex-wrap: wrap-reverse !important;
+ }
+ .flex-sm-fill {
+ -ms-flex: 1 1 auto !important;
+ flex: 1 1 auto !important;
+ }
+ .flex-sm-grow-0 {
+ -ms-flex-positive: 0 !important;
+ flex-grow: 0 !important;
+ }
+ .flex-sm-grow-1 {
+ -ms-flex-positive: 1 !important;
+ flex-grow: 1 !important;
+ }
+ .flex-sm-shrink-0 {
+ -ms-flex-negative: 0 !important;
+ flex-shrink: 0 !important;
+ }
+ .flex-sm-shrink-1 {
+ -ms-flex-negative: 1 !important;
+ flex-shrink: 1 !important;
+ }
+ .justify-content-sm-start {
+ -ms-flex-pack: start !important;
+ justify-content: flex-start !important;
+ }
+ .justify-content-sm-end {
+ -ms-flex-pack: end !important;
+ justify-content: flex-end !important;
+ }
+ .justify-content-sm-center {
+ -ms-flex-pack: center !important;
+ justify-content: center !important;
+ }
+ .justify-content-sm-between {
+ -ms-flex-pack: justify !important;
+ justify-content: space-between !important;
+ }
+ .justify-content-sm-around {
+ -ms-flex-pack: distribute !important;
+ justify-content: space-around !important;
+ }
+ .align-items-sm-start {
+ -ms-flex-align: start !important;
+ align-items: flex-start !important;
+ }
+ .align-items-sm-end {
+ -ms-flex-align: end !important;
+ align-items: flex-end !important;
+ }
+ .align-items-sm-center {
+ -ms-flex-align: center !important;
+ align-items: center !important;
+ }
+ .align-items-sm-baseline {
+ -ms-flex-align: baseline !important;
+ align-items: baseline !important;
+ }
+ .align-items-sm-stretch {
+ -ms-flex-align: stretch !important;
+ align-items: stretch !important;
+ }
+ .align-content-sm-start {
+ -ms-flex-line-pack: start !important;
+ align-content: flex-start !important;
+ }
+ .align-content-sm-end {
+ -ms-flex-line-pack: end !important;
+ align-content: flex-end !important;
+ }
+ .align-content-sm-center {
+ -ms-flex-line-pack: center !important;
+ align-content: center !important;
+ }
+ .align-content-sm-between {
+ -ms-flex-line-pack: justify !important;
+ align-content: space-between !important;
+ }
+ .align-content-sm-around {
+ -ms-flex-line-pack: distribute !important;
+ align-content: space-around !important;
+ }
+ .align-content-sm-stretch {
+ -ms-flex-line-pack: stretch !important;
+ align-content: stretch !important;
+ }
+ .align-self-sm-auto {
+ -ms-flex-item-align: auto !important;
+ align-self: auto !important;
+ }
+ .align-self-sm-start {
+ -ms-flex-item-align: start !important;
+ align-self: flex-start !important;
+ }
+ .align-self-sm-end {
+ -ms-flex-item-align: end !important;
+ align-self: flex-end !important;
+ }
+ .align-self-sm-center {
+ -ms-flex-item-align: center !important;
+ align-self: center !important;
+ }
+ .align-self-sm-baseline {
+ -ms-flex-item-align: baseline !important;
+ align-self: baseline !important;
+ }
+ .align-self-sm-stretch {
+ -ms-flex-item-align: stretch !important;
+ align-self: stretch !important;
+ }
+}
+
+@media (min-width: 768px) {
+ .flex-md-row {
+ -ms-flex-direction: row !important;
+ flex-direction: row !important;
+ }
+ .flex-md-column {
+ -ms-flex-direction: column !important;
+ flex-direction: column !important;
+ }
+ .flex-md-row-reverse {
+ -ms-flex-direction: row-reverse !important;
+ flex-direction: row-reverse !important;
+ }
+ .flex-md-column-reverse {
+ -ms-flex-direction: column-reverse !important;
+ flex-direction: column-reverse !important;
+ }
+ .flex-md-wrap {
+ -ms-flex-wrap: wrap !important;
+ flex-wrap: wrap !important;
+ }
+ .flex-md-nowrap {
+ -ms-flex-wrap: nowrap !important;
+ flex-wrap: nowrap !important;
+ }
+ .flex-md-wrap-reverse {
+ -ms-flex-wrap: wrap-reverse !important;
+ flex-wrap: wrap-reverse !important;
+ }
+ .flex-md-fill {
+ -ms-flex: 1 1 auto !important;
+ flex: 1 1 auto !important;
+ }
+ .flex-md-grow-0 {
+ -ms-flex-positive: 0 !important;
+ flex-grow: 0 !important;
+ }
+ .flex-md-grow-1 {
+ -ms-flex-positive: 1 !important;
+ flex-grow: 1 !important;
+ }
+ .flex-md-shrink-0 {
+ -ms-flex-negative: 0 !important;
+ flex-shrink: 0 !important;
+ }
+ .flex-md-shrink-1 {
+ -ms-flex-negative: 1 !important;
+ flex-shrink: 1 !important;
+ }
+ .justify-content-md-start {
+ -ms-flex-pack: start !important;
+ justify-content: flex-start !important;
+ }
+ .justify-content-md-end {
+ -ms-flex-pack: end !important;
+ justify-content: flex-end !important;
+ }
+ .justify-content-md-center {
+ -ms-flex-pack: center !important;
+ justify-content: center !important;
+ }
+ .justify-content-md-between {
+ -ms-flex-pack: justify !important;
+ justify-content: space-between !important;
+ }
+ .justify-content-md-around {
+ -ms-flex-pack: distribute !important;
+ justify-content: space-around !important;
+ }
+ .align-items-md-start {
+ -ms-flex-align: start !important;
+ align-items: flex-start !important;
+ }
+ .align-items-md-end {
+ -ms-flex-align: end !important;
+ align-items: flex-end !important;
+ }
+ .align-items-md-center {
+ -ms-flex-align: center !important;
+ align-items: center !important;
+ }
+ .align-items-md-baseline {
+ -ms-flex-align: baseline !important;
+ align-items: baseline !important;
+ }
+ .align-items-md-stretch {
+ -ms-flex-align: stretch !important;
+ align-items: stretch !important;
+ }
+ .align-content-md-start {
+ -ms-flex-line-pack: start !important;
+ align-content: flex-start !important;
+ }
+ .align-content-md-end {
+ -ms-flex-line-pack: end !important;
+ align-content: flex-end !important;
+ }
+ .align-content-md-center {
+ -ms-flex-line-pack: center !important;
+ align-content: center !important;
+ }
+ .align-content-md-between {
+ -ms-flex-line-pack: justify !important;
+ align-content: space-between !important;
+ }
+ .align-content-md-around {
+ -ms-flex-line-pack: distribute !important;
+ align-content: space-around !important;
+ }
+ .align-content-md-stretch {
+ -ms-flex-line-pack: stretch !important;
+ align-content: stretch !important;
+ }
+ .align-self-md-auto {
+ -ms-flex-item-align: auto !important;
+ align-self: auto !important;
+ }
+ .align-self-md-start {
+ -ms-flex-item-align: start !important;
+ align-self: flex-start !important;
+ }
+ .align-self-md-end {
+ -ms-flex-item-align: end !important;
+ align-self: flex-end !important;
+ }
+ .align-self-md-center {
+ -ms-flex-item-align: center !important;
+ align-self: center !important;
+ }
+ .align-self-md-baseline {
+ -ms-flex-item-align: baseline !important;
+ align-self: baseline !important;
+ }
+ .align-self-md-stretch {
+ -ms-flex-item-align: stretch !important;
+ align-self: stretch !important;
+ }
+}
+
+@media (min-width: 992px) {
+ .flex-lg-row {
+ -ms-flex-direction: row !important;
+ flex-direction: row !important;
+ }
+ .flex-lg-column {
+ -ms-flex-direction: column !important;
+ flex-direction: column !important;
+ }
+ .flex-lg-row-reverse {
+ -ms-flex-direction: row-reverse !important;
+ flex-direction: row-reverse !important;
+ }
+ .flex-lg-column-reverse {
+ -ms-flex-direction: column-reverse !important;
+ flex-direction: column-reverse !important;
+ }
+ .flex-lg-wrap {
+ -ms-flex-wrap: wrap !important;
+ flex-wrap: wrap !important;
+ }
+ .flex-lg-nowrap {
+ -ms-flex-wrap: nowrap !important;
+ flex-wrap: nowrap !important;
+ }
+ .flex-lg-wrap-reverse {
+ -ms-flex-wrap: wrap-reverse !important;
+ flex-wrap: wrap-reverse !important;
+ }
+ .flex-lg-fill {
+ -ms-flex: 1 1 auto !important;
+ flex: 1 1 auto !important;
+ }
+ .flex-lg-grow-0 {
+ -ms-flex-positive: 0 !important;
+ flex-grow: 0 !important;
+ }
+ .flex-lg-grow-1 {
+ -ms-flex-positive: 1 !important;
+ flex-grow: 1 !important;
+ }
+ .flex-lg-shrink-0 {
+ -ms-flex-negative: 0 !important;
+ flex-shrink: 0 !important;
+ }
+ .flex-lg-shrink-1 {
+ -ms-flex-negative: 1 !important;
+ flex-shrink: 1 !important;
+ }
+ .justify-content-lg-start {
+ -ms-flex-pack: start !important;
+ justify-content: flex-start !important;
+ }
+ .justify-content-lg-end {
+ -ms-flex-pack: end !important;
+ justify-content: flex-end !important;
+ }
+ .justify-content-lg-center {
+ -ms-flex-pack: center !important;
+ justify-content: center !important;
+ }
+ .justify-content-lg-between {
+ -ms-flex-pack: justify !important;
+ justify-content: space-between !important;
+ }
+ .justify-content-lg-around {
+ -ms-flex-pack: distribute !important;
+ justify-content: space-around !important;
+ }
+ .align-items-lg-start {
+ -ms-flex-align: start !important;
+ align-items: flex-start !important;
+ }
+ .align-items-lg-end {
+ -ms-flex-align: end !important;
+ align-items: flex-end !important;
+ }
+ .align-items-lg-center {
+ -ms-flex-align: center !important;
+ align-items: center !important;
+ }
+ .align-items-lg-baseline {
+ -ms-flex-align: baseline !important;
+ align-items: baseline !important;
+ }
+ .align-items-lg-stretch {
+ -ms-flex-align: stretch !important;
+ align-items: stretch !important;
+ }
+ .align-content-lg-start {
+ -ms-flex-line-pack: start !important;
+ align-content: flex-start !important;
+ }
+ .align-content-lg-end {
+ -ms-flex-line-pack: end !important;
+ align-content: flex-end !important;
+ }
+ .align-content-lg-center {
+ -ms-flex-line-pack: center !important;
+ align-content: center !important;
+ }
+ .align-content-lg-between {
+ -ms-flex-line-pack: justify !important;
+ align-content: space-between !important;
+ }
+ .align-content-lg-around {
+ -ms-flex-line-pack: distribute !important;
+ align-content: space-around !important;
+ }
+ .align-content-lg-stretch {
+ -ms-flex-line-pack: stretch !important;
+ align-content: stretch !important;
+ }
+ .align-self-lg-auto {
+ -ms-flex-item-align: auto !important;
+ align-self: auto !important;
+ }
+ .align-self-lg-start {
+ -ms-flex-item-align: start !important;
+ align-self: flex-start !important;
+ }
+ .align-self-lg-end {
+ -ms-flex-item-align: end !important;
+ align-self: flex-end !important;
+ }
+ .align-self-lg-center {
+ -ms-flex-item-align: center !important;
+ align-self: center !important;
+ }
+ .align-self-lg-baseline {
+ -ms-flex-item-align: baseline !important;
+ align-self: baseline !important;
+ }
+ .align-self-lg-stretch {
+ -ms-flex-item-align: stretch !important;
+ align-self: stretch !important;
+ }
+}
+
+@media (min-width: 1200px) {
+ .flex-xl-row {
+ -ms-flex-direction: row !important;
+ flex-direction: row !important;
+ }
+ .flex-xl-column {
+ -ms-flex-direction: column !important;
+ flex-direction: column !important;
+ }
+ .flex-xl-row-reverse {
+ -ms-flex-direction: row-reverse !important;
+ flex-direction: row-reverse !important;
+ }
+ .flex-xl-column-reverse {
+ -ms-flex-direction: column-reverse !important;
+ flex-direction: column-reverse !important;
+ }
+ .flex-xl-wrap {
+ -ms-flex-wrap: wrap !important;
+ flex-wrap: wrap !important;
+ }
+ .flex-xl-nowrap {
+ -ms-flex-wrap: nowrap !important;
+ flex-wrap: nowrap !important;
+ }
+ .flex-xl-wrap-reverse {
+ -ms-flex-wrap: wrap-reverse !important;
+ flex-wrap: wrap-reverse !important;
+ }
+ .flex-xl-fill {
+ -ms-flex: 1 1 auto !important;
+ flex: 1 1 auto !important;
+ }
+ .flex-xl-grow-0 {
+ -ms-flex-positive: 0 !important;
+ flex-grow: 0 !important;
+ }
+ .flex-xl-grow-1 {
+ -ms-flex-positive: 1 !important;
+ flex-grow: 1 !important;
+ }
+ .flex-xl-shrink-0 {
+ -ms-flex-negative: 0 !important;
+ flex-shrink: 0 !important;
+ }
+ .flex-xl-shrink-1 {
+ -ms-flex-negative: 1 !important;
+ flex-shrink: 1 !important;
+ }
+ .justify-content-xl-start {
+ -ms-flex-pack: start !important;
+ justify-content: flex-start !important;
+ }
+ .justify-content-xl-end {
+ -ms-flex-pack: end !important;
+ justify-content: flex-end !important;
+ }
+ .justify-content-xl-center {
+ -ms-flex-pack: center !important;
+ justify-content: center !important;
+ }
+ .justify-content-xl-between {
+ -ms-flex-pack: justify !important;
+ justify-content: space-between !important;
+ }
+ .justify-content-xl-around {
+ -ms-flex-pack: distribute !important;
+ justify-content: space-around !important;
+ }
+ .align-items-xl-start {
+ -ms-flex-align: start !important;
+ align-items: flex-start !important;
+ }
+ .align-items-xl-end {
+ -ms-flex-align: end !important;
+ align-items: flex-end !important;
+ }
+ .align-items-xl-center {
+ -ms-flex-align: center !important;
+ align-items: center !important;
+ }
+ .align-items-xl-baseline {
+ -ms-flex-align: baseline !important;
+ align-items: baseline !important;
+ }
+ .align-items-xl-stretch {
+ -ms-flex-align: stretch !important;
+ align-items: stretch !important;
+ }
+ .align-content-xl-start {
+ -ms-flex-line-pack: start !important;
+ align-content: flex-start !important;
+ }
+ .align-content-xl-end {
+ -ms-flex-line-pack: end !important;
+ align-content: flex-end !important;
+ }
+ .align-content-xl-center {
+ -ms-flex-line-pack: center !important;
+ align-content: center !important;
+ }
+ .align-content-xl-between {
+ -ms-flex-line-pack: justify !important;
+ align-content: space-between !important;
+ }
+ .align-content-xl-around {
+ -ms-flex-line-pack: distribute !important;
+ align-content: space-around !important;
+ }
+ .align-content-xl-stretch {
+ -ms-flex-line-pack: stretch !important;
+ align-content: stretch !important;
+ }
+ .align-self-xl-auto {
+ -ms-flex-item-align: auto !important;
+ align-self: auto !important;
+ }
+ .align-self-xl-start {
+ -ms-flex-item-align: start !important;
+ align-self: flex-start !important;
+ }
+ .align-self-xl-end {
+ -ms-flex-item-align: end !important;
+ align-self: flex-end !important;
+ }
+ .align-self-xl-center {
+ -ms-flex-item-align: center !important;
+ align-self: center !important;
+ }
+ .align-self-xl-baseline {
+ -ms-flex-item-align: baseline !important;
+ align-self: baseline !important;
+ }
+ .align-self-xl-stretch {
+ -ms-flex-item-align: stretch !important;
+ align-self: stretch !important;
+ }
+}
+
+.float-left {
+ float: left !important;
+}
+
+.float-right {
+ float: right !important;
+}
+
+.float-none {
+ float: none !important;
+}
+
+@media (min-width: 576px) {
+ .float-sm-left {
+ float: left !important;
+ }
+ .float-sm-right {
+ float: right !important;
+ }
+ .float-sm-none {
+ float: none !important;
+ }
+}
+
+@media (min-width: 768px) {
+ .float-md-left {
+ float: left !important;
+ }
+ .float-md-right {
+ float: right !important;
+ }
+ .float-md-none {
+ float: none !important;
+ }
+}
+
+@media (min-width: 992px) {
+ .float-lg-left {
+ float: left !important;
+ }
+ .float-lg-right {
+ float: right !important;
+ }
+ .float-lg-none {
+ float: none !important;
+ }
+}
+
+@media (min-width: 1200px) {
+ .float-xl-left {
+ float: left !important;
+ }
+ .float-xl-right {
+ float: right !important;
+ }
+ .float-xl-none {
+ float: none !important;
+ }
+}
+
+.overflow-auto {
+ overflow: auto !important;
+}
+
+.overflow-hidden {
+ overflow: hidden !important;
+}
+
+.position-static {
+ position: static !important;
+}
+
+.position-relative {
+ position: relative !important;
+}
+
+.position-absolute {
+ position: absolute !important;
+}
+
+.position-fixed {
+ position: fixed !important;
+}
+
+.position-sticky {
+ position: -webkit-sticky !important;
+ position: sticky !important;
+}
+
+.fixed-top {
+ position: fixed;
+ top: 0;
+ right: 0;
+ left: 0;
+ z-index: 1030;
+}
+
+.fixed-bottom {
+ position: fixed;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 1030;
+}
+
+@supports ((position: -webkit-sticky) or (position: sticky)) {
+ .sticky-top {
+ position: -webkit-sticky;
+ position: sticky;
+ top: 0;
+ z-index: 1020;
+ }
+}
+
+.sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ white-space: nowrap;
+ border: 0;
+}
+
+.sr-only-focusable:active, .sr-only-focusable:focus {
+ position: static;
+ width: auto;
+ height: auto;
+ overflow: visible;
+ clip: auto;
+ white-space: normal;
+}
+
+.shadow-sm {
+ box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075) !important;
+}
+
+.shadow {
+ box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important;
+}
+
+.shadow-lg {
+ box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.175) !important;
+}
+
+.shadow-none {
+ box-shadow: none !important;
+}
+
+.w-25 {
+ width: 25% !important;
+}
+
+.w-50 {
+ width: 50% !important;
+}
+
+.w-75 {
+ width: 75% !important;
+}
+
+.w-100 {
+ width: 100% !important;
+}
+
+.w-auto {
+ width: auto !important;
+}
+
+.h-25 {
+ height: 25% !important;
+}
+
+.h-50 {
+ height: 50% !important;
+}
+
+.h-75 {
+ height: 75% !important;
+}
+
+.h-100 {
+ height: 100% !important;
+}
+
+.h-auto {
+ height: auto !important;
+}
+
+.mw-100 {
+ max-width: 100% !important;
+}
+
+.mh-100 {
+ max-height: 100% !important;
+}
+
+.min-vw-100 {
+ min-width: 100vw !important;
+}
+
+.min-vh-100 {
+ min-height: 100vh !important;
+}
+
+.vw-100 {
+ width: 100vw !important;
+}
+
+.vh-100 {
+ height: 100vh !important;
+}
+
+.stretched-link::after {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 1;
+ pointer-events: auto;
+ content: "";
+ background-color: rgba(0, 0, 0, 0);
+}
+
+.m-0 {
+ margin: 0 !important;
+}
+
+.mt-0,
+.my-0 {
+ margin-top: 0 !important;
+}
+
+.mr-0,
+.mx-0 {
+ margin-right: 0 !important;
+}
+
+.mb-0,
+.my-0 {
+ margin-bottom: 0 !important;
+}
+
+.ml-0,
+.mx-0 {
+ margin-left: 0 !important;
+}
+
+.m-1 {
+ margin: 0.25rem !important;
+}
+
+.mt-1,
+.my-1 {
+ margin-top: 0.25rem !important;
+}
+
+.mr-1,
+.mx-1 {
+ margin-right: 0.25rem !important;
+}
+
+.mb-1,
+.my-1 {
+ margin-bottom: 0.25rem !important;
+}
+
+.ml-1,
+.mx-1 {
+ margin-left: 0.25rem !important;
+}
+
+.m-2 {
+ margin: 0.5rem !important;
+}
+
+.mt-2,
+.my-2 {
+ margin-top: 0.5rem !important;
+}
+
+.mr-2,
+.mx-2 {
+ margin-right: 0.5rem !important;
+}
+
+.mb-2,
+.my-2 {
+ margin-bottom: 0.5rem !important;
+}
+
+.ml-2,
+.mx-2 {
+ margin-left: 0.5rem !important;
+}
+
+.m-3 {
+ margin: 1rem !important;
+}
+
+.mt-3,
+.my-3 {
+ margin-top: 1rem !important;
+}
+
+.mr-3,
+.mx-3 {
+ margin-right: 1rem !important;
+}
+
+.mb-3,
+.my-3 {
+ margin-bottom: 1rem !important;
+}
+
+.ml-3,
+.mx-3 {
+ margin-left: 1rem !important;
+}
+
+.m-4 {
+ margin: 1.5rem !important;
+}
+
+.mt-4,
+.my-4 {
+ margin-top: 1.5rem !important;
+}
+
+.mr-4,
+.mx-4 {
+ margin-right: 1.5rem !important;
+}
+
+.mb-4,
+.my-4 {
+ margin-bottom: 1.5rem !important;
+}
+
+.ml-4,
+.mx-4 {
+ margin-left: 1.5rem !important;
+}
+
+.m-5 {
+ margin: 3rem !important;
+}
+
+.mt-5,
+.my-5 {
+ margin-top: 3rem !important;
+}
+
+.mr-5,
+.mx-5 {
+ margin-right: 3rem !important;
+}
+
+.mb-5,
+.my-5 {
+ margin-bottom: 3rem !important;
+}
+
+.ml-5,
+.mx-5 {
+ margin-left: 3rem !important;
+}
+
+.p-0 {
+ padding: 0 !important;
+}
+
+.pt-0,
+.py-0 {
+ padding-top: 0 !important;
+}
+
+.pr-0,
+.px-0 {
+ padding-right: 0 !important;
+}
+
+.pb-0,
+.py-0 {
+ padding-bottom: 0 !important;
+}
+
+.pl-0,
+.px-0 {
+ padding-left: 0 !important;
+}
+
+.p-1 {
+ padding: 0.25rem !important;
+}
+
+.pt-1,
+.py-1 {
+ padding-top: 0.25rem !important;
+}
+
+.pr-1,
+.px-1 {
+ padding-right: 0.25rem !important;
+}
+
+.pb-1,
+.py-1 {
+ padding-bottom: 0.25rem !important;
+}
+
+.pl-1,
+.px-1 {
+ padding-left: 0.25rem !important;
+}
+
+.p-2 {
+ padding: 0.5rem !important;
+}
+
+.pt-2,
+.py-2 {
+ padding-top: 0.5rem !important;
+}
+
+.pr-2,
+.px-2 {
+ padding-right: 0.5rem !important;
+}
+
+.pb-2,
+.py-2 {
+ padding-bottom: 0.5rem !important;
+}
+
+.pl-2,
+.px-2 {
+ padding-left: 0.5rem !important;
+}
+
+.p-3 {
+ padding: 1rem !important;
+}
+
+.pt-3,
+.py-3 {
+ padding-top: 1rem !important;
+}
+
+.pr-3,
+.px-3 {
+ padding-right: 1rem !important;
+}
+
+.pb-3,
+.py-3 {
+ padding-bottom: 1rem !important;
+}
+
+.pl-3,
+.px-3 {
+ padding-left: 1rem !important;
+}
+
+.p-4 {
+ padding: 1.5rem !important;
+}
+
+.pt-4,
+.py-4 {
+ padding-top: 1.5rem !important;
+}
+
+.pr-4,
+.px-4 {
+ padding-right: 1.5rem !important;
+}
+
+.pb-4,
+.py-4 {
+ padding-bottom: 1.5rem !important;
+}
+
+.pl-4,
+.px-4 {
+ padding-left: 1.5rem !important;
+}
+
+.p-5 {
+ padding: 3rem !important;
+}
+
+.pt-5,
+.py-5 {
+ padding-top: 3rem !important;
+}
+
+.pr-5,
+.px-5 {
+ padding-right: 3rem !important;
+}
+
+.pb-5,
+.py-5 {
+ padding-bottom: 3rem !important;
+}
+
+.pl-5,
+.px-5 {
+ padding-left: 3rem !important;
+}
+
+.m-n1 {
+ margin: -0.25rem !important;
+}
+
+.mt-n1,
+.my-n1 {
+ margin-top: -0.25rem !important;
+}
+
+.mr-n1,
+.mx-n1 {
+ margin-right: -0.25rem !important;
+}
+
+.mb-n1,
+.my-n1 {
+ margin-bottom: -0.25rem !important;
+}
+
+.ml-n1,
+.mx-n1 {
+ margin-left: -0.25rem !important;
+}
+
+.m-n2 {
+ margin: -0.5rem !important;
+}
+
+.mt-n2,
+.my-n2 {
+ margin-top: -0.5rem !important;
+}
+
+.mr-n2,
+.mx-n2 {
+ margin-right: -0.5rem !important;
+}
+
+.mb-n2,
+.my-n2 {
+ margin-bottom: -0.5rem !important;
+}
+
+.ml-n2,
+.mx-n2 {
+ margin-left: -0.5rem !important;
+}
+
+.m-n3 {
+ margin: -1rem !important;
+}
+
+.mt-n3,
+.my-n3 {
+ margin-top: -1rem !important;
+}
+
+.mr-n3,
+.mx-n3 {
+ margin-right: -1rem !important;
+}
+
+.mb-n3,
+.my-n3 {
+ margin-bottom: -1rem !important;
+}
+
+.ml-n3,
+.mx-n3 {
+ margin-left: -1rem !important;
+}
+
+.m-n4 {
+ margin: -1.5rem !important;
+}
+
+.mt-n4,
+.my-n4 {
+ margin-top: -1.5rem !important;
+}
+
+.mr-n4,
+.mx-n4 {
+ margin-right: -1.5rem !important;
+}
+
+.mb-n4,
+.my-n4 {
+ margin-bottom: -1.5rem !important;
+}
+
+.ml-n4,
+.mx-n4 {
+ margin-left: -1.5rem !important;
+}
+
+.m-n5 {
+ margin: -3rem !important;
+}
+
+.mt-n5,
+.my-n5 {
+ margin-top: -3rem !important;
+}
+
+.mr-n5,
+.mx-n5 {
+ margin-right: -3rem !important;
+}
+
+.mb-n5,
+.my-n5 {
+ margin-bottom: -3rem !important;
+}
+
+.ml-n5,
+.mx-n5 {
+ margin-left: -3rem !important;
+}
+
+.m-auto {
+ margin: auto !important;
+}
+
+.mt-auto,
+.my-auto {
+ margin-top: auto !important;
+}
+
+.mr-auto,
+.mx-auto {
+ margin-right: auto !important;
+}
+
+.mb-auto,
+.my-auto {
+ margin-bottom: auto !important;
+}
+
+.ml-auto,
+.mx-auto {
+ margin-left: auto !important;
+}
+
+@media (min-width: 576px) {
+ .m-sm-0 {
+ margin: 0 !important;
+ }
+ .mt-sm-0,
+ .my-sm-0 {
+ margin-top: 0 !important;
+ }
+ .mr-sm-0,
+ .mx-sm-0 {
+ margin-right: 0 !important;
+ }
+ .mb-sm-0,
+ .my-sm-0 {
+ margin-bottom: 0 !important;
+ }
+ .ml-sm-0,
+ .mx-sm-0 {
+ margin-left: 0 !important;
+ }
+ .m-sm-1 {
+ margin: 0.25rem !important;
+ }
+ .mt-sm-1,
+ .my-sm-1 {
+ margin-top: 0.25rem !important;
+ }
+ .mr-sm-1,
+ .mx-sm-1 {
+ margin-right: 0.25rem !important;
+ }
+ .mb-sm-1,
+ .my-sm-1 {
+ margin-bottom: 0.25rem !important;
+ }
+ .ml-sm-1,
+ .mx-sm-1 {
+ margin-left: 0.25rem !important;
+ }
+ .m-sm-2 {
+ margin: 0.5rem !important;
+ }
+ .mt-sm-2,
+ .my-sm-2 {
+ margin-top: 0.5rem !important;
+ }
+ .mr-sm-2,
+ .mx-sm-2 {
+ margin-right: 0.5rem !important;
+ }
+ .mb-sm-2,
+ .my-sm-2 {
+ margin-bottom: 0.5rem !important;
+ }
+ .ml-sm-2,
+ .mx-sm-2 {
+ margin-left: 0.5rem !important;
+ }
+ .m-sm-3 {
+ margin: 1rem !important;
+ }
+ .mt-sm-3,
+ .my-sm-3 {
+ margin-top: 1rem !important;
+ }
+ .mr-sm-3,
+ .mx-sm-3 {
+ margin-right: 1rem !important;
+ }
+ .mb-sm-3,
+ .my-sm-3 {
+ margin-bottom: 1rem !important;
+ }
+ .ml-sm-3,
+ .mx-sm-3 {
+ margin-left: 1rem !important;
+ }
+ .m-sm-4 {
+ margin: 1.5rem !important;
+ }
+ .mt-sm-4,
+ .my-sm-4 {
+ margin-top: 1.5rem !important;
+ }
+ .mr-sm-4,
+ .mx-sm-4 {
+ margin-right: 1.5rem !important;
+ }
+ .mb-sm-4,
+ .my-sm-4 {
+ margin-bottom: 1.5rem !important;
+ }
+ .ml-sm-4,
+ .mx-sm-4 {
+ margin-left: 1.5rem !important;
+ }
+ .m-sm-5 {
+ margin: 3rem !important;
+ }
+ .mt-sm-5,
+ .my-sm-5 {
+ margin-top: 3rem !important;
+ }
+ .mr-sm-5,
+ .mx-sm-5 {
+ margin-right: 3rem !important;
+ }
+ .mb-sm-5,
+ .my-sm-5 {
+ margin-bottom: 3rem !important;
+ }
+ .ml-sm-5,
+ .mx-sm-5 {
+ margin-left: 3rem !important;
+ }
+ .p-sm-0 {
+ padding: 0 !important;
+ }
+ .pt-sm-0,
+ .py-sm-0 {
+ padding-top: 0 !important;
+ }
+ .pr-sm-0,
+ .px-sm-0 {
+ padding-right: 0 !important;
+ }
+ .pb-sm-0,
+ .py-sm-0 {
+ padding-bottom: 0 !important;
+ }
+ .pl-sm-0,
+ .px-sm-0 {
+ padding-left: 0 !important;
+ }
+ .p-sm-1 {
+ padding: 0.25rem !important;
+ }
+ .pt-sm-1,
+ .py-sm-1 {
+ padding-top: 0.25rem !important;
+ }
+ .pr-sm-1,
+ .px-sm-1 {
+ padding-right: 0.25rem !important;
+ }
+ .pb-sm-1,
+ .py-sm-1 {
+ padding-bottom: 0.25rem !important;
+ }
+ .pl-sm-1,
+ .px-sm-1 {
+ padding-left: 0.25rem !important;
+ }
+ .p-sm-2 {
+ padding: 0.5rem !important;
+ }
+ .pt-sm-2,
+ .py-sm-2 {
+ padding-top: 0.5rem !important;
+ }
+ .pr-sm-2,
+ .px-sm-2 {
+ padding-right: 0.5rem !important;
+ }
+ .pb-sm-2,
+ .py-sm-2 {
+ padding-bottom: 0.5rem !important;
+ }
+ .pl-sm-2,
+ .px-sm-2 {
+ padding-left: 0.5rem !important;
+ }
+ .p-sm-3 {
+ padding: 1rem !important;
+ }
+ .pt-sm-3,
+ .py-sm-3 {
+ padding-top: 1rem !important;
+ }
+ .pr-sm-3,
+ .px-sm-3 {
+ padding-right: 1rem !important;
+ }
+ .pb-sm-3,
+ .py-sm-3 {
+ padding-bottom: 1rem !important;
+ }
+ .pl-sm-3,
+ .px-sm-3 {
+ padding-left: 1rem !important;
+ }
+ .p-sm-4 {
+ padding: 1.5rem !important;
+ }
+ .pt-sm-4,
+ .py-sm-4 {
+ padding-top: 1.5rem !important;
+ }
+ .pr-sm-4,
+ .px-sm-4 {
+ padding-right: 1.5rem !important;
+ }
+ .pb-sm-4,
+ .py-sm-4 {
+ padding-bottom: 1.5rem !important;
+ }
+ .pl-sm-4,
+ .px-sm-4 {
+ padding-left: 1.5rem !important;
+ }
+ .p-sm-5 {
+ padding: 3rem !important;
+ }
+ .pt-sm-5,
+ .py-sm-5 {
+ padding-top: 3rem !important;
+ }
+ .pr-sm-5,
+ .px-sm-5 {
+ padding-right: 3rem !important;
+ }
+ .pb-sm-5,
+ .py-sm-5 {
+ padding-bottom: 3rem !important;
+ }
+ .pl-sm-5,
+ .px-sm-5 {
+ padding-left: 3rem !important;
+ }
+ .m-sm-n1 {
+ margin: -0.25rem !important;
+ }
+ .mt-sm-n1,
+ .my-sm-n1 {
+ margin-top: -0.25rem !important;
+ }
+ .mr-sm-n1,
+ .mx-sm-n1 {
+ margin-right: -0.25rem !important;
+ }
+ .mb-sm-n1,
+ .my-sm-n1 {
+ margin-bottom: -0.25rem !important;
+ }
+ .ml-sm-n1,
+ .mx-sm-n1 {
+ margin-left: -0.25rem !important;
+ }
+ .m-sm-n2 {
+ margin: -0.5rem !important;
+ }
+ .mt-sm-n2,
+ .my-sm-n2 {
+ margin-top: -0.5rem !important;
+ }
+ .mr-sm-n2,
+ .mx-sm-n2 {
+ margin-right: -0.5rem !important;
+ }
+ .mb-sm-n2,
+ .my-sm-n2 {
+ margin-bottom: -0.5rem !important;
+ }
+ .ml-sm-n2,
+ .mx-sm-n2 {
+ margin-left: -0.5rem !important;
+ }
+ .m-sm-n3 {
+ margin: -1rem !important;
+ }
+ .mt-sm-n3,
+ .my-sm-n3 {
+ margin-top: -1rem !important;
+ }
+ .mr-sm-n3,
+ .mx-sm-n3 {
+ margin-right: -1rem !important;
+ }
+ .mb-sm-n3,
+ .my-sm-n3 {
+ margin-bottom: -1rem !important;
+ }
+ .ml-sm-n3,
+ .mx-sm-n3 {
+ margin-left: -1rem !important;
+ }
+ .m-sm-n4 {
+ margin: -1.5rem !important;
+ }
+ .mt-sm-n4,
+ .my-sm-n4 {
+ margin-top: -1.5rem !important;
+ }
+ .mr-sm-n4,
+ .mx-sm-n4 {
+ margin-right: -1.5rem !important;
+ }
+ .mb-sm-n4,
+ .my-sm-n4 {
+ margin-bottom: -1.5rem !important;
+ }
+ .ml-sm-n4,
+ .mx-sm-n4 {
+ margin-left: -1.5rem !important;
+ }
+ .m-sm-n5 {
+ margin: -3rem !important;
+ }
+ .mt-sm-n5,
+ .my-sm-n5 {
+ margin-top: -3rem !important;
+ }
+ .mr-sm-n5,
+ .mx-sm-n5 {
+ margin-right: -3rem !important;
+ }
+ .mb-sm-n5,
+ .my-sm-n5 {
+ margin-bottom: -3rem !important;
+ }
+ .ml-sm-n5,
+ .mx-sm-n5 {
+ margin-left: -3rem !important;
+ }
+ .m-sm-auto {
+ margin: auto !important;
+ }
+ .mt-sm-auto,
+ .my-sm-auto {
+ margin-top: auto !important;
+ }
+ .mr-sm-auto,
+ .mx-sm-auto {
+ margin-right: auto !important;
+ }
+ .mb-sm-auto,
+ .my-sm-auto {
+ margin-bottom: auto !important;
+ }
+ .ml-sm-auto,
+ .mx-sm-auto {
+ margin-left: auto !important;
+ }
+}
+
+@media (min-width: 768px) {
+ .m-md-0 {
+ margin: 0 !important;
+ }
+ .mt-md-0,
+ .my-md-0 {
+ margin-top: 0 !important;
+ }
+ .mr-md-0,
+ .mx-md-0 {
+ margin-right: 0 !important;
+ }
+ .mb-md-0,
+ .my-md-0 {
+ margin-bottom: 0 !important;
+ }
+ .ml-md-0,
+ .mx-md-0 {
+ margin-left: 0 !important;
+ }
+ .m-md-1 {
+ margin: 0.25rem !important;
+ }
+ .mt-md-1,
+ .my-md-1 {
+ margin-top: 0.25rem !important;
+ }
+ .mr-md-1,
+ .mx-md-1 {
+ margin-right: 0.25rem !important;
+ }
+ .mb-md-1,
+ .my-md-1 {
+ margin-bottom: 0.25rem !important;
+ }
+ .ml-md-1,
+ .mx-md-1 {
+ margin-left: 0.25rem !important;
+ }
+ .m-md-2 {
+ margin: 0.5rem !important;
+ }
+ .mt-md-2,
+ .my-md-2 {
+ margin-top: 0.5rem !important;
+ }
+ .mr-md-2,
+ .mx-md-2 {
+ margin-right: 0.5rem !important;
+ }
+ .mb-md-2,
+ .my-md-2 {
+ margin-bottom: 0.5rem !important;
+ }
+ .ml-md-2,
+ .mx-md-2 {
+ margin-left: 0.5rem !important;
+ }
+ .m-md-3 {
+ margin: 1rem !important;
+ }
+ .mt-md-3,
+ .my-md-3 {
+ margin-top: 1rem !important;
+ }
+ .mr-md-3,
+ .mx-md-3 {
+ margin-right: 1rem !important;
+ }
+ .mb-md-3,
+ .my-md-3 {
+ margin-bottom: 1rem !important;
+ }
+ .ml-md-3,
+ .mx-md-3 {
+ margin-left: 1rem !important;
+ }
+ .m-md-4 {
+ margin: 1.5rem !important;
+ }
+ .mt-md-4,
+ .my-md-4 {
+ margin-top: 1.5rem !important;
+ }
+ .mr-md-4,
+ .mx-md-4 {
+ margin-right: 1.5rem !important;
+ }
+ .mb-md-4,
+ .my-md-4 {
+ margin-bottom: 1.5rem !important;
+ }
+ .ml-md-4,
+ .mx-md-4 {
+ margin-left: 1.5rem !important;
+ }
+ .m-md-5 {
+ margin: 3rem !important;
+ }
+ .mt-md-5,
+ .my-md-5 {
+ margin-top: 3rem !important;
+ }
+ .mr-md-5,
+ .mx-md-5 {
+ margin-right: 3rem !important;
+ }
+ .mb-md-5,
+ .my-md-5 {
+ margin-bottom: 3rem !important;
+ }
+ .ml-md-5,
+ .mx-md-5 {
+ margin-left: 3rem !important;
+ }
+ .p-md-0 {
+ padding: 0 !important;
+ }
+ .pt-md-0,
+ .py-md-0 {
+ padding-top: 0 !important;
+ }
+ .pr-md-0,
+ .px-md-0 {
+ padding-right: 0 !important;
+ }
+ .pb-md-0,
+ .py-md-0 {
+ padding-bottom: 0 !important;
+ }
+ .pl-md-0,
+ .px-md-0 {
+ padding-left: 0 !important;
+ }
+ .p-md-1 {
+ padding: 0.25rem !important;
+ }
+ .pt-md-1,
+ .py-md-1 {
+ padding-top: 0.25rem !important;
+ }
+ .pr-md-1,
+ .px-md-1 {
+ padding-right: 0.25rem !important;
+ }
+ .pb-md-1,
+ .py-md-1 {
+ padding-bottom: 0.25rem !important;
+ }
+ .pl-md-1,
+ .px-md-1 {
+ padding-left: 0.25rem !important;
+ }
+ .p-md-2 {
+ padding: 0.5rem !important;
+ }
+ .pt-md-2,
+ .py-md-2 {
+ padding-top: 0.5rem !important;
+ }
+ .pr-md-2,
+ .px-md-2 {
+ padding-right: 0.5rem !important;
+ }
+ .pb-md-2,
+ .py-md-2 {
+ padding-bottom: 0.5rem !important;
+ }
+ .pl-md-2,
+ .px-md-2 {
+ padding-left: 0.5rem !important;
+ }
+ .p-md-3 {
+ padding: 1rem !important;
+ }
+ .pt-md-3,
+ .py-md-3 {
+ padding-top: 1rem !important;
+ }
+ .pr-md-3,
+ .px-md-3 {
+ padding-right: 1rem !important;
+ }
+ .pb-md-3,
+ .py-md-3 {
+ padding-bottom: 1rem !important;
+ }
+ .pl-md-3,
+ .px-md-3 {
+ padding-left: 1rem !important;
+ }
+ .p-md-4 {
+ padding: 1.5rem !important;
+ }
+ .pt-md-4,
+ .py-md-4 {
+ padding-top: 1.5rem !important;
+ }
+ .pr-md-4,
+ .px-md-4 {
+ padding-right: 1.5rem !important;
+ }
+ .pb-md-4,
+ .py-md-4 {
+ padding-bottom: 1.5rem !important;
+ }
+ .pl-md-4,
+ .px-md-4 {
+ padding-left: 1.5rem !important;
+ }
+ .p-md-5 {
+ padding: 3rem !important;
+ }
+ .pt-md-5,
+ .py-md-5 {
+ padding-top: 3rem !important;
+ }
+ .pr-md-5,
+ .px-md-5 {
+ padding-right: 3rem !important;
+ }
+ .pb-md-5,
+ .py-md-5 {
+ padding-bottom: 3rem !important;
+ }
+ .pl-md-5,
+ .px-md-5 {
+ padding-left: 3rem !important;
+ }
+ .m-md-n1 {
+ margin: -0.25rem !important;
+ }
+ .mt-md-n1,
+ .my-md-n1 {
+ margin-top: -0.25rem !important;
+ }
+ .mr-md-n1,
+ .mx-md-n1 {
+ margin-right: -0.25rem !important;
+ }
+ .mb-md-n1,
+ .my-md-n1 {
+ margin-bottom: -0.25rem !important;
+ }
+ .ml-md-n1,
+ .mx-md-n1 {
+ margin-left: -0.25rem !important;
+ }
+ .m-md-n2 {
+ margin: -0.5rem !important;
+ }
+ .mt-md-n2,
+ .my-md-n2 {
+ margin-top: -0.5rem !important;
+ }
+ .mr-md-n2,
+ .mx-md-n2 {
+ margin-right: -0.5rem !important;
+ }
+ .mb-md-n2,
+ .my-md-n2 {
+ margin-bottom: -0.5rem !important;
+ }
+ .ml-md-n2,
+ .mx-md-n2 {
+ margin-left: -0.5rem !important;
+ }
+ .m-md-n3 {
+ margin: -1rem !important;
+ }
+ .mt-md-n3,
+ .my-md-n3 {
+ margin-top: -1rem !important;
+ }
+ .mr-md-n3,
+ .mx-md-n3 {
+ margin-right: -1rem !important;
+ }
+ .mb-md-n3,
+ .my-md-n3 {
+ margin-bottom: -1rem !important;
+ }
+ .ml-md-n3,
+ .mx-md-n3 {
+ margin-left: -1rem !important;
+ }
+ .m-md-n4 {
+ margin: -1.5rem !important;
+ }
+ .mt-md-n4,
+ .my-md-n4 {
+ margin-top: -1.5rem !important;
+ }
+ .mr-md-n4,
+ .mx-md-n4 {
+ margin-right: -1.5rem !important;
+ }
+ .mb-md-n4,
+ .my-md-n4 {
+ margin-bottom: -1.5rem !important;
+ }
+ .ml-md-n4,
+ .mx-md-n4 {
+ margin-left: -1.5rem !important;
+ }
+ .m-md-n5 {
+ margin: -3rem !important;
+ }
+ .mt-md-n5,
+ .my-md-n5 {
+ margin-top: -3rem !important;
+ }
+ .mr-md-n5,
+ .mx-md-n5 {
+ margin-right: -3rem !important;
+ }
+ .mb-md-n5,
+ .my-md-n5 {
+ margin-bottom: -3rem !important;
+ }
+ .ml-md-n5,
+ .mx-md-n5 {
+ margin-left: -3rem !important;
+ }
+ .m-md-auto {
+ margin: auto !important;
+ }
+ .mt-md-auto,
+ .my-md-auto {
+ margin-top: auto !important;
+ }
+ .mr-md-auto,
+ .mx-md-auto {
+ margin-right: auto !important;
+ }
+ .mb-md-auto,
+ .my-md-auto {
+ margin-bottom: auto !important;
+ }
+ .ml-md-auto,
+ .mx-md-auto {
+ margin-left: auto !important;
+ }
+}
+
+@media (min-width: 992px) {
+ .m-lg-0 {
+ margin: 0 !important;
+ }
+ .mt-lg-0,
+ .my-lg-0 {
+ margin-top: 0 !important;
+ }
+ .mr-lg-0,
+ .mx-lg-0 {
+ margin-right: 0 !important;
+ }
+ .mb-lg-0,
+ .my-lg-0 {
+ margin-bottom: 0 !important;
+ }
+ .ml-lg-0,
+ .mx-lg-0 {
+ margin-left: 0 !important;
+ }
+ .m-lg-1 {
+ margin: 0.25rem !important;
+ }
+ .mt-lg-1,
+ .my-lg-1 {
+ margin-top: 0.25rem !important;
+ }
+ .mr-lg-1,
+ .mx-lg-1 {
+ margin-right: 0.25rem !important;
+ }
+ .mb-lg-1,
+ .my-lg-1 {
+ margin-bottom: 0.25rem !important;
+ }
+ .ml-lg-1,
+ .mx-lg-1 {
+ margin-left: 0.25rem !important;
+ }
+ .m-lg-2 {
+ margin: 0.5rem !important;
+ }
+ .mt-lg-2,
+ .my-lg-2 {
+ margin-top: 0.5rem !important;
+ }
+ .mr-lg-2,
+ .mx-lg-2 {
+ margin-right: 0.5rem !important;
+ }
+ .mb-lg-2,
+ .my-lg-2 {
+ margin-bottom: 0.5rem !important;
+ }
+ .ml-lg-2,
+ .mx-lg-2 {
+ margin-left: 0.5rem !important;
+ }
+ .m-lg-3 {
+ margin: 1rem !important;
+ }
+ .mt-lg-3,
+ .my-lg-3 {
+ margin-top: 1rem !important;
+ }
+ .mr-lg-3,
+ .mx-lg-3 {
+ margin-right: 1rem !important;
+ }
+ .mb-lg-3,
+ .my-lg-3 {
+ margin-bottom: 1rem !important;
+ }
+ .ml-lg-3,
+ .mx-lg-3 {
+ margin-left: 1rem !important;
+ }
+ .m-lg-4 {
+ margin: 1.5rem !important;
+ }
+ .mt-lg-4,
+ .my-lg-4 {
+ margin-top: 1.5rem !important;
+ }
+ .mr-lg-4,
+ .mx-lg-4 {
+ margin-right: 1.5rem !important;
+ }
+ .mb-lg-4,
+ .my-lg-4 {
+ margin-bottom: 1.5rem !important;
+ }
+ .ml-lg-4,
+ .mx-lg-4 {
+ margin-left: 1.5rem !important;
+ }
+ .m-lg-5 {
+ margin: 3rem !important;
+ }
+ .mt-lg-5,
+ .my-lg-5 {
+ margin-top: 3rem !important;
+ }
+ .mr-lg-5,
+ .mx-lg-5 {
+ margin-right: 3rem !important;
+ }
+ .mb-lg-5,
+ .my-lg-5 {
+ margin-bottom: 3rem !important;
+ }
+ .ml-lg-5,
+ .mx-lg-5 {
+ margin-left: 3rem !important;
+ }
+ .p-lg-0 {
+ padding: 0 !important;
+ }
+ .pt-lg-0,
+ .py-lg-0 {
+ padding-top: 0 !important;
+ }
+ .pr-lg-0,
+ .px-lg-0 {
+ padding-right: 0 !important;
+ }
+ .pb-lg-0,
+ .py-lg-0 {
+ padding-bottom: 0 !important;
+ }
+ .pl-lg-0,
+ .px-lg-0 {
+ padding-left: 0 !important;
+ }
+ .p-lg-1 {
+ padding: 0.25rem !important;
+ }
+ .pt-lg-1,
+ .py-lg-1 {
+ padding-top: 0.25rem !important;
+ }
+ .pr-lg-1,
+ .px-lg-1 {
+ padding-right: 0.25rem !important;
+ }
+ .pb-lg-1,
+ .py-lg-1 {
+ padding-bottom: 0.25rem !important;
+ }
+ .pl-lg-1,
+ .px-lg-1 {
+ padding-left: 0.25rem !important;
+ }
+ .p-lg-2 {
+ padding: 0.5rem !important;
+ }
+ .pt-lg-2,
+ .py-lg-2 {
+ padding-top: 0.5rem !important;
+ }
+ .pr-lg-2,
+ .px-lg-2 {
+ padding-right: 0.5rem !important;
+ }
+ .pb-lg-2,
+ .py-lg-2 {
+ padding-bottom: 0.5rem !important;
+ }
+ .pl-lg-2,
+ .px-lg-2 {
+ padding-left: 0.5rem !important;
+ }
+ .p-lg-3 {
+ padding: 1rem !important;
+ }
+ .pt-lg-3,
+ .py-lg-3 {
+ padding-top: 1rem !important;
+ }
+ .pr-lg-3,
+ .px-lg-3 {
+ padding-right: 1rem !important;
+ }
+ .pb-lg-3,
+ .py-lg-3 {
+ padding-bottom: 1rem !important;
+ }
+ .pl-lg-3,
+ .px-lg-3 {
+ padding-left: 1rem !important;
+ }
+ .p-lg-4 {
+ padding: 1.5rem !important;
+ }
+ .pt-lg-4,
+ .py-lg-4 {
+ padding-top: 1.5rem !important;
+ }
+ .pr-lg-4,
+ .px-lg-4 {
+ padding-right: 1.5rem !important;
+ }
+ .pb-lg-4,
+ .py-lg-4 {
+ padding-bottom: 1.5rem !important;
+ }
+ .pl-lg-4,
+ .px-lg-4 {
+ padding-left: 1.5rem !important;
+ }
+ .p-lg-5 {
+ padding: 3rem !important;
+ }
+ .pt-lg-5,
+ .py-lg-5 {
+ padding-top: 3rem !important;
+ }
+ .pr-lg-5,
+ .px-lg-5 {
+ padding-right: 3rem !important;
+ }
+ .pb-lg-5,
+ .py-lg-5 {
+ padding-bottom: 3rem !important;
+ }
+ .pl-lg-5,
+ .px-lg-5 {
+ padding-left: 3rem !important;
+ }
+ .m-lg-n1 {
+ margin: -0.25rem !important;
+ }
+ .mt-lg-n1,
+ .my-lg-n1 {
+ margin-top: -0.25rem !important;
+ }
+ .mr-lg-n1,
+ .mx-lg-n1 {
+ margin-right: -0.25rem !important;
+ }
+ .mb-lg-n1,
+ .my-lg-n1 {
+ margin-bottom: -0.25rem !important;
+ }
+ .ml-lg-n1,
+ .mx-lg-n1 {
+ margin-left: -0.25rem !important;
+ }
+ .m-lg-n2 {
+ margin: -0.5rem !important;
+ }
+ .mt-lg-n2,
+ .my-lg-n2 {
+ margin-top: -0.5rem !important;
+ }
+ .mr-lg-n2,
+ .mx-lg-n2 {
+ margin-right: -0.5rem !important;
+ }
+ .mb-lg-n2,
+ .my-lg-n2 {
+ margin-bottom: -0.5rem !important;
+ }
+ .ml-lg-n2,
+ .mx-lg-n2 {
+ margin-left: -0.5rem !important;
+ }
+ .m-lg-n3 {
+ margin: -1rem !important;
+ }
+ .mt-lg-n3,
+ .my-lg-n3 {
+ margin-top: -1rem !important;
+ }
+ .mr-lg-n3,
+ .mx-lg-n3 {
+ margin-right: -1rem !important;
+ }
+ .mb-lg-n3,
+ .my-lg-n3 {
+ margin-bottom: -1rem !important;
+ }
+ .ml-lg-n3,
+ .mx-lg-n3 {
+ margin-left: -1rem !important;
+ }
+ .m-lg-n4 {
+ margin: -1.5rem !important;
+ }
+ .mt-lg-n4,
+ .my-lg-n4 {
+ margin-top: -1.5rem !important;
+ }
+ .mr-lg-n4,
+ .mx-lg-n4 {
+ margin-right: -1.5rem !important;
+ }
+ .mb-lg-n4,
+ .my-lg-n4 {
+ margin-bottom: -1.5rem !important;
+ }
+ .ml-lg-n4,
+ .mx-lg-n4 {
+ margin-left: -1.5rem !important;
+ }
+ .m-lg-n5 {
+ margin: -3rem !important;
+ }
+ .mt-lg-n5,
+ .my-lg-n5 {
+ margin-top: -3rem !important;
+ }
+ .mr-lg-n5,
+ .mx-lg-n5 {
+ margin-right: -3rem !important;
+ }
+ .mb-lg-n5,
+ .my-lg-n5 {
+ margin-bottom: -3rem !important;
+ }
+ .ml-lg-n5,
+ .mx-lg-n5 {
+ margin-left: -3rem !important;
+ }
+ .m-lg-auto {
+ margin: auto !important;
+ }
+ .mt-lg-auto,
+ .my-lg-auto {
+ margin-top: auto !important;
+ }
+ .mr-lg-auto,
+ .mx-lg-auto {
+ margin-right: auto !important;
+ }
+ .mb-lg-auto,
+ .my-lg-auto {
+ margin-bottom: auto !important;
+ }
+ .ml-lg-auto,
+ .mx-lg-auto {
+ margin-left: auto !important;
+ }
+}
+
+@media (min-width: 1200px) {
+ .m-xl-0 {
+ margin: 0 !important;
+ }
+ .mt-xl-0,
+ .my-xl-0 {
+ margin-top: 0 !important;
+ }
+ .mr-xl-0,
+ .mx-xl-0 {
+ margin-right: 0 !important;
+ }
+ .mb-xl-0,
+ .my-xl-0 {
+ margin-bottom: 0 !important;
+ }
+ .ml-xl-0,
+ .mx-xl-0 {
+ margin-left: 0 !important;
+ }
+ .m-xl-1 {
+ margin: 0.25rem !important;
+ }
+ .mt-xl-1,
+ .my-xl-1 {
+ margin-top: 0.25rem !important;
+ }
+ .mr-xl-1,
+ .mx-xl-1 {
+ margin-right: 0.25rem !important;
+ }
+ .mb-xl-1,
+ .my-xl-1 {
+ margin-bottom: 0.25rem !important;
+ }
+ .ml-xl-1,
+ .mx-xl-1 {
+ margin-left: 0.25rem !important;
+ }
+ .m-xl-2 {
+ margin: 0.5rem !important;
+ }
+ .mt-xl-2,
+ .my-xl-2 {
+ margin-top: 0.5rem !important;
+ }
+ .mr-xl-2,
+ .mx-xl-2 {
+ margin-right: 0.5rem !important;
+ }
+ .mb-xl-2,
+ .my-xl-2 {
+ margin-bottom: 0.5rem !important;
+ }
+ .ml-xl-2,
+ .mx-xl-2 {
+ margin-left: 0.5rem !important;
+ }
+ .m-xl-3 {
+ margin: 1rem !important;
+ }
+ .mt-xl-3,
+ .my-xl-3 {
+ margin-top: 1rem !important;
+ }
+ .mr-xl-3,
+ .mx-xl-3 {
+ margin-right: 1rem !important;
+ }
+ .mb-xl-3,
+ .my-xl-3 {
+ margin-bottom: 1rem !important;
+ }
+ .ml-xl-3,
+ .mx-xl-3 {
+ margin-left: 1rem !important;
+ }
+ .m-xl-4 {
+ margin: 1.5rem !important;
+ }
+ .mt-xl-4,
+ .my-xl-4 {
+ margin-top: 1.5rem !important;
+ }
+ .mr-xl-4,
+ .mx-xl-4 {
+ margin-right: 1.5rem !important;
+ }
+ .mb-xl-4,
+ .my-xl-4 {
+ margin-bottom: 1.5rem !important;
+ }
+ .ml-xl-4,
+ .mx-xl-4 {
+ margin-left: 1.5rem !important;
+ }
+ .m-xl-5 {
+ margin: 3rem !important;
+ }
+ .mt-xl-5,
+ .my-xl-5 {
+ margin-top: 3rem !important;
+ }
+ .mr-xl-5,
+ .mx-xl-5 {
+ margin-right: 3rem !important;
+ }
+ .mb-xl-5,
+ .my-xl-5 {
+ margin-bottom: 3rem !important;
+ }
+ .ml-xl-5,
+ .mx-xl-5 {
+ margin-left: 3rem !important;
+ }
+ .p-xl-0 {
+ padding: 0 !important;
+ }
+ .pt-xl-0,
+ .py-xl-0 {
+ padding-top: 0 !important;
+ }
+ .pr-xl-0,
+ .px-xl-0 {
+ padding-right: 0 !important;
+ }
+ .pb-xl-0,
+ .py-xl-0 {
+ padding-bottom: 0 !important;
+ }
+ .pl-xl-0,
+ .px-xl-0 {
+ padding-left: 0 !important;
+ }
+ .p-xl-1 {
+ padding: 0.25rem !important;
+ }
+ .pt-xl-1,
+ .py-xl-1 {
+ padding-top: 0.25rem !important;
+ }
+ .pr-xl-1,
+ .px-xl-1 {
+ padding-right: 0.25rem !important;
+ }
+ .pb-xl-1,
+ .py-xl-1 {
+ padding-bottom: 0.25rem !important;
+ }
+ .pl-xl-1,
+ .px-xl-1 {
+ padding-left: 0.25rem !important;
+ }
+ .p-xl-2 {
+ padding: 0.5rem !important;
+ }
+ .pt-xl-2,
+ .py-xl-2 {
+ padding-top: 0.5rem !important;
+ }
+ .pr-xl-2,
+ .px-xl-2 {
+ padding-right: 0.5rem !important;
+ }
+ .pb-xl-2,
+ .py-xl-2 {
+ padding-bottom: 0.5rem !important;
+ }
+ .pl-xl-2,
+ .px-xl-2 {
+ padding-left: 0.5rem !important;
+ }
+ .p-xl-3 {
+ padding: 1rem !important;
+ }
+ .pt-xl-3,
+ .py-xl-3 {
+ padding-top: 1rem !important;
+ }
+ .pr-xl-3,
+ .px-xl-3 {
+ padding-right: 1rem !important;
+ }
+ .pb-xl-3,
+ .py-xl-3 {
+ padding-bottom: 1rem !important;
+ }
+ .pl-xl-3,
+ .px-xl-3 {
+ padding-left: 1rem !important;
+ }
+ .p-xl-4 {
+ padding: 1.5rem !important;
+ }
+ .pt-xl-4,
+ .py-xl-4 {
+ padding-top: 1.5rem !important;
+ }
+ .pr-xl-4,
+ .px-xl-4 {
+ padding-right: 1.5rem !important;
+ }
+ .pb-xl-4,
+ .py-xl-4 {
+ padding-bottom: 1.5rem !important;
+ }
+ .pl-xl-4,
+ .px-xl-4 {
+ padding-left: 1.5rem !important;
+ }
+ .p-xl-5 {
+ padding: 3rem !important;
+ }
+ .pt-xl-5,
+ .py-xl-5 {
+ padding-top: 3rem !important;
+ }
+ .pr-xl-5,
+ .px-xl-5 {
+ padding-right: 3rem !important;
+ }
+ .pb-xl-5,
+ .py-xl-5 {
+ padding-bottom: 3rem !important;
+ }
+ .pl-xl-5,
+ .px-xl-5 {
+ padding-left: 3rem !important;
+ }
+ .m-xl-n1 {
+ margin: -0.25rem !important;
+ }
+ .mt-xl-n1,
+ .my-xl-n1 {
+ margin-top: -0.25rem !important;
+ }
+ .mr-xl-n1,
+ .mx-xl-n1 {
+ margin-right: -0.25rem !important;
+ }
+ .mb-xl-n1,
+ .my-xl-n1 {
+ margin-bottom: -0.25rem !important;
+ }
+ .ml-xl-n1,
+ .mx-xl-n1 {
+ margin-left: -0.25rem !important;
+ }
+ .m-xl-n2 {
+ margin: -0.5rem !important;
+ }
+ .mt-xl-n2,
+ .my-xl-n2 {
+ margin-top: -0.5rem !important;
+ }
+ .mr-xl-n2,
+ .mx-xl-n2 {
+ margin-right: -0.5rem !important;
+ }
+ .mb-xl-n2,
+ .my-xl-n2 {
+ margin-bottom: -0.5rem !important;
+ }
+ .ml-xl-n2,
+ .mx-xl-n2 {
+ margin-left: -0.5rem !important;
+ }
+ .m-xl-n3 {
+ margin: -1rem !important;
+ }
+ .mt-xl-n3,
+ .my-xl-n3 {
+ margin-top: -1rem !important;
+ }
+ .mr-xl-n3,
+ .mx-xl-n3 {
+ margin-right: -1rem !important;
+ }
+ .mb-xl-n3,
+ .my-xl-n3 {
+ margin-bottom: -1rem !important;
+ }
+ .ml-xl-n3,
+ .mx-xl-n3 {
+ margin-left: -1rem !important;
+ }
+ .m-xl-n4 {
+ margin: -1.5rem !important;
+ }
+ .mt-xl-n4,
+ .my-xl-n4 {
+ margin-top: -1.5rem !important;
+ }
+ .mr-xl-n4,
+ .mx-xl-n4 {
+ margin-right: -1.5rem !important;
+ }
+ .mb-xl-n4,
+ .my-xl-n4 {
+ margin-bottom: -1.5rem !important;
+ }
+ .ml-xl-n4,
+ .mx-xl-n4 {
+ margin-left: -1.5rem !important;
+ }
+ .m-xl-n5 {
+ margin: -3rem !important;
+ }
+ .mt-xl-n5,
+ .my-xl-n5 {
+ margin-top: -3rem !important;
+ }
+ .mr-xl-n5,
+ .mx-xl-n5 {
+ margin-right: -3rem !important;
+ }
+ .mb-xl-n5,
+ .my-xl-n5 {
+ margin-bottom: -3rem !important;
+ }
+ .ml-xl-n5,
+ .mx-xl-n5 {
+ margin-left: -3rem !important;
+ }
+ .m-xl-auto {
+ margin: auto !important;
+ }
+ .mt-xl-auto,
+ .my-xl-auto {
+ margin-top: auto !important;
+ }
+ .mr-xl-auto,
+ .mx-xl-auto {
+ margin-right: auto !important;
+ }
+ .mb-xl-auto,
+ .my-xl-auto {
+ margin-bottom: auto !important;
+ }
+ .ml-xl-auto,
+ .mx-xl-auto {
+ margin-left: auto !important;
+ }
+}
+
+.text-monospace {
+ font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace !important;
+}
+
+.text-justify {
+ text-align: justify !important;
+}
+
+.text-wrap {
+ white-space: normal !important;
+}
+
+.text-nowrap {
+ white-space: nowrap !important;
+}
+
+.text-truncate {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.text-left {
+ text-align: left !important;
+}
+
+.text-right {
+ text-align: right !important;
+}
+
+.text-center {
+ text-align: center !important;
+}
+
+@media (min-width: 576px) {
+ .text-sm-left {
+ text-align: left !important;
+ }
+ .text-sm-right {
+ text-align: right !important;
+ }
+ .text-sm-center {
+ text-align: center !important;
+ }
+}
+
+@media (min-width: 768px) {
+ .text-md-left {
+ text-align: left !important;
+ }
+ .text-md-right {
+ text-align: right !important;
+ }
+ .text-md-center {
+ text-align: center !important;
+ }
+}
+
+@media (min-width: 992px) {
+ .text-lg-left {
+ text-align: left !important;
+ }
+ .text-lg-right {
+ text-align: right !important;
+ }
+ .text-lg-center {
+ text-align: center !important;
+ }
+}
+
+@media (min-width: 1200px) {
+ .text-xl-left {
+ text-align: left !important;
+ }
+ .text-xl-right {
+ text-align: right !important;
+ }
+ .text-xl-center {
+ text-align: center !important;
+ }
+}
+
+.text-lowercase {
+ text-transform: lowercase !important;
+}
+
+.text-uppercase {
+ text-transform: uppercase !important;
+}
+
+.text-capitalize {
+ text-transform: capitalize !important;
+}
+
+.font-weight-light {
+ font-weight: 300 !important;
+}
+
+.font-weight-lighter {
+ font-weight: lighter !important;
+}
+
+.font-weight-normal {
+ font-weight: 400 !important;
+}
+
+.font-weight-bold {
+ font-weight: 700 !important;
+}
+
+.font-weight-bolder {
+ font-weight: bolder !important;
+}
+
+.font-italic {
+ font-style: italic !important;
+}
+
+.text-white {
+ color: #fff !important;
+}
+
+.text-primary {
+ color: #007bff !important;
+}
+
+a.text-primary:hover, a.text-primary:focus {
+ color: #0056b3 !important;
+}
+
+.text-secondary {
+ color: #6c757d !important;
+}
+
+a.text-secondary:hover, a.text-secondary:focus {
+ color: #494f54 !important;
+}
+
+.text-success {
+ color: #28a745 !important;
+}
+
+a.text-success:hover, a.text-success:focus {
+ color: #19692c !important;
+}
+
+.text-info {
+ color: #17a2b8 !important;
+}
+
+a.text-info:hover, a.text-info:focus {
+ color: #0f6674 !important;
+}
+
+.text-warning {
+ color: #ffc107 !important;
+}
+
+a.text-warning:hover, a.text-warning:focus {
+ color: #ba8b00 !important;
+}
+
+.text-danger {
+ color: #dc3545 !important;
+}
+
+a.text-danger:hover, a.text-danger:focus {
+ color: #a71d2a !important;
+}
+
+.text-light {
+ color: #f8f9fa !important;
+}
+
+a.text-light:hover, a.text-light:focus {
+ color: #cbd3da !important;
+}
+
+.text-dark {
+ color: #343a40 !important;
+}
+
+a.text-dark:hover, a.text-dark:focus {
+ color: #121416 !important;
+}
+
+.text-body {
+ color: #212529 !important;
+}
+
+.text-muted {
+ color: #6c757d !important;
+}
+
+.text-black-50 {
+ color: rgba(0, 0, 0, 0.5) !important;
+}
+
+.text-white-50 {
+ color: rgba(255, 255, 255, 0.5) !important;
+}
+
+.text-hide {
+ font: 0/0 a;
+ color: transparent;
+ text-shadow: none;
+ background-color: transparent;
+ border: 0;
+}
+
+.text-decoration-none {
+ text-decoration: none !important;
+}
+
+.text-break {
+ word-break: break-word !important;
+ overflow-wrap: break-word !important;
+}
+
+.text-reset {
+ color: inherit !important;
+}
+
+.visible {
+ visibility: visible !important;
+}
+
+.invisible {
+ visibility: hidden !important;
+}
+
+@media print {
+ *,
+ *::before,
+ *::after {
+ text-shadow: none !important;
+ box-shadow: none !important;
+ }
+ a:not(.btn) {
+ text-decoration: underline;
+ }
+ abbr[title]::after {
+ content: " (" attr(title) ")";
+ }
+ pre {
+ white-space: pre-wrap !important;
+ }
+ pre,
+ blockquote {
+ border: 1px solid #adb5bd;
+ page-break-inside: avoid;
+ }
+ thead {
+ display: table-header-group;
+ }
+ tr,
+ img {
+ page-break-inside: avoid;
+ }
+ p,
+ h2,
+ h3 {
+ orphans: 3;
+ widows: 3;
+ }
+ h2,
+ h3 {
+ page-break-after: avoid;
+ }
+ @page {
+ size: a3;
+ }
+ body {
+ min-width: 992px !important;
+ }
+ .container {
+ min-width: 992px !important;
+ }
+ .navbar {
+ display: none;
+ }
+ .badge {
+ border: 1px solid #000;
+ }
+ .table {
+ border-collapse: collapse !important;
+ }
+ .table td,
+ .table th {
+ background-color: #fff !important;
+ }
+ .table-bordered th,
+ .table-bordered td {
+ border: 1px solid #dee2e6 !important;
+ }
+ .table-dark {
+ color: inherit;
+ }
+ .table-dark th,
+ .table-dark td,
+ .table-dark thead th,
+ .table-dark tbody + tbody {
+ border-color: #dee2e6;
+ }
+ .table .thead-dark th {
+ color: inherit;
+ border-color: #dee2e6;
+ }
+}
+/*# sourceMappingURL=bootstrap.css.map */
\ No newline at end of file
diff --git a/public/static/js/hyw/bootstrap/js/bootstrap.js b/public/static/js/hyw/bootstrap/js/bootstrap.js
new file mode 100644
index 0000000..f1e68d3
--- /dev/null
+++ b/public/static/js/hyw/bootstrap/js/bootstrap.js
@@ -0,0 +1,4521 @@
+/*!
+ * Bootstrap v4.4.1 (https://getbootstrap.com/)
+ * Copyright 2011-2019 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('jquery'), require('popper.js')) :
+ typeof define === 'function' && define.amd ? define(['exports', 'jquery', 'popper.js'], factory) :
+ (global = global || self, factory(global.bootstrap = {}, global.jQuery, global.Popper));
+}(this, (function (exports, $, Popper) { 'use strict';
+
+ $ = $ && $.hasOwnProperty('default') ? $['default'] : $;
+ Popper = Popper && Popper.hasOwnProperty('default') ? Popper['default'] : Popper;
+
+ function _defineProperties(target, props) {
+ for (var i = 0; i < props.length; i++) {
+ var descriptor = props[i];
+ descriptor.enumerable = descriptor.enumerable || false;
+ descriptor.configurable = true;
+ if ("value" in descriptor) descriptor.writable = true;
+ Object.defineProperty(target, descriptor.key, descriptor);
+ }
+ }
+
+ function _createClass(Constructor, protoProps, staticProps) {
+ if (protoProps) _defineProperties(Constructor.prototype, protoProps);
+ if (staticProps) _defineProperties(Constructor, staticProps);
+ return Constructor;
+ }
+
+ function _defineProperty(obj, key, value) {
+ if (key in obj) {
+ Object.defineProperty(obj, key, {
+ value: value,
+ enumerable: true,
+ configurable: true,
+ writable: true
+ });
+ } else {
+ obj[key] = value;
+ }
+
+ return obj;
+ }
+
+ function ownKeys(object, enumerableOnly) {
+ var keys = Object.keys(object);
+
+ if (Object.getOwnPropertySymbols) {
+ var symbols = Object.getOwnPropertySymbols(object);
+ if (enumerableOnly) symbols = symbols.filter(function (sym) {
+ return Object.getOwnPropertyDescriptor(object, sym).enumerable;
+ });
+ keys.push.apply(keys, symbols);
+ }
+
+ return keys;
+ }
+
+ function _objectSpread2(target) {
+ for (var i = 1; i < arguments.length; i++) {
+ var source = arguments[i] != null ? arguments[i] : {};
+
+ if (i % 2) {
+ ownKeys(Object(source), true).forEach(function (key) {
+ _defineProperty(target, key, source[key]);
+ });
+ } else if (Object.getOwnPropertyDescriptors) {
+ Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
+ } else {
+ ownKeys(Object(source)).forEach(function (key) {
+ Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
+ });
+ }
+ }
+
+ return target;
+ }
+
+ function _inheritsLoose(subClass, superClass) {
+ subClass.prototype = Object.create(superClass.prototype);
+ subClass.prototype.constructor = subClass;
+ subClass.__proto__ = superClass;
+ }
+
+ /**
+ * --------------------------------------------------------------------------
+ * Bootstrap (v4.4.1): util.js
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * --------------------------------------------------------------------------
+ */
+ /**
+ * ------------------------------------------------------------------------
+ * Private TransitionEnd Helpers
+ * ------------------------------------------------------------------------
+ */
+
+ var TRANSITION_END = 'transitionend';
+ var MAX_UID = 1000000;
+ var MILLISECONDS_MULTIPLIER = 1000; // Shoutout AngusCroll (https://goo.gl/pxwQGp)
+
+ function toType(obj) {
+ return {}.toString.call(obj).match(/\s([a-z]+)/i)[1].toLowerCase();
+ }
+
+ function getSpecialTransitionEndEvent() {
+ return {
+ bindType: TRANSITION_END,
+ delegateType: TRANSITION_END,
+ handle: function handle(event) {
+ if ($(event.target).is(this)) {
+ return event.handleObj.handler.apply(this, arguments); // eslint-disable-line prefer-rest-params
+ }
+
+ return undefined; // eslint-disable-line no-undefined
+ }
+ };
+ }
+
+ function transitionEndEmulator(duration) {
+ var _this = this;
+
+ var called = false;
+ $(this).one(Util.TRANSITION_END, function () {
+ called = true;
+ });
+ setTimeout(function () {
+ if (!called) {
+ Util.triggerTransitionEnd(_this);
+ }
+ }, duration);
+ return this;
+ }
+
+ function setTransitionEndSupport() {
+ $.fn.emulateTransitionEnd = transitionEndEmulator;
+ $.event.special[Util.TRANSITION_END] = getSpecialTransitionEndEvent();
+ }
+ /**
+ * --------------------------------------------------------------------------
+ * Public Util Api
+ * --------------------------------------------------------------------------
+ */
+
+
+ var Util = {
+ TRANSITION_END: 'bsTransitionEnd',
+ getUID: function getUID(prefix) {
+ do {
+ // eslint-disable-next-line no-bitwise
+ prefix += ~~(Math.random() * MAX_UID); // "~~" acts like a faster Math.floor() here
+ } while (document.getElementById(prefix));
+
+ return prefix;
+ },
+ getSelectorFromElement: function getSelectorFromElement(element) {
+ var selector = element.getAttribute('data-target');
+
+ if (!selector || selector === '#') {
+ var hrefAttr = element.getAttribute('href');
+ selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : '';
+ }
+
+ try {
+ return document.querySelector(selector) ? selector : null;
+ } catch (err) {
+ return null;
+ }
+ },
+ getTransitionDurationFromElement: function getTransitionDurationFromElement(element) {
+ if (!element) {
+ return 0;
+ } // Get transition-duration of the element
+
+
+ var transitionDuration = $(element).css('transition-duration');
+ var transitionDelay = $(element).css('transition-delay');
+ var floatTransitionDuration = parseFloat(transitionDuration);
+ var floatTransitionDelay = parseFloat(transitionDelay); // Return 0 if element or transition duration is not found
+
+ if (!floatTransitionDuration && !floatTransitionDelay) {
+ return 0;
+ } // If multiple durations are defined, take the first
+
+
+ transitionDuration = transitionDuration.split(',')[0];
+ transitionDelay = transitionDelay.split(',')[0];
+ return (parseFloat(transitionDuration) + parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER;
+ },
+ reflow: function reflow(element) {
+ return element.offsetHeight;
+ },
+ triggerTransitionEnd: function triggerTransitionEnd(element) {
+ $(element).trigger(TRANSITION_END);
+ },
+ // TODO: Remove in v5
+ supportsTransitionEnd: function supportsTransitionEnd() {
+ return Boolean(TRANSITION_END);
+ },
+ isElement: function isElement(obj) {
+ return (obj[0] || obj).nodeType;
+ },
+ typeCheckConfig: function typeCheckConfig(componentName, config, configTypes) {
+ for (var property in configTypes) {
+ if (Object.prototype.hasOwnProperty.call(configTypes, property)) {
+ var expectedTypes = configTypes[property];
+ var value = config[property];
+ var valueType = value && Util.isElement(value) ? 'element' : toType(value);
+
+ if (!new RegExp(expectedTypes).test(valueType)) {
+ throw new Error(componentName.toUpperCase() + ": " + ("Option \"" + property + "\" provided type \"" + valueType + "\" ") + ("but expected type \"" + expectedTypes + "\"."));
+ }
+ }
+ }
+ },
+ findShadowRoot: function findShadowRoot(element) {
+ if (!document.documentElement.attachShadow) {
+ return null;
+ } // Can find the shadow root otherwise it'll return the document
+
+
+ if (typeof element.getRootNode === 'function') {
+ var root = element.getRootNode();
+ return root instanceof ShadowRoot ? root : null;
+ }
+
+ if (element instanceof ShadowRoot) {
+ return element;
+ } // when we don't find a shadow root
+
+
+ if (!element.parentNode) {
+ return null;
+ }
+
+ return Util.findShadowRoot(element.parentNode);
+ },
+ jQueryDetection: function jQueryDetection() {
+ if (typeof $ === 'undefined') {
+ throw new TypeError('Bootstrap\'s JavaScript requires jQuery. jQuery must be included before Bootstrap\'s JavaScript.');
+ }
+
+ var version = $.fn.jquery.split(' ')[0].split('.');
+ var minMajor = 1;
+ var ltMajor = 2;
+ var minMinor = 9;
+ var minPatch = 1;
+ var maxMajor = 4;
+
+ if (version[0] < ltMajor && version[1] < minMinor || version[0] === minMajor && version[1] === minMinor && version[2] < minPatch || version[0] >= maxMajor) {
+ throw new Error('Bootstrap\'s JavaScript requires at least jQuery v1.9.1 but less than v4.0.0');
+ }
+ }
+ };
+ Util.jQueryDetection();
+ setTransitionEndSupport();
+
+ /**
+ * ------------------------------------------------------------------------
+ * Constants
+ * ------------------------------------------------------------------------
+ */
+
+ var NAME = 'alert';
+ var VERSION = '4.4.1';
+ var DATA_KEY = 'bs.alert';
+ var EVENT_KEY = "." + DATA_KEY;
+ var DATA_API_KEY = '.data-api';
+ var JQUERY_NO_CONFLICT = $.fn[NAME];
+ var Selector = {
+ DISMISS: '[data-dismiss="alert"]'
+ };
+ var Event = {
+ CLOSE: "close" + EVENT_KEY,
+ CLOSED: "closed" + EVENT_KEY,
+ CLICK_DATA_API: "click" + EVENT_KEY + DATA_API_KEY
+ };
+ var ClassName = {
+ ALERT: 'alert',
+ FADE: 'fade',
+ SHOW: 'show'
+ };
+ /**
+ * ------------------------------------------------------------------------
+ * Class Definition
+ * ------------------------------------------------------------------------
+ */
+
+ var Alert =
+ /*#__PURE__*/
+ function () {
+ function Alert(element) {
+ this._element = element;
+ } // Getters
+
+
+ var _proto = Alert.prototype;
+
+ // Public
+ _proto.close = function close(element) {
+ var rootElement = this._element;
+
+ if (element) {
+ rootElement = this._getRootElement(element);
+ }
+
+ var customEvent = this._triggerCloseEvent(rootElement);
+
+ if (customEvent.isDefaultPrevented()) {
+ return;
+ }
+
+ this._removeElement(rootElement);
+ };
+
+ _proto.dispose = function dispose() {
+ $.removeData(this._element, DATA_KEY);
+ this._element = null;
+ } // Private
+ ;
+
+ _proto._getRootElement = function _getRootElement(element) {
+ var selector = Util.getSelectorFromElement(element);
+ var parent = false;
+
+ if (selector) {
+ parent = document.querySelector(selector);
+ }
+
+ if (!parent) {
+ parent = $(element).closest("." + ClassName.ALERT)[0];
+ }
+
+ return parent;
+ };
+
+ _proto._triggerCloseEvent = function _triggerCloseEvent(element) {
+ var closeEvent = $.Event(Event.CLOSE);
+ $(element).trigger(closeEvent);
+ return closeEvent;
+ };
+
+ _proto._removeElement = function _removeElement(element) {
+ var _this = this;
+
+ $(element).removeClass(ClassName.SHOW);
+
+ if (!$(element).hasClass(ClassName.FADE)) {
+ this._destroyElement(element);
+
+ return;
+ }
+
+ var transitionDuration = Util.getTransitionDurationFromElement(element);
+ $(element).one(Util.TRANSITION_END, function (event) {
+ return _this._destroyElement(element, event);
+ }).emulateTransitionEnd(transitionDuration);
+ };
+
+ _proto._destroyElement = function _destroyElement(element) {
+ $(element).detach().trigger(Event.CLOSED).remove();
+ } // Static
+ ;
+
+ Alert._jQueryInterface = function _jQueryInterface(config) {
+ return this.each(function () {
+ var $element = $(this);
+ var data = $element.data(DATA_KEY);
+
+ if (!data) {
+ data = new Alert(this);
+ $element.data(DATA_KEY, data);
+ }
+
+ if (config === 'close') {
+ data[config](this);
+ }
+ });
+ };
+
+ Alert._handleDismiss = function _handleDismiss(alertInstance) {
+ return function (event) {
+ if (event) {
+ event.preventDefault();
+ }
+
+ alertInstance.close(this);
+ };
+ };
+
+ _createClass(Alert, null, [{
+ key: "VERSION",
+ get: function get() {
+ return VERSION;
+ }
+ }]);
+
+ return Alert;
+ }();
+ /**
+ * ------------------------------------------------------------------------
+ * Data Api implementation
+ * ------------------------------------------------------------------------
+ */
+
+
+ $(document).on(Event.CLICK_DATA_API, Selector.DISMISS, Alert._handleDismiss(new Alert()));
+ /**
+ * ------------------------------------------------------------------------
+ * jQuery
+ * ------------------------------------------------------------------------
+ */
+
+ $.fn[NAME] = Alert._jQueryInterface;
+ $.fn[NAME].Constructor = Alert;
+
+ $.fn[NAME].noConflict = function () {
+ $.fn[NAME] = JQUERY_NO_CONFLICT;
+ return Alert._jQueryInterface;
+ };
+
+ /**
+ * ------------------------------------------------------------------------
+ * Constants
+ * ------------------------------------------------------------------------
+ */
+
+ var NAME$1 = 'button';
+ var VERSION$1 = '4.4.1';
+ var DATA_KEY$1 = 'bs.button';
+ var EVENT_KEY$1 = "." + DATA_KEY$1;
+ var DATA_API_KEY$1 = '.data-api';
+ var JQUERY_NO_CONFLICT$1 = $.fn[NAME$1];
+ var ClassName$1 = {
+ ACTIVE: 'active',
+ BUTTON: 'btn',
+ FOCUS: 'focus'
+ };
+ var Selector$1 = {
+ DATA_TOGGLE_CARROT: '[data-toggle^="button"]',
+ DATA_TOGGLES: '[data-toggle="buttons"]',
+ DATA_TOGGLE: '[data-toggle="button"]',
+ DATA_TOGGLES_BUTTONS: '[data-toggle="buttons"] .btn',
+ INPUT: 'input:not([type="hidden"])',
+ ACTIVE: '.active',
+ BUTTON: '.btn'
+ };
+ var Event$1 = {
+ CLICK_DATA_API: "click" + EVENT_KEY$1 + DATA_API_KEY$1,
+ FOCUS_BLUR_DATA_API: "focus" + EVENT_KEY$1 + DATA_API_KEY$1 + " " + ("blur" + EVENT_KEY$1 + DATA_API_KEY$1),
+ LOAD_DATA_API: "load" + EVENT_KEY$1 + DATA_API_KEY$1
+ };
+ /**
+ * ------------------------------------------------------------------------
+ * Class Definition
+ * ------------------------------------------------------------------------
+ */
+
+ var Button =
+ /*#__PURE__*/
+ function () {
+ function Button(element) {
+ this._element = element;
+ } // Getters
+
+
+ var _proto = Button.prototype;
+
+ // Public
+ _proto.toggle = function toggle() {
+ var triggerChangeEvent = true;
+ var addAriaPressed = true;
+ var rootElement = $(this._element).closest(Selector$1.DATA_TOGGLES)[0];
+
+ if (rootElement) {
+ var input = this._element.querySelector(Selector$1.INPUT);
+
+ if (input) {
+ if (input.type === 'radio') {
+ if (input.checked && this._element.classList.contains(ClassName$1.ACTIVE)) {
+ triggerChangeEvent = false;
+ } else {
+ var activeElement = rootElement.querySelector(Selector$1.ACTIVE);
+
+ if (activeElement) {
+ $(activeElement).removeClass(ClassName$1.ACTIVE);
+ }
+ }
+ } else if (input.type === 'checkbox') {
+ if (this._element.tagName === 'LABEL' && input.checked === this._element.classList.contains(ClassName$1.ACTIVE)) {
+ triggerChangeEvent = false;
+ }
+ } else {
+ // if it's not a radio button or checkbox don't add a pointless/invalid checked property to the input
+ triggerChangeEvent = false;
+ }
+
+ if (triggerChangeEvent) {
+ input.checked = !this._element.classList.contains(ClassName$1.ACTIVE);
+ $(input).trigger('change');
+ }
+
+ input.focus();
+ addAriaPressed = false;
+ }
+ }
+
+ if (!(this._element.hasAttribute('disabled') || this._element.classList.contains('disabled'))) {
+ if (addAriaPressed) {
+ this._element.setAttribute('aria-pressed', !this._element.classList.contains(ClassName$1.ACTIVE));
+ }
+
+ if (triggerChangeEvent) {
+ $(this._element).toggleClass(ClassName$1.ACTIVE);
+ }
+ }
+ };
+
+ _proto.dispose = function dispose() {
+ $.removeData(this._element, DATA_KEY$1);
+ this._element = null;
+ } // Static
+ ;
+
+ Button._jQueryInterface = function _jQueryInterface(config) {
+ return this.each(function () {
+ var data = $(this).data(DATA_KEY$1);
+
+ if (!data) {
+ data = new Button(this);
+ $(this).data(DATA_KEY$1, data);
+ }
+
+ if (config === 'toggle') {
+ data[config]();
+ }
+ });
+ };
+
+ _createClass(Button, null, [{
+ key: "VERSION",
+ get: function get() {
+ return VERSION$1;
+ }
+ }]);
+
+ return Button;
+ }();
+ /**
+ * ------------------------------------------------------------------------
+ * Data Api implementation
+ * ------------------------------------------------------------------------
+ */
+
+
+ $(document).on(Event$1.CLICK_DATA_API, Selector$1.DATA_TOGGLE_CARROT, function (event) {
+ var button = event.target;
+
+ if (!$(button).hasClass(ClassName$1.BUTTON)) {
+ button = $(button).closest(Selector$1.BUTTON)[0];
+ }
+
+ if (!button || button.hasAttribute('disabled') || button.classList.contains('disabled')) {
+ event.preventDefault(); // work around Firefox bug #1540995
+ } else {
+ var inputBtn = button.querySelector(Selector$1.INPUT);
+
+ if (inputBtn && (inputBtn.hasAttribute('disabled') || inputBtn.classList.contains('disabled'))) {
+ event.preventDefault(); // work around Firefox bug #1540995
+
+ return;
+ }
+
+ Button._jQueryInterface.call($(button), 'toggle');
+ }
+ }).on(Event$1.FOCUS_BLUR_DATA_API, Selector$1.DATA_TOGGLE_CARROT, function (event) {
+ var button = $(event.target).closest(Selector$1.BUTTON)[0];
+ $(button).toggleClass(ClassName$1.FOCUS, /^focus(in)?$/.test(event.type));
+ });
+ $(window).on(Event$1.LOAD_DATA_API, function () {
+ // ensure correct active class is set to match the controls' actual values/states
+ // find all checkboxes/readio buttons inside data-toggle groups
+ var buttons = [].slice.call(document.querySelectorAll(Selector$1.DATA_TOGGLES_BUTTONS));
+
+ for (var i = 0, len = buttons.length; i < len; i++) {
+ var button = buttons[i];
+ var input = button.querySelector(Selector$1.INPUT);
+
+ if (input.checked || input.hasAttribute('checked')) {
+ button.classList.add(ClassName$1.ACTIVE);
+ } else {
+ button.classList.remove(ClassName$1.ACTIVE);
+ }
+ } // find all button toggles
+
+
+ buttons = [].slice.call(document.querySelectorAll(Selector$1.DATA_TOGGLE));
+
+ for (var _i = 0, _len = buttons.length; _i < _len; _i++) {
+ var _button = buttons[_i];
+
+ if (_button.getAttribute('aria-pressed') === 'true') {
+ _button.classList.add(ClassName$1.ACTIVE);
+ } else {
+ _button.classList.remove(ClassName$1.ACTIVE);
+ }
+ }
+ });
+ /**
+ * ------------------------------------------------------------------------
+ * jQuery
+ * ------------------------------------------------------------------------
+ */
+
+ $.fn[NAME$1] = Button._jQueryInterface;
+ $.fn[NAME$1].Constructor = Button;
+
+ $.fn[NAME$1].noConflict = function () {
+ $.fn[NAME$1] = JQUERY_NO_CONFLICT$1;
+ return Button._jQueryInterface;
+ };
+
+ /**
+ * ------------------------------------------------------------------------
+ * Constants
+ * ------------------------------------------------------------------------
+ */
+
+ var NAME$2 = 'carousel';
+ var VERSION$2 = '4.4.1';
+ var DATA_KEY$2 = 'bs.carousel';
+ var EVENT_KEY$2 = "." + DATA_KEY$2;
+ var DATA_API_KEY$2 = '.data-api';
+ var JQUERY_NO_CONFLICT$2 = $.fn[NAME$2];
+ var ARROW_LEFT_KEYCODE = 37; // KeyboardEvent.which value for left arrow key
+
+ var ARROW_RIGHT_KEYCODE = 39; // KeyboardEvent.which value for right arrow key
+
+ var TOUCHEVENT_COMPAT_WAIT = 500; // Time for mouse compat events to fire after touch
+
+ var SWIPE_THRESHOLD = 40;
+ var Default = {
+ interval: 5000,
+ keyboard: true,
+ slide: false,
+ pause: 'hover',
+ wrap: true,
+ touch: true
+ };
+ var DefaultType = {
+ interval: '(number|boolean)',
+ keyboard: 'boolean',
+ slide: '(boolean|string)',
+ pause: '(string|boolean)',
+ wrap: 'boolean',
+ touch: 'boolean'
+ };
+ var Direction = {
+ NEXT: 'next',
+ PREV: 'prev',
+ LEFT: 'left',
+ RIGHT: 'right'
+ };
+ var Event$2 = {
+ SLIDE: "slide" + EVENT_KEY$2,
+ SLID: "slid" + EVENT_KEY$2,
+ KEYDOWN: "keydown" + EVENT_KEY$2,
+ MOUSEENTER: "mouseenter" + EVENT_KEY$2,
+ MOUSELEAVE: "mouseleave" + EVENT_KEY$2,
+ TOUCHSTART: "touchstart" + EVENT_KEY$2,
+ TOUCHMOVE: "touchmove" + EVENT_KEY$2,
+ TOUCHEND: "touchend" + EVENT_KEY$2,
+ POINTERDOWN: "pointerdown" + EVENT_KEY$2,
+ POINTERUP: "pointerup" + EVENT_KEY$2,
+ DRAG_START: "dragstart" + EVENT_KEY$2,
+ LOAD_DATA_API: "load" + EVENT_KEY$2 + DATA_API_KEY$2,
+ CLICK_DATA_API: "click" + EVENT_KEY$2 + DATA_API_KEY$2
+ };
+ var ClassName$2 = {
+ CAROUSEL: 'carousel',
+ ACTIVE: 'active',
+ SLIDE: 'slide',
+ RIGHT: 'carousel-item-right',
+ LEFT: 'carousel-item-left',
+ NEXT: 'carousel-item-next',
+ PREV: 'carousel-item-prev',
+ ITEM: 'carousel-item',
+ POINTER_EVENT: 'pointer-event'
+ };
+ var Selector$2 = {
+ ACTIVE: '.active',
+ ACTIVE_ITEM: '.active.carousel-item',
+ ITEM: '.carousel-item',
+ ITEM_IMG: '.carousel-item img',
+ NEXT_PREV: '.carousel-item-next, .carousel-item-prev',
+ INDICATORS: '.carousel-indicators',
+ DATA_SLIDE: '[data-slide], [data-slide-to]',
+ DATA_RIDE: '[data-ride="carousel"]'
+ };
+ var PointerType = {
+ TOUCH: 'touch',
+ PEN: 'pen'
+ };
+ /**
+ * ------------------------------------------------------------------------
+ * Class Definition
+ * ------------------------------------------------------------------------
+ */
+
+ var Carousel =
+ /*#__PURE__*/
+ function () {
+ function Carousel(element, config) {
+ this._items = null;
+ this._interval = null;
+ this._activeElement = null;
+ this._isPaused = false;
+ this._isSliding = false;
+ this.touchTimeout = null;
+ this.touchStartX = 0;
+ this.touchDeltaX = 0;
+ this._config = this._getConfig(config);
+ this._element = element;
+ this._indicatorsElement = this._element.querySelector(Selector$2.INDICATORS);
+ this._touchSupported = 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0;
+ this._pointerEvent = Boolean(window.PointerEvent || window.MSPointerEvent);
+
+ this._addEventListeners();
+ } // Getters
+
+
+ var _proto = Carousel.prototype;
+
+ // Public
+ _proto.next = function next() {
+ if (!this._isSliding) {
+ this._slide(Direction.NEXT);
+ }
+ };
+
+ _proto.nextWhenVisible = function nextWhenVisible() {
+ // Don't call next when the page isn't visible
+ // or the carousel or its parent isn't visible
+ if (!document.hidden && $(this._element).is(':visible') && $(this._element).css('visibility') !== 'hidden') {
+ this.next();
+ }
+ };
+
+ _proto.prev = function prev() {
+ if (!this._isSliding) {
+ this._slide(Direction.PREV);
+ }
+ };
+
+ _proto.pause = function pause(event) {
+ if (!event) {
+ this._isPaused = true;
+ }
+
+ if (this._element.querySelector(Selector$2.NEXT_PREV)) {
+ Util.triggerTransitionEnd(this._element);
+ this.cycle(true);
+ }
+
+ clearInterval(this._interval);
+ this._interval = null;
+ };
+
+ _proto.cycle = function cycle(event) {
+ if (!event) {
+ this._isPaused = false;
+ }
+
+ if (this._interval) {
+ clearInterval(this._interval);
+ this._interval = null;
+ }
+
+ if (this._config.interval && !this._isPaused) {
+ this._interval = setInterval((document.visibilityState ? this.nextWhenVisible : this.next).bind(this), this._config.interval);
+ }
+ };
+
+ _proto.to = function to(index) {
+ var _this = this;
+
+ this._activeElement = this._element.querySelector(Selector$2.ACTIVE_ITEM);
+
+ var activeIndex = this._getItemIndex(this._activeElement);
+
+ if (index > this._items.length - 1 || index < 0) {
+ return;
+ }
+
+ if (this._isSliding) {
+ $(this._element).one(Event$2.SLID, function () {
+ return _this.to(index);
+ });
+ return;
+ }
+
+ if (activeIndex === index) {
+ this.pause();
+ this.cycle();
+ return;
+ }
+
+ var direction = index > activeIndex ? Direction.NEXT : Direction.PREV;
+
+ this._slide(direction, this._items[index]);
+ };
+
+ _proto.dispose = function dispose() {
+ $(this._element).off(EVENT_KEY$2);
+ $.removeData(this._element, DATA_KEY$2);
+ this._items = null;
+ this._config = null;
+ this._element = null;
+ this._interval = null;
+ this._isPaused = null;
+ this._isSliding = null;
+ this._activeElement = null;
+ this._indicatorsElement = null;
+ } // Private
+ ;
+
+ _proto._getConfig = function _getConfig(config) {
+ config = _objectSpread2({}, Default, {}, config);
+ Util.typeCheckConfig(NAME$2, config, DefaultType);
+ return config;
+ };
+
+ _proto._handleSwipe = function _handleSwipe() {
+ var absDeltax = Math.abs(this.touchDeltaX);
+
+ if (absDeltax <= SWIPE_THRESHOLD) {
+ return;
+ }
+
+ var direction = absDeltax / this.touchDeltaX;
+ this.touchDeltaX = 0; // swipe left
+
+ if (direction > 0) {
+ this.prev();
+ } // swipe right
+
+
+ if (direction < 0) {
+ this.next();
+ }
+ };
+
+ _proto._addEventListeners = function _addEventListeners() {
+ var _this2 = this;
+
+ if (this._config.keyboard) {
+ $(this._element).on(Event$2.KEYDOWN, function (event) {
+ return _this2._keydown(event);
+ });
+ }
+
+ if (this._config.pause === 'hover') {
+ $(this._element).on(Event$2.MOUSEENTER, function (event) {
+ return _this2.pause(event);
+ }).on(Event$2.MOUSELEAVE, function (event) {
+ return _this2.cycle(event);
+ });
+ }
+
+ if (this._config.touch) {
+ this._addTouchEventListeners();
+ }
+ };
+
+ _proto._addTouchEventListeners = function _addTouchEventListeners() {
+ var _this3 = this;
+
+ if (!this._touchSupported) {
+ return;
+ }
+
+ var start = function start(event) {
+ if (_this3._pointerEvent && PointerType[event.originalEvent.pointerType.toUpperCase()]) {
+ _this3.touchStartX = event.originalEvent.clientX;
+ } else if (!_this3._pointerEvent) {
+ _this3.touchStartX = event.originalEvent.touches[0].clientX;
+ }
+ };
+
+ var move = function move(event) {
+ // ensure swiping with one touch and not pinching
+ if (event.originalEvent.touches && event.originalEvent.touches.length > 1) {
+ _this3.touchDeltaX = 0;
+ } else {
+ _this3.touchDeltaX = event.originalEvent.touches[0].clientX - _this3.touchStartX;
+ }
+ };
+
+ var end = function end(event) {
+ if (_this3._pointerEvent && PointerType[event.originalEvent.pointerType.toUpperCase()]) {
+ _this3.touchDeltaX = event.originalEvent.clientX - _this3.touchStartX;
+ }
+
+ _this3._handleSwipe();
+
+ if (_this3._config.pause === 'hover') {
+ // If it's a touch-enabled device, mouseenter/leave are fired as
+ // part of the mouse compatibility events on first tap - the carousel
+ // would stop cycling until user tapped out of it;
+ // here, we listen for touchend, explicitly pause the carousel
+ // (as if it's the second time we tap on it, mouseenter compat event
+ // is NOT fired) and after a timeout (to allow for mouse compatibility
+ // events to fire) we explicitly restart cycling
+ _this3.pause();
+
+ if (_this3.touchTimeout) {
+ clearTimeout(_this3.touchTimeout);
+ }
+
+ _this3.touchTimeout = setTimeout(function (event) {
+ return _this3.cycle(event);
+ }, TOUCHEVENT_COMPAT_WAIT + _this3._config.interval);
+ }
+ };
+
+ $(this._element.querySelectorAll(Selector$2.ITEM_IMG)).on(Event$2.DRAG_START, function (e) {
+ return e.preventDefault();
+ });
+
+ if (this._pointerEvent) {
+ $(this._element).on(Event$2.POINTERDOWN, function (event) {
+ return start(event);
+ });
+ $(this._element).on(Event$2.POINTERUP, function (event) {
+ return end(event);
+ });
+
+ this._element.classList.add(ClassName$2.POINTER_EVENT);
+ } else {
+ $(this._element).on(Event$2.TOUCHSTART, function (event) {
+ return start(event);
+ });
+ $(this._element).on(Event$2.TOUCHMOVE, function (event) {
+ return move(event);
+ });
+ $(this._element).on(Event$2.TOUCHEND, function (event) {
+ return end(event);
+ });
+ }
+ };
+
+ _proto._keydown = function _keydown(event) {
+ if (/input|textarea/i.test(event.target.tagName)) {
+ return;
+ }
+
+ switch (event.which) {
+ case ARROW_LEFT_KEYCODE:
+ event.preventDefault();
+ this.prev();
+ break;
+
+ case ARROW_RIGHT_KEYCODE:
+ event.preventDefault();
+ this.next();
+ break;
+ }
+ };
+
+ _proto._getItemIndex = function _getItemIndex(element) {
+ this._items = element && element.parentNode ? [].slice.call(element.parentNode.querySelectorAll(Selector$2.ITEM)) : [];
+ return this._items.indexOf(element);
+ };
+
+ _proto._getItemByDirection = function _getItemByDirection(direction, activeElement) {
+ var isNextDirection = direction === Direction.NEXT;
+ var isPrevDirection = direction === Direction.PREV;
+
+ var activeIndex = this._getItemIndex(activeElement);
+
+ var lastItemIndex = this._items.length - 1;
+ var isGoingToWrap = isPrevDirection && activeIndex === 0 || isNextDirection && activeIndex === lastItemIndex;
+
+ if (isGoingToWrap && !this._config.wrap) {
+ return activeElement;
+ }
+
+ var delta = direction === Direction.PREV ? -1 : 1;
+ var itemIndex = (activeIndex + delta) % this._items.length;
+ return itemIndex === -1 ? this._items[this._items.length - 1] : this._items[itemIndex];
+ };
+
+ _proto._triggerSlideEvent = function _triggerSlideEvent(relatedTarget, eventDirectionName) {
+ var targetIndex = this._getItemIndex(relatedTarget);
+
+ var fromIndex = this._getItemIndex(this._element.querySelector(Selector$2.ACTIVE_ITEM));
+
+ var slideEvent = $.Event(Event$2.SLIDE, {
+ relatedTarget: relatedTarget,
+ direction: eventDirectionName,
+ from: fromIndex,
+ to: targetIndex
+ });
+ $(this._element).trigger(slideEvent);
+ return slideEvent;
+ };
+
+ _proto._setActiveIndicatorElement = function _setActiveIndicatorElement(element) {
+ if (this._indicatorsElement) {
+ var indicators = [].slice.call(this._indicatorsElement.querySelectorAll(Selector$2.ACTIVE));
+ $(indicators).removeClass(ClassName$2.ACTIVE);
+
+ var nextIndicator = this._indicatorsElement.children[this._getItemIndex(element)];
+
+ if (nextIndicator) {
+ $(nextIndicator).addClass(ClassName$2.ACTIVE);
+ }
+ }
+ };
+
+ _proto._slide = function _slide(direction, element) {
+ var _this4 = this;
+
+ var activeElement = this._element.querySelector(Selector$2.ACTIVE_ITEM);
+
+ var activeElementIndex = this._getItemIndex(activeElement);
+
+ var nextElement = element || activeElement && this._getItemByDirection(direction, activeElement);
+
+ var nextElementIndex = this._getItemIndex(nextElement);
+
+ var isCycling = Boolean(this._interval);
+ var directionalClassName;
+ var orderClassName;
+ var eventDirectionName;
+
+ if (direction === Direction.NEXT) {
+ directionalClassName = ClassName$2.LEFT;
+ orderClassName = ClassName$2.NEXT;
+ eventDirectionName = Direction.LEFT;
+ } else {
+ directionalClassName = ClassName$2.RIGHT;
+ orderClassName = ClassName$2.PREV;
+ eventDirectionName = Direction.RIGHT;
+ }
+
+ if (nextElement && $(nextElement).hasClass(ClassName$2.ACTIVE)) {
+ this._isSliding = false;
+ return;
+ }
+
+ var slideEvent = this._triggerSlideEvent(nextElement, eventDirectionName);
+
+ if (slideEvent.isDefaultPrevented()) {
+ return;
+ }
+
+ if (!activeElement || !nextElement) {
+ // Some weirdness is happening, so we bail
+ return;
+ }
+
+ this._isSliding = true;
+
+ if (isCycling) {
+ this.pause();
+ }
+
+ this._setActiveIndicatorElement(nextElement);
+
+ var slidEvent = $.Event(Event$2.SLID, {
+ relatedTarget: nextElement,
+ direction: eventDirectionName,
+ from: activeElementIndex,
+ to: nextElementIndex
+ });
+
+ if ($(this._element).hasClass(ClassName$2.SLIDE)) {
+ $(nextElement).addClass(orderClassName);
+ Util.reflow(nextElement);
+ $(activeElement).addClass(directionalClassName);
+ $(nextElement).addClass(directionalClassName);
+ var nextElementInterval = parseInt(nextElement.getAttribute('data-interval'), 10);
+
+ if (nextElementInterval) {
+ this._config.defaultInterval = this._config.defaultInterval || this._config.interval;
+ this._config.interval = nextElementInterval;
+ } else {
+ this._config.interval = this._config.defaultInterval || this._config.interval;
+ }
+
+ var transitionDuration = Util.getTransitionDurationFromElement(activeElement);
+ $(activeElement).one(Util.TRANSITION_END, function () {
+ $(nextElement).removeClass(directionalClassName + " " + orderClassName).addClass(ClassName$2.ACTIVE);
+ $(activeElement).removeClass(ClassName$2.ACTIVE + " " + orderClassName + " " + directionalClassName);
+ _this4._isSliding = false;
+ setTimeout(function () {
+ return $(_this4._element).trigger(slidEvent);
+ }, 0);
+ }).emulateTransitionEnd(transitionDuration);
+ } else {
+ $(activeElement).removeClass(ClassName$2.ACTIVE);
+ $(nextElement).addClass(ClassName$2.ACTIVE);
+ this._isSliding = false;
+ $(this._element).trigger(slidEvent);
+ }
+
+ if (isCycling) {
+ this.cycle();
+ }
+ } // Static
+ ;
+
+ Carousel._jQueryInterface = function _jQueryInterface(config) {
+ return this.each(function () {
+ var data = $(this).data(DATA_KEY$2);
+
+ var _config = _objectSpread2({}, Default, {}, $(this).data());
+
+ if (typeof config === 'object') {
+ _config = _objectSpread2({}, _config, {}, config);
+ }
+
+ var action = typeof config === 'string' ? config : _config.slide;
+
+ if (!data) {
+ data = new Carousel(this, _config);
+ $(this).data(DATA_KEY$2, data);
+ }
+
+ if (typeof config === 'number') {
+ data.to(config);
+ } else if (typeof action === 'string') {
+ if (typeof data[action] === 'undefined') {
+ throw new TypeError("No method named \"" + action + "\"");
+ }
+
+ data[action]();
+ } else if (_config.interval && _config.ride) {
+ data.pause();
+ data.cycle();
+ }
+ });
+ };
+
+ Carousel._dataApiClickHandler = function _dataApiClickHandler(event) {
+ var selector = Util.getSelectorFromElement(this);
+
+ if (!selector) {
+ return;
+ }
+
+ var target = $(selector)[0];
+
+ if (!target || !$(target).hasClass(ClassName$2.CAROUSEL)) {
+ return;
+ }
+
+ var config = _objectSpread2({}, $(target).data(), {}, $(this).data());
+
+ var slideIndex = this.getAttribute('data-slide-to');
+
+ if (slideIndex) {
+ config.interval = false;
+ }
+
+ Carousel._jQueryInterface.call($(target), config);
+
+ if (slideIndex) {
+ $(target).data(DATA_KEY$2).to(slideIndex);
+ }
+
+ event.preventDefault();
+ };
+
+ _createClass(Carousel, null, [{
+ key: "VERSION",
+ get: function get() {
+ return VERSION$2;
+ }
+ }, {
+ key: "Default",
+ get: function get() {
+ return Default;
+ }
+ }]);
+
+ return Carousel;
+ }();
+ /**
+ * ------------------------------------------------------------------------
+ * Data Api implementation
+ * ------------------------------------------------------------------------
+ */
+
+
+ $(document).on(Event$2.CLICK_DATA_API, Selector$2.DATA_SLIDE, Carousel._dataApiClickHandler);
+ $(window).on(Event$2.LOAD_DATA_API, function () {
+ var carousels = [].slice.call(document.querySelectorAll(Selector$2.DATA_RIDE));
+
+ for (var i = 0, len = carousels.length; i < len; i++) {
+ var $carousel = $(carousels[i]);
+
+ Carousel._jQueryInterface.call($carousel, $carousel.data());
+ }
+ });
+ /**
+ * ------------------------------------------------------------------------
+ * jQuery
+ * ------------------------------------------------------------------------
+ */
+
+ $.fn[NAME$2] = Carousel._jQueryInterface;
+ $.fn[NAME$2].Constructor = Carousel;
+
+ $.fn[NAME$2].noConflict = function () {
+ $.fn[NAME$2] = JQUERY_NO_CONFLICT$2;
+ return Carousel._jQueryInterface;
+ };
+
+ /**
+ * ------------------------------------------------------------------------
+ * Constants
+ * ------------------------------------------------------------------------
+ */
+
+ var NAME$3 = 'collapse';
+ var VERSION$3 = '4.4.1';
+ var DATA_KEY$3 = 'bs.collapse';
+ var EVENT_KEY$3 = "." + DATA_KEY$3;
+ var DATA_API_KEY$3 = '.data-api';
+ var JQUERY_NO_CONFLICT$3 = $.fn[NAME$3];
+ var Default$1 = {
+ toggle: true,
+ parent: ''
+ };
+ var DefaultType$1 = {
+ toggle: 'boolean',
+ parent: '(string|element)'
+ };
+ var Event$3 = {
+ SHOW: "show" + EVENT_KEY$3,
+ SHOWN: "shown" + EVENT_KEY$3,
+ HIDE: "hide" + EVENT_KEY$3,
+ HIDDEN: "hidden" + EVENT_KEY$3,
+ CLICK_DATA_API: "click" + EVENT_KEY$3 + DATA_API_KEY$3
+ };
+ var ClassName$3 = {
+ SHOW: 'show',
+ COLLAPSE: 'collapse',
+ COLLAPSING: 'collapsing',
+ COLLAPSED: 'collapsed'
+ };
+ var Dimension = {
+ WIDTH: 'width',
+ HEIGHT: 'height'
+ };
+ var Selector$3 = {
+ ACTIVES: '.show, .collapsing',
+ DATA_TOGGLE: '[data-toggle="collapse"]'
+ };
+ /**
+ * ------------------------------------------------------------------------
+ * Class Definition
+ * ------------------------------------------------------------------------
+ */
+
+ var Collapse =
+ /*#__PURE__*/
+ function () {
+ function Collapse(element, config) {
+ this._isTransitioning = false;
+ this._element = element;
+ this._config = this._getConfig(config);
+ this._triggerArray = [].slice.call(document.querySelectorAll("[data-toggle=\"collapse\"][href=\"#" + element.id + "\"]," + ("[data-toggle=\"collapse\"][data-target=\"#" + element.id + "\"]")));
+ var toggleList = [].slice.call(document.querySelectorAll(Selector$3.DATA_TOGGLE));
+
+ for (var i = 0, len = toggleList.length; i < len; i++) {
+ var elem = toggleList[i];
+ var selector = Util.getSelectorFromElement(elem);
+ var filterElement = [].slice.call(document.querySelectorAll(selector)).filter(function (foundElem) {
+ return foundElem === element;
+ });
+
+ if (selector !== null && filterElement.length > 0) {
+ this._selector = selector;
+
+ this._triggerArray.push(elem);
+ }
+ }
+
+ this._parent = this._config.parent ? this._getParent() : null;
+
+ if (!this._config.parent) {
+ this._addAriaAndCollapsedClass(this._element, this._triggerArray);
+ }
+
+ if (this._config.toggle) {
+ this.toggle();
+ }
+ } // Getters
+
+
+ var _proto = Collapse.prototype;
+
+ // Public
+ _proto.toggle = function toggle() {
+ if ($(this._element).hasClass(ClassName$3.SHOW)) {
+ this.hide();
+ } else {
+ this.show();
+ }
+ };
+
+ _proto.show = function show() {
+ var _this = this;
+
+ if (this._isTransitioning || $(this._element).hasClass(ClassName$3.SHOW)) {
+ return;
+ }
+
+ var actives;
+ var activesData;
+
+ if (this._parent) {
+ actives = [].slice.call(this._parent.querySelectorAll(Selector$3.ACTIVES)).filter(function (elem) {
+ if (typeof _this._config.parent === 'string') {
+ return elem.getAttribute('data-parent') === _this._config.parent;
+ }
+
+ return elem.classList.contains(ClassName$3.COLLAPSE);
+ });
+
+ if (actives.length === 0) {
+ actives = null;
+ }
+ }
+
+ if (actives) {
+ activesData = $(actives).not(this._selector).data(DATA_KEY$3);
+
+ if (activesData && activesData._isTransitioning) {
+ return;
+ }
+ }
+
+ var startEvent = $.Event(Event$3.SHOW);
+ $(this._element).trigger(startEvent);
+
+ if (startEvent.isDefaultPrevented()) {
+ return;
+ }
+
+ if (actives) {
+ Collapse._jQueryInterface.call($(actives).not(this._selector), 'hide');
+
+ if (!activesData) {
+ $(actives).data(DATA_KEY$3, null);
+ }
+ }
+
+ var dimension = this._getDimension();
+
+ $(this._element).removeClass(ClassName$3.COLLAPSE).addClass(ClassName$3.COLLAPSING);
+ this._element.style[dimension] = 0;
+
+ if (this._triggerArray.length) {
+ $(this._triggerArray).removeClass(ClassName$3.COLLAPSED).attr('aria-expanded', true);
+ }
+
+ this.setTransitioning(true);
+
+ var complete = function complete() {
+ $(_this._element).removeClass(ClassName$3.COLLAPSING).addClass(ClassName$3.COLLAPSE).addClass(ClassName$3.SHOW);
+ _this._element.style[dimension] = '';
+
+ _this.setTransitioning(false);
+
+ $(_this._element).trigger(Event$3.SHOWN);
+ };
+
+ var capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1);
+ var scrollSize = "scroll" + capitalizedDimension;
+ var transitionDuration = Util.getTransitionDurationFromElement(this._element);
+ $(this._element).one(Util.TRANSITION_END, complete).emulateTransitionEnd(transitionDuration);
+ this._element.style[dimension] = this._element[scrollSize] + "px";
+ };
+
+ _proto.hide = function hide() {
+ var _this2 = this;
+
+ if (this._isTransitioning || !$(this._element).hasClass(ClassName$3.SHOW)) {
+ return;
+ }
+
+ var startEvent = $.Event(Event$3.HIDE);
+ $(this._element).trigger(startEvent);
+
+ if (startEvent.isDefaultPrevented()) {
+ return;
+ }
+
+ var dimension = this._getDimension();
+
+ this._element.style[dimension] = this._element.getBoundingClientRect()[dimension] + "px";
+ Util.reflow(this._element);
+ $(this._element).addClass(ClassName$3.COLLAPSING).removeClass(ClassName$3.COLLAPSE).removeClass(ClassName$3.SHOW);
+ var triggerArrayLength = this._triggerArray.length;
+
+ if (triggerArrayLength > 0) {
+ for (var i = 0; i < triggerArrayLength; i++) {
+ var trigger = this._triggerArray[i];
+ var selector = Util.getSelectorFromElement(trigger);
+
+ if (selector !== null) {
+ var $elem = $([].slice.call(document.querySelectorAll(selector)));
+
+ if (!$elem.hasClass(ClassName$3.SHOW)) {
+ $(trigger).addClass(ClassName$3.COLLAPSED).attr('aria-expanded', false);
+ }
+ }
+ }
+ }
+
+ this.setTransitioning(true);
+
+ var complete = function complete() {
+ _this2.setTransitioning(false);
+
+ $(_this2._element).removeClass(ClassName$3.COLLAPSING).addClass(ClassName$3.COLLAPSE).trigger(Event$3.HIDDEN);
+ };
+
+ this._element.style[dimension] = '';
+ var transitionDuration = Util.getTransitionDurationFromElement(this._element);
+ $(this._element).one(Util.TRANSITION_END, complete).emulateTransitionEnd(transitionDuration);
+ };
+
+ _proto.setTransitioning = function setTransitioning(isTransitioning) {
+ this._isTransitioning = isTransitioning;
+ };
+
+ _proto.dispose = function dispose() {
+ $.removeData(this._element, DATA_KEY$3);
+ this._config = null;
+ this._parent = null;
+ this._element = null;
+ this._triggerArray = null;
+ this._isTransitioning = null;
+ } // Private
+ ;
+
+ _proto._getConfig = function _getConfig(config) {
+ config = _objectSpread2({}, Default$1, {}, config);
+ config.toggle = Boolean(config.toggle); // Coerce string values
+
+ Util.typeCheckConfig(NAME$3, config, DefaultType$1);
+ return config;
+ };
+
+ _proto._getDimension = function _getDimension() {
+ var hasWidth = $(this._element).hasClass(Dimension.WIDTH);
+ return hasWidth ? Dimension.WIDTH : Dimension.HEIGHT;
+ };
+
+ _proto._getParent = function _getParent() {
+ var _this3 = this;
+
+ var parent;
+
+ if (Util.isElement(this._config.parent)) {
+ parent = this._config.parent; // It's a jQuery object
+
+ if (typeof this._config.parent.jquery !== 'undefined') {
+ parent = this._config.parent[0];
+ }
+ } else {
+ parent = document.querySelector(this._config.parent);
+ }
+
+ var selector = "[data-toggle=\"collapse\"][data-parent=\"" + this._config.parent + "\"]";
+ var children = [].slice.call(parent.querySelectorAll(selector));
+ $(children).each(function (i, element) {
+ _this3._addAriaAndCollapsedClass(Collapse._getTargetFromElement(element), [element]);
+ });
+ return parent;
+ };
+
+ _proto._addAriaAndCollapsedClass = function _addAriaAndCollapsedClass(element, triggerArray) {
+ var isOpen = $(element).hasClass(ClassName$3.SHOW);
+
+ if (triggerArray.length) {
+ $(triggerArray).toggleClass(ClassName$3.COLLAPSED, !isOpen).attr('aria-expanded', isOpen);
+ }
+ } // Static
+ ;
+
+ Collapse._getTargetFromElement = function _getTargetFromElement(element) {
+ var selector = Util.getSelectorFromElement(element);
+ return selector ? document.querySelector(selector) : null;
+ };
+
+ Collapse._jQueryInterface = function _jQueryInterface(config) {
+ return this.each(function () {
+ var $this = $(this);
+ var data = $this.data(DATA_KEY$3);
+
+ var _config = _objectSpread2({}, Default$1, {}, $this.data(), {}, typeof config === 'object' && config ? config : {});
+
+ if (!data && _config.toggle && /show|hide/.test(config)) {
+ _config.toggle = false;
+ }
+
+ if (!data) {
+ data = new Collapse(this, _config);
+ $this.data(DATA_KEY$3, data);
+ }
+
+ if (typeof config === 'string') {
+ if (typeof data[config] === 'undefined') {
+ throw new TypeError("No method named \"" + config + "\"");
+ }
+
+ data[config]();
+ }
+ });
+ };
+
+ _createClass(Collapse, null, [{
+ key: "VERSION",
+ get: function get() {
+ return VERSION$3;
+ }
+ }, {
+ key: "Default",
+ get: function get() {
+ return Default$1;
+ }
+ }]);
+
+ return Collapse;
+ }();
+ /**
+ * ------------------------------------------------------------------------
+ * Data Api implementation
+ * ------------------------------------------------------------------------
+ */
+
+
+ $(document).on(Event$3.CLICK_DATA_API, Selector$3.DATA_TOGGLE, function (event) {
+ // preventDefault only for elements (which change the URL) not inside the collapsible element
+ if (event.currentTarget.tagName === 'A') {
+ event.preventDefault();
+ }
+
+ var $trigger = $(this);
+ var selector = Util.getSelectorFromElement(this);
+ var selectors = [].slice.call(document.querySelectorAll(selector));
+ $(selectors).each(function () {
+ var $target = $(this);
+ var data = $target.data(DATA_KEY$3);
+ var config = data ? 'toggle' : $trigger.data();
+
+ Collapse._jQueryInterface.call($target, config);
+ });
+ });
+ /**
+ * ------------------------------------------------------------------------
+ * jQuery
+ * ------------------------------------------------------------------------
+ */
+
+ $.fn[NAME$3] = Collapse._jQueryInterface;
+ $.fn[NAME$3].Constructor = Collapse;
+
+ $.fn[NAME$3].noConflict = function () {
+ $.fn[NAME$3] = JQUERY_NO_CONFLICT$3;
+ return Collapse._jQueryInterface;
+ };
+
+ /**
+ * ------------------------------------------------------------------------
+ * Constants
+ * ------------------------------------------------------------------------
+ */
+
+ var NAME$4 = 'dropdown';
+ var VERSION$4 = '4.4.1';
+ var DATA_KEY$4 = 'bs.dropdown';
+ var EVENT_KEY$4 = "." + DATA_KEY$4;
+ var DATA_API_KEY$4 = '.data-api';
+ var JQUERY_NO_CONFLICT$4 = $.fn[NAME$4];
+ var ESCAPE_KEYCODE = 27; // KeyboardEvent.which value for Escape (Esc) key
+
+ var SPACE_KEYCODE = 32; // KeyboardEvent.which value for space key
+
+ var TAB_KEYCODE = 9; // KeyboardEvent.which value for tab key
+
+ var ARROW_UP_KEYCODE = 38; // KeyboardEvent.which value for up arrow key
+
+ var ARROW_DOWN_KEYCODE = 40; // KeyboardEvent.which value for down arrow key
+
+ var RIGHT_MOUSE_BUTTON_WHICH = 3; // MouseEvent.which value for the right button (assuming a right-handed mouse)
+
+ var REGEXP_KEYDOWN = new RegExp(ARROW_UP_KEYCODE + "|" + ARROW_DOWN_KEYCODE + "|" + ESCAPE_KEYCODE);
+ var Event$4 = {
+ HIDE: "hide" + EVENT_KEY$4,
+ HIDDEN: "hidden" + EVENT_KEY$4,
+ SHOW: "show" + EVENT_KEY$4,
+ SHOWN: "shown" + EVENT_KEY$4,
+ CLICK: "click" + EVENT_KEY$4,
+ CLICK_DATA_API: "click" + EVENT_KEY$4 + DATA_API_KEY$4,
+ KEYDOWN_DATA_API: "keydown" + EVENT_KEY$4 + DATA_API_KEY$4,
+ KEYUP_DATA_API: "keyup" + EVENT_KEY$4 + DATA_API_KEY$4
+ };
+ var ClassName$4 = {
+ DISABLED: 'disabled',
+ SHOW: 'show',
+ DROPUP: 'dropup',
+ DROPRIGHT: 'dropright',
+ DROPLEFT: 'dropleft',
+ MENURIGHT: 'dropdown-menu-right',
+ MENULEFT: 'dropdown-menu-left',
+ POSITION_STATIC: 'position-static'
+ };
+ var Selector$4 = {
+ DATA_TOGGLE: '[data-toggle="dropdown"]',
+ FORM_CHILD: '.dropdown form',
+ MENU: '.dropdown-menu',
+ NAVBAR_NAV: '.navbar-nav',
+ VISIBLE_ITEMS: '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)'
+ };
+ var AttachmentMap = {
+ TOP: 'top-start',
+ TOPEND: 'top-end',
+ BOTTOM: 'bottom-start',
+ BOTTOMEND: 'bottom-end',
+ RIGHT: 'right-start',
+ RIGHTEND: 'right-end',
+ LEFT: 'left-start',
+ LEFTEND: 'left-end'
+ };
+ var Default$2 = {
+ offset: 0,
+ flip: true,
+ boundary: 'scrollParent',
+ reference: 'toggle',
+ display: 'dynamic',
+ popperConfig: null
+ };
+ var DefaultType$2 = {
+ offset: '(number|string|function)',
+ flip: 'boolean',
+ boundary: '(string|element)',
+ reference: '(string|element)',
+ display: 'string',
+ popperConfig: '(null|object)'
+ };
+ /**
+ * ------------------------------------------------------------------------
+ * Class Definition
+ * ------------------------------------------------------------------------
+ */
+
+ var Dropdown =
+ /*#__PURE__*/
+ function () {
+ function Dropdown(element, config) {
+ this._element = element;
+ this._popper = null;
+ this._config = this._getConfig(config);
+ this._menu = this._getMenuElement();
+ this._inNavbar = this._detectNavbar();
+
+ this._addEventListeners();
+ } // Getters
+
+
+ var _proto = Dropdown.prototype;
+
+ // Public
+ _proto.toggle = function toggle() {
+ if (this._element.disabled || $(this._element).hasClass(ClassName$4.DISABLED)) {
+ return;
+ }
+
+ var isActive = $(this._menu).hasClass(ClassName$4.SHOW);
+
+ Dropdown._clearMenus();
+
+ if (isActive) {
+ return;
+ }
+
+ this.show(true);
+ };
+
+ _proto.show = function show(usePopper) {
+ if (usePopper === void 0) {
+ usePopper = false;
+ }
+
+ if (this._element.disabled || $(this._element).hasClass(ClassName$4.DISABLED) || $(this._menu).hasClass(ClassName$4.SHOW)) {
+ return;
+ }
+
+ var relatedTarget = {
+ relatedTarget: this._element
+ };
+ var showEvent = $.Event(Event$4.SHOW, relatedTarget);
+
+ var parent = Dropdown._getParentFromElement(this._element);
+
+ $(parent).trigger(showEvent);
+
+ if (showEvent.isDefaultPrevented()) {
+ return;
+ } // Disable totally Popper.js for Dropdown in Navbar
+
+
+ if (!this._inNavbar && usePopper) {
+ /**
+ * Check for Popper dependency
+ * Popper - https://popper.js.org
+ */
+ if (typeof Popper === 'undefined') {
+ throw new TypeError('Bootstrap\'s dropdowns require Popper.js (https://popper.js.org/)');
+ }
+
+ var referenceElement = this._element;
+
+ if (this._config.reference === 'parent') {
+ referenceElement = parent;
+ } else if (Util.isElement(this._config.reference)) {
+ referenceElement = this._config.reference; // Check if it's jQuery element
+
+ if (typeof this._config.reference.jquery !== 'undefined') {
+ referenceElement = this._config.reference[0];
+ }
+ } // If boundary is not `scrollParent`, then set position to `static`
+ // to allow the menu to "escape" the scroll parent's boundaries
+ // https://github.com/twbs/bootstrap/issues/24251
+
+
+ if (this._config.boundary !== 'scrollParent') {
+ $(parent).addClass(ClassName$4.POSITION_STATIC);
+ }
+
+ this._popper = new Popper(referenceElement, this._menu, this._getPopperConfig());
+ } // If this is a touch-enabled device we add extra
+ // empty mouseover listeners to the body's immediate children;
+ // only needed because of broken event delegation on iOS
+ // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
+
+
+ if ('ontouchstart' in document.documentElement && $(parent).closest(Selector$4.NAVBAR_NAV).length === 0) {
+ $(document.body).children().on('mouseover', null, $.noop);
+ }
+
+ this._element.focus();
+
+ this._element.setAttribute('aria-expanded', true);
+
+ $(this._menu).toggleClass(ClassName$4.SHOW);
+ $(parent).toggleClass(ClassName$4.SHOW).trigger($.Event(Event$4.SHOWN, relatedTarget));
+ };
+
+ _proto.hide = function hide() {
+ if (this._element.disabled || $(this._element).hasClass(ClassName$4.DISABLED) || !$(this._menu).hasClass(ClassName$4.SHOW)) {
+ return;
+ }
+
+ var relatedTarget = {
+ relatedTarget: this._element
+ };
+ var hideEvent = $.Event(Event$4.HIDE, relatedTarget);
+
+ var parent = Dropdown._getParentFromElement(this._element);
+
+ $(parent).trigger(hideEvent);
+
+ if (hideEvent.isDefaultPrevented()) {
+ return;
+ }
+
+ if (this._popper) {
+ this._popper.destroy();
+ }
+
+ $(this._menu).toggleClass(ClassName$4.SHOW);
+ $(parent).toggleClass(ClassName$4.SHOW).trigger($.Event(Event$4.HIDDEN, relatedTarget));
+ };
+
+ _proto.dispose = function dispose() {
+ $.removeData(this._element, DATA_KEY$4);
+ $(this._element).off(EVENT_KEY$4);
+ this._element = null;
+ this._menu = null;
+
+ if (this._popper !== null) {
+ this._popper.destroy();
+
+ this._popper = null;
+ }
+ };
+
+ _proto.update = function update() {
+ this._inNavbar = this._detectNavbar();
+
+ if (this._popper !== null) {
+ this._popper.scheduleUpdate();
+ }
+ } // Private
+ ;
+
+ _proto._addEventListeners = function _addEventListeners() {
+ var _this = this;
+
+ $(this._element).on(Event$4.CLICK, function (event) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ _this.toggle();
+ });
+ };
+
+ _proto._getConfig = function _getConfig(config) {
+ config = _objectSpread2({}, this.constructor.Default, {}, $(this._element).data(), {}, config);
+ Util.typeCheckConfig(NAME$4, config, this.constructor.DefaultType);
+ return config;
+ };
+
+ _proto._getMenuElement = function _getMenuElement() {
+ if (!this._menu) {
+ var parent = Dropdown._getParentFromElement(this._element);
+
+ if (parent) {
+ this._menu = parent.querySelector(Selector$4.MENU);
+ }
+ }
+
+ return this._menu;
+ };
+
+ _proto._getPlacement = function _getPlacement() {
+ var $parentDropdown = $(this._element.parentNode);
+ var placement = AttachmentMap.BOTTOM; // Handle dropup
+
+ if ($parentDropdown.hasClass(ClassName$4.DROPUP)) {
+ placement = AttachmentMap.TOP;
+
+ if ($(this._menu).hasClass(ClassName$4.MENURIGHT)) {
+ placement = AttachmentMap.TOPEND;
+ }
+ } else if ($parentDropdown.hasClass(ClassName$4.DROPRIGHT)) {
+ placement = AttachmentMap.RIGHT;
+ } else if ($parentDropdown.hasClass(ClassName$4.DROPLEFT)) {
+ placement = AttachmentMap.LEFT;
+ } else if ($(this._menu).hasClass(ClassName$4.MENURIGHT)) {
+ placement = AttachmentMap.BOTTOMEND;
+ }
+
+ return placement;
+ };
+
+ _proto._detectNavbar = function _detectNavbar() {
+ return $(this._element).closest('.navbar').length > 0;
+ };
+
+ _proto._getOffset = function _getOffset() {
+ var _this2 = this;
+
+ var offset = {};
+
+ if (typeof this._config.offset === 'function') {
+ offset.fn = function (data) {
+ data.offsets = _objectSpread2({}, data.offsets, {}, _this2._config.offset(data.offsets, _this2._element) || {});
+ return data;
+ };
+ } else {
+ offset.offset = this._config.offset;
+ }
+
+ return offset;
+ };
+
+ _proto._getPopperConfig = function _getPopperConfig() {
+ var popperConfig = {
+ placement: this._getPlacement(),
+ modifiers: {
+ offset: this._getOffset(),
+ flip: {
+ enabled: this._config.flip
+ },
+ preventOverflow: {
+ boundariesElement: this._config.boundary
+ }
+ }
+ }; // Disable Popper.js if we have a static display
+
+ if (this._config.display === 'static') {
+ popperConfig.modifiers.applyStyle = {
+ enabled: false
+ };
+ }
+
+ return _objectSpread2({}, popperConfig, {}, this._config.popperConfig);
+ } // Static
+ ;
+
+ Dropdown._jQueryInterface = function _jQueryInterface(config) {
+ return this.each(function () {
+ var data = $(this).data(DATA_KEY$4);
+
+ var _config = typeof config === 'object' ? config : null;
+
+ if (!data) {
+ data = new Dropdown(this, _config);
+ $(this).data(DATA_KEY$4, data);
+ }
+
+ if (typeof config === 'string') {
+ if (typeof data[config] === 'undefined') {
+ throw new TypeError("No method named \"" + config + "\"");
+ }
+
+ data[config]();
+ }
+ });
+ };
+
+ Dropdown._clearMenus = function _clearMenus(event) {
+ if (event && (event.which === RIGHT_MOUSE_BUTTON_WHICH || event.type === 'keyup' && event.which !== TAB_KEYCODE)) {
+ return;
+ }
+
+ var toggles = [].slice.call(document.querySelectorAll(Selector$4.DATA_TOGGLE));
+
+ for (var i = 0, len = toggles.length; i < len; i++) {
+ var parent = Dropdown._getParentFromElement(toggles[i]);
+
+ var context = $(toggles[i]).data(DATA_KEY$4);
+ var relatedTarget = {
+ relatedTarget: toggles[i]
+ };
+
+ if (event && event.type === 'click') {
+ relatedTarget.clickEvent = event;
+ }
+
+ if (!context) {
+ continue;
+ }
+
+ var dropdownMenu = context._menu;
+
+ if (!$(parent).hasClass(ClassName$4.SHOW)) {
+ continue;
+ }
+
+ if (event && (event.type === 'click' && /input|textarea/i.test(event.target.tagName) || event.type === 'keyup' && event.which === TAB_KEYCODE) && $.contains(parent, event.target)) {
+ continue;
+ }
+
+ var hideEvent = $.Event(Event$4.HIDE, relatedTarget);
+ $(parent).trigger(hideEvent);
+
+ if (hideEvent.isDefaultPrevented()) {
+ continue;
+ } // If this is a touch-enabled device we remove the extra
+ // empty mouseover listeners we added for iOS support
+
+
+ if ('ontouchstart' in document.documentElement) {
+ $(document.body).children().off('mouseover', null, $.noop);
+ }
+
+ toggles[i].setAttribute('aria-expanded', 'false');
+
+ if (context._popper) {
+ context._popper.destroy();
+ }
+
+ $(dropdownMenu).removeClass(ClassName$4.SHOW);
+ $(parent).removeClass(ClassName$4.SHOW).trigger($.Event(Event$4.HIDDEN, relatedTarget));
+ }
+ };
+
+ Dropdown._getParentFromElement = function _getParentFromElement(element) {
+ var parent;
+ var selector = Util.getSelectorFromElement(element);
+
+ if (selector) {
+ parent = document.querySelector(selector);
+ }
+
+ return parent || element.parentNode;
+ } // eslint-disable-next-line complexity
+ ;
+
+ Dropdown._dataApiKeydownHandler = function _dataApiKeydownHandler(event) {
+ // If not input/textarea:
+ // - And not a key in REGEXP_KEYDOWN => not a dropdown command
+ // If input/textarea:
+ // - If space key => not a dropdown command
+ // - If key is other than escape
+ // - If key is not up or down => not a dropdown command
+ // - If trigger inside the menu => not a dropdown command
+ if (/input|textarea/i.test(event.target.tagName) ? event.which === SPACE_KEYCODE || event.which !== ESCAPE_KEYCODE && (event.which !== ARROW_DOWN_KEYCODE && event.which !== ARROW_UP_KEYCODE || $(event.target).closest(Selector$4.MENU).length) : !REGEXP_KEYDOWN.test(event.which)) {
+ return;
+ }
+
+ event.preventDefault();
+ event.stopPropagation();
+
+ if (this.disabled || $(this).hasClass(ClassName$4.DISABLED)) {
+ return;
+ }
+
+ var parent = Dropdown._getParentFromElement(this);
+
+ var isActive = $(parent).hasClass(ClassName$4.SHOW);
+
+ if (!isActive && event.which === ESCAPE_KEYCODE) {
+ return;
+ }
+
+ if (!isActive || isActive && (event.which === ESCAPE_KEYCODE || event.which === SPACE_KEYCODE)) {
+ if (event.which === ESCAPE_KEYCODE) {
+ var toggle = parent.querySelector(Selector$4.DATA_TOGGLE);
+ $(toggle).trigger('focus');
+ }
+
+ $(this).trigger('click');
+ return;
+ }
+
+ var items = [].slice.call(parent.querySelectorAll(Selector$4.VISIBLE_ITEMS)).filter(function (item) {
+ return $(item).is(':visible');
+ });
+
+ if (items.length === 0) {
+ return;
+ }
+
+ var index = items.indexOf(event.target);
+
+ if (event.which === ARROW_UP_KEYCODE && index > 0) {
+ // Up
+ index--;
+ }
+
+ if (event.which === ARROW_DOWN_KEYCODE && index < items.length - 1) {
+ // Down
+ index++;
+ }
+
+ if (index < 0) {
+ index = 0;
+ }
+
+ items[index].focus();
+ };
+
+ _createClass(Dropdown, null, [{
+ key: "VERSION",
+ get: function get() {
+ return VERSION$4;
+ }
+ }, {
+ key: "Default",
+ get: function get() {
+ return Default$2;
+ }
+ }, {
+ key: "DefaultType",
+ get: function get() {
+ return DefaultType$2;
+ }
+ }]);
+
+ return Dropdown;
+ }();
+ /**
+ * ------------------------------------------------------------------------
+ * Data Api implementation
+ * ------------------------------------------------------------------------
+ */
+
+
+ $(document).on(Event$4.KEYDOWN_DATA_API, Selector$4.DATA_TOGGLE, Dropdown._dataApiKeydownHandler).on(Event$4.KEYDOWN_DATA_API, Selector$4.MENU, Dropdown._dataApiKeydownHandler).on(Event$4.CLICK_DATA_API + " " + Event$4.KEYUP_DATA_API, Dropdown._clearMenus).on(Event$4.CLICK_DATA_API, Selector$4.DATA_TOGGLE, function (event) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ Dropdown._jQueryInterface.call($(this), 'toggle');
+ }).on(Event$4.CLICK_DATA_API, Selector$4.FORM_CHILD, function (e) {
+ e.stopPropagation();
+ });
+ /**
+ * ------------------------------------------------------------------------
+ * jQuery
+ * ------------------------------------------------------------------------
+ */
+
+ $.fn[NAME$4] = Dropdown._jQueryInterface;
+ $.fn[NAME$4].Constructor = Dropdown;
+
+ $.fn[NAME$4].noConflict = function () {
+ $.fn[NAME$4] = JQUERY_NO_CONFLICT$4;
+ return Dropdown._jQueryInterface;
+ };
+
+ /**
+ * ------------------------------------------------------------------------
+ * Constants
+ * ------------------------------------------------------------------------
+ */
+
+ var NAME$5 = 'modal';
+ var VERSION$5 = '4.4.1';
+ var DATA_KEY$5 = 'bs.modal';
+ var EVENT_KEY$5 = "." + DATA_KEY$5;
+ var DATA_API_KEY$5 = '.data-api';
+ var JQUERY_NO_CONFLICT$5 = $.fn[NAME$5];
+ var ESCAPE_KEYCODE$1 = 27; // KeyboardEvent.which value for Escape (Esc) key
+
+ var Default$3 = {
+ backdrop: true,
+ keyboard: true,
+ focus: true,
+ show: true
+ };
+ var DefaultType$3 = {
+ backdrop: '(boolean|string)',
+ keyboard: 'boolean',
+ focus: 'boolean',
+ show: 'boolean'
+ };
+ var Event$5 = {
+ HIDE: "hide" + EVENT_KEY$5,
+ HIDE_PREVENTED: "hidePrevented" + EVENT_KEY$5,
+ HIDDEN: "hidden" + EVENT_KEY$5,
+ SHOW: "show" + EVENT_KEY$5,
+ SHOWN: "shown" + EVENT_KEY$5,
+ FOCUSIN: "focusin" + EVENT_KEY$5,
+ RESIZE: "resize" + EVENT_KEY$5,
+ CLICK_DISMISS: "click.dismiss" + EVENT_KEY$5,
+ KEYDOWN_DISMISS: "keydown.dismiss" + EVENT_KEY$5,
+ MOUSEUP_DISMISS: "mouseup.dismiss" + EVENT_KEY$5,
+ MOUSEDOWN_DISMISS: "mousedown.dismiss" + EVENT_KEY$5,
+ CLICK_DATA_API: "click" + EVENT_KEY$5 + DATA_API_KEY$5
+ };
+ var ClassName$5 = {
+ SCROLLABLE: 'modal-dialog-scrollable',
+ SCROLLBAR_MEASURER: 'modal-scrollbar-measure',
+ BACKDROP: 'modal-backdrop',
+ OPEN: 'modal-open',
+ FADE: 'fade',
+ SHOW: 'show',
+ STATIC: 'modal-static'
+ };
+ var Selector$5 = {
+ DIALOG: '.modal-dialog',
+ MODAL_BODY: '.modal-body',
+ DATA_TOGGLE: '[data-toggle="modal"]',
+ DATA_DISMISS: '[data-dismiss="modal"]',
+ FIXED_CONTENT: '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top',
+ STICKY_CONTENT: '.sticky-top'
+ };
+ /**
+ * ------------------------------------------------------------------------
+ * Class Definition
+ * ------------------------------------------------------------------------
+ */
+
+ var Modal =
+ /*#__PURE__*/
+ function () {
+ function Modal(element, config) {
+ this._config = this._getConfig(config);
+ this._element = element;
+ this._dialog = element.querySelector(Selector$5.DIALOG);
+ this._backdrop = null;
+ this._isShown = false;
+ this._isBodyOverflowing = false;
+ this._ignoreBackdropClick = false;
+ this._isTransitioning = false;
+ this._scrollbarWidth = 0;
+ } // Getters
+
+
+ var _proto = Modal.prototype;
+
+ // Public
+ _proto.toggle = function toggle(relatedTarget) {
+ return this._isShown ? this.hide() : this.show(relatedTarget);
+ };
+
+ _proto.show = function show(relatedTarget) {
+ var _this = this;
+
+ if (this._isShown || this._isTransitioning) {
+ return;
+ }
+
+ if ($(this._element).hasClass(ClassName$5.FADE)) {
+ this._isTransitioning = true;
+ }
+
+ var showEvent = $.Event(Event$5.SHOW, {
+ relatedTarget: relatedTarget
+ });
+ $(this._element).trigger(showEvent);
+
+ if (this._isShown || showEvent.isDefaultPrevented()) {
+ return;
+ }
+
+ this._isShown = true;
+
+ this._checkScrollbar();
+
+ this._setScrollbar();
+
+ this._adjustDialog();
+
+ this._setEscapeEvent();
+
+ this._setResizeEvent();
+
+ $(this._element).on(Event$5.CLICK_DISMISS, Selector$5.DATA_DISMISS, function (event) {
+ return _this.hide(event);
+ });
+ $(this._dialog).on(Event$5.MOUSEDOWN_DISMISS, function () {
+ $(_this._element).one(Event$5.MOUSEUP_DISMISS, function (event) {
+ if ($(event.target).is(_this._element)) {
+ _this._ignoreBackdropClick = true;
+ }
+ });
+ });
+
+ this._showBackdrop(function () {
+ return _this._showElement(relatedTarget);
+ });
+ };
+
+ _proto.hide = function hide(event) {
+ var _this2 = this;
+
+ if (event) {
+ event.preventDefault();
+ }
+
+ if (!this._isShown || this._isTransitioning) {
+ return;
+ }
+
+ var hideEvent = $.Event(Event$5.HIDE);
+ $(this._element).trigger(hideEvent);
+
+ if (!this._isShown || hideEvent.isDefaultPrevented()) {
+ return;
+ }
+
+ this._isShown = false;
+ var transition = $(this._element).hasClass(ClassName$5.FADE);
+
+ if (transition) {
+ this._isTransitioning = true;
+ }
+
+ this._setEscapeEvent();
+
+ this._setResizeEvent();
+
+ $(document).off(Event$5.FOCUSIN);
+ $(this._element).removeClass(ClassName$5.SHOW);
+ $(this._element).off(Event$5.CLICK_DISMISS);
+ $(this._dialog).off(Event$5.MOUSEDOWN_DISMISS);
+
+ if (transition) {
+ var transitionDuration = Util.getTransitionDurationFromElement(this._element);
+ $(this._element).one(Util.TRANSITION_END, function (event) {
+ return _this2._hideModal(event);
+ }).emulateTransitionEnd(transitionDuration);
+ } else {
+ this._hideModal();
+ }
+ };
+
+ _proto.dispose = function dispose() {
+ [window, this._element, this._dialog].forEach(function (htmlElement) {
+ return $(htmlElement).off(EVENT_KEY$5);
+ });
+ /**
+ * `document` has 2 events `Event.FOCUSIN` and `Event.CLICK_DATA_API`
+ * Do not move `document` in `htmlElements` array
+ * It will remove `Event.CLICK_DATA_API` event that should remain
+ */
+
+ $(document).off(Event$5.FOCUSIN);
+ $.removeData(this._element, DATA_KEY$5);
+ this._config = null;
+ this._element = null;
+ this._dialog = null;
+ this._backdrop = null;
+ this._isShown = null;
+ this._isBodyOverflowing = null;
+ this._ignoreBackdropClick = null;
+ this._isTransitioning = null;
+ this._scrollbarWidth = null;
+ };
+
+ _proto.handleUpdate = function handleUpdate() {
+ this._adjustDialog();
+ } // Private
+ ;
+
+ _proto._getConfig = function _getConfig(config) {
+ config = _objectSpread2({}, Default$3, {}, config);
+ Util.typeCheckConfig(NAME$5, config, DefaultType$3);
+ return config;
+ };
+
+ _proto._triggerBackdropTransition = function _triggerBackdropTransition() {
+ var _this3 = this;
+
+ if (this._config.backdrop === 'static') {
+ var hideEventPrevented = $.Event(Event$5.HIDE_PREVENTED);
+ $(this._element).trigger(hideEventPrevented);
+
+ if (hideEventPrevented.defaultPrevented) {
+ return;
+ }
+
+ this._element.classList.add(ClassName$5.STATIC);
+
+ var modalTransitionDuration = Util.getTransitionDurationFromElement(this._element);
+ $(this._element).one(Util.TRANSITION_END, function () {
+ _this3._element.classList.remove(ClassName$5.STATIC);
+ }).emulateTransitionEnd(modalTransitionDuration);
+
+ this._element.focus();
+ } else {
+ this.hide();
+ }
+ };
+
+ _proto._showElement = function _showElement(relatedTarget) {
+ var _this4 = this;
+
+ var transition = $(this._element).hasClass(ClassName$5.FADE);
+ var modalBody = this._dialog ? this._dialog.querySelector(Selector$5.MODAL_BODY) : null;
+
+ if (!this._element.parentNode || this._element.parentNode.nodeType !== Node.ELEMENT_NODE) {
+ // Don't move modal's DOM position
+ document.body.appendChild(this._element);
+ }
+
+ this._element.style.display = 'block';
+
+ this._element.removeAttribute('aria-hidden');
+
+ this._element.setAttribute('aria-modal', true);
+
+ if ($(this._dialog).hasClass(ClassName$5.SCROLLABLE) && modalBody) {
+ modalBody.scrollTop = 0;
+ } else {
+ this._element.scrollTop = 0;
+ }
+
+ if (transition) {
+ Util.reflow(this._element);
+ }
+
+ $(this._element).addClass(ClassName$5.SHOW);
+
+ if (this._config.focus) {
+ this._enforceFocus();
+ }
+
+ var shownEvent = $.Event(Event$5.SHOWN, {
+ relatedTarget: relatedTarget
+ });
+
+ var transitionComplete = function transitionComplete() {
+ if (_this4._config.focus) {
+ _this4._element.focus();
+ }
+
+ _this4._isTransitioning = false;
+ $(_this4._element).trigger(shownEvent);
+ };
+
+ if (transition) {
+ var transitionDuration = Util.getTransitionDurationFromElement(this._dialog);
+ $(this._dialog).one(Util.TRANSITION_END, transitionComplete).emulateTransitionEnd(transitionDuration);
+ } else {
+ transitionComplete();
+ }
+ };
+
+ _proto._enforceFocus = function _enforceFocus() {
+ var _this5 = this;
+
+ $(document).off(Event$5.FOCUSIN) // Guard against infinite focus loop
+ .on(Event$5.FOCUSIN, function (event) {
+ if (document !== event.target && _this5._element !== event.target && $(_this5._element).has(event.target).length === 0) {
+ _this5._element.focus();
+ }
+ });
+ };
+
+ _proto._setEscapeEvent = function _setEscapeEvent() {
+ var _this6 = this;
+
+ if (this._isShown && this._config.keyboard) {
+ $(this._element).on(Event$5.KEYDOWN_DISMISS, function (event) {
+ if (event.which === ESCAPE_KEYCODE$1) {
+ _this6._triggerBackdropTransition();
+ }
+ });
+ } else if (!this._isShown) {
+ $(this._element).off(Event$5.KEYDOWN_DISMISS);
+ }
+ };
+
+ _proto._setResizeEvent = function _setResizeEvent() {
+ var _this7 = this;
+
+ if (this._isShown) {
+ $(window).on(Event$5.RESIZE, function (event) {
+ return _this7.handleUpdate(event);
+ });
+ } else {
+ $(window).off(Event$5.RESIZE);
+ }
+ };
+
+ _proto._hideModal = function _hideModal() {
+ var _this8 = this;
+
+ this._element.style.display = 'none';
+
+ this._element.setAttribute('aria-hidden', true);
+
+ this._element.removeAttribute('aria-modal');
+
+ this._isTransitioning = false;
+
+ this._showBackdrop(function () {
+ $(document.body).removeClass(ClassName$5.OPEN);
+
+ _this8._resetAdjustments();
+
+ _this8._resetScrollbar();
+
+ $(_this8._element).trigger(Event$5.HIDDEN);
+ });
+ };
+
+ _proto._removeBackdrop = function _removeBackdrop() {
+ if (this._backdrop) {
+ $(this._backdrop).remove();
+ this._backdrop = null;
+ }
+ };
+
+ _proto._showBackdrop = function _showBackdrop(callback) {
+ var _this9 = this;
+
+ var animate = $(this._element).hasClass(ClassName$5.FADE) ? ClassName$5.FADE : '';
+
+ if (this._isShown && this._config.backdrop) {
+ this._backdrop = document.createElement('div');
+ this._backdrop.className = ClassName$5.BACKDROP;
+
+ if (animate) {
+ this._backdrop.classList.add(animate);
+ }
+
+ $(this._backdrop).appendTo(document.body);
+ $(this._element).on(Event$5.CLICK_DISMISS, function (event) {
+ if (_this9._ignoreBackdropClick) {
+ _this9._ignoreBackdropClick = false;
+ return;
+ }
+
+ if (event.target !== event.currentTarget) {
+ return;
+ }
+
+ _this9._triggerBackdropTransition();
+ });
+
+ if (animate) {
+ Util.reflow(this._backdrop);
+ }
+
+ $(this._backdrop).addClass(ClassName$5.SHOW);
+
+ if (!callback) {
+ return;
+ }
+
+ if (!animate) {
+ callback();
+ return;
+ }
+
+ var backdropTransitionDuration = Util.getTransitionDurationFromElement(this._backdrop);
+ $(this._backdrop).one(Util.TRANSITION_END, callback).emulateTransitionEnd(backdropTransitionDuration);
+ } else if (!this._isShown && this._backdrop) {
+ $(this._backdrop).removeClass(ClassName$5.SHOW);
+
+ var callbackRemove = function callbackRemove() {
+ _this9._removeBackdrop();
+
+ if (callback) {
+ callback();
+ }
+ };
+
+ if ($(this._element).hasClass(ClassName$5.FADE)) {
+ var _backdropTransitionDuration = Util.getTransitionDurationFromElement(this._backdrop);
+
+ $(this._backdrop).one(Util.TRANSITION_END, callbackRemove).emulateTransitionEnd(_backdropTransitionDuration);
+ } else {
+ callbackRemove();
+ }
+ } else if (callback) {
+ callback();
+ }
+ } // ----------------------------------------------------------------------
+ // the following methods are used to handle overflowing modals
+ // todo (fat): these should probably be refactored out of modal.js
+ // ----------------------------------------------------------------------
+ ;
+
+ _proto._adjustDialog = function _adjustDialog() {
+ var isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight;
+
+ if (!this._isBodyOverflowing && isModalOverflowing) {
+ this._element.style.paddingLeft = this._scrollbarWidth + "px";
+ }
+
+ if (this._isBodyOverflowing && !isModalOverflowing) {
+ this._element.style.paddingRight = this._scrollbarWidth + "px";
+ }
+ };
+
+ _proto._resetAdjustments = function _resetAdjustments() {
+ this._element.style.paddingLeft = '';
+ this._element.style.paddingRight = '';
+ };
+
+ _proto._checkScrollbar = function _checkScrollbar() {
+ var rect = document.body.getBoundingClientRect();
+ this._isBodyOverflowing = rect.left + rect.right < window.innerWidth;
+ this._scrollbarWidth = this._getScrollbarWidth();
+ };
+
+ _proto._setScrollbar = function _setScrollbar() {
+ var _this10 = this;
+
+ if (this._isBodyOverflowing) {
+ // Note: DOMNode.style.paddingRight returns the actual value or '' if not set
+ // while $(DOMNode).css('padding-right') returns the calculated value or 0 if not set
+ var fixedContent = [].slice.call(document.querySelectorAll(Selector$5.FIXED_CONTENT));
+ var stickyContent = [].slice.call(document.querySelectorAll(Selector$5.STICKY_CONTENT)); // Adjust fixed content padding
+
+ $(fixedContent).each(function (index, element) {
+ var actualPadding = element.style.paddingRight;
+ var calculatedPadding = $(element).css('padding-right');
+ $(element).data('padding-right', actualPadding).css('padding-right', parseFloat(calculatedPadding) + _this10._scrollbarWidth + "px");
+ }); // Adjust sticky content margin
+
+ $(stickyContent).each(function (index, element) {
+ var actualMargin = element.style.marginRight;
+ var calculatedMargin = $(element).css('margin-right');
+ $(element).data('margin-right', actualMargin).css('margin-right', parseFloat(calculatedMargin) - _this10._scrollbarWidth + "px");
+ }); // Adjust body padding
+
+ var actualPadding = document.body.style.paddingRight;
+ var calculatedPadding = $(document.body).css('padding-right');
+ $(document.body).data('padding-right', actualPadding).css('padding-right', parseFloat(calculatedPadding) + this._scrollbarWidth + "px");
+ }
+
+ $(document.body).addClass(ClassName$5.OPEN);
+ };
+
+ _proto._resetScrollbar = function _resetScrollbar() {
+ // Restore fixed content padding
+ var fixedContent = [].slice.call(document.querySelectorAll(Selector$5.FIXED_CONTENT));
+ $(fixedContent).each(function (index, element) {
+ var padding = $(element).data('padding-right');
+ $(element).removeData('padding-right');
+ element.style.paddingRight = padding ? padding : '';
+ }); // Restore sticky content
+
+ var elements = [].slice.call(document.querySelectorAll("" + Selector$5.STICKY_CONTENT));
+ $(elements).each(function (index, element) {
+ var margin = $(element).data('margin-right');
+
+ if (typeof margin !== 'undefined') {
+ $(element).css('margin-right', margin).removeData('margin-right');
+ }
+ }); // Restore body padding
+
+ var padding = $(document.body).data('padding-right');
+ $(document.body).removeData('padding-right');
+ document.body.style.paddingRight = padding ? padding : '';
+ };
+
+ _proto._getScrollbarWidth = function _getScrollbarWidth() {
+ // thx d.walsh
+ var scrollDiv = document.createElement('div');
+ scrollDiv.className = ClassName$5.SCROLLBAR_MEASURER;
+ document.body.appendChild(scrollDiv);
+ var scrollbarWidth = scrollDiv.getBoundingClientRect().width - scrollDiv.clientWidth;
+ document.body.removeChild(scrollDiv);
+ return scrollbarWidth;
+ } // Static
+ ;
+
+ Modal._jQueryInterface = function _jQueryInterface(config, relatedTarget) {
+ return this.each(function () {
+ var data = $(this).data(DATA_KEY$5);
+
+ var _config = _objectSpread2({}, Default$3, {}, $(this).data(), {}, typeof config === 'object' && config ? config : {});
+
+ if (!data) {
+ data = new Modal(this, _config);
+ $(this).data(DATA_KEY$5, data);
+ }
+
+ if (typeof config === 'string') {
+ if (typeof data[config] === 'undefined') {
+ throw new TypeError("No method named \"" + config + "\"");
+ }
+
+ data[config](relatedTarget);
+ } else if (_config.show) {
+ data.show(relatedTarget);
+ }
+ });
+ };
+
+ _createClass(Modal, null, [{
+ key: "VERSION",
+ get: function get() {
+ return VERSION$5;
+ }
+ }, {
+ key: "Default",
+ get: function get() {
+ return Default$3;
+ }
+ }]);
+
+ return Modal;
+ }();
+ /**
+ * ------------------------------------------------------------------------
+ * Data Api implementation
+ * ------------------------------------------------------------------------
+ */
+
+
+ $(document).on(Event$5.CLICK_DATA_API, Selector$5.DATA_TOGGLE, function (event) {
+ var _this11 = this;
+
+ var target;
+ var selector = Util.getSelectorFromElement(this);
+
+ if (selector) {
+ target = document.querySelector(selector);
+ }
+
+ var config = $(target).data(DATA_KEY$5) ? 'toggle' : _objectSpread2({}, $(target).data(), {}, $(this).data());
+
+ if (this.tagName === 'A' || this.tagName === 'AREA') {
+ event.preventDefault();
+ }
+
+ var $target = $(target).one(Event$5.SHOW, function (showEvent) {
+ if (showEvent.isDefaultPrevented()) {
+ // Only register focus restorer if modal will actually get shown
+ return;
+ }
+
+ $target.one(Event$5.HIDDEN, function () {
+ if ($(_this11).is(':visible')) {
+ _this11.focus();
+ }
+ });
+ });
+
+ Modal._jQueryInterface.call($(target), config, this);
+ });
+ /**
+ * ------------------------------------------------------------------------
+ * jQuery
+ * ------------------------------------------------------------------------
+ */
+
+ $.fn[NAME$5] = Modal._jQueryInterface;
+ $.fn[NAME$5].Constructor = Modal;
+
+ $.fn[NAME$5].noConflict = function () {
+ $.fn[NAME$5] = JQUERY_NO_CONFLICT$5;
+ return Modal._jQueryInterface;
+ };
+
+ /**
+ * --------------------------------------------------------------------------
+ * Bootstrap (v4.4.1): tools/sanitizer.js
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * --------------------------------------------------------------------------
+ */
+ var uriAttrs = ['background', 'cite', 'href', 'itemtype', 'longdesc', 'poster', 'src', 'xlink:href'];
+ var ARIA_ATTRIBUTE_PATTERN = /^aria-[\w-]*$/i;
+ var DefaultWhitelist = {
+ // Global attributes allowed on any supplied element below.
+ '*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN],
+ a: ['target', 'href', 'title', 'rel'],
+ area: [],
+ b: [],
+ br: [],
+ col: [],
+ code: [],
+ div: [],
+ em: [],
+ hr: [],
+ h1: [],
+ h2: [],
+ h3: [],
+ h4: [],
+ h5: [],
+ h6: [],
+ i: [],
+ img: ['src', 'alt', 'title', 'width', 'height'],
+ li: [],
+ ol: [],
+ p: [],
+ pre: [],
+ s: [],
+ small: [],
+ span: [],
+ sub: [],
+ sup: [],
+ strong: [],
+ u: [],
+ ul: []
+ };
+ /**
+ * A pattern that recognizes a commonly useful subset of URLs that are safe.
+ *
+ * Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts
+ */
+
+ var SAFE_URL_PATTERN = /^(?:(?:https?|mailto|ftp|tel|file):|[^&:/?#]*(?:[/?#]|$))/gi;
+ /**
+ * A pattern that matches safe data URLs. Only matches image, video and audio types.
+ *
+ * Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts
+ */
+
+ var DATA_URL_PATTERN = /^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[a-z0-9+/]+=*$/i;
+
+ function allowedAttribute(attr, allowedAttributeList) {
+ var attrName = attr.nodeName.toLowerCase();
+
+ if (allowedAttributeList.indexOf(attrName) !== -1) {
+ if (uriAttrs.indexOf(attrName) !== -1) {
+ return Boolean(attr.nodeValue.match(SAFE_URL_PATTERN) || attr.nodeValue.match(DATA_URL_PATTERN));
+ }
+
+ return true;
+ }
+
+ var regExp = allowedAttributeList.filter(function (attrRegex) {
+ return attrRegex instanceof RegExp;
+ }); // Check if a regular expression validates the attribute.
+
+ for (var i = 0, l = regExp.length; i < l; i++) {
+ if (attrName.match(regExp[i])) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ function sanitizeHtml(unsafeHtml, whiteList, sanitizeFn) {
+ if (unsafeHtml.length === 0) {
+ return unsafeHtml;
+ }
+
+ if (sanitizeFn && typeof sanitizeFn === 'function') {
+ return sanitizeFn(unsafeHtml);
+ }
+
+ var domParser = new window.DOMParser();
+ var createdDocument = domParser.parseFromString(unsafeHtml, 'text/html');
+ var whitelistKeys = Object.keys(whiteList);
+ var elements = [].slice.call(createdDocument.body.querySelectorAll('*'));
+
+ var _loop = function _loop(i, len) {
+ var el = elements[i];
+ var elName = el.nodeName.toLowerCase();
+
+ if (whitelistKeys.indexOf(el.nodeName.toLowerCase()) === -1) {
+ el.parentNode.removeChild(el);
+ return "continue";
+ }
+
+ var attributeList = [].slice.call(el.attributes);
+ var whitelistedAttributes = [].concat(whiteList['*'] || [], whiteList[elName] || []);
+ attributeList.forEach(function (attr) {
+ if (!allowedAttribute(attr, whitelistedAttributes)) {
+ el.removeAttribute(attr.nodeName);
+ }
+ });
+ };
+
+ for (var i = 0, len = elements.length; i < len; i++) {
+ var _ret = _loop(i);
+
+ if (_ret === "continue") continue;
+ }
+
+ return createdDocument.body.innerHTML;
+ }
+
+ /**
+ * ------------------------------------------------------------------------
+ * Constants
+ * ------------------------------------------------------------------------
+ */
+
+ var NAME$6 = 'tooltip';
+ var VERSION$6 = '4.4.1';
+ var DATA_KEY$6 = 'bs.tooltip';
+ var EVENT_KEY$6 = "." + DATA_KEY$6;
+ var JQUERY_NO_CONFLICT$6 = $.fn[NAME$6];
+ var CLASS_PREFIX = 'bs-tooltip';
+ var BSCLS_PREFIX_REGEX = new RegExp("(^|\\s)" + CLASS_PREFIX + "\\S+", 'g');
+ var DISALLOWED_ATTRIBUTES = ['sanitize', 'whiteList', 'sanitizeFn'];
+ var DefaultType$4 = {
+ animation: 'boolean',
+ template: 'string',
+ title: '(string|element|function)',
+ trigger: 'string',
+ delay: '(number|object)',
+ html: 'boolean',
+ selector: '(string|boolean)',
+ placement: '(string|function)',
+ offset: '(number|string|function)',
+ container: '(string|element|boolean)',
+ fallbackPlacement: '(string|array)',
+ boundary: '(string|element)',
+ sanitize: 'boolean',
+ sanitizeFn: '(null|function)',
+ whiteList: 'object',
+ popperConfig: '(null|object)'
+ };
+ var AttachmentMap$1 = {
+ AUTO: 'auto',
+ TOP: 'top',
+ RIGHT: 'right',
+ BOTTOM: 'bottom',
+ LEFT: 'left'
+ };
+ var Default$4 = {
+ animation: true,
+ template: '',
+ trigger: 'hover focus',
+ title: '',
+ delay: 0,
+ html: false,
+ selector: false,
+ placement: 'top',
+ offset: 0,
+ container: false,
+ fallbackPlacement: 'flip',
+ boundary: 'scrollParent',
+ sanitize: true,
+ sanitizeFn: null,
+ whiteList: DefaultWhitelist,
+ popperConfig: null
+ };
+ var HoverState = {
+ SHOW: 'show',
+ OUT: 'out'
+ };
+ var Event$6 = {
+ HIDE: "hide" + EVENT_KEY$6,
+ HIDDEN: "hidden" + EVENT_KEY$6,
+ SHOW: "show" + EVENT_KEY$6,
+ SHOWN: "shown" + EVENT_KEY$6,
+ INSERTED: "inserted" + EVENT_KEY$6,
+ CLICK: "click" + EVENT_KEY$6,
+ FOCUSIN: "focusin" + EVENT_KEY$6,
+ FOCUSOUT: "focusout" + EVENT_KEY$6,
+ MOUSEENTER: "mouseenter" + EVENT_KEY$6,
+ MOUSELEAVE: "mouseleave" + EVENT_KEY$6
+ };
+ var ClassName$6 = {
+ FADE: 'fade',
+ SHOW: 'show'
+ };
+ var Selector$6 = {
+ TOOLTIP: '.tooltip',
+ TOOLTIP_INNER: '.tooltip-inner',
+ ARROW: '.arrow'
+ };
+ var Trigger = {
+ HOVER: 'hover',
+ FOCUS: 'focus',
+ CLICK: 'click',
+ MANUAL: 'manual'
+ };
+ /**
+ * ------------------------------------------------------------------------
+ * Class Definition
+ * ------------------------------------------------------------------------
+ */
+
+ var Tooltip =
+ /*#__PURE__*/
+ function () {
+ function Tooltip(element, config) {
+ if (typeof Popper === 'undefined') {
+ throw new TypeError('Bootstrap\'s tooltips require Popper.js (https://popper.js.org/)');
+ } // private
+
+
+ this._isEnabled = true;
+ this._timeout = 0;
+ this._hoverState = '';
+ this._activeTrigger = {};
+ this._popper = null; // Protected
+
+ this.element = element;
+ this.config = this._getConfig(config);
+ this.tip = null;
+
+ this._setListeners();
+ } // Getters
+
+
+ var _proto = Tooltip.prototype;
+
+ // Public
+ _proto.enable = function enable() {
+ this._isEnabled = true;
+ };
+
+ _proto.disable = function disable() {
+ this._isEnabled = false;
+ };
+
+ _proto.toggleEnabled = function toggleEnabled() {
+ this._isEnabled = !this._isEnabled;
+ };
+
+ _proto.toggle = function toggle(event) {
+ if (!this._isEnabled) {
+ return;
+ }
+
+ if (event) {
+ var dataKey = this.constructor.DATA_KEY;
+ var context = $(event.currentTarget).data(dataKey);
+
+ if (!context) {
+ context = new this.constructor(event.currentTarget, this._getDelegateConfig());
+ $(event.currentTarget).data(dataKey, context);
+ }
+
+ context._activeTrigger.click = !context._activeTrigger.click;
+
+ if (context._isWithActiveTrigger()) {
+ context._enter(null, context);
+ } else {
+ context._leave(null, context);
+ }
+ } else {
+ if ($(this.getTipElement()).hasClass(ClassName$6.SHOW)) {
+ this._leave(null, this);
+
+ return;
+ }
+
+ this._enter(null, this);
+ }
+ };
+
+ _proto.dispose = function dispose() {
+ clearTimeout(this._timeout);
+ $.removeData(this.element, this.constructor.DATA_KEY);
+ $(this.element).off(this.constructor.EVENT_KEY);
+ $(this.element).closest('.modal').off('hide.bs.modal', this._hideModalHandler);
+
+ if (this.tip) {
+ $(this.tip).remove();
+ }
+
+ this._isEnabled = null;
+ this._timeout = null;
+ this._hoverState = null;
+ this._activeTrigger = null;
+
+ if (this._popper) {
+ this._popper.destroy();
+ }
+
+ this._popper = null;
+ this.element = null;
+ this.config = null;
+ this.tip = null;
+ };
+
+ _proto.show = function show() {
+ var _this = this;
+
+ if ($(this.element).css('display') === 'none') {
+ throw new Error('Please use show on visible elements');
+ }
+
+ var showEvent = $.Event(this.constructor.Event.SHOW);
+
+ if (this.isWithContent() && this._isEnabled) {
+ $(this.element).trigger(showEvent);
+ var shadowRoot = Util.findShadowRoot(this.element);
+ var isInTheDom = $.contains(shadowRoot !== null ? shadowRoot : this.element.ownerDocument.documentElement, this.element);
+
+ if (showEvent.isDefaultPrevented() || !isInTheDom) {
+ return;
+ }
+
+ var tip = this.getTipElement();
+ var tipId = Util.getUID(this.constructor.NAME);
+ tip.setAttribute('id', tipId);
+ this.element.setAttribute('aria-describedby', tipId);
+ this.setContent();
+
+ if (this.config.animation) {
+ $(tip).addClass(ClassName$6.FADE);
+ }
+
+ var placement = typeof this.config.placement === 'function' ? this.config.placement.call(this, tip, this.element) : this.config.placement;
+
+ var attachment = this._getAttachment(placement);
+
+ this.addAttachmentClass(attachment);
+
+ var container = this._getContainer();
+
+ $(tip).data(this.constructor.DATA_KEY, this);
+
+ if (!$.contains(this.element.ownerDocument.documentElement, this.tip)) {
+ $(tip).appendTo(container);
+ }
+
+ $(this.element).trigger(this.constructor.Event.INSERTED);
+ this._popper = new Popper(this.element, tip, this._getPopperConfig(attachment));
+ $(tip).addClass(ClassName$6.SHOW); // If this is a touch-enabled device we add extra
+ // empty mouseover listeners to the body's immediate children;
+ // only needed because of broken event delegation on iOS
+ // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
+
+ if ('ontouchstart' in document.documentElement) {
+ $(document.body).children().on('mouseover', null, $.noop);
+ }
+
+ var complete = function complete() {
+ if (_this.config.animation) {
+ _this._fixTransition();
+ }
+
+ var prevHoverState = _this._hoverState;
+ _this._hoverState = null;
+ $(_this.element).trigger(_this.constructor.Event.SHOWN);
+
+ if (prevHoverState === HoverState.OUT) {
+ _this._leave(null, _this);
+ }
+ };
+
+ if ($(this.tip).hasClass(ClassName$6.FADE)) {
+ var transitionDuration = Util.getTransitionDurationFromElement(this.tip);
+ $(this.tip).one(Util.TRANSITION_END, complete).emulateTransitionEnd(transitionDuration);
+ } else {
+ complete();
+ }
+ }
+ };
+
+ _proto.hide = function hide(callback) {
+ var _this2 = this;
+
+ var tip = this.getTipElement();
+ var hideEvent = $.Event(this.constructor.Event.HIDE);
+
+ var complete = function complete() {
+ if (_this2._hoverState !== HoverState.SHOW && tip.parentNode) {
+ tip.parentNode.removeChild(tip);
+ }
+
+ _this2._cleanTipClass();
+
+ _this2.element.removeAttribute('aria-describedby');
+
+ $(_this2.element).trigger(_this2.constructor.Event.HIDDEN);
+
+ if (_this2._popper !== null) {
+ _this2._popper.destroy();
+ }
+
+ if (callback) {
+ callback();
+ }
+ };
+
+ $(this.element).trigger(hideEvent);
+
+ if (hideEvent.isDefaultPrevented()) {
+ return;
+ }
+
+ $(tip).removeClass(ClassName$6.SHOW); // If this is a touch-enabled device we remove the extra
+ // empty mouseover listeners we added for iOS support
+
+ if ('ontouchstart' in document.documentElement) {
+ $(document.body).children().off('mouseover', null, $.noop);
+ }
+
+ this._activeTrigger[Trigger.CLICK] = false;
+ this._activeTrigger[Trigger.FOCUS] = false;
+ this._activeTrigger[Trigger.HOVER] = false;
+
+ if ($(this.tip).hasClass(ClassName$6.FADE)) {
+ var transitionDuration = Util.getTransitionDurationFromElement(tip);
+ $(tip).one(Util.TRANSITION_END, complete).emulateTransitionEnd(transitionDuration);
+ } else {
+ complete();
+ }
+
+ this._hoverState = '';
+ };
+
+ _proto.update = function update() {
+ if (this._popper !== null) {
+ this._popper.scheduleUpdate();
+ }
+ } // Protected
+ ;
+
+ _proto.isWithContent = function isWithContent() {
+ return Boolean(this.getTitle());
+ };
+
+ _proto.addAttachmentClass = function addAttachmentClass(attachment) {
+ $(this.getTipElement()).addClass(CLASS_PREFIX + "-" + attachment);
+ };
+
+ _proto.getTipElement = function getTipElement() {
+ this.tip = this.tip || $(this.config.template)[0];
+ return this.tip;
+ };
+
+ _proto.setContent = function setContent() {
+ var tip = this.getTipElement();
+ this.setElementContent($(tip.querySelectorAll(Selector$6.TOOLTIP_INNER)), this.getTitle());
+ $(tip).removeClass(ClassName$6.FADE + " " + ClassName$6.SHOW);
+ };
+
+ _proto.setElementContent = function setElementContent($element, content) {
+ if (typeof content === 'object' && (content.nodeType || content.jquery)) {
+ // Content is a DOM node or a jQuery
+ if (this.config.html) {
+ if (!$(content).parent().is($element)) {
+ $element.empty().append(content);
+ }
+ } else {
+ $element.text($(content).text());
+ }
+
+ return;
+ }
+
+ if (this.config.html) {
+ if (this.config.sanitize) {
+ content = sanitizeHtml(content, this.config.whiteList, this.config.sanitizeFn);
+ }
+
+ $element.html(content);
+ } else {
+ $element.text(content);
+ }
+ };
+
+ _proto.getTitle = function getTitle() {
+ var title = this.element.getAttribute('data-original-title');
+
+ if (!title) {
+ title = typeof this.config.title === 'function' ? this.config.title.call(this.element) : this.config.title;
+ }
+
+ return title;
+ } // Private
+ ;
+
+ _proto._getPopperConfig = function _getPopperConfig(attachment) {
+ var _this3 = this;
+
+ var defaultBsConfig = {
+ placement: attachment,
+ modifiers: {
+ offset: this._getOffset(),
+ flip: {
+ behavior: this.config.fallbackPlacement
+ },
+ arrow: {
+ element: Selector$6.ARROW
+ },
+ preventOverflow: {
+ boundariesElement: this.config.boundary
+ }
+ },
+ onCreate: function onCreate(data) {
+ if (data.originalPlacement !== data.placement) {
+ _this3._handlePopperPlacementChange(data);
+ }
+ },
+ onUpdate: function onUpdate(data) {
+ return _this3._handlePopperPlacementChange(data);
+ }
+ };
+ return _objectSpread2({}, defaultBsConfig, {}, this.config.popperConfig);
+ };
+
+ _proto._getOffset = function _getOffset() {
+ var _this4 = this;
+
+ var offset = {};
+
+ if (typeof this.config.offset === 'function') {
+ offset.fn = function (data) {
+ data.offsets = _objectSpread2({}, data.offsets, {}, _this4.config.offset(data.offsets, _this4.element) || {});
+ return data;
+ };
+ } else {
+ offset.offset = this.config.offset;
+ }
+
+ return offset;
+ };
+
+ _proto._getContainer = function _getContainer() {
+ if (this.config.container === false) {
+ return document.body;
+ }
+
+ if (Util.isElement(this.config.container)) {
+ return $(this.config.container);
+ }
+
+ return $(document).find(this.config.container);
+ };
+
+ _proto._getAttachment = function _getAttachment(placement) {
+ return AttachmentMap$1[placement.toUpperCase()];
+ };
+
+ _proto._setListeners = function _setListeners() {
+ var _this5 = this;
+
+ var triggers = this.config.trigger.split(' ');
+ triggers.forEach(function (trigger) {
+ if (trigger === 'click') {
+ $(_this5.element).on(_this5.constructor.Event.CLICK, _this5.config.selector, function (event) {
+ return _this5.toggle(event);
+ });
+ } else if (trigger !== Trigger.MANUAL) {
+ var eventIn = trigger === Trigger.HOVER ? _this5.constructor.Event.MOUSEENTER : _this5.constructor.Event.FOCUSIN;
+ var eventOut = trigger === Trigger.HOVER ? _this5.constructor.Event.MOUSELEAVE : _this5.constructor.Event.FOCUSOUT;
+ $(_this5.element).on(eventIn, _this5.config.selector, function (event) {
+ return _this5._enter(event);
+ }).on(eventOut, _this5.config.selector, function (event) {
+ return _this5._leave(event);
+ });
+ }
+ });
+
+ this._hideModalHandler = function () {
+ if (_this5.element) {
+ _this5.hide();
+ }
+ };
+
+ $(this.element).closest('.modal').on('hide.bs.modal', this._hideModalHandler);
+
+ if (this.config.selector) {
+ this.config = _objectSpread2({}, this.config, {
+ trigger: 'manual',
+ selector: ''
+ });
+ } else {
+ this._fixTitle();
+ }
+ };
+
+ _proto._fixTitle = function _fixTitle() {
+ var titleType = typeof this.element.getAttribute('data-original-title');
+
+ if (this.element.getAttribute('title') || titleType !== 'string') {
+ this.element.setAttribute('data-original-title', this.element.getAttribute('title') || '');
+ this.element.setAttribute('title', '');
+ }
+ };
+
+ _proto._enter = function _enter(event, context) {
+ var dataKey = this.constructor.DATA_KEY;
+ context = context || $(event.currentTarget).data(dataKey);
+
+ if (!context) {
+ context = new this.constructor(event.currentTarget, this._getDelegateConfig());
+ $(event.currentTarget).data(dataKey, context);
+ }
+
+ if (event) {
+ context._activeTrigger[event.type === 'focusin' ? Trigger.FOCUS : Trigger.HOVER] = true;
+ }
+
+ if ($(context.getTipElement()).hasClass(ClassName$6.SHOW) || context._hoverState === HoverState.SHOW) {
+ context._hoverState = HoverState.SHOW;
+ return;
+ }
+
+ clearTimeout(context._timeout);
+ context._hoverState = HoverState.SHOW;
+
+ if (!context.config.delay || !context.config.delay.show) {
+ context.show();
+ return;
+ }
+
+ context._timeout = setTimeout(function () {
+ if (context._hoverState === HoverState.SHOW) {
+ context.show();
+ }
+ }, context.config.delay.show);
+ };
+
+ _proto._leave = function _leave(event, context) {
+ var dataKey = this.constructor.DATA_KEY;
+ context = context || $(event.currentTarget).data(dataKey);
+
+ if (!context) {
+ context = new this.constructor(event.currentTarget, this._getDelegateConfig());
+ $(event.currentTarget).data(dataKey, context);
+ }
+
+ if (event) {
+ context._activeTrigger[event.type === 'focusout' ? Trigger.FOCUS : Trigger.HOVER] = false;
+ }
+
+ if (context._isWithActiveTrigger()) {
+ return;
+ }
+
+ clearTimeout(context._timeout);
+ context._hoverState = HoverState.OUT;
+
+ if (!context.config.delay || !context.config.delay.hide) {
+ context.hide();
+ return;
+ }
+
+ context._timeout = setTimeout(function () {
+ if (context._hoverState === HoverState.OUT) {
+ context.hide();
+ }
+ }, context.config.delay.hide);
+ };
+
+ _proto._isWithActiveTrigger = function _isWithActiveTrigger() {
+ for (var trigger in this._activeTrigger) {
+ if (this._activeTrigger[trigger]) {
+ return true;
+ }
+ }
+
+ return false;
+ };
+
+ _proto._getConfig = function _getConfig(config) {
+ var dataAttributes = $(this.element).data();
+ Object.keys(dataAttributes).forEach(function (dataAttr) {
+ if (DISALLOWED_ATTRIBUTES.indexOf(dataAttr) !== -1) {
+ delete dataAttributes[dataAttr];
+ }
+ });
+ config = _objectSpread2({}, this.constructor.Default, {}, dataAttributes, {}, typeof config === 'object' && config ? config : {});
+
+ if (typeof config.delay === 'number') {
+ config.delay = {
+ show: config.delay,
+ hide: config.delay
+ };
+ }
+
+ if (typeof config.title === 'number') {
+ config.title = config.title.toString();
+ }
+
+ if (typeof config.content === 'number') {
+ config.content = config.content.toString();
+ }
+
+ Util.typeCheckConfig(NAME$6, config, this.constructor.DefaultType);
+
+ if (config.sanitize) {
+ config.template = sanitizeHtml(config.template, config.whiteList, config.sanitizeFn);
+ }
+
+ return config;
+ };
+
+ _proto._getDelegateConfig = function _getDelegateConfig() {
+ var config = {};
+
+ if (this.config) {
+ for (var key in this.config) {
+ if (this.constructor.Default[key] !== this.config[key]) {
+ config[key] = this.config[key];
+ }
+ }
+ }
+
+ return config;
+ };
+
+ _proto._cleanTipClass = function _cleanTipClass() {
+ var $tip = $(this.getTipElement());
+ var tabClass = $tip.attr('class').match(BSCLS_PREFIX_REGEX);
+
+ if (tabClass !== null && tabClass.length) {
+ $tip.removeClass(tabClass.join(''));
+ }
+ };
+
+ _proto._handlePopperPlacementChange = function _handlePopperPlacementChange(popperData) {
+ var popperInstance = popperData.instance;
+ this.tip = popperInstance.popper;
+
+ this._cleanTipClass();
+
+ this.addAttachmentClass(this._getAttachment(popperData.placement));
+ };
+
+ _proto._fixTransition = function _fixTransition() {
+ var tip = this.getTipElement();
+ var initConfigAnimation = this.config.animation;
+
+ if (tip.getAttribute('x-placement') !== null) {
+ return;
+ }
+
+ $(tip).removeClass(ClassName$6.FADE);
+ this.config.animation = false;
+ this.hide();
+ this.show();
+ this.config.animation = initConfigAnimation;
+ } // Static
+ ;
+
+ Tooltip._jQueryInterface = function _jQueryInterface(config) {
+ return this.each(function () {
+ var data = $(this).data(DATA_KEY$6);
+
+ var _config = typeof config === 'object' && config;
+
+ if (!data && /dispose|hide/.test(config)) {
+ return;
+ }
+
+ if (!data) {
+ data = new Tooltip(this, _config);
+ $(this).data(DATA_KEY$6, data);
+ }
+
+ if (typeof config === 'string') {
+ if (typeof data[config] === 'undefined') {
+ throw new TypeError("No method named \"" + config + "\"");
+ }
+
+ data[config]();
+ }
+ });
+ };
+
+ _createClass(Tooltip, null, [{
+ key: "VERSION",
+ get: function get() {
+ return VERSION$6;
+ }
+ }, {
+ key: "Default",
+ get: function get() {
+ return Default$4;
+ }
+ }, {
+ key: "NAME",
+ get: function get() {
+ return NAME$6;
+ }
+ }, {
+ key: "DATA_KEY",
+ get: function get() {
+ return DATA_KEY$6;
+ }
+ }, {
+ key: "Event",
+ get: function get() {
+ return Event$6;
+ }
+ }, {
+ key: "EVENT_KEY",
+ get: function get() {
+ return EVENT_KEY$6;
+ }
+ }, {
+ key: "DefaultType",
+ get: function get() {
+ return DefaultType$4;
+ }
+ }]);
+
+ return Tooltip;
+ }();
+ /**
+ * ------------------------------------------------------------------------
+ * jQuery
+ * ------------------------------------------------------------------------
+ */
+
+
+ $.fn[NAME$6] = Tooltip._jQueryInterface;
+ $.fn[NAME$6].Constructor = Tooltip;
+
+ $.fn[NAME$6].noConflict = function () {
+ $.fn[NAME$6] = JQUERY_NO_CONFLICT$6;
+ return Tooltip._jQueryInterface;
+ };
+
+ /**
+ * ------------------------------------------------------------------------
+ * Constants
+ * ------------------------------------------------------------------------
+ */
+
+ var NAME$7 = 'popover';
+ var VERSION$7 = '4.4.1';
+ var DATA_KEY$7 = 'bs.popover';
+ var EVENT_KEY$7 = "." + DATA_KEY$7;
+ var JQUERY_NO_CONFLICT$7 = $.fn[NAME$7];
+ var CLASS_PREFIX$1 = 'bs-popover';
+ var BSCLS_PREFIX_REGEX$1 = new RegExp("(^|\\s)" + CLASS_PREFIX$1 + "\\S+", 'g');
+
+ var Default$5 = _objectSpread2({}, Tooltip.Default, {
+ placement: 'right',
+ trigger: 'click',
+ content: '',
+ template: ''
+ });
+
+ var DefaultType$5 = _objectSpread2({}, Tooltip.DefaultType, {
+ content: '(string|element|function)'
+ });
+
+ var ClassName$7 = {
+ FADE: 'fade',
+ SHOW: 'show'
+ };
+ var Selector$7 = {
+ TITLE: '.popover-header',
+ CONTENT: '.popover-body'
+ };
+ var Event$7 = {
+ HIDE: "hide" + EVENT_KEY$7,
+ HIDDEN: "hidden" + EVENT_KEY$7,
+ SHOW: "show" + EVENT_KEY$7,
+ SHOWN: "shown" + EVENT_KEY$7,
+ INSERTED: "inserted" + EVENT_KEY$7,
+ CLICK: "click" + EVENT_KEY$7,
+ FOCUSIN: "focusin" + EVENT_KEY$7,
+ FOCUSOUT: "focusout" + EVENT_KEY$7,
+ MOUSEENTER: "mouseenter" + EVENT_KEY$7,
+ MOUSELEAVE: "mouseleave" + EVENT_KEY$7
+ };
+ /**
+ * ------------------------------------------------------------------------
+ * Class Definition
+ * ------------------------------------------------------------------------
+ */
+
+ var Popover =
+ /*#__PURE__*/
+ function (_Tooltip) {
+ _inheritsLoose(Popover, _Tooltip);
+
+ function Popover() {
+ return _Tooltip.apply(this, arguments) || this;
+ }
+
+ var _proto = Popover.prototype;
+
+ // Overrides
+ _proto.isWithContent = function isWithContent() {
+ return this.getTitle() || this._getContent();
+ };
+
+ _proto.addAttachmentClass = function addAttachmentClass(attachment) {
+ $(this.getTipElement()).addClass(CLASS_PREFIX$1 + "-" + attachment);
+ };
+
+ _proto.getTipElement = function getTipElement() {
+ this.tip = this.tip || $(this.config.template)[0];
+ return this.tip;
+ };
+
+ _proto.setContent = function setContent() {
+ var $tip = $(this.getTipElement()); // We use append for html objects to maintain js events
+
+ this.setElementContent($tip.find(Selector$7.TITLE), this.getTitle());
+
+ var content = this._getContent();
+
+ if (typeof content === 'function') {
+ content = content.call(this.element);
+ }
+
+ this.setElementContent($tip.find(Selector$7.CONTENT), content);
+ $tip.removeClass(ClassName$7.FADE + " " + ClassName$7.SHOW);
+ } // Private
+ ;
+
+ _proto._getContent = function _getContent() {
+ return this.element.getAttribute('data-content') || this.config.content;
+ };
+
+ _proto._cleanTipClass = function _cleanTipClass() {
+ var $tip = $(this.getTipElement());
+ var tabClass = $tip.attr('class').match(BSCLS_PREFIX_REGEX$1);
+
+ if (tabClass !== null && tabClass.length > 0) {
+ $tip.removeClass(tabClass.join(''));
+ }
+ } // Static
+ ;
+
+ Popover._jQueryInterface = function _jQueryInterface(config) {
+ return this.each(function () {
+ var data = $(this).data(DATA_KEY$7);
+
+ var _config = typeof config === 'object' ? config : null;
+
+ if (!data && /dispose|hide/.test(config)) {
+ return;
+ }
+
+ if (!data) {
+ data = new Popover(this, _config);
+ $(this).data(DATA_KEY$7, data);
+ }
+
+ if (typeof config === 'string') {
+ if (typeof data[config] === 'undefined') {
+ throw new TypeError("No method named \"" + config + "\"");
+ }
+
+ data[config]();
+ }
+ });
+ };
+
+ _createClass(Popover, null, [{
+ key: "VERSION",
+ // Getters
+ get: function get() {
+ return VERSION$7;
+ }
+ }, {
+ key: "Default",
+ get: function get() {
+ return Default$5;
+ }
+ }, {
+ key: "NAME",
+ get: function get() {
+ return NAME$7;
+ }
+ }, {
+ key: "DATA_KEY",
+ get: function get() {
+ return DATA_KEY$7;
+ }
+ }, {
+ key: "Event",
+ get: function get() {
+ return Event$7;
+ }
+ }, {
+ key: "EVENT_KEY",
+ get: function get() {
+ return EVENT_KEY$7;
+ }
+ }, {
+ key: "DefaultType",
+ get: function get() {
+ return DefaultType$5;
+ }
+ }]);
+
+ return Popover;
+ }(Tooltip);
+ /**
+ * ------------------------------------------------------------------------
+ * jQuery
+ * ------------------------------------------------------------------------
+ */
+
+
+ $.fn[NAME$7] = Popover._jQueryInterface;
+ $.fn[NAME$7].Constructor = Popover;
+
+ $.fn[NAME$7].noConflict = function () {
+ $.fn[NAME$7] = JQUERY_NO_CONFLICT$7;
+ return Popover._jQueryInterface;
+ };
+
+ /**
+ * ------------------------------------------------------------------------
+ * Constants
+ * ------------------------------------------------------------------------
+ */
+
+ var NAME$8 = 'scrollspy';
+ var VERSION$8 = '4.4.1';
+ var DATA_KEY$8 = 'bs.scrollspy';
+ var EVENT_KEY$8 = "." + DATA_KEY$8;
+ var DATA_API_KEY$6 = '.data-api';
+ var JQUERY_NO_CONFLICT$8 = $.fn[NAME$8];
+ var Default$6 = {
+ offset: 10,
+ method: 'auto',
+ target: ''
+ };
+ var DefaultType$6 = {
+ offset: 'number',
+ method: 'string',
+ target: '(string|element)'
+ };
+ var Event$8 = {
+ ACTIVATE: "activate" + EVENT_KEY$8,
+ SCROLL: "scroll" + EVENT_KEY$8,
+ LOAD_DATA_API: "load" + EVENT_KEY$8 + DATA_API_KEY$6
+ };
+ var ClassName$8 = {
+ DROPDOWN_ITEM: 'dropdown-item',
+ DROPDOWN_MENU: 'dropdown-menu',
+ ACTIVE: 'active'
+ };
+ var Selector$8 = {
+ DATA_SPY: '[data-spy="scroll"]',
+ ACTIVE: '.active',
+ NAV_LIST_GROUP: '.nav, .list-group',
+ NAV_LINKS: '.nav-link',
+ NAV_ITEMS: '.nav-item',
+ LIST_ITEMS: '.list-group-item',
+ DROPDOWN: '.dropdown',
+ DROPDOWN_ITEMS: '.dropdown-item',
+ DROPDOWN_TOGGLE: '.dropdown-toggle'
+ };
+ var OffsetMethod = {
+ OFFSET: 'offset',
+ POSITION: 'position'
+ };
+ /**
+ * ------------------------------------------------------------------------
+ * Class Definition
+ * ------------------------------------------------------------------------
+ */
+
+ var ScrollSpy =
+ /*#__PURE__*/
+ function () {
+ function ScrollSpy(element, config) {
+ var _this = this;
+
+ this._element = element;
+ this._scrollElement = element.tagName === 'BODY' ? window : element;
+ this._config = this._getConfig(config);
+ this._selector = this._config.target + " " + Selector$8.NAV_LINKS + "," + (this._config.target + " " + Selector$8.LIST_ITEMS + ",") + (this._config.target + " " + Selector$8.DROPDOWN_ITEMS);
+ this._offsets = [];
+ this._targets = [];
+ this._activeTarget = null;
+ this._scrollHeight = 0;
+ $(this._scrollElement).on(Event$8.SCROLL, function (event) {
+ return _this._process(event);
+ });
+ this.refresh();
+
+ this._process();
+ } // Getters
+
+
+ var _proto = ScrollSpy.prototype;
+
+ // Public
+ _proto.refresh = function refresh() {
+ var _this2 = this;
+
+ var autoMethod = this._scrollElement === this._scrollElement.window ? OffsetMethod.OFFSET : OffsetMethod.POSITION;
+ var offsetMethod = this._config.method === 'auto' ? autoMethod : this._config.method;
+ var offsetBase = offsetMethod === OffsetMethod.POSITION ? this._getScrollTop() : 0;
+ this._offsets = [];
+ this._targets = [];
+ this._scrollHeight = this._getScrollHeight();
+ var targets = [].slice.call(document.querySelectorAll(this._selector));
+ targets.map(function (element) {
+ var target;
+ var targetSelector = Util.getSelectorFromElement(element);
+
+ if (targetSelector) {
+ target = document.querySelector(targetSelector);
+ }
+
+ if (target) {
+ var targetBCR = target.getBoundingClientRect();
+
+ if (targetBCR.width || targetBCR.height) {
+ // TODO (fat): remove sketch reliance on jQuery position/offset
+ return [$(target)[offsetMethod]().top + offsetBase, targetSelector];
+ }
+ }
+
+ return null;
+ }).filter(function (item) {
+ return item;
+ }).sort(function (a, b) {
+ return a[0] - b[0];
+ }).forEach(function (item) {
+ _this2._offsets.push(item[0]);
+
+ _this2._targets.push(item[1]);
+ });
+ };
+
+ _proto.dispose = function dispose() {
+ $.removeData(this._element, DATA_KEY$8);
+ $(this._scrollElement).off(EVENT_KEY$8);
+ this._element = null;
+ this._scrollElement = null;
+ this._config = null;
+ this._selector = null;
+ this._offsets = null;
+ this._targets = null;
+ this._activeTarget = null;
+ this._scrollHeight = null;
+ } // Private
+ ;
+
+ _proto._getConfig = function _getConfig(config) {
+ config = _objectSpread2({}, Default$6, {}, typeof config === 'object' && config ? config : {});
+
+ if (typeof config.target !== 'string') {
+ var id = $(config.target).attr('id');
+
+ if (!id) {
+ id = Util.getUID(NAME$8);
+ $(config.target).attr('id', id);
+ }
+
+ config.target = "#" + id;
+ }
+
+ Util.typeCheckConfig(NAME$8, config, DefaultType$6);
+ return config;
+ };
+
+ _proto._getScrollTop = function _getScrollTop() {
+ return this._scrollElement === window ? this._scrollElement.pageYOffset : this._scrollElement.scrollTop;
+ };
+
+ _proto._getScrollHeight = function _getScrollHeight() {
+ return this._scrollElement.scrollHeight || Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
+ };
+
+ _proto._getOffsetHeight = function _getOffsetHeight() {
+ return this._scrollElement === window ? window.innerHeight : this._scrollElement.getBoundingClientRect().height;
+ };
+
+ _proto._process = function _process() {
+ var scrollTop = this._getScrollTop() + this._config.offset;
+
+ var scrollHeight = this._getScrollHeight();
+
+ var maxScroll = this._config.offset + scrollHeight - this._getOffsetHeight();
+
+ if (this._scrollHeight !== scrollHeight) {
+ this.refresh();
+ }
+
+ if (scrollTop >= maxScroll) {
+ var target = this._targets[this._targets.length - 1];
+
+ if (this._activeTarget !== target) {
+ this._activate(target);
+ }
+
+ return;
+ }
+
+ if (this._activeTarget && scrollTop < this._offsets[0] && this._offsets[0] > 0) {
+ this._activeTarget = null;
+
+ this._clear();
+
+ return;
+ }
+
+ var offsetLength = this._offsets.length;
+
+ for (var i = offsetLength; i--;) {
+ var isActiveTarget = this._activeTarget !== this._targets[i] && scrollTop >= this._offsets[i] && (typeof this._offsets[i + 1] === 'undefined' || scrollTop < this._offsets[i + 1]);
+
+ if (isActiveTarget) {
+ this._activate(this._targets[i]);
+ }
+ }
+ };
+
+ _proto._activate = function _activate(target) {
+ this._activeTarget = target;
+
+ this._clear();
+
+ var queries = this._selector.split(',').map(function (selector) {
+ return selector + "[data-target=\"" + target + "\"]," + selector + "[href=\"" + target + "\"]";
+ });
+
+ var $link = $([].slice.call(document.querySelectorAll(queries.join(','))));
+
+ if ($link.hasClass(ClassName$8.DROPDOWN_ITEM)) {
+ $link.closest(Selector$8.DROPDOWN).find(Selector$8.DROPDOWN_TOGGLE).addClass(ClassName$8.ACTIVE);
+ $link.addClass(ClassName$8.ACTIVE);
+ } else {
+ // Set triggered link as active
+ $link.addClass(ClassName$8.ACTIVE); // Set triggered links parents as active
+ // With both and